Frame

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值