前言
本文主要介绍了一些在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;