FFMPeg代码分析:AVFormatContext结构体

从先前的demo中可以看到,进入main函数所定义的第一个变量就是AVFormatContext的指针:

int main(int argc, char *argv[])
{
	AVFormatContext *pFormatCtx = NULL;
        ....
}

而且,往下看就会知道这个结构体将贯穿函数始终,avformat_open_input、av_find_stream_info、av_read_frame、av_close_input_file都需要这个结构作为参数。该结构在FFMPEG中的作用是解封装,即根据视频文件容器的后缀名(.mp4, .avi, .flv等)分析其中编码过的音频和视频流的保存方式并将其提取以供解码器进行解码。该结构的部分定义如下:

typedef struct AVFormatContext
{
    const AVClass *av_class;
    struct AVInputFormat *iformat;
    struct AVOutputFormat *oformat;
    void *priv_data;
    AVIOContext *pb;
    int ctx_flags;
    unsigned int nb_streams;
    AVStream **streams;
    char filename[1024];
    int64_t start_time;
    int64_t duration;
    int bit_rate;
    unsigned int packet_size;
    int max_delay;
    int flags;
#define AVFMT_FLAG_GENPTS       0x0001 ///< Generate missing pts even if it requires parsing future frames.
#define AVFMT_FLAG_IGNIDX       0x0002 ///< Ignore index.
#define AVFMT_FLAG_NONBLOCK     0x0004 ///< Do not block when reading packets from input.
#define AVFMT_FLAG_IGNDTS       0x0008 ///< Ignore DTS on frames that contain both DTS & PTS
#define AVFMT_FLAG_NOFILLIN     0x0010 ///< Do not infer any values from other values, just return what is stored in the container
#define AVFMT_FLAG_NOPARSE      0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled
#define AVFMT_FLAG_NOBUFFER     0x0040 ///< Do not buffer frames when possible
#define AVFMT_FLAG_CUSTOM_IO    0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it.
#define AVFMT_FLAG_DISCARD_CORRUPT  0x0100 ///< Discard frames marked corrupted
#define AVFMT_FLAG_FLUSH_PACKETS    0x0200 ///< Flush the AVIOContext every packet.
#define AVFMT_FLAG_MP4A_LATM    0x8000 ///< Enable RTP MP4A-LATM payload
#define AVFMT_FLAG_SORT_DTS    0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down)
#define AVFMT_FLAG_PRIV_OPT    0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted)
#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate.

    unsigned int probesize;

    int max_analyze_duration;

    const uint8_t *key;
    int keylen;

    unsigned int nb_programs;
    AVProgram **programs;

    enum AVCodecID video_codec_id;

    enum AVCodecID audio_codec_id;

    enum AVCodecID subtitle_codec_id;

    unsigned int max_index_size;

    unsigned int max_picture_buffer;

    unsigned int nb_chapters;
    AVChapter **chapters;

    AVDictionary *metadata;

    int64_t start_time_realtime;

    int fps_probe_size;

    int error_recognition;

    AVIOInterruptCB interrupt_callback;

    int debug;
#define FF_FDEBUG_TS        0x0001

    int ts_id;

    int audio_preload;

    int max_chunk_duration;

    int max_chunk_size;

    int use_wallclock_as_timestamps;

    int avoid_negative_ts;

    int avio_flags;

    enum AVDurationEstimationMethod duration_estimation_method;

    unsigned int skip_initial_bytes;

    unsigned int correct_ts_overflow;

    int seek2any;

    int flush_packets;

    int probe_score;
} AVFormatContext;

该定义中部分成员变量的作用,其中与解码相关的由红色标出:
const AVClass *av_class:用于记录日志和设置AVOption的类,由avformat_alloc_context()设置。AVClass类中保存了包含该成员的类名、对象名和AVOption实例等信息;

AVInputFormat *iformat / AVOutputFormat *oformat:输入/输出格式,或为输入或为输出,这两个不能同时存在;分别用于编码和解码。AVOutputFormat类保存了文件名、扩展名、默认音视频和字幕解码器等信息;

void *priv_data:私有数据,只有iformat/oformat.priv_class非0时有效;

AVIOContext *pb:指向IO数据的缓存的指针;

int ctx_flags:媒体流信息;

unsigned int nb_streams:包含的媒体流的数量;

AVStream **streams:指向实际输入/输出数据的指针;

char filename[1024]:输入或输出的文件名;

int64_t start_time / int64_t duration:媒体的开始时间和持续时长,一般由AVStream **streams导出;

int bit_rate:视频码率,由ffmpeg自动计算;

unsigned int packet_size:包尺寸;

int max_delay:最大延迟;

unsigned int probesize:在解码时用于探测的数据的大小,编码时不使用;

int max_analyze_duration:允许输入数据在avformat_find_stream_info()中分析的最长时间;

const uint8_t *key:关键字;

int keylen:关键字长;

unsigned int nb_programs:包含节目的数量;

AVProgram **programs:指向实际节目的指针;

enum AVCodecID video_codec_id:指定的视频解码器ID;由用户指定;

enum AVCodecID audio_codec_id:指定的音频解码器ID;由用户指定;

enum AVCodecID subtitle_codec_id:指定的字幕解码器ID;由用户指定;

unsigned int max_index_size:用于检索码流的索引值的最大字长;

unsigned int max_picture_buffer:图像缓存的最大尺寸;

unsigned int nb_chapters:章节数目;

AVChapter **chapters:指向章节数据的指针;

AVDictionary *metadata:元数据;

int64_t start_time_realtime;:码流开始的实际时间,以毫秒为单位;

int fps_probe_size:表明使用了多少帧用于格式检测;

int error_recognition:错误检测的阈值;

AVIOInterruptCB interrupt_callback:在IO层用户自定义的回调结构;

int ts_id:传输流的ID;

unsigned int skip_initial_bytes:打开媒体流时跳过的字节数;

int seek2any:是否可以移动到任意一帧的位置;

int flush_packets:每处理一个packet之后刷新io上下文;

int probe_score:格式探测的分值,上限为AVPROBE_SCORE_MAX;

指向元数据的结构体AVDictionary由两部分组成:统计计数和指向AVDictionaryEntry的指针,也就是说AVDictionary可以说是AVDictionaryEntry的封装。

struct AVDictionary
{
    int count;
    AVDictionaryEntry *elems;
};
typedef struct AVDictionaryEntry
 {
    char *key;
    char *value;
} AVDictionaryEntry;

了解了AVFormatContext的构造之后,可以再来看看它是如何被初始化的了。在函数avformat_open_input中,AVFormatContext的初始化由avformat_alloc_context()及其调用的函数实现:

AVFormatContext *avformat_alloc_context(void)
{
    AVFormatContext *ic;
    ic = av_malloc(sizeof(AVFormatContext));
    if (!ic) return ic;
    avformat_get_context_defaults(ic);
    return ic;
}
static void avformat_get_context_defaults(AVFormatContext *s)
{
    memset(s, 0, sizeof(AVFormatContext));

    s->av_class = &av_format_context_class;

    av_opt_set_defaults(s);
}
void av_opt_set_defaults(void *s)
{
#if FF_API_OLD_AVOPTIONS
    av_opt_set_defaults2(s, 0, 0);
}
void av_opt_set_defaults2(void *s, int mask, int flags)
{
#endif
    const AVClass *class = *(AVClass **)s;
    const AVOption *opt = NULL;
    while ((opt = av_opt_next(s, opt)) != NULL) {
#if FF_API_OLD_AVOPTIONS
        if ((opt->flags & mask) != flags)
            continue;
#endif
        switch (opt->type) {
            case AV_OPT_TYPE_CONST:
                /* Nothing to be done here */
            break;
            case AV_OPT_TYPE_FLAGS:
            case AV_OPT_TYPE_INT:
            case AV_OPT_TYPE_INT64:
            case AV_OPT_TYPE_DURATION:
            case AV_OPT_TYPE_CHANNEL_LAYOUT:
                av_opt_set_int(s, opt->name, opt->default_val.i64, 0);
            break;
            case AV_OPT_TYPE_DOUBLE:
            case AV_OPT_TYPE_FLOAT: {
                double val;
                val = opt->default_val.dbl;
                av_opt_set_double(s, opt->name, val, 0);
            }
            break;
            case AV_OPT_TYPE_RATIONAL: {
                AVRational val;
                val = av_d2q(opt->default_val.dbl, INT_MAX);
                av_opt_set_q(s, opt->name, val, 0);
            }
            break;
            case AV_OPT_TYPE_COLOR:
            case AV_OPT_TYPE_STRING:
            case AV_OPT_TYPE_IMAGE_SIZE:
            case AV_OPT_TYPE_VIDEO_RATE:
                av_opt_set(s, opt->name, opt->default_val.str, 0);
                break;
            case AV_OPT_TYPE_PIXEL_FMT:
#if LIBAVUTIL_VERSION_MAJOR < 53
                if (class->version && class->version < AV_VERSION_INT(52, 10, 100))
                    av_opt_set(s, opt->name, opt->default_val.str, 0);
                else
#endif
                    av_opt_set_pixel_fmt(s, opt->name, opt->default_val.i64, 0);
                break;
            case AV_OPT_TYPE_SAMPLE_FMT:
#if LIBAVUTIL_VERSION_MAJOR < 53
                if (class->version && class->version < AV_VERSION_INT(52, 10, 100))
                    av_opt_set(s, opt->name, opt->default_val.str, 0);
                else
#endif
                    av_opt_set_sample_fmt(s, opt->name, opt->default_val.i64, 0);
                break;
            case AV_OPT_TYPE_BINARY:
                /* Cannot set default for binary */
            break;
            default:
                av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n", opt->type, opt->name);
        }
    }
}

可以看到,在av_opt_set_defaults2()函数中,首先会调用av_opt_next(s, opt),并根据opt->type的不同值来进行下一步的设置。av_opt_next()的代码:

const AVOption *av_opt_next(void *obj, const AVOption *last)
{
    AVClass *class = *(AVClass**)obj;
    if (!last && class && class->option && class->option[0].name)
        return class->option;
    if (last && last[1].name)
        return ++last;
    return NULL;
}

由于初始化时opt=NULL,因此该函数返回AVFormatContext::AVClass::option,并在下一次调用的时候作为参数传入。在options_table.h中可以看出该函数将avformat_options[]表中的设置数据逐条地返回给调用者,并通过其type进行下一步操作。以av_opt_set_int()为例:

int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
{
    return set_number(obj, name, 1, 1, val, search_flags);
}
static int set_number(void *obj, const char *name, double num, int den, int64_t intnum, int search_flags)
{
    void *dst, *target_obj;
    const AVOption *o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);

    if (!o || !target_obj)
        return AVERROR_OPTION_NOT_FOUND;

    dst = ((uint8_t*)target_obj) + o->offset;
    return write_number(obj, o, dst, num, den, intnum);
}

由此我们知道了,AVFormatContext结构体中AVOption的元素通过这些函数,由options_table.h中的预定义表生成。

时间: 2024-09-20 08:33:11

FFMPeg代码分析:AVFormatContext结构体的相关文章

FFMPeg代码分析:AVCodec结构体以及编解码器的查找和加载

书接上回.在调用av_find_stream_info函数分析媒体文件并找到其中的视频流之后,视频流的相关信息被存放在了AVFormatContext结构体实例中.此时AVCodecContext实例所保存的AVCodec仍然为空.该结构体的定义如下: typedef struct AVCodec { const char *name;//codec名称,如果是解码HEVC的文件,那就是"hevc" const char *long_name;//codec全名,"HEVC(

FFMPeg代码分析:AVCodecContext结构体

在调用avformat_open_input打开文件后,下一步调用av_find_stream_info函数从文件中读取音视频流的信息,而后AVFormatContext的结构体将会将这些信息保存在其中.在找到AVFormatContext的视频stream后,获取其codec保存到指向AVCodecContext的指针: // Find the first video stream for(i=0; i<pFormatCtx->nb_streams; i++) { if(pFormatCtx

FFMPeg代码分析:AVPacket结构体和av_read_frame函数

AVPacket结构用于保存压缩编码过的数据.在解码时,该结构的实例通常作为解复用器(demuxer)的输出并输入到解码器中:在编码时,通常是编码器的输出,并输入到复用器(muxer)中.该结构体的定义如下: typedef struct AVPacket { /** * A reference to the reference-counted buffer where the packet data is * stored. * May be NULL, then the packet dat

FFMPeg代码分析:AVFrame结构体及其相关的函数

AVFrame结构体保存的是解码后和原始的音视频信息.AVFrame通过函数av_frame_alloc()初始化,该函数仅仅分配AVFrame实例本身,而没有分配其内部的缓存.AVFrame实例由av_frame_free()释放:AVFrame实例通常分配一次,重复使用,如分配一个AVFrame实例来保留解码器中输出的视频帧(此时应在恰当的时候使用av_frame_unref()清理参考帧并将AVFrame归零).该类所描述的数据通常由AVBuffer的API来保存一个引用计数,并保存于AV

FFMPeg代码分析:av_read_frame()函数的内部构造

上文中贴出了av_read_frame()函数的实现,现在更细致地分析一下其内部的实现流程. av_read_frame()开始后,通常会调用read_frame_internal(s, pkt)函数: static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) { int ret = 0, i, got_packet = 0; av_init_packet(pkt); while (!got_packet && !s

FFMPeg代码分析:avcodec_decode_video2函数

该函数的作用是实现压缩视频的解码.在avcodec.h中的声明方式如下: int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, const AVPacket *avpkt); 待解码的数据保存在avpkt->data中,大小为avpkt->size:解码完成后,picture用于保存输出图像数据. 该方法的各个参数: AVCodecContext *avctx:编解码上下

编程-c/c++关于结构体的函数中调用时如何存储数据并实现排序

问题描述 c/c++关于结构体的函数中调用时如何存储数据并实现排序 template struct acc_med { inline acc_med(int poolHeight, int poolWidth, type derOutput = 0) : value(-std::numeric_limits::infinity()), derOutput(derOutput), derDataActivePt(NULL) { } int i; type* array; inline void a

结构体中指针赋值问题的分析及C代码示例

问题描述 某结构体的定义如下: typedef struct { int iAge; // 年龄 char szAddr1[100]; // 地址1 char *pszAddr2; // 地址2 char **pszAddr3; // 地址3 } T_PeopleInfo; 请问如何对结构体中的各个成员变量(尤其是指针变量)进行赋值? 问题分析及C代码示例 我们可以看到,在结构体T_PeopleInfo中,pszAddr2和pszAddr3均为指针,其中pszAddr2为一级指针,pszAddr

Go语言里的结构体文法实例分析_Golang

本文实例讲述了Go语言里的结构体文法.分享给大家供大家参考.具体分析如下: 结构体文法表示通过结构体字段的值作为列表来新分配一个结构体. 使用 Name: 语法可以仅列出部分字段.(字段名的顺序无关.) 特殊的前缀 & 构造了指向结构体文法的指针. 复制代码 代码如下: package main import "fmt" type Vertex struct {     X, Y int } var (     p = Vertex{1, 2}  // has type Ver