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函数调用图:

av_write_frame函数调用图:

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、解释

可以看出2个函数的区别在于av_interleaved_write_frame调用了interleave_packet,而av_write_frame没有调用,看看interleave_packet的代码:
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(音频预加载时间)的话,还需要把音频包的预加载时间算进去。

好了,现在整体逻辑就清晰了,得出的结论是:在有多个流的情况下要用av_interleaved_write_frame,只有单一流2个函数都可以用。

ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frame相关推荐

  1. 雷神FFMpeg源码学习笔记

    雷神FFMpeg源码学习笔记 文章目录 雷神FFMpeg源码学习笔记 读取编码并依据编码初始化内容结构 每一帧的视频解码处理 读取编码并依据编码初始化内容结构 在开始编解码视频的时候首先第一步需要注册 ...

  2. ffmpeg源码学习笔记三

    9. 关于如何parse mkv 前面为了不把战线拉太长,把如何parse mkv container 内容直接跳过了 接下来还是从read_header 开始讲解 static int matros ...

  3. ffmpeg源码学习笔记五

    14.read_frame_internal static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) {...//初始化pa ...

  4. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...

  5. Yii源码阅读笔记 - 日志组件

    2015-03-09 一 By youngsterxyf 使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category); Yii: ...

  6. RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?

    RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...

  7. React深入学习与源码解析笔记

    ***当前阶段的笔记 *** 「面向实习生阶段」https://www.aliyundrive.com/s/VTME123M4T9 提取码: 8s6v 点击链接保存,或者复制本段内容,打开「阿里云盘」 ...

  8. wrappers.php,PHP源码阅读笔记六:stream_get_wrappers函数

    PHP源码阅读笔记stream_get_wrappers函数 stream_get_wrappers (PHP 5) stream_get_wrappers - 返回注册的数据流列表 Descript ...

  9. Apache log4j-1.2.17源码学习笔记

    (1)Apache log4j-1.2.17源码学习笔记 http://blog.csdn.net/zilong_zilong/article/details/78715500 (2)Apache l ...

最新文章

  1. oracle阻塞查询,oracle 查询阻塞的sql语句
  2. 【0ms优化】剑指 Offer 18. 删除链表的节点
  3. Multisim14仿真入门笔记
  4. java的Random类详解
  5. Kafka 为什么能那么快的 6 个原因
  6. 马上就5g时代了,5g时代有什么风口吗?
  7. 详解飞书新功能,如何让开发者“爽”起来?
  8. YOLO系列专题——YOLOv1实践篇
  9. 处理服务器故障的前5分钟(转)
  10. indesign教程,如何改变内容的顺序?
  11. 菜鸟的系统架构师如何应对交易系统激增的系统流量
  12. Excel如何实现随机不重复抽取
  13. 指派问题的遗传算法求解 Java实现
  14. python 人物关系图_python简单实战项目:《冰与火之歌1-5》角色关系图谱构建——人物关系可视化...
  15. 夏普Sharp MX-B401 一体机驱动
  16. 【2021年更新】面向通信技术的机器学习和深度学习文献汇总
  17. 详解Tensor用法
  18. 一文彻底讲透@Async注解的原理和使用方法
  19. 51单片机之电子钟设计
  20. CAD软件中如何标注曲线长度?

热门文章

  1. Unity(lua) Text添加空格导致换行问题
  2. 协同办公系统解决方案
  3. 使用C语言判断奇偶性
  4. 宅男之品味生活节奏篇
  5. 【计算机网络】第三章数据链路层知识点及经典例题汇总
  6. 史上最极品美女乞丐,难道是犀利姐来了?(有图有真相)
  7. 数组的排序、常用方法
  8. ueditor抓取远程图片php版,Ueditor百度编辑器远程抓取图片的开启与关闭
  9. 这位读者让我看到了很多学历之外的东西
  10. 安卓各种传感器的使用