avcodec send_packet和receive_frame

2024-01-15 02:20

本文主要是介绍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

这篇关于avcodec send_packet和receive_frame的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/607335

相关文章

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

Qt中window frame的影响

window frame 在创建图形化界面的时候,会创建窗口主体,上面会多出一条,周围多次一圈细边,这就叫window frame窗口框架,这是操作系统自带的。 这个对geometry的一些属性有一定影响,主要体现在Qt坐标系体系: 窗口当中包含一个按钮,这个按钮的坐标系是以父元素为参考,那么这个参考是widget本体作为参考,还是window frame作为参考,这两种参考体系都存在

UVa 10820 Send a Table (Farey数列欧拉函数求和)

这里先说一下欧拉函数的求法 先说一下筛选素数的方法 void Get_Prime(){ /*筛选素数法*/for(int i = 0; i < N; i++) vis[i] = 1;vis[0] = vis[1] = 0;for(int i = 2; i * i < N; i++)if(vis[i]){for(int j = i * i; j < N; j += i)vis[j] =

iOS——frame和bounds的区别

把frame理解为占用区域,把bounds理解为边界。View在旋转过程中,其实自己的坐标系统并没有发生改变,bounds中的origin只能通过setBounds方法修改。 frame 定义了视图在其父视图坐标系统中的位置和大小。其坐标系是相对于俯视图的坐标系。 bounds 定义了视图自身坐标系统中的位置和大小。其坐标系是相对于自己本身视图的坐标系。 UIView.h中的注释: // 如

IOS界面开发基础——Frame与Bounds

参考资料:http://blog.csdn.net/hherima/article/details/39501857 在IOS的UI开发中,经常需要对view进行定位。比较常用的概念就是Frame和Bound,通过view这两个属性,就可以任意的“摆弄”我们的view了。 这两个属性都可以定义view的位置和大小,但这两个属性之间有什么区别和联系呢?经过资料查找,记录如下: Frame

Cisco Packet Tracer的下载与安装+中文

Cisco Packet Tracer的下载与安装+中文 一、引言 Cisco Packet Tracer 是一款由思科公司开发的网络模拟软件,广泛用于网络设计和模拟。它支持模拟路由器、交换机、防火墙等网络设备,是学习网络技术的好帮手。本文将介绍如何下载、安装Cisco Packet Tracer,并将其设置为中文界面。 二、下载 1、下载方式 1.1、思科官网下载 首先推荐从思科官方

FFmpeg源码:compute_frame_duration函数分析

一、compute_frame_duration函数的定义 compute_frame_duration函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/demux.c中: /*** Return the frame duration in seconds. Return 0 if not available.*/static void

FFmpeg源码:avcodec_descriptor_get函数分析

一、avcodec_descriptor_get函数的声明 avcodec_descriptor_get函数声明在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavcodec/codec_desc.h中: /*** @return descriptor for given codec ID or NULL if no descriptor exists.*/c

MQTT: Packet Identifier

Packet Identifier 长度:两个字节 有 Packet id 的 MQTT 包: PUBLISH (QoS > 0), PUBACK, PUBREC, PUBREL, PUBCOMPSUBSCRIBE, SUBACKUNSUBSCRIBE, UNSUBACK 客户端每次发送一个新的包的时候,必须给这个包设置一个从未使用过的 Packet Id. 当客户端重新发送一个包的时候

FFmpeg源码:append_packet_chunked、av_get_packet函数分析

================================================================= AVPacket结构体和其相关的函数分析: FFmpeg存放压缩后的音视频数据的结构体:AVPacket简介 FFmpeg源码:av_init_packet、get_packet_defaults、av_packet_alloc函数分析 FFmpeg源码:av