ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

FFMpeg AVPacket 之理解与掌握

2022-01-23 19:02:18  阅读:255  来源: 互联网

标签:AVPacket FFMpeg pkt 掌握 data packet av buf size


本人的描述基本是白话文, 我没有时间和能力去画图,真正的理解需要调试附带的代码. 代码是我整理的.
ffmpeg 中引入了很多概念,今天,
介绍一个重要的概念AVPacket  
c 的时代还没有引入类class的概念, 放在一起的数据叫结构struct, 用结构声明一个变量结构变量.
文件中的各个函数,操作的就是这个结构变量.
这里有几个问题需要关注.
1. 为什么要引入这样一个结构?
答. 当然是为了方便使用. 否则写这么多代码干什么.
2. 关注变量的生成和释放, 相当于类的构造和析构.

当你声明了一个包, AVPacket packet;
你也可以从堆中声明一个包, AVPacket *pkt = av_packet_alloc();
堆中声明的,需要释放, 函数 av_packet_free(pkt);

包,当然是用来装东西的. 可以理解为包头加包体构成一个整体包
嗯,现在只是声明或创造了一个包头, 真正装数据的地方是包体.
继续执行函数调用 av_new_packet((pkt,nLen);
创建了包体,可以装nLen 长度的数据. 你可以用memcpy 向pkt->data 中copy 数据.

为啥要搞这么复杂,申请一块内存,copy数据,只要malloc 就可以了,为什么要用packet?
那是因为packet在处理av数据时有很多特殊需要, 例如,包的copy, 经常是只copy包头,不copy包体,
这样,包的copy 就非常有效率. 这就是包这个概念存在的价值了.

那包怎么copy呢?  千万不要这样写 packet = *pkt, 这样只是copy了包头,使得packet的包头与pkt
所指的包头完全一样, 好多问题都是这么引入的.
为什么呢? 究其原因还是c语言没有赋值构造函数, 只是简单的copy了数据和指针, 指针所指向的数据它就管不了了.
你应该调用函数av_packet_ref(&packet,pkt)
这样,packet 就copy 了pkt, 其实它真正的实现是把pkt所指向的数据引用计数增加了一次.
当不用这个packet了,你需要释放av_packet_unref(&packet), 此时包头就会恢复成默认值.
但是,包体却未必会释放, 只有当包体的引用计数减为0时,包体会被释放.

包体对应着一个结构,叫AVBuffer, 可以理解为malloc 的一个实现. 这个结构并没有对用户开放, 不过开源的代码
我们还是能看到它的实现.里面包含了一个refcount

包头中包含一个AVBufferRef 结构, 主要是一个AVBuffer 的指针.
如果比较两个packet, AVBufferRef 地址不同, data指针相同, 那这两个包都指向了同一个AVBuffer.
其实, 包的使用还是很简单的,主要是
av_packet_ref
av_packet_unref
同时还要掌握av_new_packet, av_packet_alloc,av_packet_free
认真掌握下面的程序:

#include <stdio.h>
#include <libavcodec/packet.h>
#include <libavcodec/avcodec.h>

char *string="hello";

// packet 的内存分配问题
int main()
{
	int nLen = strlen(string);
	AVPacket packet;		// 一个局部变量
	AVPacket *pkt= &packet;
	av_new_packet(pkt,nLen);		// new_packet 会分配packet 的存储数据,并由AVBufferRef 指向AVBuffer, AVBuffer是实际装数据的地方
	memcpy(pkt->data,string,nLen);

	AVPacket *pkt2 = av_packet_alloc();
//	av_new_packet(pkt2,nLen);
//	memcpy(pkt2->data,string,nLen);
	av_packet_ref(pkt2,pkt);
	av_packet_unref(pkt2);	
	av_packet_unref(pkt2);	
	av_packet_unref(pkt);	
	av_packet_free(&pkt2); //pkt2 是av_packet_alloc 分配的,所以需要free

	return 0;
}

想进一步理解后面到底发生了什么,可以把玩下面这个程序, 一株小草,下面带了一大块泥巴才活的健康.

#include <stdio.h>
#include <libavcodec/packet.h>
#include <libavcodec/avcodec.h>

struct AVBuffer {
    uint8_t *data; /**< data described by this buffer */
    int size; /**< size of data in bytes */

    /**
     *  number of existing AVBufferRef instances referring to this buffer
     */
    int refcount;

    /**
     * a callback for freeing the data
     */
    void (*free)(void *opaque, uint8_t *data);

    /**
     * an opaque pointer, to be used by the freeing callback
     */
    void *opaque;

    /**
     * A combination of AV_BUFFER_FLAG_*
     */
    int flags;

    /**
     * A combination of BUFFER_FLAG_*
     */
    int flags_internal;
};

static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
{
    AVBuffer *b;

    b = (*dst)->buffer;

    if (src) {			// 源存在
        **dst = **src;	//源向目标copy
        av_freep(src);  //源被释放
    } else
        av_freep(dst); // 源为空,目标释放

//    if (atomic_fetch_sub_explicit(&b->refcount, 1, memory_order_acq_rel) == 1) 
	b->refcount --;
    if (b->refcount == 0) {	//buf 参考为0时,
        b->free(b->opaque, b->data);	//buf 内容被释放
        av_freep(&b);		//buf 被释放
    }
}

void av_buffer_default_free(void *opaque, uint8_t *data)
{
	(void) opaque;
    av_free(data);
}
AVBufferRef *av_buffer_create(uint8_t *data, int size,
                              void (*free)(void *opaque, uint8_t *data),
                              void *opaque, int flags)
{
    AVBufferRef *ref = NULL;
    AVBuffer    *buf = NULL;

    buf = av_mallocz(sizeof(*buf));
    if (!buf)
        return NULL;

    buf->data     = data;
    buf->size     = size;
    buf->free     = free ? free : av_buffer_default_free;
    buf->opaque   = opaque;

    buf->refcount = 1;

    buf->flags = flags;

    ref = av_mallocz(sizeof(*ref));
    if (!ref) {
        av_freep(&buf);
        return NULL;
    }

    ref->buffer = buf;
    ref->data   = data;
    ref->size   = size;

    return ref;
}

int av_buffer_realloc(AVBufferRef **pbuf, int size)
{
    AVBufferRef *buf = *pbuf;
    uint8_t *tmp;
    int ret;

    if (!buf) 
	{
        /* allocate a new buffer with av_realloc(), so it will be reallocatable
         * later */
        uint8_t *data = av_realloc(NULL, size);
        if (!data)
            return AVERROR(ENOMEM);

        buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
        if (!buf) {
            av_freep(&data);
            return AVERROR(ENOMEM);
        }

        buf->buffer->flags_internal |= 1; //BUFFER_FLAG_REALLOCATABLE;
        *pbuf = buf;

        return 0;
    } 
	else if (buf->size == size) return 0;

    if (!(buf->buffer->flags_internal & 1/*BUFFER_FLAG_REALLOCATABLE*/) ||
        !av_buffer_is_writable(buf) || buf->data != buf->buffer->data) 
	{
        /* cannot realloc, allocate a new reallocable buffer and copy data */
        AVBufferRef *new = NULL;

        ret = av_buffer_realloc(&new, size);
        if (ret < 0)
            return ret;

        memcpy(new->data, buf->data, FFMIN(size, buf->size));

        buffer_replace(pbuf, &new);
        return 0;
    }

    tmp = av_realloc(buf->buffer->data, size);
    if (!tmp)
        return AVERROR(ENOMEM);

    buf->buffer->data = buf->data = tmp;
    buf->buffer->size = buf->size = size;
    return 0;
}

static void get_packet_defaults(AVPacket *pkt)
{
    memset(pkt, 0, sizeof(*pkt));

    pkt->pts             = AV_NOPTS_VALUE;
    pkt->dts             = AV_NOPTS_VALUE;
    pkt->pos             = -1;
}

static int packet_alloc(AVBufferRef **buf, int size)
{
    int ret;
    if (size < 0 || size >= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
        return AVERROR(EINVAL);

    ret = av_buffer_realloc(buf, size + AV_INPUT_BUFFER_PADDING_SIZE);
    if (ret < 0)
        return ret;

    memset((*buf)->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    return 0;
}


int av_new_packet(AVPacket *pkt, int size)
{
    AVBufferRef *buf = NULL;
    int ret = packet_alloc(&buf, size);
    if (ret < 0)
        return ret;

    get_packet_defaults(pkt);
    pkt->buf      = buf;
    pkt->data     = buf->data;
    pkt->size     = size;

    return 0;
}

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

void av_packet_free_side_data(AVPacket *pkt)
{
    int i;
    for (i = 0; i < pkt->side_data_elems; i++)
        av_freep(&pkt->side_data[i].data);
    av_freep(&pkt->side_data);
    pkt->side_data_elems = 0;
}

void av_buffer_unref(AVBufferRef **buf)
{
    if (!buf || !*buf)
        return;

    buffer_replace(buf, NULL);
}

void av_packet_unref(AVPacket *pkt)
{
    av_packet_free_side_data(pkt);
    av_buffer_unref(&pkt->buf);
    get_packet_defaults(pkt);
}

void av_packet_free(AVPacket **pkt)
{
    if (!pkt || !*pkt)
        return;

    av_packet_unref(*pkt);
    av_freep(pkt);
}
char *string="hello";

// packet 的内存分配问题
int main()
{
	int nLen = strlen(string);
	AVPacket packet;		// 一个局部变量
	AVPacket *pkt= &packet;
	av_new_packet(pkt,nLen);		// new_packet 会分配packet 的存储数据,并由AVBufferRef 指向AVBuffer, AVBuffer是实际装数据的地方
	memcpy(pkt->data,string,nLen);

	AVPacket *pkt2 = av_packet_alloc();
//	av_new_packet(pkt2,nLen);
//	memcpy(pkt2->data,string,nLen);
	av_packet_ref(pkt2,pkt);
	av_packet_unref(pkt2);	
	av_packet_unref(pkt);	
	av_packet_free(&pkt2); //pkt2 是av_packet_alloc 分配的,所以需要free

	return 0;
}

标签:AVPacket,FFMpeg,pkt,掌握,data,packet,av,buf,size
来源: https://blog.csdn.net/hejinjing_tom_com/article/details/122655298

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有