avcodec send_packet和receive_frame

下面是解码的过程代码,对输入给解码器的pkt桢类型进行判断,关键桢打印出is key frame,解码出来的桢根据pict_type打印桢类型出I/P/B桢类型,从这里也可以看出来,没解码之前,AVPacket只能得到是否关键帧,要知道桢类型,必须在解码后。

完整代码可以从github上获取

    /* Read packets from input file and decode them */while (av_read_frame(fmt_ctx, pkt) >= 0) {if (pkt->stream_index == video_stream_idx) {if (pkt->flags & AV_PKT_FLAG_KEY) {av_log(NULL, AV_LOG_INFO, " in   is key frame!\n");} else {av_log(NULL, AV_LOG_INFO, " in is't key frame!\n");}/* Send packet to decoder */ret = avcodec_send_packet(codec_ctx, pkt);if (ret < 0) {fprintf(stderr, "Error sending packet to decoder\n");exit(1);}/* Receive frame from decoder */while (ret >= 0) {ret = avcodec_receive_frame(codec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {fprintf(stderr, "Error receiving frame from decoder\n");exit(1);}if (frame->pict_type == AV_PICTURE_TYPE_I) {av_log(NULL, AV_LOG_INFO, "  out   is I frame!\n");} else if (frame->pict_type == AV_PICTURE_TYPE_P) {av_log(NULL, AV_LOG_INFO, "  out   is P frame!\n");} else if (frame->pict_type == AV_PICTURE_TYPE_B) {av_log(NULL, AV_LOG_INFO, "  out   is B frame!\n");}/* Write YUV data to output file */fwrite(frame->data[0], 1, codec_ctx->width * codec_ctx->height, outfile);fwrite(frame->data[1], 1, codec_ctx->width * codec_ctx->height / 4, outfile);fwrite(frame->data[2], 1, codec_ctx->width * codec_ctx->height / 4, outfile);}}av_packet_unref(pkt);}

FFmpeg解码流程


avcodec_send_packet先对avpkt进行ref操作,然后发送给bsf,然后判断avci->buffer_frame->buf为NULL,就调用decode_receive_frame_internal进行解码,进入decode_simple_internal后,ff_decode_get_packet会先从bsf中获取packet,然后调用解码器解码函数进行解码。

avcodec_receive_frame中也会先判断avci->buffer_frame->buf[0],如果不为NULL,说明前面send_packet的时候已经解码出来了,这次调用只需要进行ref操作。如果avci->buffer_frame->buf[0]为NULL,调用decode_receive_frame_internal解码,和前面send_packet中的调用流程一样。

avcodec_send_packet-> av_packet_ref(avci->buffer_pkt, avpkt)-> av_bsf_send_packet(avci->bsf, avci->buffer_pkt)-> !avci->buffer_frame->buf[0]-> decode_receive_frame_internal-> decode_simple_receive_frame(avctx, frame)-> decode_simple_internal-> ff_decode_get_packet-> av_bsf_receive_packet-> ff_bsf(ctx->filter)->filter(ctx, pkt)-> h264_decode_framegot_frame -> returnavcodec_receive_frame # 循环receive,直到返回EAGAIN-> avci->buffer_frame->buf[0] # 前面已经解码出来-> av_frame_move_ref(frame, avci->buffer_frame) #返回继续解码-> !avci->buffer_frame->buf[0] #前面没有解码-> decode_receive_frame_internal-> decode_simple_receive_frame(avctx, frame)-> decode_simple_internal-> ff_decode_get_packet-> av_bsf_receive_packet-> ff_bsf(ctx->filter)->filter(ctx, pkt)-> h264_decode_framegot_frame -> return

解码含B桢的视频


解码含B桢的视频,通过ffprobe把前面几桢的frame信息输出到xml,可以看到桢的分布如下:

ffprobe -show_frames -select_streams v -of xml ~/video/b-frame.mp4 > videoframes.info
<frame key_frame="1" pts="0"  pkt_dts="0" pkt_dts_time="0.000000" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="I"><side_data_list><side_data type="H.26[45] User Data Unregistered SEI message"><side_datum key="side_data_type" value="H.26[45] User Data Unregistered SEI message"/></side_data></side_data_list></frame><frame key_frame="0" pkt_dts="512" pkt_dts_time="0.040000" pkt_size="921" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="1024" pkt_dts_time="0.080000" pkt_size="250" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="1536" pkt_dts_time="0.120000" pkt_size="2663" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="P"  /><frame key_frame="0" pkt_dts="2048" pkt_dts_time="0.160000" pkt_size="400" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="2560" pkt_dts_time="0.200000" pkt_size="247" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="3072" pkt_dts_time="0.240000" pkt_size="774" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="P"  /><frame key_frame="0" pkt_dts="3584" pkt_dts_time="0.280000" pkt_size="353" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="4096" pkt_dts_time="0.320000" pkt_size="197" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="4608" pkt_dts_time="0.360000" pkt_size="206" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="P"  /><frame key_frame="0" pkt_dts="5120" pkt_dts_time="0.400000" pkt_size="332" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="5632" pkt_dts_time="0.440000" pkt_size="15012" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="B"  /><frame key_frame="0" pkt_dts="6144" pkt_dts_time="0.480000" pkt_size="20861" width="1920" height="1080"  pix_fmt="yuv420p" pict_type="P"  />

从前面的xml中可以看到输入桢的次序是:

I B B P B B P B B P
 in   is key frame! # in Iin is't key frame! # in Bin is't key frame! # in B# 进三桢I B B解码出一帧Iout   is I frame! #     out I# 然后进一帧P解码出一帧Bin is't key frame! # in Pout   is B frame! #     out B# 然后进一帧B解码出一帧Bin is't key frame! # in Bout   is B frame! #     out B# 然后进一帧B解码出一帧Pin is't key frame! # in Bout   is P frame! #     out P# 然后进一帧P解码出一帧Bin is't key frame! # in Pout   is B frame! #     out B# 然后进一帧B解码出一帧Bin is't key frame! # in Bout   is B frame! #     out B

解码不含B桢的视频


通过debug和log可以看出,不含B桢的视频是进一帧出一帧,avcodec_send_packet之后,decoder实际上会被调用一次解码出来一帧,avcodec_receive_frame中调用ff_decode_receive_frame也是判断avci->buffer_frame->buf[0]是否为NULL,不为NULL时,av_frame_move_ref返回,不会再走decode_receive_frame_internal解码流程。

 in   is key frame!out   is I frame!in   is key frame!out   is I frame!in   is key frame!out   is I frame!in   is key frame!

所以使用avcodec_send_packet/avcodec_receive_frameavcodec_send_packet之后需要将frame及时取出来,不然第三次avcodec_send_packet之后,判断avci->buffer_pkt的地方就会返回EAGAIN。

        if (!AVPACKET_IS_EMPTY(avci->buffer_pkt))return AVERROR(EAGAIN);

原因是这样的:

第一个avcodec_send_packet进来,av_packet_ref(avci->buffer_pkt, avpkt)后,调用decode_receive_frame_internal解码,正常返回。

第二个avcodec_send_packet进来,av_packet_ref(avci->buffer_pkt, avpkt)后,因为avci->buffer_frame->buf[0]不为NULL,没有调用decode_receive_frame_internal解码消耗掉。

    if (consumed >= pkt->size || ret < 0) {av_packet_unref(pkt);

消耗掉以后就会在decode_simple_internal中调用av_packet_unref

第三个avcodec_send_packet进来,因为前面没有解码消耗,avci->buffer_pkt->data不为NULL,AVPACKET_IS_EMPTY判断失败,返回EAGAIN。

所以在使用FFmpeg解码时候,avcodec_send_packetavcodec_receive_frame得在一起出现,receive_frame需要及时的将frame从avci->buffer_frame中取出。

  • 对于没有B桢的视频,没有及时取走后,因为第二个packet没有被解码,第三个以上的buf进来都会报EAGAIN
  • 对于有B桢的视频,进去几桢之后,没有及时取走,后面就不会再调用decode解码,前面的IBBPBB的桢排布,在第五个buf进来的时候也会报EAGAIN

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/622622.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zung氏焦虑症测试SAS

SAS被称为焦虑自评量表&#xff0c;是一种用来测量焦虑症状程度以及观察治疗过程中变化情况的心理量表。主要用于评估心理状态&#xff0c;辅助参考数据&#xff0c;也是焦虑评定的标准。焦虑自评量表系是由William W.K. Zung编制的&#xff0c;该量表已成为心理咨询师、心理医…

【Python基础】一文搞懂:Python中文件路径的处理方式

文章目录 1 引言2 os 模块2.1 常用函数2.2 示例 3 pathlib 模块3.1 常用类和方法3.1 示例 4 比较 os 和 pathlib5 实例演示5.1 使用 os 模块5.2 使用 pathlib 模块5.3 封装为pathlib_example.py脚本 6 结语 1 引言 在 Python 编程中&#xff0c;正确处理文件路径是一个常见且重…

【GitHub项目推荐--克隆你的声音】【转载】

今天推荐一个黑科技开源项目&#xff0c;只需要你 5 秒钟的声音对话&#xff0c;就能克隆出你的声音&#xff0c;而且能够实时的生成你任意语音。 是不是很顶&#xff1f; 我举个例子&#xff0c;如果我这里有 300 条你说话的语音&#xff0c;我把你的语音数据用这个开源项目…

为求增长,这家消金公司也开始发力小微

来源 | 镭射财经&#xff08;leishecaijing&#xff09; 消金公司业务转向&#xff0c;小微贷越来越香&#xff0c;已成消金行业近年转型奋进的一个插曲。 为求业绩增长&#xff0c;拓宽获客基数&#xff0c;越来越多的消金公司不再局限于单一的C端客群&#xff0c;将目光瞄向…

Android jar包编译及集成

Jar包编译和集成有两种编译方式&#xff0c;mk和bp&#xff0c;Android 7版本之后逐渐采用bp格式编译&#xff0c;目前14版本还是兼容mk方式编译&#xff0c;具体写法入下&#xff1a; Android jar包编译 mk&#xff1a; 如果需要打包到systemimg&#xff0c;则需要将此jar包添…

MOJO基础语法

文章目录 打印变量及方法声明结构体python集成 打印 print("Hello Mojo!")变量及方法声明 变量&#xff1a; 使用’ var ‘创建一个可变的值&#xff0c;或者用’ let 创建一个不可变的值。 方法&#xff1a; 方法可以使用python中的def 方法声明&#xff0c;也引…

vue组件中data为什么必须是一个函数?

在Vue组件中&#xff0c;data为组件的私有数据对象&#xff0c;每个实例都必须要有自己独立的数据对象。为了确保每个实例都有独立的数据对象&#xff0c;Vue规定data必须是一个函数&#xff0c;而不是一个简单的对象。 当data是一个简单的对象时&#xff0c;所有组件的实例会…

认知觉醒(九)

认知觉醒(九) 专注力——情绪和智慧的交叉地带 第一节 情绪专注&#xff1a;一招提振你的注意力 用元认知来观察自己的注意力是一件很有意思的事情&#xff0c;相信你可以轻易观察到这种现象&#xff1a;身体做着A&#xff0c;脑子却想着B。 跑步的时候&#xff0c;手脚在…

录第第五十八天——每日温度,下一个更大元素|

单调栈 栈里的元素保持单调递增或者递减&#xff0c;栈内元素是元素下标。单调栈的本质是空间换时间&#xff0c;因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素&#xff0c;优点是整个数组只需要遍历一次求一个元素右边第一个更大元素&#xff0c;单调栈…

行业分享----dbaplus174期:美团基于Orchestrator的MySQL高可用实践

记录 MySQL高可用方案-MMM、MHA、MGR、PXC https://blog.csdn.net/jycjyc/article/details/119731980 美团数据库高可用架构的演进与设想 https://tech.meituan.com/2017/06/29/database-availability-architecture.html

js事件委托是什么?

事件委托是一种 JavaScript 编程技术&#xff0c;用于在父元素上设置事件监听器&#xff0c;以捕获其子元素的事件。这种技术通常用于动态生成元素的场景&#xff0c;其中子元素的增删改频繁&#xff0c;如果直接在子元素上设置事件监听器&#xff0c;会使得代码复杂且难以维护…

C语言实现双向链表

1.版本一 由于节点之间的连接变多 所以我们最好提前将前驱节点和后继节点用变量保存下来 以免等下在进行节点之间的指向时出错 #include <stdio.h> #include <stdlib.h> #include <stdbool.h> // 节点类 typedef struct Node {// 数据域int data;// 指针域…

【python playwright 安装及验证】

python playwright pip install playwright pip install playwright -i http://mirrors.aliyun.com/pypi/simple/ playwright codegen -o script.py -b chromium --ignore-https-errors --viewport-size “2560,1440” --proxy-server “http://100.8.64.8:60497” https://w…

Harbor安装

采用原生的方式安装Harbor 下载Harbor安装包&#xff1a;https://github.com/goharbor/harbor/releases/download/v2.3.4/harbor-offline-installer-v2.3.4.tgz 拖拽到Linux并解压&#xff1a; tar -zxvf harbor-offline-installer-v2.3.4.tgz -C /usr/local/修改Harbor配置文…

webpack的性能优化(二)——减少打包体积

优化webpack性能时&#xff0c;主要集中在两个方面&#xff1a;优化构建后的结果和优化构建时的速度。前一篇文章已经介绍了如何通过webpack的分包来优化构建后的结果。而在本篇文章中&#xff0c;我们将从减少打包体积的角度来探讨。 1.通过CDN链接引入第三方库 CDN是指通过相…

基于Python编程实现简单网络爬虫实现

引言 网络爬虫&#xff08;英语&#xff1a;web crawler&#xff09;&#xff0c;也叫网络蜘蛛&#xff08;spider&#xff09;&#xff0c;是一种用来自动浏览万维网的网络机器人。其目的一般为编纂网络索引。 --维基百科 网络爬虫可以将自己所访问的页面保存下来&#xff0c…

python 通过定时任务执行pytest case

这段Python代码使用了schedule库来安排一个任务&#xff0c;在每天的22:50时运行。这个任务执行一个命令来运行pytest&#xff0c;并生成一个报告。 代码开始时将job_done变量设为False&#xff0c;然后运行预定的任务。一旦任务完成&#xff0c;将job_done设置为True并跳出循…

Netty-Netty实现自己的通信框架

通信框架功能设计 功能描述 通信框架承载了业务内部各模块之间的消息交互和服务调用&#xff0c;它的主要功能如下&#xff1a; 基于 Netty 的 NIO 通信框架&#xff0c;提供高性能的异步通信能力&#xff1b; 提供消息的编解码框架&#xff0c;可以实现 POJO 的序列化和反…

AI智能分析网关V4烟火检测算法解决方案

一、背景需求 根据国家消防救援局公布的数据显示&#xff0c;2023年共接报处置各类警情213.8万起&#xff0c;督促整改风险隐患397万处。火灾危害巨大&#xff0c;必须引起重视。传统靠人工报警的方法存在人员管理难、场地数量多且分散等问题&#xff0c;无法有效发现险情降低…

【量化交易故事】小明开启了量化创业之旅-01

故事开始于2023年的春天&#xff0c;小明是一位对金融市场充满热情的IT工程师。在经历了数次基于主观判断和个人情绪进行投资却收获平平后&#xff0c;他意识到传统交易方式中的人为因素难以避免&#xff0c;而这往往成为影响投资决策稳定性和准确性的关键障碍。在一次偶然的机…