1. 程式人生 > >FFmpeg原始碼簡單分析:常見結構體的初始化和銷燬(AVFormatContext,AVFrame等)

FFmpeg原始碼簡單分析:常見結構體的初始化和銷燬(AVFormatContext,AVFrame等)

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

FFmpeg的庫函式原始碼分析文章列表:

【架構圖】

【通用】

【解碼】

【編碼】

【其它】

【指令碼】

【H.264】

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


本文簡單分析FFmpeg常見結構體的初始化和銷燬函式的原始碼。常見的結構體在文章:

AVFormatContext:統領全域性的基本結構體。主要用於處理封裝格式(FLV/MKV/RMVB等)。

AVIOContext:輸入輸出對應的結構體,用於輸入輸出(讀寫檔案,RTMP協議等)。

AVStream,AVCodecContext:視音訊流對應的結構體,用於視音訊編解碼。

AVFrame:儲存非壓縮的資料(視訊對應RGB/YUV畫素資料,音訊對應PCM取樣資料)

AVPacket:儲存壓縮資料(視訊對應H.264等碼流資料,音訊對應AAC/MP3等碼流資料)

他們之間的關係如下圖所示(詳細資訊可以參考上文提到的文章)。


下文簡單分析一下上述幾個結構體的初始化和銷燬函式。這些函式列表如下。

結構體

初始化

銷燬

AVFormatContext

avformat_alloc_context()

avformat_free_context()

AVIOContext

avio_alloc_context()

AVStream

avformat_new_stream()

AVCodecContext

avcodec_alloc_context3()

AVFrame

av_frame_alloc();

av_image_fill_arrays()

av_frame_free()

AVPacket

av_init_packet();

av_new_packet()

av_free_packet()


下面進入正文。


AVFormatContext

AVFormatContext的初始化函式是avformat_alloc_context(),銷燬函式是avformat_free_context()。

avformat_alloc_context()

avformat_alloc_context()的宣告位於libavformat\avformat.h,如下所示。

/**
 * Allocate an AVFormatContext.
 * avformat_free_context() can be used to freethe context and everything
 * allocated by the framework within it.
 */
AVFormatContext*avformat_alloc_context(void);

avformat_alloc_context()的定義位於libavformat\options.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;
    }


    return ic;
}

從程式碼中可以看出,avformat_alloc_context()呼叫av_malloc()為AVFormatContext結構體分配了記憶體,而且同時也給AVFormatContext中的internal欄位分配記憶體(這個欄位是FFmpeg內部使用的,先不分析)。此外呼叫了一個avformat_get_context_defaults()函式。該函式用於設定AVFormatContext的欄位的預設值。它的定義也位於libavformat\options.c,確切的說就位於avformat_alloc_context()上面。我們看一下該函式的定義。
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);
}

從程式碼可以看出,avformat_get_context_defaults()首先呼叫memset()將AVFormatContext的所有欄位置0。而後呼叫了一個函式av_opt_set_defaults()。av_opt_set_defaults()用於給欄位設定預設值。
avformat_alloc_context()程式碼的函式呼叫關係如下圖所示。


avformat_free_context()

avformat_free_context()的宣告位於libavformat\avformat.h,如下所示。

/**
 * Free an AVFormatContext and all its streams.
 * @param s context to free
 */
void avformat_free_context(AVFormatContext *s);

avformat_free_context()的定義位於libavformat\options.c。程式碼如下所示。

void avformat_free_context(AVFormatContext *s)
{
    int i;


    if (!s)
        return;


    av_opt_free(s);
    if (s->iformat && s->iformat->priv_class && s->priv_data)
        av_opt_free(s->priv_data);
    if (s->oformat && s->oformat->priv_class && s->priv_data)
        av_opt_free(s->priv_data);


    for (i = s->nb_streams - 1; i >= 0; i--) {
        ff_free_stream(s, s->streams[i]);
    }
    for (i = s->nb_programs - 1; i >= 0; i--) {
        av_dict_free(&s->programs[i]->metadata);
        av_freep(&s->programs[i]->stream_index);
        av_freep(&s->programs[i]);
    }
    av_freep(&s->programs);
    av_freep(&s->priv_data);
    while (s->nb_chapters--) {
        av_dict_free(&s->chapters[s->nb_chapters]->metadata);
        av_freep(&s->chapters[s->nb_chapters]);
    }
    av_freep(&s->chapters);
    av_dict_free(&s->metadata);
    av_freep(&s->streams);
    av_freep(&s->internal);
    flush_packet_queue(s);
    av_free(s);
}

從程式碼中可以看出,avformat_free_context()呼叫了各式各樣的銷燬函式:av_opt_free(),av_freep(),av_dict_free()。這些函式分別用於釋放不同種類的變數,在這裡不再詳細討論。在這裡看一個釋放AVStream的函式ff_free_stream()。該函式的定義位於libavformat\options.c(其實就在avformat_free_context()上方)。
void ff_free_stream(AVFormatContext *s, AVStream *st) {
    int j;
    av_assert0(s->nb_streams>0);
    av_assert0(s->streams[ s->nb_streams - 1 ] == st);


    for (j = 0; j < st->nb_side_data; j++)
        av_freep(&st->side_data[j].data);
    av_freep(&st->side_data);
    st->nb_side_data = 0;


    if (st->parser) {
        av_parser_close(st->parser);
    }
    if (st->attached_pic.data)
        av_free_packet(&st->attached_pic);
    av_dict_free(&st->metadata);
    av_freep(&st->probe_data.buf);
    av_freep(&st->index_entries);
    av_freep(&st->codec->extradata);
    av_freep(&st->codec->subtitle_header);
    av_freep(&st->codec);
    av_freep(&st->priv_data);
    if (st->info)
        av_freep(&st->info->duration_error);
    av_freep(&st->info);
    av_freep(&s->streams[ --s->nb_streams ]);
}

從程式碼中可以看出,與釋放AVFormatContext類似,釋放AVStream的時候,也是呼叫了av_freep(),av_dict_free()這些函式釋放有關的欄位。如果使用了parser的話,會呼叫av_parser_close()關閉該parser。

AVIOContext

avio_alloc_context()

AVIOContext的初始化函式是avio_alloc_context(),銷燬的時候使用av_free()釋放掉其中的快取即可。它的宣告位於libavformat\avio.h中,如下所示。

/**
 * Allocate and initialize an AVIOContext for buffered I/O. It must be later
 * freed with av_free().
 *
 * @param buffer Memory block for input/output operations via AVIOContext.
 *        The buffer must be allocated with av_malloc() and friends.
 * @param buffer_size The buffer size is very important for performance.
 *        For protocols with fixed blocksize it should be set to this blocksize.
 *        For others a typical size is a cache page, e.g. 4kb.
 * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
 * @param opaque An opaque pointer to user-specific data.
 * @param read_packet  A function for refilling the buffer, may be NULL.
 * @param write_packet A function for writing the buffer contents, may be NULL.
 *        The function may not change the input buffers content.
 * @param seek A function for seeking to specified byte position, may be NULL.
 *
 * @return Allocated AVIOContext or NULL on failure.
 */
AVIOContext *avio_alloc_context(
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence));

avio_alloc_context()定義位於libavformat\aviobuf.c中,如下所示。

AVIOContext *avio_alloc_context(
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence))
{
    AVIOContext *s = av_mallocz(sizeof(AVIOContext));
    if (!s)
        return NULL;
    ffio_init_context(s, buffer, buffer_size, write_flag, opaque,
                  read_packet, write_packet, seek);
    return s;
}

從程式碼中可以看出,avio_alloc_context()首先呼叫av_mallocz()為AVIOContext分配記憶體。而後呼叫了一個函式ffio_init_context()。該函式完成了真正的初始化工作。我們看一下ffio_init_context()函式的定義。
int ffio_init_context(AVIOContext *s,
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence))
{
    s->buffer      = buffer;
    s->orig_buffer_size =
    s->buffer_size = buffer_size;
    s->buf_ptr     = buffer;
    s->opaque      = opaque;
    s->direct      = 0;


    url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);


    s->write_packet    = write_packet;
    s->read_packet     = read_packet;
    s->seek            = seek;
    s->pos             = 0;
    s->must_flush      = 0;
    s->eof_reached     = 0;
    s->error           = 0;
    s->seekable        = seek ? AVIO_SEEKABLE_NORMAL : 0;
    s->max_packet_size = 0;
    s->update_checksum = NULL;


    if (!read_packet && !write_flag) {
        s->pos     = buffer_size;
        s->buf_end = s->buffer + buffer_size;
    }
    s->read_pause = NULL;
    s->read_seek  = NULL;


    return 0;
}

從函式的程式碼可以看出,ffio_init_context()對AVIOContext中的快取,函式指標等等進行了賦值。


AVStream,AVCodecContext

AVStream的初始化函式是avformat_new_stream(),銷燬函式使用銷燬AVFormatContext的avformat_free_context()就可以了。

avformat_new_stream()

avformat_new_stream()的宣告位於libavformat\avformat.h中,如下所示。

/**
 * Add a new stream to a media file.
 *
 * When demuxing, it is called by the demuxer in read_header(). If the
 * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also
 * be called in read_packet().
 *
 * When muxing, should be called by the user before avformat_write_header().
 *
 * User is required to call avcodec_close() and avformat_free_context() to
 * clean up the allocation by avformat_new_stream().
 *
 * @param s media file handle
 * @param c If non-NULL, the AVCodecContext corresponding to the new stream
 * will be initialized to use this codec. This is needed for e.g. codec-specific
 * defaults to be set, so codec should be provided if it is known.
 *
 * @return newly created stream or NULL on error.
 */
AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);

avformat_new_stream()的定義位於libavformat\utils.c中,如下所示。

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


    if (s->nb_streams >= INT_MAX/sizeof(*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;


    st->codec = avcodec_alloc_context3(c);
    if (s->iformat) {
        /* no default bitrate if decoding */
        st->codec->bit_rate = 0;


        /* default pts setting is MPEG-like */
        avpriv_set_pts_info(st, 33, 1, 90000);
    }


    st->index      = s->nb_streams;
    st->start_time = AV_NOPTS_VALUE;
    st->duration   = AV_NOPTS_VALUE;
    /* we set the current DTS to 0 so that formats without any timestamps
     * but durations get some timestamps, formats with some unknown
     * timestamps have their first few packets buffered and the
     * timestamps corrected before they are returned to the user */
    st->cur_dts       = s->iformat ? RELATIVE_TS_BASE : 0;
    st->first_dts     = AV_NOPTS_VALUE;
    st->probe_packets = MAX_PROBE_PACKETS;
    st->pts_wrap_reference = AV_NOPTS_VALUE;
    st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE;


    st->last_IP_pts = AV_NOPTS_VALUE;
    st->last_dts_for_order_check = AV_NOPTS_VALUE;
    for (i = 0; i < MAX_REORDER_DELAY + 1; i++)
        st->pts_buffer[i] = AV_NOPTS_VALUE;


    st->sample_aspect_ratio = (AVRational) { 0, 1 };


#if FF_API_R_FRAME_RATE
    st->info->last_dts      = AV_NOPTS_VALUE;
#endif
    st->info->fps_first_dts = AV_NOPTS_VALUE;
    st->info->fps_last_dts  = AV_NOPTS_VALUE;


    st->inject_global_side_data = s->internal->inject_global_side_data;


    s->streams[s->nb_streams++] = st;
    return st;
}

從程式碼中可以看出,avformat_new_stream()首先呼叫av_mallocz()為AVStream分配記憶體。接著給新分配的AVStream的各個欄位賦上預設值。然後呼叫了另一個函式avcodec_alloc_context3()初始化AVStream中的AVCodecContext。

avcodec_alloc_context3()

avcodec_alloc_context3()的宣告位於libavcodec\avcodec.h中,如下所示。

/**
 * Allocate an AVCodecContext and set its fields to default values. The
 * resulting struct should be freed with avcodec_free_context().
 *
 * @param codec if non-NULL, allocate private data and initialize defaults
 *              for the given codec. It is illegal to then call avcodec_open2()
 *              with a different codec.
 *              If NULL, then the codec-specific defaults won't be initialized,
 *              which may result in suboptimal default settings (this is
 *              important mainly for encoders, e.g. libx264).
 *
 * @return An AVCodecContext filled with default values or NULL on failure.
 * @see avcodec_get_context_defaults
 */
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

下面我們看一下avcodec_alloc_context3()的定義。下面我們看一下avcodec_alloc_context3()的定義。avcodec_alloc_context3()的定義位於libavcodec\options.c中。

AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)
{
    AVCodecContext *avctx= av_malloc(sizeof(AVCodecContext));


    if (!avctx)
        return NULL;


    if(avcodec_get_context_defaults3(avctx, codec) < 0){
        av_free(avctx);
        return NULL;
    }


    return avctx;
}

從程式碼中可以看出,avcodec_alloc_context3()首先呼叫av_malloc()為AVCodecContext分配儲存空間,然後呼叫了一個函式avcodec_get_context_defaults3()用於設定該AVCodecContext的預設值。avcodec_get_context_defaults3()的定義如下。

int avcodec_get_context_defaults3(AVCodecContext *s, const AVCodec *codec)
{
    int flags=0;
    memset(s, 0, sizeof(AVCodecContext));


    s->av_class = &av_codec_context_class;


    s->codec_type = codec ? codec->type : AVMEDIA_TYPE_UNKNOWN;
    if (codec)
        s->codec_id = codec->id;


    if(s->codec_type == AVMEDIA_TYPE_AUDIO)
        flags= AV_OPT_FLAG_AUDIO_PARAM;
    else if(s->codec_type == AVMEDIA_TYPE_VIDEO)
        flags= AV_OPT_FLAG_VIDEO_PARAM;
    else if(s->codec_type == AVMEDIA_TYPE_SUBTITLE)
        flags= AV_OPT_FLAG_SUBTITLE_PARAM;
    av_opt_set_defaults2(s, flags, flags);


    s->time_base           = (AVRational){0,1};
    s->get_buffer2         = avcodec_default_get_buffer2;
    s->get_format          = avcodec_default_get_format;
    s->execute             = avcodec_default_execute;
    s->execute2            = avcodec_default_execute2;
    s->sample_aspect_ratio = (AVRational){0,1};
    s->pix_fmt             = AV_PIX_FMT_NONE;
    s->sample_fmt          = AV_SAMPLE_FMT_NONE;
    s->timecode_frame_start = -1;


    s->reordered_opaque    = AV_NOPTS_VALUE;
    if(codec && codec->priv_data_size){
        if(!s->priv_data){
            s->priv_data= av_mallocz(codec->priv_data_size);
            if (!s->priv_data) {
                return AVERROR(ENOMEM);
            }
        }
        if(codec->priv_class){
            *(const AVClass**)s->priv_data = codec->priv_class;
            av_opt_set_defaults(s->priv_data);
        }
    }
    if (codec && codec->defaults) {
        int ret;
        const AVCodecDefault *d = codec->defaults;
        while (d->key) {
            ret = av_opt_set(s, d->key, d->value, 0);
            av_assert0(ret >= 0);
            d++;
        }
    }
    return 0;
}

avformat_new_stream()函式的呼叫結構如下所示。
 

AVFrame

AVFrame的初始化函式是av_frame_alloc(),銷燬函式是av_frame_free()。在這裡有一點需要注意,舊版的FFmpeg都是使用avcodec_alloc_frame()初始化AVFrame的,但是我在寫這篇文章的時候,avcodec_alloc_frame()已經被標記為“過時的”了,為了保證與時俱進,決定分析新的API——av_frame_alloc()。

av_frame_alloc()

av_frame_alloc()的宣告位於libavutil\frame.h,如下所示。

/**
 * Allocate an AVFrame and set its fields to default values.  The resulting
 * struct must be freed using av_frame_free().
 *
 * @return An AVFrame filled with default values or NULL on failure.
 *
 * @note this only allocates the AVFrame itself, not the data buffers. Those
 * must be allocated through other means, e.g. with av_frame_get_buffer() or
 * manually.
 */
AVFrame *av_frame_alloc(void);

av_frame_alloc()的定義位於libavutil\frame.c。程式碼如下所示。

AVFrame *av_frame_alloc(void)
{
    AVFrame *frame = av_mallocz(sizeof(*frame));


    if (!frame)
        return NULL;


    frame->extended_data = NULL;
    get_frame_defaults(frame);


    return frame;
}

從程式碼可以看出,av_frame_alloc()首先呼叫av_mallocz()為AVFrame結構體分配記憶體。而後呼叫了一個函式get_frame_defaults()用於設定一些預設引數。get_frame_defaults()定義如下。
static void get_frame_defaults(AVFrame *frame)
{
    if (frame->extended_data != frame->data)
        av_freep(&frame->extended_data);


    memset(frame, 0, sizeof(*frame));


    frame->pts                   =
    frame->pkt_dts               =
    frame->pkt_pts               = AV_NOPTS_VALUE;
    av_frame_set_best_effort_timestamp(frame, AV_NOPTS_VALUE);
    av_frame_set_pkt_duration         (frame, 0);
    av_frame_set_pkt_pos              (frame, -1);
    av_frame_set_pkt_size             (frame, -1);
    frame->key_frame           = 1;
    frame->sample_aspect_ratio = (AVRational){ 0, 1 };
    frame->format              = -1; /* unknown */
    frame->extended_data       = frame->data;
    frame->color_primaries     = AVCOL_PRI_UNSPECIFIED;
    frame->color_trc           = AVCOL_TRC_UNSPECIFIED;
    frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
    frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
    frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
}

從av_frame_alloc()的程式碼我們可以看出,該函式並沒有為AVFrame的畫素資料分配空間。因此AVFrame中的畫素資料的空間需要自行分配空間,例如使用avpicture_fill(),av_image_fill_arrays()等函式。

av_frame_alloc()函式的呼叫結構如下所示。


avpicture_fill()

avpicture_fill()的宣告位於libavcodec\avcodec.h,如下所示。

/**
 * Setup the picture fields based on the specified image parameters
 * and the provided image data buffer.
 *
 * The picture fields are filled in by using the image data buffer
 * pointed to by ptr.
 *
 * If ptr is NULL, the function will fill only the picture linesize
 * array and return the required size for the image buffer.
 *
 * To allocate an image buffer and fill the picture data in one call,
 * use avpicture_alloc().
 *
 * @param picture       the picture to be filled in
 * @param ptr           buffer where the image data is stored, or NULL
 * @param pix_fmt       the pixel format of the image
 * @param width         the width of the image in pixels
 * @param height        the height of the image in pixels
 * @return the size in bytes required for src, a negative error code
 * in case of failure
 *
 * @see av_image_fill_arrays()
 */
int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
                   enum AVPixelFormat pix_fmt, int width, int height);

avpicture_fill()的定義位於libavcodec\avpicture.c,如下所示。

PS:目測這個函式未來也有可能成為“過時的”函式,因為通過觀察這一年FFmpeg程式碼的變化,發現FFmpeg組織似乎想把AVFrame相關的函式(原先定義在AVCodec的標頭檔案中)從AVCodec的程式碼中分離出來,形成一套單獨的API。所以很多和AVFrame相關的名稱為avcodec_XXX()的函式都被標記上了“過時的”標記。當然,上述推測也是我自己猜測的。
int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
                   enum AVPixelFormat pix_fmt, int width, int height)
{
    return av_image_fill_arrays(picture->data, picture->linesize,
                                ptr, pix_fmt, width, height, 1);
}

從程式碼中可以看出,avpicture_fill()僅僅是簡單呼叫了一下av_image_fill_arrays()。也就是說這兩個函式實際上是等同的。

av_image_fill_arrays()

av_image_fill_arrays()的宣告位於libavutil\imgutils.h中,如下所示。

/**
 * Setup the data pointers and linesizes based on the specified image
 * parameters and the provided array.
 *
 * The fields of the given image are filled in by using the src
 * address which points to the image data buffer. Depending on the
 * specified pixel format, one or multiple image data pointers and
 * line sizes will be set.  If a planar format is specified, several
 * pointers will be set pointing to the different picture planes and
 * the line sizes of the different planes will be stored in the
 * lines_sizes array. Call with !src to get the required
 * size for the src buffer.
 *
 * To allocate the buffer and fill in the dst_data and dst_linesize in
 * one call, use av_image_alloc().
 *
 * @param dst_data      data pointers to be filled in
 * @param dst_linesizes linesizes for the image in dst_data to be filled in
 * @param src           buffer which will contain or contains the actual image data, can be NULL
 * @param pix_fmt       the pixel format of the image
 * @param width         the width of the image in pixels
 * @param height        the height of the image in pixels
 * @param align         the value used in src for linesize alignment
 * @return the size in bytes required for src, a negative error code
 * in case of failure
 */
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
                         const uint8_t *src,
                         enum AVPixelFormat pix_fmt, int width, int height, int align);

av_image_fill_arrays()的定義位於libavutil\imgutils.c中。

int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
                         const uint8_t *src,
                         enum AVPixelFormat pix_fmt, int width, int height, int align)
{
    int ret, i;


    if ((ret = av_image_check_size(width, height, 0, NULL)) < 0)
        return ret;


    if ((ret = av_image_fill_linesizes(dst_linesize, pix_fmt, width)) < 0)
        return ret;


    for (i = 0; i < 4; i++)
        dst_linesize[i] = FFALIGN(dst_linesize[i], align);


    if ((ret = av_image_fill_pointers(dst_data, pix_fmt, width, NULL, dst_linesize)) < 0)
        return ret;


    return av_image_fill_pointers(dst_data, pix_fmt, height, (uint8_t *)src, dst_linesize);
}

av_image_fill_arrays()函式中包含3個函式:av_image_check_size(),av_image_fill_linesizes(),av_image_fill_pointers()。av_image_check_size()用於檢查輸入的寬高參數是否合理,即不能太大或者為負數。av_image_fill_linesizes()用於填充dst_linesize。av_image_fill_pointers()則用於填充dst_data。它們的定義相對比較簡單,不再詳細分析。
av_image_check_size()程式碼如下所示。
int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx)
{
    ImgUtils imgutils = { &imgutils_class, log_offset, log_ctx };


    if ((int)w>0 && (int)h>0 && (w+128)*(uint64_t)(h+128) < INT_MAX/8)
        return 0;


    av_log(&imgutils, AV_LOG_ERROR, "Picture size %ux%u is invalid\n", w, h);
    return AVERROR(EINVAL);
}

av_image_fill_linesizes()程式碼如下所示。
int av_image_fill_linesizes(int linesizes[4], enum AVPixelFormat pix_fmt, int width)
{
    int i, ret;
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
    int max_step     [4];       /* max pixel step for each plane */
    int max_step_comp[4];       /* the component for each plane which has the max pixel step */


    memset(linesizes, 0, 4*sizeof(linesizes[0]));


    if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
        return AVERROR(EINVAL);


    av_image_fill_max_pixsteps(max_step, max_step_comp, desc);
    for (i = 0; i < 4; i++) {
        if ((ret = image_get_linesize(width, i, max_step[i], max_step_comp[i], desc)) < 0)
            return ret;
        linesizes[i] = ret;
    }


    return 0;
}

av_image_fill_pointers()程式碼如下所示。
int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height,
                           uint8_t *ptr, const int linesizes[4])
{
    int i, total_size, size[4] = { 0 }, has_plane[4] = { 0 };


    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
    memset(data     , 0, sizeof(data[0])*4);


    if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
        return AVERROR(EINVAL);


    data[0] = ptr;
    if (linesizes[0] > (INT_MAX - 1024) / height)
        return AVERROR(EINVAL);
    size[0] = linesizes[0] * height;


    if (desc->flags & AV_PIX_FMT_FLAG_PAL ||
        desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {
        size[0] = (size[0] + 3) & ~3;
        data[1] = ptr + size[0]; /* palette is stored here as 256 32 bits words */
        return size[0] + 256 * 4;
    }


    for (i = 0; i < 4; i++)
        has_plane[desc->comp[i].plane] = 1;


    total_size = size[0];
    for (i = 1; i < 4 && has_plane[i]; i++) {
        int h, s = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;
        data[i] = data[i-1] + size[i-1];
        h = (height + (1 << s) - 1) >> s;
        if (linesizes[i] > INT_MAX / h)
            return AVERROR(EINVAL);
        size[i] = h * linesizes[i];
        if (total_size > INT_MAX - size[i])
            return AVERROR(EINVAL);
        total_size += size[i];
    }


    return total_size;
}

avpicture_fill()函式呼叫關係如下圖所示。
 

av_frame_free()

av_frame_free ()的宣告位於libavutil\frame.h,如下所示。

/**
 * Free the frame and any dynamically allocated objects in it,
 * e.g. extended_data. If the frame is reference counted, it will be
 * unreferenced first.
 *
 * @param frame frame to be freed. The pointer will be set to NULL.
 */
void av_frame_free(AVFrame **frame);

av_frame_free ()的定義位於libavutil\frame.c。程式碼如下所示。

void av_frame_free(AVFrame **frame)
{
    if (!frame || !*frame)
        return;


    av_frame_unref(*frame);
    av_freep(frame);
}

在釋放AVFrame結構體之前,首先呼叫了一個函式av_frame_unref()。av_frame_unref()也是一個FFmpeg的API,它的作用是釋放AVFrame中參考的快取(還沒完全弄懂),並且重置AVFrame中的欄位。呼叫這個函式的目的應該是為了確保AVFrame可以被正常釋放。程式碼如下。
void av_frame_unref(AVFrame *frame)
{
    int i;


    for (i = 0; i < frame->nb_side_data; i++) {
        free_side_data(&frame->side_data[i]);
    }
    av_freep(&frame->side_data);


    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
        av_buffer_unref(&frame->buf[i]);
    for (i = 0; i < frame->nb_extended_buf; i++)
        av_buffer_unref(&frame->extended_buf[i]);
    av_freep(&frame->extended_buf);
    av_dict_free(&frame->metadata);
    av_buffer_unref(&frame->qp_table_buf);


    get_frame_defaults(frame);
}

AVPacket

AVPacket的初始化函式有兩個:av_init_packet(),av_new_packet()。銷燬函式是av_free_packet()。在初始化函式中av_init_packet()比較簡單,初始化一些欄位;而av_new_packet()相對“高階”一些,除了包含av_init_packet()的功能之外,還包含了AVPacket內部記憶體的分配。下面分別看看這些函式。

av_init_packet()

av_init_packet()的宣告位於libavcodec\avcodec.h,如下所示。

/**
 * Initialize optional fields of a packet with default values.
 *
 * Note, this does not touch the data and size members, which have to be
 * initialized separately.
 *
 * @param pkt packet
 */
void av_init_packet(AVPacket *pkt);

av_init_packet()的定義位於libavcodec\avpacket.c。如下所示。

void av_init_packet(AVPacket *pkt)
{
    pkt->pts                  = AV_NOPTS_VALUE;
    pkt->dts                  = AV_NOPTS_VALUE;
    pkt->pos                  = -1;
    pkt->duration             = 0;
    pkt->convergence_duration = 0;
    pkt->flags                = 0;
    pkt->stream_index         = 0;
#if FF_API_DESTRUCT_PACKET
FF_DISABLE_DEPRECATION_WARNINGS
    pkt->destruct             = NULL;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
    pkt->buf                  = NULL;
    pkt->side_data            = NULL;
    pkt->side_data_elems      = 0;
}

av_new_packet()

av_new_packet()的宣告位於libavcodec\avcodec.h。如下所示。

/**
 * Allocate the payload of a packet and initialize its fields with
 * default values.
 *
 * @param pkt packet
 * @param size wanted payload size
 * @return 0 if OK, AVERROR_xxx otherwise
 */
int av_new_packet(AVPacket *pkt, int size);

av_new_packet()的定義位於libavcodec\avpacket.c。如下所示。

int av_new_packet(AVPacket *pkt, int size)
{
    AVBufferRef *buf = NULL;
    int ret = packet_alloc(&buf, size);
    if (ret < 0)
        return ret;


    av_init_packet(pkt);
    pkt->buf      = buf;
    pkt->data     = buf->data;
    pkt->size     = size;
#if FF_API_DESTRUCT_PACKET
FF_DISABLE_DEPRECATION_WARNINGS
    pkt->destruct = dummy_destruct_packet;
FF_ENABLE_DEPRECATION_WARNINGS
#endif


    return 0;
}

從程式碼可以看出,av_new_packet()呼叫了av_init_packet(pkt)。此外還呼叫了一個函式packet_alloc()。packet_alloc()函式的定義如下。
static int packet_alloc(AVBufferRef **buf, int size)
{
    int ret;
    if ((unsigned)size >= (unsigned)size + FF_INPUT_BUFFER_PADDING_SIZE)
        return AVERROR(EINVAL);


    ret = av_buffer_realloc(buf, size + FF_INPUT_BUFFER_PADDING_SIZE);
    if (ret < 0)
        return ret;


    memset((*buf)->data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);


    return 0;
}

packet_alloc()中呼叫av_buffer_realloc()為AVPacket分配記憶體。然後呼叫memset()將分配的記憶體置0。
PS:發現AVPacket的結構隨著FFmpeg的發展越發複雜了。原先AVPacket中的資料僅僅存在一個uint8_t型別的數組裡,而現在已經使用一個專門的結構體AVBufferRef儲存資料。
av_new_packet()程式碼的函式呼叫關係如下圖所示。
 

av_free_packet()

av_free_packet()的宣告位於libavcodec\avcodec.h,如下所示。

/**
 * Free a packet.
 *
 * @param pkt packet to free
 */
void av_free_packet(AVPacket *pkt);

av_free_packet()的定義位於libavcodec\avpacket.c。如下所示。

void av_free_packet(AVPacket *pkt)
{
    if (pkt) {
FF_DISABLE_DEPRECATION_WARNINGS
        if (pkt->buf)
            av_buffer_unref(&pkt->buf);
#if FF_API_DESTRUCT_PACKET
        else if (pkt->destruct)
            pkt->destruct(pkt);
        pkt->destruct = NULL;
#endif
FF_ENABLE_DEPRECATION_WARNINGS
        pkt->data            = NULL;
        pkt->size            = 0;


        av_packet_free_side_data(pkt);
    }
}

從程式碼可以看出,av_free_packet()呼叫av_buffer_unref()釋放AVPacket中的資料,而後還呼叫了av_packet_free_side_data()釋放了side_data(儲存封裝格式可以提供的額外的資料)。

雷霄驊 (Lei Xiaohua)
[email protected]
http://blog.csdn.net/leixiaohua1020

相關推薦

FFmpeg原始碼簡單分析常見結構初始銷燬AVFormatContextAVFrame

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg常見結構初始銷燬AVFormatContextAVFrame——雷神神文

===================================================== FFmpeg的庫函式原始碼分析文章列表: 【架構圖】 【通用】 【解碼】 【編碼】

FFmpeg源代碼簡單分析常見結構初始銷毀AVFormatContextAVFrame

new init _array border 代碼 alloc ecc .com VC 結構體 初始化 銷毀 AVFormatContext avformat_alloc_context() avfo

FFmpeg原始碼簡單分析結構成員管理系統-AVClass

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

ffmpeg 原始碼簡單分析 av_read_frame()

此前寫了好幾篇ffmpeg原始碼分析文章,列表如下: ============================ ffmpeg中的av_read_frame()的作用是讀取碼流中的音訊若干幀或者視訊一幀。例如,解碼視訊的時

ffmpeg 原始碼簡單分析 av_register_all()

此前寫了好幾篇ffmpeg原始碼分析文章,列表如下: ============================ 前一陣子看了一下ffmpeg的原始碼,並且做了一些註釋,在此貼出來以作備忘。 本文分析一下ffmpeg註冊

FFmpeg原始碼簡單分析makefile

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avcodec_open2()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avformat_close_input()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析av_write_frame()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析記憶體的分配釋放av_malloc()、av_free()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avcodec_encode_video()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析configure

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

C語言結構初始結構指標

結構體初始化 #include <stdio.h> //結構體的宣告和定義方法 //1.宣告和定義分離 struct weapon{ char name[20]; in

FFmpeg的H.264解碼器原始碼簡單分析概述

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

FFmpeg的HEVC解碼器原始碼簡單分析CTU解碼CTU Decode部分-TU

=====================================================HEVC原始碼分析文章列表:【解碼 -libavcodec HEVC 解碼器】==============================================

FFmpeg的H.264解碼器原始碼簡單分析解碼器主幹部分

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

FFMPeg程式碼分析AVCodecContext結構

在呼叫avformat_open_input開啟檔案後,下一步呼叫av_find_stream_info函式從檔案中讀取音視訊流的資訊,而後AVFormatContext的結構體將會將這些資訊儲存在其中。在找到AVFormatContext的視訊stream後,獲取其cod

FFmpeg的H.264解碼器原始碼簡單分析熵解碼Entropy Decoding部分

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

H264編碼器5( x264原始碼簡單分析x264_slice_write() 與H264 編碼簡介)

  x264原始碼簡單分析:x264_slice_write() 來自:https://blog.csdn.net/leixiaohua1020/article/details/45536607     H264 編碼簡介 https://blo