http://blog.csdn.net/wstarx/article/details/1572393

FFMPEG源码分析(二)

http://www.cnblogs.com/qingquan/archive/2011/08/29/2158912.html

简介
FFmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。FFmpeg支持MPEG、DivX、MPEG4、AC3、DV、FLV等40多种编码,AVI、MPEG、OGG、Matroska、ASF等90多种解码.
TCPMP, VLC, MPlayer等开源播放器都用到了FFmpeg。
FFmpeg主目录下主要有libavcodec、libavformat和libavutil等子目录。其中libavcodec用于存放各 个encode/decode模块,libavformat用于存放muxer/demuxer模块,libavutil用于存放内存操作等常用模块。
以flash movie的flv文件格式为例, muxer/demuxer的flvenc.c和flvdec.c文件在libavformat目录下,encode/decode的mpegvideo.c和h263de.c在libavcodec目录下。
 
muxer/demuxer与encoder/decoder定义与初始化
muxer/demuxer和encoder/decoder在FFmpeg中的实现代码里,有许多相同的地方,而二者最大的差别是muxer 和demuxer分别是不同的结构AVOutputFormat与AVInputFormat,而encoder和decoder都是用的AVCodec 结构。
 
muxer/demuxer和encoder/decoder在FFmpeg中相同的地方有:
l         二者都是在main()开始的av_register_all()函数内初始化的。
l         二者都是以链表的形式保存在全局变量中的。
muxer/demuxer是分别保存在全局变量AVOutputFormat *first_oformat与AVInputFormat *first_iformat中的。
encoder/decoder都是保存在全局变量AVCodec *first_avcodec中的。
l         二者都用函数指针的方式作为开放的公共接口。
demuxer开放的接口有:
       int (*read_probe)(AVProbeData *);
       int(*read_header)(struct AVFormatContext *, AVFormatParameters *ap);
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
int (*read_close)(struct AVFormatContext *);
int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);
         muxer开放的接口有:
                   int (*write_header)(struct AVFormatContext *);
                   int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
                int (*write_trailer)(struct AVFormatContext *);
 
encoder/decoder的接口都是一样的,只不过二者分别只实现encoder和decoder函数:
int (*init)(AVCodecContext *);
               int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);
               int (*close)(AVCodecContext *);
                 int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, uint8_t *buf, int buf_size);
 
仍以flv文件为例来说明muxer/demuxer的初始化。
在libavformat/allformats.c文件的av_register_all(void)函数中,通过执行
REGISTER_MUXDEMUX(FLV, flv);
将支持flv 格式的flv_muxerflv_demuxer变量分别注册到全局变量first_oformatfirst_iformat链表的最后位置。
其中flv_muxer在libavformat/flvenc.c中定义如下:
       AVOutputFormat flv_muxer = {
    "flv",
    "flv format",
    "video/x-flv",
    "flv",
    sizeof(FLVContext),
#ifdef CONFIG_LIBMP3LAME
    CODEC_ID_MP3,
#else // CONFIG_LIBMP3LAME
    CODEC_ID_NONE,
    CODEC_ID_FLV1,
    flv_write_header,
    flv_write_packet,
    flv_write_trailer,
    .codec_tag= (const AVCodecTag*[]){flv_video_codec_ids, flv_audio_codec_ids, 0},
}
AVOutputFormat结构的定义如下:
typedef struct AVOutputFormat {
    const char *name;
    const char *long_name;
    const char *mime_type;
    const char *extensions; /**< comma separated filename extensions */
    /** size of private data so that it can be allocated in the wrapper */
    int priv_data_size;
    /* output support */
    enum CodecID audio_codec; /**< default audio codec */
    enum CodecID video_codec; /**< default video codec */
    int (*write_header)(struct AVFormatContext *);
    int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
    int (*write_trailer)(struct AVFormatContext *);
    /** can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER */
    int flags;
    /** currently only used to set pixel format if not YUV420P */
    int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *);
    int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush);
 
    /**
     * list of supported codec_id-codec_tag pairs, ordered by "better choice first"
     * the arrays are all CODEC_ID_NONE terminated
     */
    const struct AVCodecTag **codec_tag;
    /* private fields */
    struct AVOutputFormat *next;
} AVOutputFormat;
AVOutputFormat结构的定义可知,flv_muxer变 量初始化的第一、第二个成员分别为该muxer的名称与长名称,第三、第四个成员为所对应MIMIE Type和后缀名,第五个成员是所对应的私有结构的大小,第六、第七个成员为所对应的音频编码和视频编码类型ID,接下来就是三个重要的接口函数,该 muxer的功能也就是通过调用这三个接口实现的。
 
flv_demuxer在libavformat/flvdec.c中定义如下, 与flv_muxer类似,在这儿主要也是设置了5个接口函数,其中flv_probe接口用途是测试传入的数据段是否是符合当前文件格式,这个接口在匹配当前demuxer的时候会用到。
AVInputFormat flv_demuxer = {
    "flv",
    "flv format",
    0,
    flv_probe,
    flv_read_header,
    flv_read_packet,
    flv_read_close,
    flv_read_seek,
    .extensions = "flv",
    .value = CODEC_ID_FLV1,
};
 
在上述av_register_all(void)函数中通过执行libavcodec/allcodecs.c文件里的avcodec_register_all(void)函数来初始化全部的encoder/decoder。
 
因为不是每种编码方式都支持encode和decode,所以有以下三种注册方式:
#define REGISTER_ENCODER(X,x) /
                    if(ENABLE_##X##_ENCODER) register_avcodec(&x##_encoder)
#define REGISTER_DECODER(X,x) /
                  if(ENABLE_##X##_DECODER) register_avcodec(&x##_decoder)
#define REGISTER_ENCDEC(X,x)
 REGISTER_ENCODER(X,x); REGISTER_DECODER(X,x)
 
如支持flv的flv_encoderflv_decoder变量就分别是在libavcodec/mpegvideo.c和libavcodec/h263de.c中创建的。
 
当前muxer/demuxer的匹配
在FFmpeg的文件转换过程中,首先要做的就是根据传入文件和传出文件的后缀名匹配合适的demuxer和muxer。
匹配上的demuxer和muxer都保存在如下所示,定义在ffmpeg.c里的全局变量file_iformatfile_oformat中:
static AVInputFormat *file_iformat;
static AVOutputFormat *file_oformat;
 
1.         demuxer匹配
在libavformat/utils.c中的static AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据,依次调用每个demuxer的read_probe接口,来进行该demuxer是否和传入的文件内容匹配的判断。其调用顺序如下:
void parse_options(int argc, char **argv, const OptionDef *options)   
static void opt_input_file(const char *filename)
static void opt_input_file(const char *filename)
                     int av_open_input_file(…… )
                                     AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
static AVInputFormat *av_probe_input_format2(……)
                                                
opt_input_file函数是在保存在const OptionDef options[]数组中,用于void parse_options(int argc, char **argv, const OptionDef *options)中解析argv里的“-i” 参数,也就是输入文件名时调用的。
 
2.         muxer匹配
与demuxer的匹配不同,muxer的匹配是调用guess_format函数,根据main( ) 函数的argv里的输出文件后缀名来进行的。
void parse_options(int argc, char **argv, const OptionDef *options)
void parse_arg_file(const char *filename)
static void opt_output_file(const char *filename)
AVOutputFormat *guess_format(const char *short_name, const char *filename,
                             const char *mime_type)
 
当前encoder/decoder的匹配
main( )函数中除了解析传入参数并初始化demuxer与muxer的parse_options( )函数以外,其他的功能都是在av_encode( )函数里完成的。
在libavcodec/utils.c中有如下二个函数。
              AVCodec *avcodec_find_encoder(enum CodecID id)
                   AVCodec *avcodec_find_decoder(enum CodecID id)
他们的功能就是根据传入的CodecID,找到匹配的encoder和decoder。
 
av_encode( )函数的开头,首先初始化各个AVInputStreamAVOutputStream,然后分别调用上述二个函数,并将匹配上的encoder与decoder分别保存在AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codecAVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec变量中。
 
 
 
 
其他主要数据结构
1.        AVFormatContext
AVFormatContext是FFMpeg格式转换过程中实现输入和输出功能、保存相关数据的主要结构。每一个输入和输出文件,都在如下定义的指针数组全局变量中有对应的实体。
static AVFormatContext *output_files[MAX_FILES];
static AVFormatContext *input_files[MAX_FILES];
对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformatoformat成员赋值。
   struct AVInputFormat *iformat;
    struct AVOutputFormat *oformat;
对一个AVFormatContext来说,二个成员不能同时有值,即一个AVFormatContext不能同时含有demuxer和muxer。
main( )函数开头的parse_options( )函数中找到了匹配的muxer和demuxer之后,根据传入的argv参数,初始化每个输入和输出的AVFormatContext结构,并保存在相应的output_filesinput_files指针数组中。
av_encode( )函数中,output_filesinput_files是作为函数参数传入后,在其他地方就没有用到了。
 
2.         AVCodecContext
保存AVCodec指针和与codec相关的数据,如video的width、height,audio的sample rate等。AVCodecContext中的codec_typecodec_id二个变量对于encoder/decoder的匹配来说,最为重要。
          enum CodecType codec_type; /* see CODEC_TYPE_xxx */
                enum CodecID codec_id; /* see CODEC_ID_xxx */
 
如上所示,codec_type保存的是CODEC_TYPE_VIDEOCODEC_TYPE_AUDIO等媒体类型,
codec_id保存的是CODEC_ID_FLV1CODEC_ID_VP6F等编码方式。
 
以支持flv格式为例,在前述的av_open_input_file(…… ) 函数中,匹配到正确的AVInputFormat demuxer后,通过av_open_input_stream( )函数中调用AVInputFormatread_header接口来执行flvdec.c中的flv_read_header( )函数。在flv_read_header( )函数内,根据文件头中的数据,创建相应的视频或音频AVStream,并设置AVStreamAVCodecContext的正确的codec_type值。codec_id值是在解码过程中flv_read_packet( )函数执行时根据每一个packet头中的数据来设置的。
 
3.         AVStream
AVStream结构保存与数据流相关的编解码器,数据段等信息。比较重要的有如下二个成员:
            AVCodecContext *codec; /**< codec context */
void *priv_data;
其中codec指针保存的就是上节所述的encoder或decoder结构。priv_data指针保存的是和具体编解码流相关的数据,如下代码所示,在ASF的解码过程中,priv_data保存的就是ASFStream结构的数据。
    AVStream *st;
ASFStream *asf_st;   
     … …
st->priv_data = asf_st;
 
 
4.         AVInputStream/ AVOutputStream
根据输入和输出流的不同,前述的AVStream结构都是封装在AVInputStreamAVOutputStream结构中,在av_encode( )函数中使用。
AVInputStream中还保存的有与时间有关的信息。
AVOutputStream中还保存有与音视频同步等相关的信息。
 
5.         AVPacket
AVPacket结构定义如下,其是用于保存读取的packet数据。
typedef struct AVPacket {
    int64_t pts;            ///< presentation time stamp in time_base units
    int64_t dts;            ///< decompression time stamp in time_base units
    uint8_t *data;
    int   size;
    int   stream_index;
    int   flags;
    int   duration;        ///< presentation duration in time_base units (0 if not available)
    void (*destruct)(struct AVPacket *);
    void *priv;
    int64_t pos;           ///< byte position in stream, -1 if unknown
} AVPacket;
 
av_encode( )函数中,调用AVInputFormat(*read_packet)(struct AVFormatContext *, AVPacket *pkt);接口,读取输入文件的一帧数据保存在当前输入AVFormatContextAVPacket成员中。
 
av_encode函数主要流程
av_encode( )函数是FFMpeg中最重要的函数,编解码和输出等大部分功能都在此函数内完成,因此有必要详细描述一下这个函数的主要流程。
1.         input streams initializing
2.         output streams initializing
3.         encoders and decoders initializing
4.         set meta data information from input file if required.
5.         write output files header
6.         loop of handling each frame
a.       read frame from input file:
b.       decode frame data
c.       encode new frame data
d.       write new frame to output file
7.         write output files trailer
8.         close each encoder and decoder
 
 
 
 
FFMPEG相关堆栈调用:
源文件mp4:
v=h264,a=aac
 
#0  ff_decode_frame_props (avctx=0xb1f859c0, frame=0xad3135b4) at libavcodec/utils.c:848
#1  0x086bca46 in get_buffer_internal (avctx=0xb1f859c0, frame=0xad3135b4, flags=1) at libavcodec/utils.c:877
#2  0x086bcf93 in ff_get_buffer (avctx=0xb1f859c0, frame=0xad3135b4, flags=1) at libavcodec/utils.c:1012
#3  0x08610aca in thread_get_buffer_internal (avctx=0xb1f859c0, f=0xad313b74, flags=1) at libavcodec/pthread_frame.c:802
#4  0x08610cfa in ff_thread_get_buffer (avctx=0xb1f859c0, f=0xad313b74, flags=1) at libavcodec/pthread_frame.c:857
#5  0x083f6412 in alloc_picture (h=0xb3ae9020, pic=0xad3135b4) at libavcodec/h264_slice.c:254
#6  0x083f80ff in h264_frame_start (h=0xb3ae9020) at libavcodec/h264_slice.c:748
#7  0x083fb00f in ff_h264_decode_slice_header (h=0xb3ae9020, h0=0xb3ae9020) at libavcodec/h264_slice.c:1673
#8  0x08371d52 in decode_nal_units (h=0xb3ae9020, buf=0xb1f17b60 "", buf_size=250, parse_extradata=0) at libavcodec/h264.c:1570
#9  0x08372a90 in h264_decode_frame (avctx=0xb1f859c0, data=0xb1f85d80, got_frame=0xb1fa2090, avpkt=0xb1fa203c) at libavcodec/h264.c:1870
#10 0x0860f176 in frame_worker_thread (arg=0xb1fa1f6c) at libavcodec/pthread_frame.c:153
#11 0xb79afd4c in start_thread (arg=0xb3ae8b40) at pthread_create.c:308

FFMpeg框架代码阅读的更多相关文章

  1. [转]FFMpeg框架代码阅读

    简介 FFmpeg是一个集录制.转换.音/视频编码解码功能为一体的完整的开源解决方案. FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用.FFmpeg支持MPEG.Di ...

  2. 第07节-开源蓝牙协议BTStack框架代码阅读(下)

    上篇博客中已经对BTStack框架进行了较为详细的说明,本篇博客将进一步总结一下(由韦大仙笔记所得). 可以从5个方面来理解BTStack的框架: 1.硬件操作:hci_transport_t BTS ...

  3. 第07节-开源蓝牙协议BTStack框架代码阅读(上)

    首先来看一下,对于硬件操作,它是如何来进行处理的.在上篇文章中曾说过,在main函数里面它会调用硬件相关的代码,调用操作系统相关的代码.在BTStack中,可以搜索一下main.c,将会发现有很多ma ...

  4. Deepctr框架代码阅读

    DeepCtr是一个简易的CTR模型框架,集成了深度学习流行的所有模型,适合学推荐系统模型的人参考. 我在参加比赛中用到了这个框架,但是效果一般,为了搞清楚原因从算法和框架两方面入手.在读代码的过程中 ...

  5. CI框架源代码阅读笔记3 全局函数Common.php

    从本篇開始.将深入CI框架的内部.一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说.全局函数具有最高的载入优先权.因此大多数的框架中BootStrap ...

  6. MediaInfo代码阅读

      MediaInfo是一个用来分析媒体文件的开源工具. 支持的文件非常全面,基本上支持所有的媒体文件. 最近是在做HEVC开发,所以比较关注MediaInfo中关于HEVC的分析与处理. 从Meid ...

  7. 脚本病毒分析扫描专题2-Powershell代码阅读扫盲

    4.2.PowerShell 为了保障木马样本的体积很小利于传播.攻击者会借助宏->WMI->Powershell的方式下载可执行文件恶意代码.最近也经常会遇见利用Powershell通过 ...

  8. CI框架源代码阅读笔记2 一切的入口 index.php

    上一节(CI框架源代码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程.这里再次贴出流程图.以备參考: 作为CI框架的入口文件.源代码阅读,自然由此開始. 在源代码阅读的 ...

  9. CGAL代码阅读跳坑指南

    CGAL代码阅读跳坑指南 整体框架介绍 CGAL中的算法和数据结构由它们使用的对象类型和操作参数化.它们可以处理满足特定语法和语义需求的任何具体模板参数.为了避免长参数列表,参数类型被收集到一个单独的 ...

随机推荐

  1. Linux基础系列-Day6

    Samba服务(基于CentOS 7.0) Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成,Samba主要用于Linux或UNIX和Windows系统之 ...

  2. C++中的error C2662,const的this指针问题

    今天在写C++代码的时候遇到一个错误,涉及到了常量的this指针的问题. 简化后的代码如下: #include <iostream> #include <string> usi ...

  3. 【BZOJ 3669】 3669: [Noi2014]魔法森林 (动态spfa)

    3669: [Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N ...

  4. Java 线程池的实现

    http://blog.csdn.net/iterzebra/article/details/6758481 http://blog.sina.com.cn/s/blog_4914a33b010118 ...

  5. JZYZOJ1530 [haoi2013]开关控制 状压 dfs 折半搜索

    http://172.20.6.3/Problem_Show.asp?id=1530 元宵节快要到了,某城市人民公园将举办一次灯展.Dr.Kong准备设计出一个奇妙的展品,他计划将编号为1到N的N(1 ...

  6. JZYZOJ1349 SPOJ839 星屑幻想 xor 网络流 最大流

    http://172.20.6.3/Problem_Show.asp?id=1349 调了两个小时发现数组开小了[doge].题意:给出几个点,有的点的权值确定,连接两点的边的权值为两点值的异或和,求 ...

  7. [TCO2013]DirectionBoard

    题意:给一个网格,每个格子有一个方向表示在这个格子上要往哪个方向走,你可以改变某些格子的方向,问最少多少次操作使得从任意格子出发都能回到这个格子 woc这都不会我还是回家种田去吧... 题目的要求是改 ...

  8. [PKUSC2018]最大前缀和

    [PKUSC2018]最大前缀和 题目大意: 有\(n(n\le20)\)个数\(A_i(|A_i|\le10^9)\).求这\(n\)个数在随机打乱后最大前缀和的期望值与\(n!\)的积在模\(99 ...

  9. Redis简单入门认识

    写在前面: 最近一直都在按照老大的学习路线来进行学习,这几天看了下redis,从刚开始的摸不着头脑,到后面慢慢的查资料,对其逐渐有了简单的了解,也通过一个与ssm框架整合的小demo去动手实践 了,知 ...

  10. JavaScript之引用类型(Object类型)

    ECMAScript提供了很多原生的引用类型,以便开发人员进行常见的计算任务. 对象是某一个特定引用类型的的实例. Object类型 用的最多.虽然这个Object实例不具备多少功能,但是在应用程序的 ...