ffmpeg源码分析 (二)

前言

    本文主要介绍了一些在ffmpeg中经常用到的方法以及一些常用结构体,本文将会在系列过程中不断完善,如果你发现本文依然很简陋,不要着急,慢慢会丰富起来的。

内存分配方法

    内存管理永远是c的精髓,即便受无数人诟病,戕害了一代代程序员,但是本身却依旧如此具有魅力。

av_malloc / av_mallocz

    这是在ffmpeg中经常出现的一个方法,用于分配内存空间。定义在mem.c文件中

void *av_malloc(size_t size)
{
    void *ptr = NULL;

    /* let's disallow possibly ambiguous cases */
    if (size > (max_alloc_size - 32))
        return NULL;

#if HAVE_POSIX_MEMALIGN
    if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation
    if (posix_memalign(&ptr, ALIGN, size))
        ptr = NULL;
#elif HAVE_ALIGNED_MALLOC
    ptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__
    ptr = memalign(ALIGN, size);
#else
    ptr = memalign(size, ALIGN);
#endif
    /* Why 64?
     * Indeed, we should align it:
     *   on  4 for 386
     *   on 16 for 486
     *   on 32 for 586, PPro - K6-III
     *   on 64 for K7 (maybe for P3 too).
     * Because L1 and L2 caches are aligned on those values.
     * But I don't want to code such logic here!
     */
    /* Why 32?
     * For AVX ASM. SSE / NEON needs only 16.
     * Why not larger? Because I did not see a difference in benchmarks ...
     */
    /* benchmarks with P3
     * memalign(64) + 1          3071, 3051, 3032
     * memalign(64) + 2          3051, 3032, 3041
     * memalign(64) + 4          2911, 2896, 2915
     * memalign(64) + 8          2545, 2554, 2550
     * memalign(64) + 16         2543, 2572, 2563
     * memalign(64) + 32         2546, 2545, 2571
     * memalign(64) + 64         2570, 2533, 2558
     *
     * BTW, malloc seems to do 8-byte alignment by default here.
     */
#else
    ptr = malloc(size);
#endif
    if(!ptr && !size) {
        size = 1;
        ptr= av_malloc(1);
    }
#if CONFIG_MEMORY_POISONING
    if (ptr)
        memset(ptr, FF_MEMORY_POISON, size);
#endif
    return ptr;
}

void *av_mallocz(size_t size)
{
    void *ptr = av_malloc(size);
    if (ptr)
        memset(ptr, 0, size);
    return ptr;
}

    方法看上去很复杂,但是实际上多是多平台处理的代码,实际作用大概就是调用memalign(malloc或realloc返回的内存块地址都是8的倍数(如果是64位系统,则为16的倍数)。如果你需要更大的粒度,请使用memalign或valloc。)来分配指定大小的内存空间。

    av_mallocz 方法是在av_malloc基础上再将分配所得的内存全部空间都置为0.

 

av_realloc / av_realloc_f / av_reallocp

void *av_realloc(void *ptr, size_t size)
{
    /* let's disallow possibly ambiguous cases */
    if (size > (max_alloc_size - 32))
        return NULL;

#if HAVE_ALIGNED_MALLOC
    return _aligned_realloc(ptr, size + !size, ALIGN);
#else
    return realloc(ptr, size + !size);
#endif
}

void *av_realloc_f(void *ptr, size_t nelem, size_t elsize)
{
    size_t size;
    void *r;

    if (av_size_mult(elsize, nelem, &size)) {
        av_free(ptr);
        return NULL;
    }
    r = av_realloc(ptr, size);
    if (!r)
        av_free(ptr);
    return r;
}

int av_reallocp(void *ptr, size_t size)
{
    void *val;

    if (!size) {
        av_freep(ptr);
        return 0;
    }

    memcpy(&val, ptr, sizeof(val));
    val = av_realloc(val, size);

    if (!val) {
        av_freep(ptr);
        return AVERROR(ENOMEM);
    }

    memcpy(ptr, &val, sizeof(val));
    return 0;
}

    用于重新分配内存大小,相当于realloc不同的是在分配的时候会对size进行一些操作。假设我的size是110101,那么!size就是 1010,相加之后就是 111111,变成了一个2的指数倍数。相当于对其了。

    av_realloc_f作用类似,他会把两个size_t表示的小相乘,再调用av_realloc进行重新分配。

    av_reallocp 是现将原来内存中的内容拷贝出来,再使用av_realoc进行分配内存,分配完成之后再将原来的内存拷回去。

    

av_free / av_freep

void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC
    _aligned_free(ptr);
#else
    free(ptr);
#endif
}

void av_freep(void *arg)
{
    void *val;

    memcpy(&val, arg, sizeof(val));
    memcpy(arg, &(void *){ NULL }, sizeof(val));
    av_free(val);
}

    av_free表示其实就是free,释放内存。

    av_freep的作用是释放内存,并且将指针置NULL。

常用结构体

 

AVFormatContext

    这个结构体在ffmpeg中基本上是一个基础一样的存在,主要用于处理封装(muxer)和解封装(demuxer)。

    定义在avformat.h头文件中。一个视频是由多个音频通道和视频通道组成的,这里在demuxer时就会将元视频的所有通道拆分成出来。

/**
 * Format I/O context.
 * sizeof(AVFormatContext) must not be used outside libav*, use
 * 使用 avformat_alloc_context() 方法来创建AVFormatContext 结构体
 * 使用 avformat_free_context() 来释放
 *
 */
typedef struct AVFormatContext {
    
     /**
     * avformat_alloc_context() 方法自动设置,包含一些预设的信息。
     * Exports (de)muxer private options if they exist.
     */
    const AVClass *av_class;

    /**
     *输入数据的格式,仅在解封装的时候使用,一般会通过avformat_open_input方法赋值
     *
     * Demuxing only, set by avformat_open_input().
     */
    struct AVInputFormat *iformat;

    /**
     * 仅在封装的时候使用,用户自己设置,并且需要在调用avformat_write_header之前调用
     *
     * Muxing only, must be set by the caller before avformat_write_header().
     */
    struct AVOutputFormat *oformat;

    
    ......

    /**
     * I/O context.
     *
     * - demuxing: either set by the user before avformat_open_input() (then
     *             the user must close it manually) or set by avformat_open_input().
     * - muxing: set by the user before avformat_write_header(). The caller must
     *           take care of closing / freeing the IO context.
     *
     * Do NOT set this field if AVFMT_NOFILE flag is set in
     * iformat/oformat.flags. In such a case, the (de)muxer will handle
     * I/O in some other way and this field will be NULL.
     * 输入数据的缓存
     */
    AVIOContext *pb;

    /**
     * Flags modifying the (de)muxer behaviour. A combination of AVFMT_FLAG_*.
     * Set by the user before avformat_open_input() / avformat_write_header().
     * 这是一个标志位,用来标记 demuxer和muxer中的一些情况
     */
    int flags;

   .....

    /**
     * Number of elements in AVFormatContext.streams.
     *
     * Set by avformat_new_stream(), must not be modified by any other code.
     * 音视频流的个数
     */
    unsigned int nb_streams;
    /**
     * A list of all streams in the file. New streams are created with
     * avformat_new_stream().
     *
     * - demuxing: streams are created by libavformat in avformat_open_input().
     *             If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also
     *             appear in av_read_frame().
     * - muxing: streams are created by the user before avformat_write_header().
     *
     * Freed by libavformat in avformat_free_context().
     * 音视频流
     */
    AVStream **streams;

    .....


    /**
     * 输入或者输出文件地址
     *
     * - demuxing: 通过 avformat_open_input() 设置
     * - muxing: 用户自己设置
     *
     */
    char *url;

    /**
     * Position of the first frame of the component, in
     * AV_TIME_BASE fractional seconds. NEVER set this value directly:
     * It is deduced from the AVStream values.
     *
     * Demuxing only, set by libavformat.
     */
    int64_t start_time;

    /**
     * 时长 单位:微秒us,转换为秒需要除以1000000)
     */
    int64_t duration;

    /**
     * 比特率 比特率(单位bps,转换为kbps需要除以1000)
     */
    int64_t bit_rate;

    ....

    /**
     * 元数据
     */
    AVDictionary *metadata;

    ...

} AVFormatContext;

 

avformat_alloc_context方法实现在option.c文件中,

AVFormatContext *avformat_alloc_context(void)
{
    AVFormatContext *ic;
    ic = av_malloc(sizeof(AVFormatContext));
    if (!ic) return ic;
    avformat_get_context_defaults(ic);

    ic->internal = av_mallocz(sizeof(*ic->internal));
    if (!ic->internal) {
        avformat_free_context(ic);
        return NULL;
    }
    ic->internal->offset = AV_NOPTS_VALUE;
    ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
    ic->internal->shortest_end = AV_NOPTS_VALUE;

    return ic;
}

    除了分配对象之外,还初始化了一些参数。比如internal,av_class等。

 

AVStream

    AVStream是存储每一个视频/音频流信息的结构体,定义在avformat.h中

typedef struct AVStream {
    int index;    /**< stream index in AVFormatContext 一般AVStream都会数组形式存储在一个AVFormatContext中,而这个index就表示它在这个数组中的位置*/
    /**
     * Format-specific stream ID.
     * decoding: set by libavformat
     * encoding: set by the user, replaced by libavformat if left unset
     */
    int id;
#if FF_API_LAVF_AVCTX
    /**
     * @deprecated use the codecpar struct instead 
     * 用来描述这个流的codec信息 新版本中AVStream中这个字段即将被废弃,已经不建议使用
     */
    attribute_deprecated
    AVCodecContext *codec;
#endif
    void *priv_data;

    /**
     * This is the fundamental unit of time (in seconds) in terms
     * of which frame timestamps are represented.
     * 时基。通过该值可以把PTS,DTS转化为真正的时间。PTS*time_base=真正的时间
     * decoding: set by libavformat
     * encoding: May be set by the caller before avformat_write_header() to
     *           provide a hint to the muxer about the desired timebase. In
     *           avformat_write_header(), the muxer will overwrite this field
     *           with the timebase that will actually be used for the timestamps
     *           written into the file (which may or may not be related to the
     *           user-provided one, depending on the format).
     */
    AVRational time_base;

    /**
     * Decoding: pts of the first frame of the stream in presentation order, in stream time base.
     * Only set this if you are absolutely 100% sure that the value you set
     * it to really is the pts of the first frame.
     * This may be undefined (AV_NOPTS_VALUE).
     * @note The ASF header does NOT contain a correct start_time the ASF
     * demuxer must NOT set this.
     */
    int64_t start_time;

    /**
     * Decoding: duration of the stream, in stream time base.
     * If a source file does not specify a duration, but does specify
     * a bitrate, this value will be estimated from bitrate and file size.
     *
     * Encoding: May be set by the caller before avformat_write_header() to
     * provide a hint to the muxer about the estimated duration.
     * 视频/音频流长度
     */
    int64_t duration;

    int64_t nb_frames;                 ///< number of frames in this stream if known or 0

    int disposition; /**< AV_DISPOSITION_* bit field */

    enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed.

    /**
     * sample aspect ratio (0 if unknown)
     * - encoding: Set by user.
     * - decoding: Set by libavformat.
     */
    AVRational sample_aspect_ratio;
    
    /**
     * 元数据信息
     */
    AVDictionary *metadata;

    /**
     * Average framerate
     * 帧率
     * - demuxing: May be set by libavformat when creating the stream or in
     *             avformat_find_stream_info().
     * - muxing: May be set by the caller before avformat_write_header().
     */
    AVRational avg_frame_rate;

    /**
     * For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet
     * will contain the attached picture.
     * 附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面。
     * decoding: set by libavformat, must not be modified by the caller.
     * encoding: unused
     */
    AVPacket attached_pic;

    ........

    /**
     * Codec parameters associated with this stream. Allocated and freed by
     * libavformat in avformat_new_stream() and avformat_free_context()
     * respectively.
     * 新版本中替代AVCodecContext的结构体,用来表示codec的一些参数。能够和AVCodecContext相互转化
     *    avcodec_parameters_to_context
     *    avcodec_parameters_from_context
     * - demuxing: filled by libavformat on stream creation or in
     *             avformat_find_stream_info()
     * - muxing: filled by the caller before avformat_write_header()
     */
    AVCodecParameters *codecpar;

   ...........

} AVStream;

    该结构体一般会在avformat_new_stream()中进行初始化,并且填入AVFormatContext中。avformat_new_stream这个方法一般会在AVInputFormat的read_head中调用,但是到了现在的ffmpeg这个规则已经松动,不是所有情况都会在read_head中调用的。

AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
{
    AVStream *st;
    int i;
    AVStream **streams;

    if (s->nb_streams >= FFMIN(s->max_streams, INT_MAX/sizeof(*streams))) {
        if (s->max_streams < INT_MAX/sizeof(*streams))
            av_log(s, AV_LOG_ERROR, "Number of streams exceeds max_streams parameter (%d), see the documentation if you wish to increase it\n", s->max_streams);
        return NULL;
    }
    //分配内存
    streams = av_realloc_array(s->streams, s->nb_streams + 1, sizeof(*streams));
    if (!streams)
        return NULL;
    s->streams = streams;

    st = av_mallocz(sizeof(AVStream));
    if (!st)
        return NULL;
    if (!(st->info = av_mallocz(sizeof(*st->info)))) {
        av_free(st);
        return NULL;
    }
    st->info->last_dts = AV_NOPTS_VALUE;

#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
    //初始化 AVCodecContext 之后版本这一步将不再需要
    st->codec = avcodec_alloc_context3(c);
    if (!st->codec) {
        av_free(st->info);
        av_free(st);
        return NULL;
    }
FF_ENABLE_DEPRECATION_WARNINGS
#endif

    st->internal = av_mallocz(sizeof(*st->internal));
    if (!st->internal)
        goto fail;
    //初始化 AVCodecParameters
    st->codecpar = avcodec_parameters_alloc();
    if (!st->codecpar)
        goto fail;
    //以下代码为初始化的AVSteam填入默认值
    st->internal->avctx = avcodec_alloc_context3(NULL);
    if (!st->internal->avctx)
        goto fail;

    ........

    s->streams[s->nb_streams++] = st;
    return st;
fail:
    free_stream(&st);
    return NULL;
}

    avcodec_alloc_context3的代码实际上并不复杂,不再贴出来,作用就是分配AVCodecContext的内存,并且附上默认值……

    avcodec_parameters_alloc实际上也是这样,分配内存,附上默认值。    

AVCodec

    表示一个或者编码器组件,类似于AVOutputFormat和AVInputFormat。

 

AVCodecContext

    ffmpeg中最重要,也是最庞大的一个结构体,就像AVFormatContext表示demuxer和muxer的上下文,那么AVCodecContext久表示了encodec和decodec的上下文。

 

AVOutputFormat

    描述一个 muxer组件。

    

 

AVInputFormat

    描述一个demuxer组件。

 

URLProtocol

    描述一个协议,比如文件协议,比如rtsp协议等。定义了打开文件,读取文件的函数指针。

typedef struct URLProtocol {
    const char *name;
    int     (*url_open)( URLContext *h, const char *url, int flags);
    /**
     * This callback is to be used by protocols which open further nested
     * protocols. options are then to be passed to ffurl_open()/ffurl_connect()
     * for those nested protocols.
     */
    int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
    int     (*url_accept)(URLContext *s, URLContext **c);
    int     (*url_handshake)(URLContext *c);

    /**
     * Read data from the protocol.
     * If data is immediately available (even less than size), EOF is
     * reached or an error occurs (including EINTR), return immediately.
     * Otherwise:
     * In non-blocking mode, return AVERROR(EAGAIN) immediately.
     * In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
     * and return AVERROR(EAGAIN) on timeout.
     * Checking interrupt_callback, looping on EINTR and EAGAIN and until
     * enough data has been read is left to the calling function; see
     * retry_transfer_wrapper in avio.c.
     */
    int     (*url_read)( URLContext *h, unsigned char *buf, int size);
    int     (*url_write)(URLContext *h, const unsigned char *buf, int size);
    int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
    int     (*url_close)(URLContext *h);
    int (*url_read_pause)(URLContext *h, int pause);
    int64_t (*url_read_seek)(URLContext *h, int stream_index,
                             int64_t timestamp, int flags);
    int (*url_get_file_handle)(URLContext *h);
    int (*url_get_multi_file_handle)(URLContext *h, int **handles,
                                     int *numhandles);
    int (*url_get_short_seek)(URLContext *h);
    int (*url_shutdown)(URLContext *h, int flags);
    int priv_data_size;
    const AVClass *priv_data_class;
    int flags;
    int (*url_check)(URLContext *h, int mask);
    int (*url_open_dir)(URLContext *h);
    int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
    int (*url_close_dir)(URLContext *h);
    int (*url_delete)(URLContext *h);
    int (*url_move)(URLContext *h_src, URLContext *h_dst);
    const char *default_whitelist;
} URLProtocol;

URLContext    

    相当于文件的上下文,其中一个成员变量就是URLProtocol。

typedef struct URLContext {
    const AVClass *av_class;    /**< information for av_log(). Set by url_open(). */
    const struct URLProtocol *prot;
    void *priv_data; 相应通信方式的句柄,对于文件为fd句柄,对于网络为socket句柄等
    char *filename;             /**< specified URL */
    int flags;
    int max_packet_size;        /**< if non zero, the stream is packetized with this max packet size */
    int is_streamed;            /**< true if streamed (no seek possible), default = false */
    int is_connected;
    AVIOInterruptCB interrupt_callback;
    int64_t rw_timeout;         /**< maximum time to wait for (network) read/write operation completion, in mcs */
    const char *protocol_whitelist;
    const char *protocol_blacklist;
    int min_packet_size;        /**< if non zero, the stream is packetized with this min packet size */
} URLContext;

 

AVDictionary

    实际上这是一个键值对类型,可以说就是在c中的一个简易map类型

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

 

AVCodecParser

    解析器,我们最常见的可能就是Gson这种json解析器了。ffmpeg中也有解析器,主要是用于将数据切片。

    AVCodecParser用于解析输入的数据流并把它们分成一帧一帧的压缩编码数据。比较形象的说法就是把长长的一段连续的数据“切割”成一段段的数据。

typedef struct AVCodecParser {
    int codec_ids[5]; /* several codec IDs are permitted */
    int priv_data_size;
    int (*parser_init)(AVCodecParserContext *s);
    /* This callback never returns an error, a negative value means that
     * the frame start was in a previous packet. */
    int (*parser_parse)(AVCodecParserContext *s,
                        AVCodecContext *avctx,
                        const uint8_t **poutbuf, int *poutbuf_size,
                        const uint8_t *buf, int buf_size);
    void (*parser_close)(AVCodecParserContext *s);
    int (*split)(AVCodecContext *avctx, const uint8_t *buf, int buf_size);
    struct AVCodecParser *next;
} AVCodecParser;

 

转载于:https://my.oschina.net/zzxzzg/blog/1806996

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值