FFmpeg源代码简单分析:av_write_trailer()
=====================================================
FFmpeg的库函数源代码分析文章列表:
【架构图】
【通用】
FFmpeg 源代码简单分析:av_register_all()
FFmpeg 源代码简单分析:avcodec_register_all()
FFmpeg 源代码简单分析:内存的分配和释放(av_malloc()、av_free()等)
FFmpeg 源代码简单分析:常见结构体的初始化和销毁(AVFormatContext,AVFrame等)
FFmpeg 源代码简单分析:av_find_decoder()和av_find_encoder()
FFmpeg 源代码简单分析:avcodec_open2()
FFmpeg 源代码简单分析:avcodec_close()
【解码】
图解FFMPEG打开媒体的函数avformat_open_input
FFmpeg 源代码简单分析:avformat_open_input()
FFmpeg 源代码简单分析:avformat_find_stream_info()
FFmpeg 源代码简单分析:av_read_frame()
FFmpeg 源代码简单分析:avcodec_decode_video2()
FFmpeg 源代码简单分析:avformat_close_input()
【编码】
FFmpeg 源代码简单分析:avformat_alloc_output_context2()
FFmpeg 源代码简单分析:avformat_write_header()
FFmpeg 源代码简单分析:avcodec_encode_video()
FFmpeg 源代码简单分析:av_write_frame()
FFmpeg 源代码简单分析:av_write_trailer()
【其它】
FFmpeg源代码简单分析:日志输出系统(av_log()等)
FFmpeg源代码简单分析:结构体成员管理系统-AVClass
FFmpeg源代码简单分析:结构体成员管理系统-AVOption
FFmpeg源代码简单分析:libswscale的sws_getContext()
FFmpeg源代码简单分析:libswscale的sws_scale()
FFmpeg源代码简单分析:libavdevice的avdevice_register_all()
FFmpeg源代码简单分析:libavdevice的gdigrab
【脚本】
【H.264】
=====================================================
打算写两篇文章简单分析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; //写入AVPacket ret = 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"); else put_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"); else put_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的格式,如下所示。
可以看出包含EOS NALU的AVCVIDEOPACKET的AVCPacketType为2。在这种情况下,AVCVIDEOPACKET的CompositionTime字段取0,并且无需包含Data字段。
雷霄骅
leixiaohua1020@126.com
http://blog.csdn.net/leixiaohua1020
FFmpeg源代码简单分析:av_write_trailer()的更多相关文章
- FFmpeg源代码简单分析:libavdevice的gdigrab
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:libavdevice的avdevice_register_all()
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:configure
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:makefile
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:libswscale的sws_scale()
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:libswscale的sws_getContext()
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:结构体成员管理系统-AVOption
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:结构体成员管理系统-AVClass
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:日志输出系统(av_log()等)
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
随机推荐
- 绝世好题bzoj4300
Description 给定一个长度为n的数列ai,求ai的子序列bi的最长长度,满足bi&bi-1!=0(2<=i<=len). Input 输入文件共2行. 第一行包括一个整数 ...
- ●BOZJ 3144 [Hnoi2013]切糕
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3144 题解: "这是一个经典的最小割模型" ---引用自别人的博客 .. ...
- 例10-3 uva10375(唯一分解定理)
题意:已知C(m,n) = m!/(n!(m-n)!),已知p,q,r,s,求C(p,q)/C(r,s) 思路: 全部分解成质因子,相乘则加,除则减 #include <iostream> ...
- poj3185 高斯消元
The Water Bowls Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5329 Accepted: 2081 D ...
- java9学习之模块化
截止到目前JDK的版本已经更新到10了,虽然java9的生命周期才半年,但是我认为这个版本带来的变革是不可磨灭的,它是第一次深层次的针对架构以及依赖上的革新.下面我们就来学习一下. 一.模块化项目构建 ...
- 在 Mac OS X 上安装 TensorFlow
在 Mac OS X 上安装 TensorFlow 这个文档说明了如何在 Mac OS X 上安装 TensorFlow. 注意:从 1.2 版本开始,在 Mac OS X 上 TensorFlow ...
- javap -c命令详解
https://www.cnblogs.com/beautiful-code/p/6424977.html
- HTML中鼠标滚轮事件onmousewheel处理
滚轮事件是不同浏览器会有一点点区别,一个像Firefox使用DOMMouseScroll ,ff也可以使用addEventListener方法绑定DomMouseScroll事件,其他的浏览器滚轮事件 ...
- 用go实现常用算法与数据结构——队列(queue)
queue 简介 队列是一种非常常见的数据结构,日常生活中也能经常看到.一个典型的队列如下图(图片来自 segmentfault): 可以看出队列和我们日常生活中排队是基本一致的.都遵循 FIFO(F ...
- Node.js 集群
稳定性: 2 - 不稳定 单个 Node 实例运行在一个线程中.为了更好的利用多核系统的能力,可以启动 Node 集群来处理负载. 在集群模块里很容易就能创建一个共享所有服务器接口的进程. var c ...