FFmpeg 音视频开发笔记(一):H.264 解码为 YUV
1. 看了一下雷霄哥的代码自己用ai学习一版h.264转换为.yuv文件的代码。h.264代码生成可以使用ffmpeg的命令行比如ffmpeg -i input.mp4 -c:v libx264 -s 1280x720 -an output.h264-c:v libx264 使用 H.264 编码器-an 不要音频-s 1280x720 分辨率-b:v 2M 视频码率-r 25 帧率-d 10 时长秒2. 我使用了ffplay对.yuv文件进行播放代码ffplay -video_size 1280x720 -pix_fmt yuv420p output.yuv遇到了几个问题第一个问题就是如果你h264的分辨率是1280x720 如果你设置ffplay为 640x480但实际视频可能是别的尺寸。就会出现闪烁因为播放分辨率不对3. 实现代码#include stdio.h #define __STDC_CONSTANT_MACROS #ifdef __cplusplus extern C { #endif #include libavcodec/avcodec.h #include libavutil/pixdesc.h #ifdef __cplusplus } #endif // 解决 FF_INPUT_BUFFER_PADDING_SIZE 未定义的问题 #ifndef FF_INPUT_BUFFER_PADDING_SIZE #define FF_INPUT_BUFFER_PADDING_SIZE 64 #endif #define TEST_H264 1 #define TEST_HEVC 0 int main(int argc, char *argv[]){ #if TEST_HEVC enum AVCodecID codec_id AV_CODEC_ID_HEVC; const char* input_file test.hevc; #elif TEST_H264 enum AVCodecID codec_id AV_CODEC_ID_H264; const char* input_file test.h264; #endif const char* output_file output.yuv; const AVCodec* codec avcodec_find_decoder(codec_id); if(!codec){ fprintf(stderr, Codec not found\n); return -1; } AVCodecContext* codec_ctx avcodec_alloc_context3(codec); if(!codec_ctx){ fprintf(stderr, Codec context allocation failed\n); return -1; } if(avcodec_open2(codec_ctx,codec,NULL) 0){ fprintf(stderr, Codec open failed\n); return -1; } // 5. 初始化解析器 AVCodecParserContext* parser_ctx av_parser_init(codec_id); if(!parser_ctx){ fprintf(stderr, Parser context initialization failed\n); return -1; } FILE* in_file fopen(input_file,rb); if(!in_file){ fprintf(stderr, Input file open failed\n); av_parser_close(parser_ctx); return -1; } FILE* out_file fopen(output_file,wb); if(!out_file){ fprintf(stderr, Output file open failed\n); fclose(in_file); av_parser_close(parser_ctx); avcodec_free_context(codec_ctx); return -1; } AVFrame* frame av_frame_alloc(); AVPacket* pkt av_packet_alloc(); if(!frame || !pkt){ fprintf(stderr, Frame or packet allocation failed\n); if(frame) av_frame_free(frame); if(pkt) av_packet_free(pkt); fclose(in_file); fclose(out_file); av_parser_close(parser_ctx); avcodec_free_context(codec_ctx); return -1; } const size_t buf_size 4096; uint8_t* buffer (uint8_t*)malloc(buf_size AV_INPUT_BUFFER_PADDING_SIZE); if(!buffer){ fprintf(stderr, Buffer allocation failed\n); av_frame_free(frame); av_packet_free(pkt); fclose(in_file); fclose(out_file); av_parser_close(parser_ctx); avcodec_free_context(codec_ctx); return -1; } memset(buffer,0,buf_size AV_INPUT_BUFFER_PADDING_SIZE); int first_frame 1; int frame_count 0; int write_count 0; int ret 0; while(1){ size_t data_size fread(buffer,1,buf_size,in_file); if(data_size 0) { break; } uint8_t* cur_ptr buffer; size_t cur_size data_size; while(cur_size 0){ int len av_parser_parse2(parser_ctx,codec_ctx,pkt-data,pkt-size, cur_ptr,(int)cur_size,AV_NOPTS_VALUE,AV_NOPTS_VALUE,AV_NOPTS_VALUE); cur_ptr len; cur_size - len; if(pkt-size 0) { continue; } printf([Packet] size:%d\n,pkt-size); switch(parser_ctx-pict_type){ case AV_PICTURE_TYPE_I: printf([Packet] I frame\n); break; case AV_PICTURE_TYPE_P: printf([Packet] P frame\n); break; case AV_PICTURE_TYPE_B: printf([Packet] B frame\n); break; default: printf([Packet] Unknown frame\n); break; } printf(number: %d\n,parser_ctx-output_picture_number); int ret avcodec_send_packet(codec_ctx,pkt); if(ret 0 ret ! AVERROR(EAGAIN) ret ! AVERROR_EOF){ fprintf(stderr, Error sending packet to decoder: %d\n,ret); break; } while(1){ ret avcodec_receive_frame(codec_ctx,frame); if(ret AVERROR(EAGAIN) || ret AVERROR_EOF){ break; }else if(ret 0){ // const char *err_str av_err2str(ret); fprintf(stderr, 冲刷时出错: %d\n, ret); break; } if(first_frame){ printf(解码器: %s\n,codec_ctx-codec-long_name); printf([Frame] width:%d, height:%d, format:%s\n, frame-width,frame-height,av_get_pix_fmt_name((AVPixelFormat)frame-format)); printf([Frame] linesize: Y%d, U%d, V%d\n, frame-linesize[0], frame-linesize[1], frame-linesize[2]); first_frame 0; } if(frame-format AV_PIX_FMT_YUV420P){ for(int i 0;i frame-height;i){ fwrite(frame-data[0]i*frame-linesize[0],1,frame-width,out_file); } for(int i 0;i frame-height/2;i){ fwrite(frame-data[1]i*frame-linesize[1],1,frame-width/2,out_file); } for(int i 0;i frame-height/2;i){ fwrite(frame-data[2]i*frame-linesize[2],1,frame-width/2,out_file); } write_count; }else{ fprintf(stderr, Unsupported pixel format: %s\n,av_get_pix_fmt_name((AVPixelFormat)frame-format)); } frame_count; printf([Frame] number: %d\n,frame_count); } } } avcodec_send_packet(codec_ctx,NULL); while(1){ ret avcodec_receive_frame(codec_ctx,frame); if(ret AVERROR_EOF) break; if(ret 0){ fprintf(stderr, 冲刷时出错: %d\n, ret); break; } if(frame-format AV_PIX_FMT_YUV420P){ for(int i 0;i frame-height;i){ fwrite(frame-data[0]i*frame-linesize[0],1,frame-width,out_file); } for(int i 0;i frame-height/2;i){ fwrite(frame-data[1]i*frame-linesize[1],1,frame-width/2,out_file); } for(int i 0;i frame-height/2;i){ fwrite(frame-data[2]i*frame-linesize[2],1,frame-width/2,out_file); } write_count; } frame_count; printf([Frame] number: %d\n,frame_count); } printf(解码完成共解码%d帧写入%d帧\n,frame_count, write_count); // 11. 释放资源 free(buffer); av_packet_free(pkt); av_frame_free(frame); fclose(out_file); fclose(in_file); av_parser_close(parser_ctx); avcodec_free_context(codec_ctx); return 0; }