news 2026/11/5 5:28:09

Linux FFmpeg深度解析之avcodec_receive_packet调用流程与实战(二十七)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux FFmpeg深度解析之avcodec_receive_packet调用流程与实战(二十七)

简介: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区分EAGAIN与EOF
      • 🌻4.实战案例
        • 4.1准备编码器输出循环
        • 4.2读取包并处理背压状态
        • 4.3完整代码
      • 🌻5.总结

🌻1.前言

本篇目的:理解avcodec_receive_packet()如何从编码器读取压缩数据,分析内部包缓存、编码器输出和调用者持有的AVPacket之间的关系,并通过收包循环验证编码输出的读取方式。

编码器接收原始AVFrame后,不一定马上输出压缩包。编码器可能积累输入,执行帧重排序,或等待更多数据。avcodec_receive_packet()负责读取当前已经可用的编码结果。

成功返回时,输出包是引用计数的AVPacket。下一次调用该接口前,FFmpeg会先对传入的包执行av_packet_unref(),因此调用者应在再次收包前处理或转移上一次包的数据。

已提交的AVFrame

编码器内部处理

内部包缓存或编码器输出

avcodec_receive_packet

调用者得到引用计数AVPacket

处理后释放或转移包引用

🌻2.应用场景和用法

2.1循环读取编码输出

avcodec_receive_packet()通常在每次提交音视频帧后调用,并循环读取,直到编码器返回AVERROR(EAGAIN)。这个循环可以处理一帧输入产生零个、一个或多个编码包的情况。

使用场景读取方式判断依据
普通编码过程提交帧后循环收包AVERROR(EAGAIN)表示当前需要更多输入
编码器有延迟多次提交帧后继续收包每个输入帧不保证立即对应一个输出包
结束排空先发送NULL帧,再持续收包AVERROR_EOF表示编码器输出完成
写入封装器或文件收到包后写入,再释放包引用包数据有效期由引用计数对象管理
2.2函数原型与返回语义

函数原型如下:

intavcodec_receive_packet(AVCodecContext*avctx,AVPacket*avpkt);
参数或返回值含义
avctx已打开的编码器上下文
avpkt输出包对象;函数开始时会先释放其已有引用
0成功得到一个编码包
AVERROR(EAGAIN)当前没有可读输出,应继续提交输入帧
AVERROR_EOF编码器已经排空,不会再产生输出包
其他负值编码器未打开、上下文类型不符或实际编码错误

接口只读取编码输出,不负责写文件、封装容器或永久保存包数据。调用者必须在下一次复用同一个AVPacket前处理当前输出,并通过av_packet_unref()释放引用;若要延长数据生命周期,应使用引用或移动引用接口管理包。

🌻3.调用流程剖析

3.1优先取出内部缓存包

avcodec_receive_packet()首先调用av_packet_unref(avpkt)清理调用者传入对象的旧内容,然后检查编码器上下文是否打开,以及它是否确实是编码器。

如果FFmpeg内部的buffer_pkt中已有数据,函数通过av_packet_move_ref()把内部包引用移动到调用者的avpkt。移动后,内部包不再持有该引用,调用者负责管理输出包的生命周期。

如果内部缓存为空,函数进入编码器实际输出路径。根据编码器接口类型,FFmpeg调用新式的receive_packet回调,或使用兼容路径执行编码并取得包。

否

是

是

否

调用avcodec_receive_packet

释放avpkt旧引用

编码器状态有效

返回错误码

内部缓存包可用

移动内部包引用

调用编码器输出路径

返回输出包

成功输出的数据包必须是引用计数的。调用者若持续收包但不释放或转移包引用,会造成内存持续占用。

3.2区分EAGAIN与EOF

普通编码阶段收到AVERROR(EAGAIN),表示当前没有新包可取,编码器需要更多输入。调用者应提交下一帧。

结束时,调用者先通过avcodec_send_frame(ctx, NULL)通知编码器停止接收普通输入。随后继续调用avcodec_receive_packet(),直到返回AVERROR_EOF。这个结束信号和普通阶段的AVERROR(EAGAIN)含义不同,不能将两者都当作“编码结束”。

成功

EAGAIN

输入结束时发送NULL

否

是

已提交输入帧

循环读取编码包

接收结果

处理并释放AVPacket

继续提交输入

编码器进入排空状态

继续读取延迟输出

收到EOF

编码输出结束

🌻4.实战案例

4.1准备编码器输出循环

下面的函数假设AVCodecContext已经配置并打开,输入帧也已经准备好。AVPacket由调用者创建,函数负责读取所有当前可用的包。

4.2读取包并处理背压状态

收包循环中,成功时处理包内容并释放引用。AVERROR(EAGAIN)表示当前批次读取完成;其他错误码交给上层处理。结束排空阶段使用同一循环,但需要持续读取到AVERROR_EOF。

4.3完整代码
#include<stdio.h>#include<libavcodec/avcodec.h>#include<libavutil/error.h>/* 读取当前可用的全部编码包 */staticintreceive_packets(AVCodecContext*ctx,AVPacket*pkt,FILE*output){intret;while(1){/* receive_packet会先释放pkt此前持有的引用 */ret=avcodec_receive_packet(ctx,pkt);if(ret==AVERROR(EAGAIN))return0;if(ret==AVERROR_EOF)returnAVERROR_EOF;if(ret<0)returnret;/* 在下一次收包前处理当前有效数据 */if(fwrite(pkt->data,1,pkt->size,output)!=(size_t)pkt->size){av_packet_unref(pkt);returnAVERROR(EIO);}/* 释放本次拿到的包缓冲区引用 */av_packet_unref(pkt);}}/* 普通输入阶段:发送帧后立即读取可用输出 */staticintencode_one_frame(AVCodecContext*ctx,constAVFrame*frame,AVPacket*pkt,FILE*output){intret;ret=avcodec_send_frame(ctx,frame);if(ret<0)returnret;ret=receive_packets(ctx,pkt,output);returnret==AVERROR_EOF?0:ret;}/* 输入结束时先发送NULL帧,再读取到AVERROR_EOF */staticintfinish_encoder(AVCodecContext*ctx,AVPacket*pkt,FILE*output){intret;ret=avcodec_send_frame(ctx,NULL);if(ret<0)returnret;while(1){ret=receive_packets(ctx,pkt,output);if(ret==AVERROR_EOF)return0;if(ret<0)returnret;}}

如果avcodec_send_frame()返回AVERROR(EAGAIN),上层应先调用receive_packets()读取输出,再重试同一个输入帧。普通阶段不能把AVERROR(EAGAIN)误认为编码器已结束。

🌻5.总结

avcodec_receive_packet()从编码器内部缓存或实际输出路径取出引用计数的压缩包,并用AVERROR(EAGAIN)与AVERROR_EOF区分等待输入和排空完成。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/5 5:27:25

热泵空调怎么救冬季续航:拆解热泵制热的工作逻辑

热泵"救"续航的方式&#xff0c;本质是把"用电造热"换成了"用电搬热"&#xff1a;它花1度电&#xff0c;从车外空气里"扛"回2&#xff5e;4度的热量送进车厢&#xff0c;而PTC是1度电只能烧出1度热。省下来的那部分电&#xff0c;就直…

作者头像 李华
网站建设 2026/11/5 5:21:51

基于springboot的校园自律碎片时间搭子匹配系统的设计与实现论文代码

学弟学妹们&#xff0c;大家好&#x1f44b;&#xff01;作为已经顺利上岸的计算机往届学长&#xff0c;当年独立调试 SpringBoot 后端接口、适配微信小程序交互逻辑、规整 MySQL 表结构、梳理整套系统业务与论文内容的过程依旧记忆犹新。非常清楚做校园互助类毕设的难处&#…

作者头像 李华
网站建设 2026/11/5 5:09:12

桌面窗口总乱糟糟?Seelen UI 平铺窗口管理与 Windows 桌面定制指南

桌面窗口总乱糟糟?Seelen UI 平铺窗口管理与 Windows 桌面定制指南 【免费下载链接】Seelen-UI The Fully Customizable Desktop Environment for Windows 10/11. 项目地址: https://gitcode.com/GitHub_Trending/se/Seelen-UI 窗口越开越乱、图标越攒越多,切一次虚拟桌…

作者头像 李华
网站建设 2026/11/5 5:07:56

免费上手 3D 高斯斑点编辑:SuperSplat 快速使用指南

免费上手 3D 高斯斑点编辑&#xff1a;SuperSplat 快速使用指南 【免费下载链接】super-splat 3D Gaussian Splat Editor 项目地址: https://gitcode.com/gh_mirrors/su/super-splat SuperSplat 是一款免费、开源、完全运行在浏览器里的 3D 高斯斑点编辑工具&#xff0c…

作者头像 李华