ZigZag Sin
登 陆
前面没有了 下一篇:AVFrame

AVPacket

乔红
2021-4-6 10:05 阅读 2684

AVPacket

FFmpeg 会将读到的码流封装成一个一个的 AVPacket,AVPacket 是解码器的输入,是编码器的输出。

AVPacket 可以分为两个部分,一个部分我们可以称之为 prop,这部分主要存放了 AVPacket 的一些属性,例如,pts,dts,还有 AVPacket 所属流的 index;另一个部分,我们可以称之为 data,这部分则存放了 AVPacket 中的核心数据,例如 H.264 的原始码流等。

avpacket_struct

常用属性

typedef struct AVPacket {

    AVBufferRef *buf;

    int64_t pts;

    int64_t dts;

    uint8_t *data;

    int   size;

    int   stream_index;

    int   flags;
    
    ...
    
}AVPacket;
  • int64_t pts;

    播放时间戳,pts。

  • int64_t dts;

    解码时间戳,dts。

  • int stream_index;

    所属 stream 的 index。

  • uint8_t *data;

    存放数据的指针。

  • int size;

    存放数据的大小。

常用函数

  • AVPacket *av_packet_alloc(void);

    用来分配一个 AVPacket,返回值是一个指针。使用该函数分配的 AVPacket 必须使用 av_packet_free() 来释放掉。该函数分配的时候,只会分配 AVPacket 本身的内容,而不会分配 data 数据。

    AVPacket * packet = av_packet_alloc();
    
  • AVPacket *av_packet_clone(const AVPacket *src);

    复制一个 AVPacket。这个函数会复制 AVPacket 的基本属性,而对于 AVPacket 中的 data 数据,这里只是复制了其 data 的一个引用。

    distPacket = av_packet_clone(srcPacket);
    
  • void av_packet_free(AVPacket **pkt);

    销毁一个 AVPacket,注意这里的参数是一个二级指针。在释放调用之后,传入的指针会变成 NULL。

    av_packet_free(&packet);
    
  • void av_init_packet(AVPacket *pkt);

    初始化一个 AVPacket,但是需要注意的是,这个函数不会动 data 指针和 size 这两个属性。

    av_init_packet(packet);
    
  • int av_new_packet(AVPacket *pkt, int size);

    这个函数可以为一个 AVPacket 分为 data 部分,默认来说,我们 alloc 出来的 AVPacket 是没有 data 部分的。如果我们需要让 AVPacket 分配一段数据的话,这里建议使用这个函数。size表示想要申请的大小。

    av_new_packet(packet, size);
    
  • void av_shrink_packet(AVPacket *pkt, int size);

    这个函数用来减少 AvPacket 中 data 数据大小。

    av_shrink_packet(packet, size);
    
  • int av_grow_packet(AVPacket *pkt, int grow_by);

    这个函数用来增加 AvPacket 中 data 数据大小。

    av_grow_packet(packet, grow_by);
    
  • int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size)

    把data指针指向数据交给 Packet 的 buffer 进行管理,也就是把 Packet 的 data 赋值为传入的 data,指向新的数据区域。

    av_packet_from_data(packet, data, size);
    
  • int av_packet_ref(AVPacket *dst, const AVPacket *src)

    与 av_packet_clone 类似,把 src 中的所有数据赋值到新的 dst 中,对于引用数据,data 数据会创建一块新的缓冲区进行赋值。

    av_packet_ref(dst, packet);
    
  • void av_packet_unref(AVPacket *pkt);

    把 AvPacket 各个数据进行解引用和释放对应的空间,释放 buffer 缓冲区,data 重新置为 NULL,size 置为0。

    av_packet_unref(packet);
    
  • void av_packet_move_ref(AVPacket *dst, AVPacket *src);

    把 dst 中的 data 指向 src 中的 data,把 src 进行初始化,src 的 data 置 NULL,size置0。

    av_packet_move_ref(dst, packet);
    

FFmpeg 中的引用计数

AVpacket 本质也是一个容器,它本身不包含 data 数据, 而是有一个指向 data 数据区域的指针(地址)。

  • 存在2个 packet ,但2个 packet 指向不同的 data 区域, 但 data 数据一样。

2个 packet 不会相互干扰,数据各自储存在各自的空间当中,但是显而易见的就是会造成更多空间内存上的浪费。

  • 存在2个 packet ,但2个 packet 同时指向同一个 data 区域。

当多个 packet 同时指向一个 data 数据缓存空间,FFmpeg 采用引用计数的方式来管理 data 数据区。

那么 AVpacket 中引用的数量记录在哪里呢?

​ 引用数量就记录在 AVBufferRef *buf 这个结构体当中。

struct AVBuffer {

    atomic_uint refcount;

    ......
};

typedef struct AVBufferRef {
    AVBuffer *buffer;

    uint8_t *data;

    int      size;
    
} AVBufferRef;


在 int av_packet_ref(AVPacket *dst, const AVPacket *src) 函数中:

​ 如果 src 里的 AVBufferRef *buf 为空,dst 分配一个新的缓冲区并复制 src 的数据,同时 dst 的引用计数记为 1。

if (!src->buf) {
        ret = packet_alloc(&dst->buf, src->size);
        
        av_assert1(!src->size || src->data);
        
        if (src->size)
            memcpy(dst->buf->data, src->data, src->size);

        dst->data = dst->buf->data;
    }

​ 如果 dat 里的 AVBufferRef *buf 不为空,dst 的 buf 作为新的对 src 中 buf 的引用, buf 引用计数加一。

else {
        dst->buf = av_buffer_ref(src->buf);
        
        dst->data = src->data;
    }

在 void av_packet_unref(AVPacket *pkt) 函数中,

​ 把 pkt 中 AVBufferRef *buf 引用计数 -1,如果引用次数为 0 ,销毁数据缓存空间。

前面没有了 下一篇:AVFrame
给我买个键盘吧。。。求打赏。。。
欢迎加群,一起交流~~~