文档章节

FFmpeg源代码简单分析:av_write_trailer()

abcijkxyz
 abcijkxyz
发布于 2016/08/06 12:24
字数 1324
阅读 12
收藏 0

=====================================================

FFmpeg的库函数源代码分析文章列表:

【架构图】

FFmpeg源代码结构图 - 解码

FFmpeg源代码结构图 - 编码

【通用】

FFmpeg 源代码简单分析:av_register_all()

FFmpeg 源代码简单分析:avcodec_register_all()

FFmpeg 源代码简单分析:内存的分配和释放(av_malloc()av_free()等)

FFmpeg 源代码简单分析:常见结构体的初始化和销毁(AVFormatContextAVFrame等)

FFmpeg 源代码简单分析:avio_open2()

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源代码简单分析:libswscalesws_getContext()

FFmpeg源代码简单分析:libswscalesws_scale()

FFmpeg源代码简单分析:libavdeviceavdevice_register_all()

FFmpeg源代码简单分析:libavdevicegdigrab

【脚本】

FFmpeg源代码简单分析:makefile

FFmpeg源代码简单分析:configure

【H.264】

FFmpegH.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



本文转载自:http://blog.csdn.net/leixiaohua1020/article/details/44201645

abcijkxyz
粉丝 63
博文 6196
码字总数 1876
作品 0
深圳
项目经理
私信 提问
最简单的基于FFmpeg的AVUtil例子 (AVLog, AVOption等)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/leixiaohua1020/article/details/46890739 本文的示例程序记录了FFmpeg的libavutil中几种工具函数的使用方法:...

雷霄骅
2015/07/18
0
0
FFmpeg命令行工具学习(五):FFmpeg 编解码 API 分析

在上一篇文章 FFmpeg命令行工具学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编解码时使用的A...

灰色飘零
2018/07/20
0
0
android端采用FFmpeg进行音频混合与拼接剪切

接触FFmpeg有一段时间了,它是音视频开发的开源库,几乎其他所有播放器、直播平台都基于FFmpeg进行二次开发。本篇文章来总结下采用FFmpeg进行音频处理:音频混合、音频剪切、音频拼接与音频转...

mp624183768
2018/05/13
0
0
关于ffmpeg编解码的问题,主要是关于ffmpeg网络流录制的

我现在有几个疑问: 我用ffmpeg做音频的编解码,在编码之前可能需要先做解码,假设编码的帧是一个超大帧,通过网络传输,理论上是应该先把帧折成小块封装成一定格式的数据包(有协议,协议里...

chentian08
2014/07/11
1K
0
FFmpeg 是如何实现多态的?

前言 众所周知,FFmpeg 在解码的时候,无论输入文件是 MP4 文件还是 FLV 文件,或者其它文件格式,都能正确解封装、解码,而代码不需要针对不同的格式做出任何改变,这是面向对象中很常见的多...

shzwork
03/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周二乱弹 —— 吾不好梦中插人

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @鱼豆腐233 :#今日歌曲分享# 分享My Chemical Romance的单曲《I Don't Love You》: 《I Don't Love You》- My Chemical Romance 手机党少年们...

小小编辑
今天
260
12
ss5 vpn 安装(linux版本)

1. 创建一个文件夹 /ss5 你也可以自定义,不过后续的地方需要注意自己的地址 2. 下载ss5文件(如果你的服务器没有安装wget请使用 yum -y install wget 命令安装 如果连yum都没安装自己查去)(下...

太黑_thj
今天
2
0
八、RabbitMQ的集群原理

集群架构 写在前面 RabbitMQ集群是按照低延迟环境设计的,千万不要跨越WAN或者互联网来搭建RabbitMQ集群。如果一定要在高延迟环境下使用RabbitMQ集群,可以参考使用Shovel和Federation工具。...

XuePeng77
今天
7
0
mac系统下,brew 安装mysql,用终端可以连接,navicat却连接不上?

问题: 1.报错? 2059 - Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(../Frameworks/caching_sha2_password.so, 2): image not found 2.自己通过设置,已经把密......

写bug的攻城狮
昨天
3
0
老生常谈,HashMap的死循环

问题 最近的几次面试中,我都问了是否了解HashMap在并发使用时可能发生死循环,导致cpu100%,结果让我很意外,都表示不知道有这样的问题,让我意外的是面试者的工作年限都不短。 由于HashMap...

群星纪元
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部