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中的预定义表生成。


上一篇:智捷教育关东升老师Java视频


下一篇:国际领先运营商财务状况分析:多元布局拉动规模增长