Linux FFmpeg深度解析之av_parser_parse2调用流程与实战(二十二)
发布时间:2026/11/3 10:36:21 锦皓数字建站
`)
简介CSDN博客专家、《Android系统多媒体进阶实战》作者博主新书推荐《Android系统多媒体进阶实战》Android Audio工程师专栏地址Audio工程师进阶系列【原创干货持续更新中……】Android多媒体专栏地址多媒体系统工程师系列【原创干货持续更新中……】专题一 二AAOS车载系统AOSP14系统攻城狮入门视频实战课专题三Android14 Binder之HIDL与AIDL通信实战课专题四Android15快速自定义与集成音效实战课专题五Android15音频策略实战课专题六Android15音频性能实战课(无声/杂音/断音/爆音实战案例)人生格言人生从来没有捷径只有行动才是治疗恐惧和懒惰的唯一良药.更多原创,欢迎关注Android系统攻城狮点击获取二维码文章目录1.前言2.应用场景和用法2.1拆分连续压缩码流2.2函数原型与返回语义3.调用流程剖析3.1记录输入位置并调用解析器实现3.2更新帧偏移并返回消耗量4.实战案例4.1准备裸H.264输入和解析器4.2循环解析并冲刷文件尾部4.3完整代码5.总结1.前言本篇目的理解av_parser_parse2()如何把任意输入字节块交给对应解析器分析输入消耗量、输出帧数据和时间戳状态之间的关系。裸码流的读取块不一定与压缩帧边界一致。av_parser_parse2()接收一段输入数据调用解析器识别已完成的帧并报告本次消耗的输入字节数。它负责解析码流边界不负责解码压缩帧。输入压缩字节块av_parser_parse2编码格式解析器输出帧指针和大小返回已消耗字节数2.应用场景和用法2.1拆分连续压缩码流当应用从文件或网络读到的字节块包含半帧、多帧或跨帧数据时需要逐次调用av_parser_parse2()。每次调用都要根据返回的消耗字节数推进输入指针并在输出大小大于0时处理一帧。使用场景输入特点解析器作用裸H.264或MPEG视频读取块可能包含多个访问单元按码流边界输出完整帧连续压缩音频一次读取可能只包含部分音频帧在多次调用间保留解析状态携带时间戳的码流输入块具有PTS、DTS和文件位置记录输入区间并关联帧时间戳文件结束解析器内部可能还有未输出数据以零长度输入冲刷解析器如果输入本身已经是完整压缩包通常直接把包交给解码器即可。不要把解析器输出大小与本次输入消耗量混为一谈前者通过poutbuf_size返回后者由函数返回值表示。2.2函数原型与返回语义函数原型如下intav_parser_parse2(AVCodecParserContext*s,AVCodecContext*avctx,uint8_t**poutbuf,int*poutbuf_size,constuint8_t*buf,intbuf_size,int64_tpts,int64_tdts,int64_tpos);参数含义s由av_parser_init()创建的解析器上下文avctxcodec_id必须与解析器支持的编码格式匹配poutbuf、poutbuf_size接收解析出的帧数据指针和大小buf、buf_size当前输入字节块输入末尾需提供AV_INPUT_BUFFER_PADDING_SIZE个填充字节pts、dts、pos当前输入块对应的时间戳和字节位置函数返回本次消耗的输入字节数不是错误码也不是输出帧大小。若*poutbuf_size为0当前调用没有形成完整帧*poutbuf会被设为NULL。文件结束时传入buf_size0解析器可以输出仍保留在内部的最后一帧。最小调用方式如下uint8_t*parsed_dataNULL;intparsed_size0;intconsumedav_parser_parse2(parser,avctx,parsed_data,parsed_size,input_data,input_size,pts,dts,stream_pos);/* consumed用于推进输入parsed_size大于0时处理一帧 */解析器返回的数据指针不应直接释放。它可能指向输入缓冲区也可能指向解析器内部缓冲区需要跨越下一次解析调用保留时应复制到调用者管理的AVPacket存储中。3.调用流程剖析3.1记录输入位置并调用解析器实现函数先断言AVCodecContext.codec_id与解析器支持的编码ID匹配。首次调用时它使用pos初始化当前和下一帧偏移每次输入还会把字节区间及对应的PTS、DTS、文件位置记录到解析器的时间戳队列。随后函数调用s-parser-parser_parse()处理输入。若buf_size为0函数会传入内部的填充数据以通知解析器冲刷剩余内容。输入字节及时间信息检查codec_id匹配记录输入区间和时间戳调用parser_parse得到解析帧和消耗量3.2更新帧偏移并返回消耗量解析器识别出完整帧后av_parser_parse2()更新当前帧偏移和下一帧偏移并设置fetch_timestamp让下一次调用继续获取对应时间戳。对于视频它还会在AVCodecContext未设置相关字段时补充解析得到的场序、宽高等信息。如果本次没有输出帧函数把输出指针设为NULL。无论是否输出帧输入推进都应依据返回的消耗字节数完成当文件读完后还要用零长度输入执行冲刷。4.实战案例4.1准备裸H.264输入和解析器案例读取一个裸H.264文件将整个文件加载到带零填充的缓冲区。AVCodecContext只用于提供匹配的编码ID和视频媒体类型本例不打开解码器也不解码帧。4.2循环解析并冲刷文件尾部循环调用av_parser_parse2()按返回值推进输入指针和文件位置。每当parsed_size大于0帧计数加一。若一次调用既没有消耗输入也没有输出帧程序停止避免陷入无进展循环。所有输入处理完后以零长度输入冲刷解析器。4.3完整代码#includelimits.h#includestdio.h#includelibavcodec/avcodec.h#includelibavutil/mem.h#defineINPUT_PADDINGAV_INPUT_BUFFER_PADDING_SIZEintmain(intargc,char**argv){constAVCodec*codecNULL;AVCodecContext*avctxNULL;AVCodecParserContext*parserNULL;FILE*fileNULL;uint8_t*input_bufferNULL;uint8_t*data;uint8_t*parsed_dataNULL;longfile_size;intremaining;intparsed_size0;intparsed_frames0;intret1;int64_tpos0;if(argc!2)gotodone;/* 查找H.264解码器信息用其编码ID创建匹配的上下文 */codecavcodec_find_decoder(AV_CODEC_ID_H264);if(codecNULL)gotodone;avctxavcodec_alloc_context3(codec);if(avctxNULL)gotodone;/* 创建H.264解析器当前构建缺少解析器时返回失败 */parserav_parser_init(codec-id);if(parserNULL)gotodone;filefopen(argv[1],rb);if(fileNULL)gotodone;if(fseek(file,0,SEEK_END)!0)gotodone;file_sizeftell(file);if(file_size0||file_sizeINT_MAX-INPUT_PADDING)gotodone;if(fseek(file,0,SEEK_SET)!0)gotodone;/* 解析器要求有效输入末尾保留指定长度的零填充区 */input_bufferav_mallocz((size_t)file_sizeINPUT_PADDING);if(input_bufferNULL)gotodone;if(fread(input_buffer,1,file_size,file)!(size_t)file_size)gotodone;datainput_buffer;remaining(int)file_size;/* 返回值是已消耗输入字节数输出帧大小由parsed_size给出 */while(remaining0){intconsumed;parsed_dataNULL;parsed_size0;consumedav_parser_parse2(parser,avctx,parsed_data,parsed_size,data,remaining,AV_NOPTS_VALUE,AV_NOPTS_VALUE,pos);if(parsed_size0)parsed_frames;if(consumed0parsed_size0)gotodone;dataconsumed;remaining-consumed;posconsumed;}/* 以零长度输入通知解析器处理文件尾部缓存的数据 */for(;;){intconsumed;parsed_dataNULL;parsed_size0;consumedav_parser_parse2(parser,avctx,parsed_data,parsed_size,NULL,0,AV_NOPTS_VALUE,AV_NOPTS_VALUE,pos);if(parsed_size0)break;parsed_frames;}/* 至少得到一帧解析结果才认为本案例完成 */if(parsed_frames0)ret0;done:av_free(input_buffer);if(file!NULL)fclose(file);av_parser_close(parser);avcodec_free_context(avctx);returnret;}5.总结av_parser_parse2()把输入字节交给匹配的解析器返回本次消耗量并通过输出参数提供已识别的压缩帧。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。