frame.c阅读笔记

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 08:15   45   0

代码片段一:

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;
}

说明:

分配AVFrame结构体大小的内存,并将所有字段内容设置为0。

通过get_frame_defaults函数将AVFrame结构体中一些特殊的字段设置为特殊值。

这里并没有分配任何的视频或音频数据空间。


代码片段二:

与av_frame_alloc对应的函数

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


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

说明:

av_freep(frame);完成的功能才是真正与av_frame_alloc对应。av_freep(frame)函数完成将av_frame_alloc分配的AVFrame结构体指针内存释放。

av_frame_unref(*frame);函数完成释放音视频数据。


代码片段三:

void av_frame_unref(AVFrame *frame)
{
int i;


for (i = 0; i < frame->nb_side_data; i++) {
av_freep(&frame->side_data[i]->data);
av_dict_free(&frame->side_data[i]->metadata);
av_freep(&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);
}

说明:

1、

函数av_buffer_unref(&frame->buf[i]);释放了真正的音视频数据。

依据:get_audio_buffer函数中调用frame->extended_data[i] = frame->data[i] = frame->buf[i]->data;

get_video_buffer函数中调用frame->data[1] = frame->buf[1]->data; frame->extended_data = frame->data;

从get_audio_buffer及get_video_buffer函数中可以看出extended_data 与data指向同一位置。

2、

当音频通道数超过AV_NUM_DATA_POINTERS(8)时,多余通道数据存放在extended_buf中。


代码片段四:

static int get_video_buffer(AVFrame *frame, int align)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
int ret, i;


if (!desc)
return AVERROR(EINVAL);


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


if (!frame->linesize[0]) {
for(i=1; i<=align; i+=i) {
ret = av_image_fill_linesizes(frame->linesize, frame->format,
FFALIGN(frame->width, i));
if (ret < 0)
return ret;
if (!(frame->linesize[0] & (align-1)))
break;
}


for (i = 0; i < 4 && frame->linesize[i]; i++)
frame->linesize[i] = FFALIGN(frame->linesize[i], align);
}


for (i = 0; i < 4 && frame->linesize[i]; i++) {
int h = FFALIGN(frame->height, 32);
if (i == 1 || i == 2)
h = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);


frame->buf[i] = av_buffer_alloc(frame->linesize[i] * h + 16);
if (!frame->buf[i])
goto fail;


frame->data[i] = frame->buf[i]->data;
}
if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {
av_buffer_unref(&frame->buf[1]);
frame->buf[1] = av_buffer_alloc(1024);
if (!frame->buf[1])
goto fail;
frame->data[1] = frame->buf[1]->data;
}


frame->extended_data = frame->data;


return 0;
fail:
av_frame_unref(frame);
return AVERROR(ENOMEM);
}

说明:

如果linesize=值不为0,根据linesize值*高度的大小分配内存。

如果linesize值为0,先根据宽,高,图像格式得到linesize值,然后再分配内存。

内存分配由frame->buf[1]->data指向。然后指向frame->data及frame->extended_data

linesize对应的值会32字节对齐。


代码片段五:

static int get_audio_buffer(AVFrame *frame, int align)
{
int channels = frame->channels;
int planar = av_sample_fmt_is_planar(frame->format);
int planes = planar ? channels : 1;
int ret, i;


CHECK_CHANNELS_CONSISTENCY(frame);
if (!frame->linesize[0]) {
ret = av_samples_get_buffer_size(&frame->linesize[0], channels,
frame->nb_samples, frame->format,
align);
if (ret < 0)
return ret;
}


if (planes > AV_NUM_DATA_POINTERS) {
frame->extended_data = av_mallocz(planes *
sizeof(*frame->extended_data));
frame->extended_buf = av_mallocz((planes - AV_NUM_DATA_POINTERS) *
sizeof(*frame->extended_buf));
if (!frame->extended_data || !frame->extended_buf) {
av_freep(&frame->extended_data);
av_freep(&frame->extended_buf);
return AVERROR(ENOMEM);
}
frame->nb_extended_buf = planes - AV_NUM_DATA_POINTERS;
} else
frame->extended_data = frame->data;


for (i = 0; i < FFMIN(planes, AV_NUM_DATA_POINTERS); i++) {
frame->buf[i] = av_buffer_alloc(frame->linesize[0]);
if (!frame->buf[i]) {
av_frame_unref(frame);
return AVERROR(ENOMEM);
}
frame->extended_data[i] = frame->data[i] = frame->buf[i]->data;
}
for (i = 0; i < planes - AV_NUM_DATA_POINTERS; i++) {
frame->extended_buf[i] = av_buffer_alloc(frame->linesize[0]);
if (!frame->extended_buf[i]) {
av_frame_unref(frame);
return AVERROR(ENOMEM);
}
frame->extended_data[i + AV_NUM_DATA_POINTERS] = frame->extended_buf[i]->data;
}
return 0;


}

说明:

linesize大小判断,为0则根据channels,nb_samples,format算出。以32字节对齐。

音频帧分为平面方式和包方式,如果为包方式,则只有一个平面;如果为平面方式,则有几个channel就有几个平面。

如果平面数超过8,则将多余8部分平面的数据放到extended_buf中。8平面内的数据放到frame->buf[i]->data数据中,同时frame->extended_data[i] = frame->data[i]也指向8平面内的数据。


代码片段六:

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);
}

说明:

判断是分配视频还是音频的方法为frame->width,frame->height.音频关键字段为nb_sampleschannel_layoutchannels


代码片段七:

int av_frame_ref(AVFrame *dst, AVFrame *src)
{
int i, ret = 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 = av_frame_copy_props(dst, src);
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;


if (src->nb_samples) {
int ch = src->channels;
CHECK_CHANNELS_CONSISTENCY(src);
av_samples_copy(dst->extended_data, src->extended_data, 0, 0,
dst->nb_samples, ch, dst->format);
} else {
av_image_copy(dst->data, dst->linesize, src->data, src->linesize,
dst->format, dst->width, dst->height);
}
return 0;
}


/* 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(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;
}
}
}


/* 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(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;
}

说明:

完成将src Frame内容拷贝到dst Frame

先拷贝关键字段format,width,height,channels,channel_layout,nb_samples。

av_frame_copy_props拷贝各属性字段

在src的buf[0]指向为NULL时,给dst的buf分配空间,将src中data内数据拷贝到dst中。视频解码函数解出来的frame的buf[0]为NULL,但data及extended_data有内容,猜测解码函数内部自行负责数据的清空与分配。

* When AVCodecContext.refcounted_frames is set to 0, the returned
* reference belongs to the decoder and is valid only until the
* next call to this function or until closing or flushing the
* decoder. The caller may not write to it.

实验发现解码后refcounted_frames=0,buf[0]=NULL。

执行视频的av_image_copy函数,音频数据拷贝av_samples_copy。视频拷贝linesize在需要改变时会发生改变。如分辨率为720x576的h264解码后的linesize[0] = 768经过

av_samples_copy后为732。

如果buf[0] 不为NULL,则将dst指针与src指向相同,将src的引用计数+1。


代码片段八:

void av_frame_move_ref(AVFrame *dst, AVFrame *src)
{
*dst = *src;
if (src->extended_data == src->data)
dst->extended_data = dst->data;
memset(src, 0, sizeof(*src));
get_frame_defaults(src);
}

说明:

将src的内容完全拷贝到dst。将src的data数据相关指针该有dst来指向。


代码片段九:

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;
}

说明:

先计算有几个plane。视频为4个,音频根据format的planar属性来判断。

planes计算后用于判断是否参数plane是否有效,有效后找出plan对应的数据指针,

根据数据指针值找到对应的buf。













分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP