AVFrame介绍
AVFrame结构体是存储音视频原始数据(即未被编码的数据)的结构体。该结构体位于libavutil/frame.h中
AVFrame必须使用av_frame_alloc()来分配。注意,这只是分配AVFrame本身,缓冲区的数据(解码成功后的数据)必须通过其他途径被管理.
因为AVFrame通常只分配一次,然后多次复用来保存不同类型的数据,复用的时候需要调用av_frame_unref()将其重置到它前面的原始清洁状态.
注意调用avcodec_receive_frame()时会自动引用减1后再获取frame,所以解码过程中无需每次调用
释放的时候必须用av_frame_free()释放。
常用函数使用
AVFrame *av_frame_alloc(void); //创建空间,分配内存
// 初始化
void av_frame_unref(AVFrame *frame);
//引用减1.若为0则释放缓冲区数据,注意调用avcodec_receive_frame()时会自动引用减1后再获取frame,所以解码过程中无需每次调用
void av_frame_free(AVFrame **frame);
//释放frame本身
av_frame_ref(AVFrame *dst, const AVFrame *src);
//从src复制到一个初始化好的dst中,并引用+1
av_frame_clone(const AVFrame *src);
//创建并返回一个复制好的AVPacket(在音视频同步处理中用到该函数)
//获取frame相关函数
int avcodec_send_packet(pCodecCtx, pPacket);
//发送要解码的数据到解码队列中,并引用+1.返回0表示发送成功
int avcodec_receive_frame(pCodecCtx, pFrame);
//从解码队列中获取一帧AVFrame,并且获取的AVFrame是已经通过pts排列好的数据,返回0表示获取成功
AVFrame源码
frame.h
typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
uint8_t *data[AV_NUM_DATA_POINTERS];
//存储原始帧数据(视频的YUV, RGB, 音频的PCM),数组的每一个元素是一个指针,指向了AVBufferRef *buf中的data
//对于packet格式,都存在data[0]中,比如yuv,data[0]中就存的是yuvyuvyuv...,pcm则是lrlrlrlr...
//对于planar格式,则是分开存储,比如yuv,data[0]存y,data[1]存u,data[2]存v,pcm则是data[0]存L,data[1]存R
int linesize[AV_NUM_DATA_POINTERS];
//对于视频,linesize存储的是每个图像的一行(宽)数据大小(字节数)。注意有对齐要求(16或32对齐)
//由于有对齐要求,所以如果width!=linesize[0],那么表示需要行对齐.//比如yuv420p,那么data[0]里的数据可能就是YYYYY000YYYYY000(0表示是不需要的数据),获取示例如下所示:
//for(int i = 0; i < height; i++) //获取Y原始数据 //QByteArray datas[0].append(width,frame->data[0] + frame->linesize[0]*i); //只要width个数据
//对于音频,则是每个data[]通道里的字节大小,并且每个通道(一般就两通道:L和R)的字节数相同
uint8_t **extended_data;
//extended_data:*extended_data始终等于data[0]里的成员。
//之所以取名为extended_data,是因为data[]最大只能8个通道.
//比如planar格式的pcm的通道数超过了8个,那么就只能使用extended_data来获取数据.
int width, height;
//视频帧的尺寸(以像素为单位)
//用户可以通过if (frame->width > 0 && frame->height > 0)来判断是否为视频流
int nb_samples;
//音频帧的单通道样本数据数量(不是以字节为单位,以单个音频数据为单位)//比如frame的linesize[0]=8192,LR双通道,format为float planar格式(4字节),那么nb_samples=8192/2/4=1024
int format;
//帧的格式,如果未知或未设置为-1
//对于视频帧,参考AVPixelFormat枚举值,比如:AV_PIX_FMT_YUV420P
//对于音频帧,参考AVSampleFormat枚举值,比如:AV_SAMPLE_FMT_U8
int key_frame;
//是否为一幅完整的画面,关键帧(I帧)的标识
//1->关键帧,0->非关键帧
enum AVPictureType pict_type;
//视频帧类型(I、B、P等),比如:AV_PICTURE_TYPE_I(I帧)
//I帧:一幅完整的画面
//B帧:参考前面和后面两帧的数据加上本帧的变化而得出的本帧数据
//P帧:参考前面而得出的本帧数据.
//如果I帧不完整,那么整个GOP(Group of Picture)都是花屏的.
/**
* Sample aspect ratio for the video frame, 0/1 if unknown/unspecified.
*/
AVRational sample_aspect_ratio;
//像素的宽高比,通过av_q2d()来获取值,如果未知/未指定,为0/1。
/**
* Presentation timestamp in time_base units (time when frame should be shown to user).
*/
int64_t pts;
//显示时间戳,表示当前为第几帧,如果要换算为时分秒,则需要AVStream的time_base时基来一起换算
//比如:
//int timeVal=av_q2d(pFormatCtx->streams[videoindex]->time_base) * pFrame->pts*100;
//int hour = timeVal/360000;
//int minute = timeVal%360000/6000;
//int second = timeVal%6000/100;
//int msec = timeVal%100*10;
#if FF_API_PKT_PTS
/**
* PTS copied from the AVPacket that was decoded to produce this frame.
* @deprecated use the pts field instead
*/
int64_t pkt_pts; //使用pts字段代替(pts=pkt_pts)
#endif
int64_t pkt_dts;
//pkt_dts:解码时间戳,等于AVPacket的dts,如果AVPacket只有dts而未设置pts,此值也是此frame的pts
//比如mjpeg格式的视频,就只有I帧,不需要对pts进行排序,所以dts和pts值一样
int coded_picture_number;
//编码顺序的图像
int display_picture_number;
//播放顺序的图像
/**
* quality (between 1 (good) and FF_LAMBDA_MAX (bad))
*/
int quality;
//视频质量,值越小越好
int repeat_pict;
//当解码时,这表示图片必须延迟多少.extra_delay = repeat_pict / (2*fps)
int interlaced_frame;
//图像逐行/隔行模式标识。
int top_field_first;
//如果内容是隔行模式扫描,则首先显示顶部字段。
int palette_has_changed;
//用来告诉应用程序,调色板已从前一帧更改。
int64_t reordered_opaque;
//重新排序的不透明64位(通常是整数或双精度浮点PTS,但可以是任何东西)。
int sample_rate;
//音频数据的采样率。
uint64_t channel_layout;
//音频数据的通道布局,参考channel_layout.h
//比如AV_CH_FRONT_LEFT:表示前左声道
/**
* AVBuffer references backing the data for this frame. If all elements of
* this array are NULL, then this frame is not reference counted. This array
* must be filled contiguously -- if buf[i] is non-NULL then buf[j] must
* also be non-NULL for all j < i.
*
* There may be at most one AVBuffer per data plane, so for video this array
* always contains all the references. For planar audio with more than
* AV_NUM_DATA_POINTERS channels, there may be more buffers than can fit in
* this array. Then the extra AVBufferRef pointers are stored in the
* extended_buf array.
*/
AVBufferRef *buf[AV_NUM_DATA_POINTERS];
//通过引用计数,使该AVBufferRef来间接使用AVBuffer缓冲区,也就是data[]指向的原始数据.
//用户不应直接使用data成员,应通过buf成员间接使用data成员
//如果buf[]的所有元素都为NULL,则此帧不会被引用计数。必须连续填充buf[] - 如果buf[i]为非NULL,则对于所有j<i,buf[j]也必须为非NULL
AVBufferRef **extended_buf;
int nb_extended_buf;
//和extended_data类似,因为buf最多存储8通道.
//extended_buf和AVFrame.extended_data唯一不同在于:extended_data包含所有指向各plane的指针,而extended_buf只包含buf中装不下的指针。
AVFrameSideData **side_data;
int nb_side_data;
//边缘数据和数目
/**
* @defgroup lavu_frame_flags AV_FRAME_FLAGS
* @ingroup lavu_frame
* Flags describing additional frame properties.
*
* @{
*/
#define AV_FRAME_FLAG_CORRUPT (1 << 0)
//标记需要解码但不应该输出的帧的标志。帧数据可能被损坏,例如由于解码错误
#define AV_FRAME_FLAG_DISCARD (1 << 2)
//标记需要解码但不应该输出的帧的标志。
int flags; //编解码失败后,用户可以通过该flag查看是否为AV_FRAME_FLAG_CORRUPT或者AV_FRAME_FLAG_DISCARD
enum AVColorRange color_range; //图像的编码格式(MPEG/JPEG),解码时,由库设置,编码时,由用户来设置
enum AVColorPrimaries color_primaries; //图像源初选的色度坐标
enum AVColorTransferCharacteristic color_trc; //图像颜色传输特性
/**
* YUV colorspace type.
* - encoding: Set by user
* - decoding: Set by libavcodec
*/
enum AVColorSpace colorspace;
//图像彩色空间类型,解码时,由库设置,编码时,由用户来设置
//比如等于AVCOL_SPC_RGB时,那么color_trc等于AVCOL_TRC_IEC61966_2_1
enum AVChromaLocation chroma_location;
//储存的颜色色度样品的位置
/**
* reordered pos from the last AVPacket that has been input into the decoder
* - encoding: unused
* - decoding: Read by user.
*/
int64_t pkt_pos;
//标记最后一个解码的packet在输入文件中的位置偏移量。
int64_t pkt_duration;
//该帧的持续时间,需要通过AVStream的time_base时基来换算
/**
* metadata.
* - encoding: Set by user.
* - decoding: Set by libavcodec.
*/
AVDictionary *metadata;
int decode_error_flags; //解码帧的错误标志
#define FF_DECODE_ERROR_INVALID_BITSTREAM 1
#define FF_DECODE_ERROR_MISSING_REFERENCE 2
#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE 4
#define FF_DECODE_ERROR_DECODE_SLICES 8
int channels;
//音频通道数量,仅用于音频
//用户可以通过 if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))来判断该frame是否为音频
int pkt_size;
//压缩帧的相应数据包的大小。
size_t crop_top;
size_t crop_bottom;
size_t crop_left;
size_t crop_right;
//用于裁剪视频帧图像用的。四个值分别为从frame的上/下/左/右边界裁切的像素数。
//...
}AVFrame;
av_get_colorspace_name
frame.h
/**
* 获取颜色空间的名称。
* @return 一个标识色彩空间的静态字符串; 可以为 NULL。
*/
const char *av_get_colorspace_name(enum AVColorSpace val);
//源码:
const char *av_get_colorspace_name(enum AVColorSpace val)
{
static const char * const name[] = {
[AVCOL_SPC_RGB] = "GBR",
[AVCOL_SPC_BT709] = "bt709",
[AVCOL_SPC_FCC] = "fcc",
[AVCOL_SPC_BT470BG] = "bt470bg",
[AVCOL_SPC_SMPTE170M] = "smpte170m",
[AVCOL_SPC_SMPTE240M] = "smpte240m",
[AVCOL_SPC_YCOCG] = "YCgCo",
};
if ((unsigned)val >= FF_ARRAY_ELEMS(name))
return NULL;
return name[val];
}
av_frame_alloc()
/**
* 分配一个 AVFrame 并将其字段设置为默认值。 所结果的
* 必须使用 av_frame_free() 释放结构体。
*
* @return 一个 AVFrame 填充默认值或失败时为 NULL。
*
* @note 这只会分配 AVFrame 本身,而不是数据缓冲区。 那些
* 必须通过其他方式分配,例如 使用 av_frame_get_buffer() 或
* 手动。
*/
AVFrame *av_frame_alloc(void);
//源码:
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_free
frame.h
/**
* 释放框架和其中任何动态分配的对象,
* 例如 扩展数据。 如果帧被引用计数,它将是
* 首先未引用。
*
* @param 要释放的帧帧。 指针将被设置为 NULL。
*/
void av_frame_free(AVFrame **frame);
//源码:
void av_frame_free(AVFrame **frame)
{
if (!frame || !*frame)
return;
av_frame_unref(*frame);
av_freep(frame);
}
av_frame_ref
frame.h
/**
* 设置对源帧描述的数据的新引用。
*
* 将帧属性从 src 复制到 dst 并为每个创建一个新的参考
* 来自 src 的 AVBufferRef。
*
* 如果 src 没有被引用计数,则分配新的缓冲区并且数据是
* 复制。
*
* @warning: dst 必须被 av_frame_unref(dst) 未引用,
* 或在调用此之前使用 av_frame_alloc() 新分配
* 函数,否则会发生未定义的行为。
*
* @return 成功时返回 0,错误时返回负 AVERROR
*/
int av_frame_ref(AVFrame *dst, const AVFrame *src);
//源码:
int av_frame_ref(AVFrame *dst, const AVFrame *src)
{
int i, ret = 0;
av_assert1(dst->width == 0 && dst->height == 0);
av_assert1(dst->channels == 0);
dst->format = src->format;
dst->width = src->width;
dst->height = src->height;
dst->channels = src->channels;
dst->channel_layout = src->channel_layout;
dst->nb_samples = src->nb_samples;
ret = frame_copy_props(dst, src, 0);
if (ret < 0)
return ret;
/* duplicate the frame data if it's not refcounted */
if (!src->buf[0]) {
ret = av_frame_get_buffer(dst, 32);
if (ret < 0)
return ret;
ret = av_frame_copy(dst, src);
if (ret < 0)
av_frame_unref(dst);
return ret;
}
/* ref the buffers */
for (i = 0; i < FF_ARRAY_ELEMS(src->buf); i++) {
if (!src->buf[i])
continue;
dst->buf[i] = av_buffer_ref(src->buf[i]);
if (!dst->buf[i]) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (src->extended_buf) {
dst->extended_buf = av_mallocz_array(sizeof(*dst->extended_buf),
src->nb_extended_buf);
if (!dst->extended_buf) {
ret = AVERROR(ENOMEM);
goto fail;
}
dst->nb_extended_buf = src->nb_extended_buf;
for (i = 0; i < src->nb_extended_buf; i++) {
dst->extended_buf[i] = av_buffer_ref(src->extended_buf[i]);
if (!dst->extended_buf[i]) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
}
if (src->hw_frames_ctx) {
dst->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);
if (!dst->hw_frames_ctx) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
/* duplicate extended data */
if (src->extended_data != src->data) {
int ch = src->channels;
if (!ch) {
ret = AVERROR(EINVAL);
goto fail;
}
CHECK_CHANNELS_CONSISTENCY(src);
dst->extended_data = av_malloc_array(sizeof(*dst->extended_data), ch);
if (!dst->extended_data) {
ret = AVERROR(ENOMEM);
goto fail;
}
memcpy(dst->extended_data, src->extended_data, sizeof(*src->extended_data) * ch);
} else
dst->extended_data = dst->data;
memcpy(dst->data, src->data, sizeof(src->data));
memcpy(dst->linesize, src->linesize, sizeof(src->linesize));
return 0;
fail:
av_frame_unref(dst);
return ret;
}
av_frame_clone
/**
* 创建一个引用与 src 相同数据的新框架。
*
* 这是 av_frame_alloc()+av_frame_ref() 的快捷方式。
*
* @return 成功时新创建的 AVFrame,错误时为 NULL。
*/
AVFrame *av_frame_clone(const AVFrame *src);
//源码:
AVFrame *av_frame_clone(const AVFrame *src)
{
AVFrame *ret = av_frame_alloc();
if (!ret)
return NULL;
if (av_frame_ref(ret, src) < 0)
av_frame_free(&ret);
return ret;
}
av_frame_unref
/**
* 取消引用帧引用的所有缓冲区并重置帧字段。
*/
void av_frame_unref(AVFrame *frame);
av_frame_move_ref
/**
* 将 src 中包含的所有内容移动到 dst 并重置 src。
*
* @warning: dst 不是未引用,而是直接覆盖而不读取
* 或释放其内容。 手动调用 av_frame_unref(dst)
* 在调用此函数之前确保没有内存泄漏。
*/
void av_frame_move_ref(AVFrame *dst, AVFrame *src);
//源码:
void av_frame_unref(AVFrame *frame)
{
int i;
if (!frame)
return;
wipe_side_data(frame);
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);
#if FF_API_FRAME_QP
FF_DISABLE_DEPRECATION_WARNINGS
av_buffer_unref(&frame->qp_table_buf);
FF_ENABLE_DEPRECATION_WARNINGS
#endif
av_buffer_unref(&frame->hw_frames_ctx);
av_buffer_unref(&frame->opaque_ref);
av_buffer_unref(&frame->private_ref);
get_frame_defaults(frame);
}
av_frame_get_buffer
/**
* 为音频或视频数据分配新的缓冲区。
*
* 在调用此函数之前,必须在框架上设置以下字段:
* - 格式(视频的像素格式,音频的样本格式)
* - 视频的宽度和高度
* - 用于音频的 nb_samples 和 channel_layout
*
* 此函数将填充 AVFrame.data 和 AVFrame.buf 数组,如果
* 必要,分配和填充 AVFrame.extended_data 和 AVFrame.extended_buf。
* 对于平面格式,将为每个平面分配一个缓冲区。
*
* @warning: 如果帧已经被分配,调用这个函数会
* 泄漏内存。此外,在某些情况下可能会发生未定义的行为
* 案例。
*
* @param frame frame 用于存储新缓冲区。
* @param align 所需的缓冲区大小对齐。如果等于 0,对齐将是
* 为当前 CPU 自动选择。它是高度
* 建议在此处传递 0,除非您知道自己在做什么。
*
* @return 成功时返回 0,错误时返回负 AVERROR。
*/
int av_frame_get_buffer(AVFrame *frame, int align);
//源码:
int av_frame_get_buffer(AVFrame *frame, int align)
{
if (frame->format < 0)
return AVERROR(EINVAL);
if (frame->width > 0 && frame->height > 0)
return get_video_buffer(frame, align);
else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
return get_audio_buffer(frame, align);
return AVERROR(EINVAL);
}
av_frame_is_writable
/**
* 检查帧数据是否可写。
*
* @return 如果帧数据是可写的,则为正值(如果和
* 仅当每个底层缓冲区只有一个引用时,即
* 存储在此帧中)。 否则返回 0。
*
* 如果返回 1,则答案有效,直到 av_buffer_ref() 被调用
* 底层 AVBufferRefs(例如通过 av_frame_ref() 或直接)。
*
* @see av_frame_make_writable(), av_buffer_is_writable()
*/
int av_frame_is_writable(AVFrame *frame);
//源码:
int av_frame_is_writable(AVFrame *frame)
{
int i, ret = 1;
/* assume non-refcounted frames are not writable */
if (!frame->buf[0])
return 0;
for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
if (frame->buf[i])
ret &= !!av_buffer_is_writable(frame->buf[i]);
for (i = 0; i < frame->nb_extended_buf; i++)
ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
return ret;
}
av_frame_make_writable
/**
* 确保帧数据可写,尽可能避免数据复制。
*
* 如果帧可写,则什么都不做,分配新缓冲区并复制数据
* 如果不是。
*
* @return 成功时返回 0,错误时返回负 AVERROR。
*
* @see av_frame_is_writable(), av_buffer_is_writable(),
* av_buffer_make_writable()
*/
int av_frame_make_writable(AVFrame *frame);
//源码:
int av_frame_make_writable(AVFrame *frame)
{
AVFrame tmp;
int ret;
if (!frame->buf[0])
return AVERROR(EINVAL);
if (av_frame_is_writable(frame))
return 0;
memset(&tmp, 0, sizeof(tmp));
tmp.format = frame->format;
tmp.width = frame->width;
tmp.height = frame->height;
tmp.channels = frame->channels;
tmp.channel_layout = frame->channel_layout;
tmp.nb_samples = frame->nb_samples;
ret = av_frame_get_buffer(&tmp, 32);
if (ret < 0)
return ret;
ret = av_frame_copy(&tmp, frame);
if (ret < 0) {
av_frame_unref(&tmp);
return ret;
}
ret = av_frame_copy_props(&tmp, frame);
if (ret < 0) {
av_frame_unref(&tmp);
return ret;
}
av_frame_unref(frame);
*frame = tmp;
if (tmp.data == tmp.extended_data)
frame->extended_data = frame->data;
return 0;
}
av_frame_copy
/**
* 将帧数据从 src 复制到 dst。
*
* 该函数不分配任何东西,dst 必须已经初始化并且
* 分配与 src 相同的参数。
*
* 该函数只复制帧数据(即数据的内容/
* 扩展数据数组),而不是任何其他属性。
*
* @return >= 0 成功,负 AVERROR 错误。
*/
int av_frame_copy(AVFrame *dst, const AVFrame *src);
//源码:
int av_frame_copy(AVFrame *dst, const AVFrame *src)
{
if (dst->format != src->format || dst->format < 0)
return AVERROR(EINVAL);
if (dst->width > 0 && dst->height > 0)
return frame_copy_video(dst, src);
else if (dst->nb_samples > 0 && dst->channels > 0)
return frame_copy_audio(dst, src);
av_frame_copy_props
/**
* 仅将“元数据”字段从 src 复制到 dst。
*
* 用于此功能的元数据是那些不影响
* 缓冲区中的数据布局。 例如。 pts,采样率(音频)或样本
* 纵横比(对于视频),但不是宽度/高度或通道布局。
* 侧面数据也被复制。
*/
int av_frame_copy_props(AVFrame *dst, const AVFrame *src);
//源码:
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
{
return frame_copy_props(dst, src, 1);
}
av_frame_get_plane_buffer
/**
* 获取存储给定数据平面的缓冲区引用。
*
* @param 在frame->extended_data 中感兴趣的数据平面的平面索引。
*
* @return 包含平面的缓冲区引用或 NULL 如果输入
* 框架无效。
*/
AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane);
//源码:
AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
{
uint8_t *data;
int planes, i;
if (frame->nb_samples) {
int channels = frame->channels;
if (!channels)
return NULL;
CHECK_CHANNELS_CONSISTENCY(frame);
planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
} else
planes = 4;
if (plane < 0 || plane >= planes || !frame->extended_data[plane])
return NULL;
data = frame->extended_data[plane];
for (i = 0; i < FF_ARRAY_ELEMS(frame->buf) && frame->buf[i]; i++) {
AVBufferRef *buf = frame->buf[i];
if (data >= buf->data && data < buf->data + buf->size)
return buf;
}
for (i = 0; i < frame->nb_extended_buf; i++) {
AVBufferRef *buf = frame->extended_buf[i];
if (data >= buf->data && data < buf->data + buf->size)
return buf;
}
return NULL;
}
av_frame_new_side_data
/**
* 向框架添加新的边数据。
*
* @param frame 应添加边数据的帧
* @param type 添加的边数据类型
* @param size 侧边数据的大小
*
* @return 成功时新添加的边数据,错误时为 NULL
*/
AVFrameSideData *av_frame_new_side_data(AVFrame *frame,
enum AVFrameSideDataType type,
int size);
//源码:
AVFrameSideData *av_frame_new_side_data(AVFrame *frame,
enum AVFrameSideDataType type,
int size)
{
AVFrameSideData *ret;
AVBufferRef *buf = av_buffer_alloc(size);
ret = av_frame_new_side_data_from_buf(frame, type, buf);
if (!ret)
av_buffer_unref(&buf);
return ret;
}
av_frame_new_side_data_from_buf
/**
* 从现有的 AVBufferRef 向帧添加新的边数据
*
* @param frame 应添加边数据的帧
* @param type 添加的边数据类型
* @param buf 一个 AVBufferRef 添加为辅助数据。 所有权
* 参考被转移到框架。
*
* @return 成功时新添加的边数据,错误时为 NULL。 失败时
* 帧不变,AVBufferRef 仍归其所有
* 呼叫者,召集者。
*/
AVFrameSideData *av_frame_new_side_data_from_buf(AVFrame *frame,
enum AVFrameSideDataType type,
AVBufferRef *buf);
//源码:
AVFrameSideData *av_frame_new_side_data_from_buf(AVFrame *frame,
enum AVFrameSideDataType type,
AVBufferRef *buf)
{
AVFrameSideData *ret, **tmp;
if (!buf)
return NULL;
if (frame->nb_side_data > INT_MAX / sizeof(*frame->side_data) - 1)
return NULL;
tmp = av_realloc(frame->side_data,
(frame->nb_side_data + 1) * sizeof(*frame->side_data));
if (!tmp)
return NULL;
frame->side_data = tmp;
ret = av_mallocz(sizeof(*ret));
if (!ret)
return NULL;
ret->buf = buf;
ret->data = ret->buf->data;
ret->size = buf->size;
ret->type = type;
frame->side_data[frame->nb_side_data++] = ret;
return ret;
}
av_frame_get_side_data
/**
* @return 成功时指向给定类型的边数据的指针,如果存在则为 NULL
* 在此帧中没有此类类型的边数据。
*/
AVFrameSideData *av_frame_get_side_data(const AVFrame *frame,
enum AVFrameSideDataType type);
//源码:
AVFrameSideData *av_frame_get_side_data(const AVFrame *frame,
enum AVFrameSideDataType type)
{
int i;
for (i = 0; i < frame->nb_side_data; i++) {
if (frame->side_data[i]->type == type)
return frame->side_data[i];
}
return NULL;
}
av_frame_remove_side_data
/**
* 如果框架中存在所提供类型的边数据,请将其释放并删除
* 从框架。
*/
void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type);
//源码:
void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
{
int i;
for (i = 0; i < frame->nb_side_data; i++) {
AVFrameSideData *sd = frame->side_data[i];
if (sd->type == type) {
free_side_data(&frame->side_data[i]);
frame->side_data[i] = frame->side_data[frame->nb_side_data - 1];
frame->nb_side_data--;
}
}
}
av_frame_apply_cropping
/**
* Flags for frame cropping.
*/
enum {
/**
* 应用最大可能的裁剪,即使它需要设置
* AVFrame.data[] 未对齐指针的条目。 传递未对齐的数据
* 到 FFmpeg API 通常是不允许的,并且会导致未定义的行为
*(例如崩溃)。 您只能将未对齐的数据传递给
* 明确记录以接受它。 仅当您使用此标志时
* 绝对知道你在做什么。
*/
AV_FRAME_CROP_UNALIGNED = 1 << 0,
};
/**
* 根据其crop_left/crop_top/crop_right/裁剪给定的视频AVFrame
*crop_bottom 字段。 如果裁剪成功,该功能将调整
* 数据指针和宽度/高度字段,并将裁剪字段设置为 0。
*
* 在所有情况下,裁剪边界将四舍五入到固有
* 像素格式的对齐方式。 在某些情况下,例如对于不透明的 hwaccel
* 格式,左侧/顶部裁剪将被忽略。 作物领域甚至设置为0
* 如果裁剪被四舍五入或忽略。
*
* @param frame 应该被裁剪的框架
* @param flags AV_FRAME_CROP_* 标志的某种组合,或 0。
*
* @return >= 0 成功,负 AVERROR 错误。 如果耕地
* 无效,返回 AVERROR(ERANGE),并且没有任何改变。
*/
int av_frame_apply_cropping(AVFrame *frame, int flags);
//源码:
int av_frame_apply_cropping(AVFrame *frame, int flags)
{
const AVPixFmtDescriptor *desc;
size_t offsets[4];
int i;
if (!(frame->width > 0 && frame->height > 0))
return AVERROR(EINVAL);
if (frame->crop_left >= INT_MAX - frame->crop_right ||
frame->crop_top >= INT_MAX - frame->crop_bottom ||
(frame->crop_left + frame->crop_right) >= frame->width ||
(frame->crop_top + frame->crop_bottom) >= frame->height)
return AVERROR(ERANGE);
desc = av_pix_fmt_desc_get(frame->format);
if (!desc)
return AVERROR_BUG;
/* Apply just the right/bottom cropping for hwaccel formats. Bitstream
* formats cannot be easily handled here either (and corresponding decoders
* should not export any cropping anyway), so do the same for those as well.
* */
if (desc->flags & (AV_PIX_FMT_FLAG_BITSTREAM | AV_PIX_FMT_FLAG_HWACCEL)) {
frame->width -= frame->crop_right;
frame->height -= frame->crop_bottom;
frame->crop_right = 0;
frame->crop_bottom = 0;
return 0;
}
/* calculate the offsets for each plane */
calc_cropping_offsets(offsets, frame, desc);
/* adjust the offsets to avoid breaking alignment */
if (!(flags & AV_FRAME_CROP_UNALIGNED)) {
int log2_crop_align = frame->crop_left ? ff_ctz(frame->crop_left) : INT_MAX;
int min_log2_align = INT_MAX;
for (i = 0; frame->data[i]; i++) {
int log2_align = offsets[i] ? ff_ctz(offsets[i]) : INT_MAX;
min_log2_align = FFMIN(log2_align, min_log2_align);
}
/* we assume, and it should always be true, that the data alignment is
* related to the cropping alignment by a constant power-of-2 factor */
if (log2_crop_align < min_log2_align)
return AVERROR_BUG;
if (min_log2_align < 5) {
frame->crop_left &= ~((1 << (5 + log2_crop_align - min_log2_align)) - 1);
calc_cropping_offsets(offsets, frame, desc);
}
}
for (i = 0; frame->data[i]; i++)
frame->data[i] += offsets[i];
frame->width -= (frame->crop_left + frame->crop_right);
frame->height -= (frame->crop_top + frame->crop_bottom);
frame->crop_left = 0;
frame->crop_right = 0;
frame->crop_top = 0;
frame->crop_bottom = 0;
return 0;
}
av_frame_side_data_name
/**
* @return 标识侧数据类型的字符串
*/
const char *av_frame_side_data_name(enum AVFrameSideDataType type);
//源码:
const char *av_frame_side_data_name(enum AVFrameSideDataType type)
{
switch(type) {
case AV_FRAME_DATA_PANSCAN: return "AVPanScan";
case AV_FRAME_DATA_A53_CC: return "ATSC A53 Part 4 Closed Captions";
case AV_FRAME_DATA_STEREO3D: return "Stereo 3D";
case AV_FRAME_DATA_MATRIXENCODING: return "AVMatrixEncoding";
case AV_FRAME_DATA_DOWNMIX_INFO: return "Metadata relevant to a downmix procedure";
case AV_FRAME_DATA_REPLAYGAIN: return "AVReplayGain";
case AV_FRAME_DATA_DISPLAYMATRIX: return "3x3 displaymatrix";
case AV_FRAME_DATA_AFD: return "Active format description";
case AV_FRAME_DATA_MOTION_VECTORS: return "Motion vectors";
case AV_FRAME_DATA_SKIP_SAMPLES: return "Skip samples";
case AV_FRAME_DATA_AUDIO_SERVICE_TYPE: return "Audio service type";
case AV_FRAME_DATA_MASTERING_DISPLAY_METADATA: return "Mastering display metadata";
case AV_FRAME_DATA_CONTENT_LIGHT_LEVEL: return "Content light level metadata";
case AV_FRAME_DATA_GOP_TIMECODE: return "GOP timecode";
case AV_FRAME_DATA_S12M_TIMECODE: return "SMPTE 12-1 timecode";
case AV_FRAME_DATA_SPHERICAL: return "Spherical Mapping";
case AV_FRAME_DATA_ICC_PROFILE: return "ICC profile";
#if FF_API_FRAME_QP
case AV_FRAME_DATA_QP_TABLE_PROPERTIES: return "QP table properties";
case AV_FRAME_DATA_QP_TABLE_DATA: return "QP table data";
#endif
case AV_FRAME_DATA_DYNAMIC_HDR_PLUS: return "HDR Dynamic Metadata SMPTE2094-40 (HDR10+)";
case AV_FRAME_DATA_REGIONS_OF_INTEREST: return "Regions Of Interest";
}
return NULL;
}
测试
#include "avframe.h"
#define MEM_ITEM_SIZE (20*1024*102) //记忆项目大小
#define AVPACKET_LOOP_COUNT 1000
// 测试 内存泄漏
void av_frame_test1()
{
AVFrame *frame = NULL;
int ret = 0;
frame = av_frame_alloc();// 没有类似的AVPacket的av_new_packet的API
// 1024 *2 * (16/8) =
frame->nb_samples = 1024;
frame->format = AV_SAMPLE_FMT_S16;//AV_SAMPLE_FMT_S16P AV_SAMPLE_FMT_S16
frame->channel_layout = AV_CH_LAYOUT_STEREO; //AV_CH_LAYOUT_MONO单声道 AV_CH_LAYOUT_STEREO立体声
ret = av_frame_get_buffer(frame, 0); // 根据格式分配内存
if(frame->buf && frame->buf[0])
printf("%s(%d) 1 frame->buf[0]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[0]->size); //受frame->format等参数影响,左声道
if(frame->buf && frame->buf[1])
printf("%s(%d) 1 frame->buf[1]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[1]->size); //受frame->format等参数影响, 右声道
if(frame->buf && frame->buf[0]) // 打印referenc-counted,必须保证传入的是有效指针
printf("%s(%d) ref_count1(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));
ret = av_frame_make_writable(frame); // 当frame本身为空时不能make writable
printf("av_frame_make_writable ret = %d\n", ret);
if(frame->buf && frame->buf[0]) // 打印referenc-counted,必须保证传入的是有效指针
printf("%s(%d) ref_count2(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));
av_frame_unref(frame);
if(frame->buf && frame->buf[0]) // 打印referenc-counted,必须保证传入的是有效指针
printf("%s(%d) ref_count3(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));
av_frame_free(&frame);
}
void av_frame_test()
{
av_frame_test1();
}