av_write_trailer()

打算写两篇文章简单分析FFmpeg的写文件用到的3个函数avformat_write_header(),av_write_frame()以及av_write_trailer()。这篇文章继续分析av_write_trailer()。av_write_trailer()用于输出文件尾,它的声明位于libavformat\avformat.h,如下所示。/** * Write the stream trailer to an output media file and free the * file private data. * * May only be called after a successful call to avformat_write_header. * * @param s media file handle * @return 0 if OK, AVERROR_xxx on error */int av_write_trailer(AVFormatContext *s);它只需要指定一个参数,即用于输出的AVFormatContext。函数正常执行后返回值等于0。

这2个函数最典型的例子可以参考:

最简单的基于FFMPEG的视频编码器(YUV编码为H.264)

函数调用关系图

av_write_trailer()的调用关系如下图所示。

av_write_trailer()av_write_trailer()的定义位于libavformat\mux.c,如下所示。int av_write_trailer(AVFormatContext *s){int ret, i;for (;; ) {AVPacket pkt;ret = interleave_packet(s, &pkt, NULL, 1);if (ret < 0)goto fail;if (!ret)break;//写入AVPacketret = write_packet(s, &pkt);if (ret >= 0)s->streams[pkt.stream_index]->nb_frames++;av_free_packet(&pkt);if (ret < 0)goto fail;if(s->pb && s->pb->error)goto fail;}fail://写文件尾if (s->oformat->write_trailer)if (ret >= 0) {ret = s->oformat->write_trailer(s);} else {s->oformat->write_trailer(s);}if (s->pb)avio_flush(s->pb);if (ret == 0)ret = s->pb ? s->pb->error : 0;for (i = 0; i < s->nb_streams; i++) {av_freep(&s->streams[i]->priv_data);av_freep(&s->streams[i]->index_entries);}if (s->oformat->priv_class)av_opt_free(s->priv_data);av_freep(&s->priv_data);return ret;}

从源代码可以看出av_write_trailer()主要完成了以下两步工作:

(1)循环调用interleave_packet()以及write_packet(),将还未输出的AVPacket输出出来。

(2)调用AVOutputFormat的write_trailer(),,输出文件尾。

其中第一步和av_write_frame()中的步骤大致是一样的(interleave_packet()这一部分在并不包含在av_write_frame()中,而是包含在av_interleaved_write_frame()中,这一部分源代码还没有分析),可以参考文章《FFmpeg源代码简单分析:av_write_frame()》。下面分析一下第二步。

AVOutputFormat->write_trailer()AVOutputFormat的write_trailer()是一个函数指针,指向特定的AVOutputFormat中的实现函数。我们以FLV对应的AVOutputFormat为例,看一下它的定义,如下所示。AVOutputFormat ff_flv_muxer = {.name= "flv",.long_name= NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),.mime_type= "video/x-flv",.extensions= "flv",.priv_data_size = sizeof(FLVContext),.audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,.video_codec = AV_CODEC_ID_FLV1,.write_header = flv_write_header,.write_packet = flv_write_packet,.write_trailer = flv_write_trailer,.codec_tag= (const AVCodecTag* const []) {flv_video_codec_ids, flv_audio_codec_ids, 0},.flags= AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |AVFMT_TS_NONSTRICT,};

从FLV对应的AVOutputFormat结构体的定义我们可以看出,write_trailer()指向了flv_write_trailer()函数。

flv_write_trailer()

flv_write_trailer()函数的定义位于libavformat\flvenc.c,如下所示。

static int flv_write_trailer(AVFormatContext *s){int64_t file_size;AVIOContext *pb = s->pb;FLVContext *flv = s->priv_data;int i;/* Add EOS tag */for (i = 0; i < s->nb_streams; i++) {AVCodecContext *enc = s->streams[i]->codec;FLVStreamContext *sc = s->streams[i]->priv_data;if (enc->codec_type == AVMEDIA_TYPE_VIDEO &&(enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4))put_avc_eos_tag(pb, sc->last_ts);}file_size = avio_tell(pb);/* update information */if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0)av_log(s, AV_LOG_WARNING, "Failed to update header with correct duration.\n");elseput_amf_double(pb, flv->duration / (double)1000);if (avio_seek(pb, flv->filesize_offset, SEEK_SET) < 0)av_log(s, AV_LOG_WARNING, "Failed to update header with correct filesize.\n");elseput_amf_double(pb, file_size);avio_seek(pb, file_size, SEEK_SET);return 0;}从flv_write_trailer()的源代码可以看出该函数做了以下两步工作:(1)如果视频流是H.264,则添加包含EOS(End Of Stream) NALU的Tag。(2)更新FLV的时长信息,以及文件大小信息。其中,put_avc_eos_tag()函数用于添加包含EOS NALU的Tag(包含结尾的一个PreviousTagSize),如下所示。static void put_avc_eos_tag(AVIOContext *pb, unsigned ts){avio_w8(pb, FLV_TAG_TYPE_VIDEO);avio_wb24(pb, 5);/* Tag Data Size */avio_wb24(pb, ts);/* lower 24 bits of timestamp in ms */avio_w8(pb, (ts >> 24) & 0x7F); /* MSB of ts in ms */avio_wb24(pb, 0);/* StreamId = 0 */avio_w8(pb, 23);/* ub[4] FrameType = 1, ub[4] CodecId = 7 */avio_w8(pb, 2);/* AVC end of sequence */avio_wb24(pb, 0);/* Always 0 for AVC EOS. */avio_wb32(pb, 16);/* Size of FLV tag */}可以参考FLV封装格式理解上述函数。由于前面的文章中已经描述过FLV封装格式,在这里不再重复叙述,在这里仅在此记录一下AVCVIDEOPACKET的格式,如下所示。最快乐的时候,就是去旅行。

av_write_trailer()

相关文章:

你感兴趣的文章:

标签云: