ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frame
1、函数介绍
av_interleaved_write_frame函数介绍:
/*** Write a packet to an output media file ensuring correct interleaving.** This function will buffer the packets internally as needed to make sure the* packets in the output file are properly interleaved in the order of* increasing dts. Callers doing their own interleaving should call* av_write_frame() instead of this function.** @param s media file handle* @param pkt The packet containing the data to be written.* <br>* If the packet is reference-counted, this function will take* ownership of this reference and unreference it later when it sees* fit.* The caller must not access the data through this reference after* this function returns. If the packet is not reference-counted,* libavformat will make a copy.* <br>* This parameter can be NULL (at any time, not just at the end), to* flush the interleaving queues.* <br>* Packet's @ref AVPacket.stream_index "stream_index" field must be* set to the index of the corresponding stream in @ref* AVFormatContext.streams "s->streams". It is very strongly* recommended that timing information (@ref AVPacket.pts "pts", @ref* AVPacket.dts "dts", @ref AVPacket.duration "duration") is set to* correct values.** @return 0 on success, a negative AVERROR on error. Libavformat will always* take care of freeing the packet, even if this function fails.** @see av_write_frame(), AVFormatContext.max_interleave_delta*/
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
av_write_frame函数介绍:
/*** Write a packet to an output media file.** This function passes the packet directly to the muxer, without any buffering* or reordering. The caller is responsible for correctly interleaving the* packets if the format requires it. Callers that want libavformat to handle* the interleaving should call av_interleaved_write_frame() instead of this* function.** @param s media file handle* @param pkt The packet containing the data to be written. Note that unlike* av_interleaved_write_frame(), this function does not take* ownership of the packet passed to it (though some muxers may make* an internal reference to the input packet).* <br>* This parameter can be NULL (at any time, not just at the end), in* order to immediately flush data buffered within the muxer, for* muxers that buffer up data internally before writing it to the* output.* <br>* Packet's @ref AVPacket.stream_index "stream_index" field must be* set to the index of the corresponding stream in @ref* AVFormatContext.streams "s->streams". It is very strongly* recommended that timing information (@ref AVPacket.pts "pts", @ref* AVPacket.dts "dts", @ref AVPacket.duration "duration") is set to* correct values.* @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush** @see av_interleaved_write_frame()*/
int av_write_frame(AVFormatContext *s, AVPacket *pkt);
2、函数调用图
av_interleaved_write_frame函数调用图:
![](/assets/blank.gif)
av_write_frame函数调用图:
![](/assets/blank.gif)
3、函数代码
av_interleaved_write_frame函数代码:
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
{int ret, flush = 0;//检查pkt是否合法ret = check_packet(s, pkt);if (ret < 0)goto fail;if (pkt) {AVStream *st = s->streams[pkt->stream_index];av_dlog(s, "av_interleaved_write_frame size:%d dts:%s pts:%s\n",pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts));if ((ret = compute_pkt_fields2(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))//计算和检查pts、dts、durationgoto fail;if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {ret = AVERROR(EINVAL);goto fail;}} else {av_dlog(s, "av_interleaved_write_frame FLUSH\n");flush = 1;}for (;; ) {AVPacket opkt;int ret = interleave_packet(s, &opkt, pkt, flush);if (pkt) {memset(pkt, 0, sizeof(*pkt));av_init_packet(pkt);pkt = NULL;}if (ret <= 0) //FIXME cleanup needed for ret<0 ?return ret;ret = write_packet(s, &opkt);if (ret >= 0)s->streams[opkt.stream_index]->nb_frames++;av_free_packet(&opkt);if (ret < 0)return ret;if(s->pb && s->pb->error)return s->pb->error;}
fail:av_packet_unref(pkt);return ret;
}
av_write_frame函数代码:
int av_write_frame(AVFormatContext *s, AVPacket *pkt)
{int ret;ret = check_packet(s, pkt);if (ret < 0)return ret;if (!pkt) {if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {ret = s->oformat->write_packet(s, NULL);if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)avio_flush(s->pb);if (ret >= 0 && s->pb && s->pb->error < 0)ret = s->pb->error;return ret;}return 1;}ret = compute_pkt_fields2(s, s->streams[pkt->stream_index], pkt);if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))return ret;ret = write_packet(s, pkt);if (ret >= 0 && s->pb && s->pb->error < 0)ret = s->pb->error;if (ret >= 0)s->streams[pkt->stream_index]->nb_frames++;return ret;
}
4、解释
static int interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush)
{if (s->oformat->interleave_packet) {int ret = s->oformat->interleave_packet(s, out, in, flush);if (in)av_free_packet(in);return ret;} elsereturn ff_interleave_packet_per_dts(s, out, in, flush);
}
代码非常简单,如果AVOutputFormat有interleave_packet函数指针,则调用,如果没有则调用ff_interleave_packet_per_dts,看看ff_interleave_packet_per_dts的代码:
int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,AVPacket *pkt, int flush)
{AVPacketList *pktl;int stream_count = 0;int noninterleaved_count = 0;int i, ret;if (pkt) {//把pkt加入到AVPacketList中去if ((ret = ff_interleave_add_packet(s, pkt, interleave_compare_dts)) < 0)return ret;}for (i = 0; i < s->nb_streams; i++) {if (s->streams[i]->last_in_packet_buffer) {++stream_count;} else if (s->streams[i]->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT &&s->streams[i]->codec->codec_id != AV_CODEC_ID_VP8 &&s->streams[i]->codec->codec_id != AV_CODEC_ID_VP9) {++noninterleaved_count;}}if (s->internal->nb_interleaved_streams == stream_count)flush = 1;if (s->max_interleave_delta > 0 &&s->internal->packet_buffer &&!flush &&s->internal->nb_interleaved_streams == stream_count+noninterleaved_count) {AVPacket *top_pkt = &s->internal->packet_buffer->pkt;int64_t delta_dts = INT64_MIN;int64_t top_dts = av_rescale_q(top_pkt->dts,s->streams[top_pkt->stream_index]->time_base,AV_TIME_BASE_Q);for (i = 0; i < s->nb_streams; i++) {int64_t last_dts;const AVPacketList *last = s->streams[i]->last_in_packet_buffer;if (!last)continue;last_dts = av_rescale_q(last->pkt.dts,s->streams[i]->time_base,AV_TIME_BASE_Q);delta_dts = FFMAX(delta_dts, last_dts - top_dts);}if (delta_dts > s->max_interleave_delta) {av_log(s, AV_LOG_DEBUG,"Delay between the first packet and last packet in the ""muxing queue is %"PRId64" > %"PRId64": forcing output\n",delta_dts, s->max_interleave_delta);flush = 1;}}if (stream_count && flush) {AVStream *st;pktl = s->internal->packet_buffer;*out = pktl->pkt;st = s->streams[out->stream_index];s->internal->packet_buffer = pktl->next;if (!s->internal->packet_buffer)s->internal->packet_buffer_end = NULL;if (st->last_in_packet_buffer == pktl)st->last_in_packet_buffer = NULL;av_freep(&pktl);return 1;} else {av_init_packet(out);return 0;}
}
从代码中看到,函数ff_interleave_packet_per_dts调用ff_interleave_add_packet把pkt加入缓存,然后再从缓存中取出第一个pkt返回。
看看ff_interleave_add_packet的代码:
int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt,int (*compare)(AVFormatContext *, AVPacket *, AVPacket *))
{int ret;AVPacketList **next_point, *this_pktl;AVStream *st = s->streams[pkt->stream_index];int chunked = s->max_chunk_size || s->max_chunk_duration;this_pktl = av_mallocz(sizeof(AVPacketList));if (!this_pktl)return AVERROR(ENOMEM);this_pktl->pkt = *pkt;
#if FF_API_DESTRUCT_PACKET
FF_DISABLE_DEPRECATION_WARNINGSpkt->destruct = NULL; // do not free original but only the copy
FF_ENABLE_DEPRECATION_WARNINGS
#endifpkt->buf = NULL;pkt->side_data = NULL;pkt->side_data_elems = 0;if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE);av_assert0(((AVFrame *)pkt->data)->buf);} else {// Duplicate the packet if it uses non-allocated memoryif ((ret = av_dup_packet(&this_pktl->pkt)) < 0) {av_free(this_pktl);return ret;}}if (s->streams[pkt->stream_index]->last_in_packet_buffer) {next_point = &(st->last_in_packet_buffer->next);} else {next_point = &s->internal->packet_buffer;}if (chunked) {uint64_t max= av_rescale_q_rnd(s->max_chunk_duration, AV_TIME_BASE_Q, st->time_base, AV_ROUND_UP);st->interleaver_chunk_size += pkt->size;st->interleaver_chunk_duration += pkt->duration;if ( (s->max_chunk_size && st->interleaver_chunk_size > s->max_chunk_size)|| (max && st->interleaver_chunk_duration > max)) {st->interleaver_chunk_size = 0;this_pktl->pkt.flags |= CHUNK_START;if (max && st->interleaver_chunk_duration > max) {int64_t syncoffset = (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)*max/2;int64_t syncto = av_rescale(pkt->dts + syncoffset, 1, max)*max - syncoffset;st->interleaver_chunk_duration += (pkt->dts - syncto)/8 - max;} elsest->interleaver_chunk_duration = 0;}}if (*next_point) {if (chunked && !(this_pktl->pkt.flags & CHUNK_START))goto next_non_null;if (compare(s, &s->internal->packet_buffer_end->pkt, pkt)) {while ( *next_point&& ((chunked && !((*next_point)->pkt.flags&CHUNK_START))|| !compare(s, &(*next_point)->pkt, pkt)))next_point = &(*next_point)->next;if (*next_point)goto next_non_null;} else {next_point = &(s->internal->packet_buffer_end->next);}}av_assert1(!*next_point);s->internal->packet_buffer_end = this_pktl;
next_non_null:this_pktl->next = *next_point;s->streams[pkt->stream_index]->last_in_packet_buffer =*next_point = this_pktl;return 0;
}
代码中可以看到,函数ff_interleave_add_packet把pkt经过排序(如果缓存中有pkt的话)加入到缓存中,这个排序是根据ff_interleave_packet_per_dts函数传入的compare(其实就是函数interleave_compare_dts)指针做的,来看看interleave_compare_dts函数的代码:
static int interleave_compare_dts(AVFormatContext *s, AVPacket *next,AVPacket *pkt)
{AVStream *st = s->streams[pkt->stream_index];AVStream *st2 = s->streams[next->stream_index];int comp = av_compare_ts(next->dts, st2->time_base, pkt->dts,st->time_base);if (s->audio_preload && ((st->codec->codec_type == AVMEDIA_TYPE_AUDIO) != (st2->codec->codec_type == AVMEDIA_TYPE_AUDIO))) {int64_t ts = av_rescale_q(pkt ->dts, st ->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO);int64_t ts2= av_rescale_q(next->dts, st2->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO);if (ts == ts2) {ts= ( pkt ->dts* st->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO)* st->time_base.den)*st2->time_base.den-( next->dts*st2->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO)*st2->time_base.den)* st->time_base.den;ts2=0;}comp= (ts>ts2) - (ts<ts2);}if (comp == 0)return pkt->stream_index < next->stream_index;return comp > 0;
}
从代码中可以看到interleave_compare_dts函数其实就是在比较传入的2个pkt的dts值,其实就是比那个的dts更早,如果有音频还有audio_preload(音频预加载时间)的话,还需要把音频包的预加载时间算进去。
ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frame相关推荐
- 雷神FFMpeg源码学习笔记
雷神FFMpeg源码学习笔记 文章目录 雷神FFMpeg源码学习笔记 读取编码并依据编码初始化内容结构 每一帧的视频解码处理 读取编码并依据编码初始化内容结构 在开始编解码视频的时候首先第一步需要注册 ...
- ffmpeg源码学习笔记三
9. 关于如何parse mkv 前面为了不把战线拉太长,把如何parse mkv container 内容直接跳过了 接下来还是从read_header 开始讲解 static int matros ...
- ffmpeg源码学习笔记五
14.read_frame_internal static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) {...//初始化pa ...
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...
- Yii源码阅读笔记 - 日志组件
2015-03-09 一 By youngsterxyf 使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category); Yii: ...
- RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?
RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...
- React深入学习与源码解析笔记
***当前阶段的笔记 *** 「面向实习生阶段」https://www.aliyundrive.com/s/VTME123M4T9 提取码: 8s6v 点击链接保存,或者复制本段内容,打开「阿里云盘」 ...
- wrappers.php,PHP源码阅读笔记六:stream_get_wrappers函数
PHP源码阅读笔记stream_get_wrappers函数 stream_get_wrappers (PHP 5) stream_get_wrappers - 返回注册的数据流列表 Descript ...
- Apache log4j-1.2.17源码学习笔记
(1)Apache log4j-1.2.17源码学习笔记 http://blog.csdn.net/zilong_zilong/article/details/78715500 (2)Apache l ...
最新文章
- oracle阻塞查询,oracle 查询阻塞的sql语句
- 【0ms优化】剑指 Offer 18. 删除链表的节点
- Multisim14仿真入门笔记
- java的Random类详解
- Kafka 为什么能那么快的 6 个原因
- 马上就5g时代了,5g时代有什么风口吗?
- 详解飞书新功能,如何让开发者“爽”起来?
- YOLO系列专题——YOLOv1实践篇
- 处理服务器故障的前5分钟(转)
- indesign教程,如何改变内容的顺序?
- 菜鸟的系统架构师如何应对交易系统激增的系统流量
- Excel如何实现随机不重复抽取
- 指派问题的遗传算法求解 Java实现
- python 人物关系图_python简单实战项目:《冰与火之歌1-5》角色关系图谱构建——人物关系可视化...
- 夏普Sharp MX-B401 一体机驱动
- 【2021年更新】面向通信技术的机器学习和深度学习文献汇总
- 详解Tensor用法
- 一文彻底讲透@Async注解的原理和使用方法
- 51单片机之电子钟设计
- CAD软件中如何标注曲线长度?