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原始碼簡單分析:常見結構體的初始化和銷燬(AVFormatContext,AVFrame等)
=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================
FFmpeg:常見結構體的初始化和銷燬(AVFormatContext,AVFrame等)——雷神神文
===================================================== FFmpeg的庫函式原始碼分析文章列表: 【架構圖】 【通用】 【解碼】 【編碼】
FFmpeg源代碼簡單分析:常見結構體的初始化和銷毀(AVFormatContext,AVFrame等)
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