ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frame
来源:互联网 发布:域名注册收费 编辑:程序博客网 时间:2024/05/06 18:37
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、duration goto 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; } else return 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_PACKETFF_DISABLE_DEPRECATION_WARNINGS pkt->destruct = NULL; // do not free original but only the copyFF_ENABLE_DEPRECATION_WARNINGS#endif pkt->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 memory if ((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; } else st->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个函数都可以用。
1 0
- ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frame
- av_write_frame 与 av_interleaved_write_frame
- av_write_frame 与 av_interleaved_write_frame
- av_write_frame 与 av_interleaved_write_frame
- ffmpeg——av_write_frame/av_interleaved_write_frame写文件包
- ffmpeg源码跟踪笔记之avformat_open_input
- ffmpeg源码跟踪笔记之avformat_find_stream_info
- ffmpeg源码跟踪笔记之avcodec_open2
- ffmpeg源码跟踪笔记之av_read_frame
- ffmpeg源码跟踪笔记之avcodec_decode_video2
- ffmpeg中av_interleaved_write_frame函数的源码分析及其用法
- ffmpeg源码简析(六)编码-av_write_frame(),av_write_trailer()
- FFmpeg av_interleaved_write_frame错误
- ffmpeg源码跟踪之时间基转换,时间戳比较笔记
- FFmpeg源代码简单分析:av_write_frame()
- av_write_frame
- av_write_frame()
- Cinder源码跟踪笔记
- Android GPS定位,取得城市名称
- html中li与图片的距离
- COM组件注册
- cocos2dx之CocosWidget项目介绍
- 可伸缩性架构常用技术——之数据切分(Data Sharding/Partition)
- ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frame
- 单元模型对象必须保护共享数据
- js最简单幻灯片切换支持同一页面多次调用
- Yii学习笔记之六(HTML生成帮助类api 翻译)
- EXCEL之读取列数据
- android编译系统的makefile文件Android.mk写法
- Linux入门学习:常用命令总结一
- property_get/property_set
- Construct Binary Tree from Inorder and Postorder Traversal