FFmpeg 详解

news2024/11/28 18:54:07

FFmpeg 详解

  • FFmpeg 详解
    • 整体结构
    • 不同下载版本的区别
    • 常用库
    • 常用函数
      • 初始化
      • 封装格式
      • 解码器
    • 版本对比
      • 组件注册方式对比
        • FFmpeg 3.x 组件注册方式
        • FFmpeg 4.x 组件注册方式
      • 结构体比对
      • 函数对比
        • avcodec_decode_video2()
        • vcodec_encode_video2()
    • 数据结构
      • 结构体分析
        • AVFormatContext
        • AVInputFormat
        • AVStream
        • AVCodecParameters
        • AVCodecContext
        • AVCodec
        • AVPacket
        • AVFrame
      • 数据结构之间的关系
        • AVFormatContext和AVInputFormat之间的关系
        • AVCodecContext和AVCodec之间的关系
        • AVFormatContext, AVStream和AVCodecContext之间的关系
      • 如何区别不同的码流?
    • 内存模型

FFmpeg 详解

整体结构

在这里插入图片描述

不同下载版本的区别

官网下载的 FFmpeg 分为3个版本:Static,Shared,Dev。介绍如下。

前两个版本可以直接在命令行中使用,他们的区别在于:

  1. Static里面只有3个应用程序:ffmpeg.exe,ffplay.exe,ffprobe.exe,每个exe的体积都很大,相关的Dll已经被编译到exe里面去了。
  2. Shared里面除了3个应用程序:ffmpeg.exe,ffplay.exe,ffprobe.exe之外,还有一些Dll,比如说avcodec-54.dll之类的。Shared里面的exe体积很小,他们在运行的时候,到相应的Dll中调用功能。
  3. Dev版本是用于开发的,里面包含了include(头文件xxx.h)和lib(库文件xxx.lib),这个版本不包含exe文件。

常用库

  • AVUtil:核心工具库,下面的许多其他模块都会依赖该库做一些基本的音视频处理操作。
  • AVFormat:文件格式和协议库,该模块是最重要的模块之一,封装了Protocol层和Demuxer、Muxer层,使得协议和格式对于开发者来说是透明的。
  • AVCodec:编解码库,封装了Codec层,但是有一些Codec是具备自己的License的,FFmpeg是不会默认添加像libx264、FDK-AAC等库的,但是FFmpeg就像一个平台一样,可以将其他的第三方的Codec以插件的方式添加进来,然后为开发者提供统一的接口。
  • AVFilter:音视频滤镜库,该模块提供了包括音频特效和视频特效的处理,在使用FFmpeg的API进行编解码的过程中,直接使用该模块为音视频数据做特效处理是非常方便同时也非常高效的一种方式。
  • AVDevice:输入输出设备库,比如,需要编译出播放声音或者视频的工具ffplay,就需要确保该模块是打开的,同时也需要SDL的预先编译,因为该设备模块播放声音与播放视频使用的都是SDL库。
  • SwResample:该模块可用于音频重采样,可以对数字音频进行声道数、数据格式、采样率等多种基本信息的转换。
  • SWScale:该模块是将图像进行格式转换的模块,比如,可以将YUV的数据转换为RGB的数据,缩放尺寸由1280×720变为800×480。
  • PostProc:该模块可用于进行后期处理,当我们使用AVFilter的时候需要打开该模块的开关,因为Filter中会使用到该模块的一些基础函数。

常用函数

初始化

  • av_register_all():注册所有组件,4.0已经弃用。
  • avdevice_register_all():对设备进行注册,比如V4L2等。
  • avformat_network_init():初始化网络库以及网络加密协议相关的库(比如openssl)。

封装格式

  • avformat_alloc_context():负责申请一个AVFormatContext结构的内存,并进行简单初始化。
  • avformat_free_context():释放该结构里的所有东西以及该结构本身。
  • avformat_close_input():关闭解复用器。关闭后就不再需要使用avformat_free_context 进行释放。
  • avformat_open_input():打开多媒体数据并且获得一些相关的信息。
  • avformat_find_stream_info():获取媒体文件中每个音视频流的详细信息,包括解码器类型、采样率、声道数、码率、关键帧等信息。
  • av_read_frame():读取码流中的音频若干帧或者视频一帧。
  • avformat_seek_file():定位文件。
  • av_seek_frame():定位帧。

流程:

在这里插入图片描述

解码器

  • avcodec_alloc_context3():分配解码器上下文。
  • avcodec_find_decoder():根据ID查找解码器。
  • avcodec_find_decoder_by_name():根据解码器名字。
  • avcodec_open2():打开编解码器。
  • avcodec_decode_video2():解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame。
  • avcodec_decode_audio4():解码一帧音频数据。
  • avcodec_send_packet():发送编码数据包。
  • avcodec_receive_frame():接收解码后数据。
  • avcodec_free_context():释放解码器上下文,包含了avcodec_close()。
  • avcodec_close():关闭解码器。

在这里插入图片描述

版本对比

FFmpeg的版本众多,从2010年开始计算的项目的话,基本上还在使用的有ffmpeg2/3/4/5/6,最近几年版本彪的比较厉害,直接4/5/6,大版本之间接口有一些变化,特别是一些废弃接口被彻底删除了,编程时要特别注意兼容性的问题。

在这里插入图片描述

组件注册方式对比

FFmpeg 3.x 组件注册方式

我们使用FFmpeg,首先要执行av_register_all,把全局的解码器、编码器等结构体注册到各自全局的对象链表里,以便后面查找调用。

在这里插入图片描述

FFmpeg 4.x 组件注册方式

FFmpeg内部去做,不需要用户调用API去注册。

以codec编解码器为例:在configure的时候生成要注册的组件,这里会生成一个codec_list.c文件,里面只有static const AVCodec *const codec_list[]数组。在libavcodec/allcodecs.c将static const AVCodec *const codec_list[]的编解码器用链表的方式组织起来。

对于demuxer/muxer:libavformat/muxer_list.c、libavformat/demuxer_list.c这两个文件也是在configure的时候生成,直接下载源码是没有这两个文件的。在libavformat/allformats.c将demuxer_list[]和muexr_list[]以链表的方式组织。

其他组件也是类似的方式。

结构体比对

  1. PIX_FMT_YUV420P变成了AV_PIX_FMT_YUV420P。
  2. 解码器 AVStream::codec 被声明为已否决,现在去掉了stream->codec,解码器放在 stream->codecpar 中。

更多差别参见:

  1. ffmpeg新旧函数对比

函数对比

avcodec_decode_video2()

avcodec_decode_video2() 原本的解码函数被拆解为两个函数avcodec_send_packet()和avcodec_receive_frame()。具体用法如下:

old:

avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, pPacket);

new:

avcodec_send_packet(pCodecCtx, pPacket);
avcodec_receive_frame(pCodecCtx, pFrame);
vcodec_encode_video2()

vcodec_encode_video2() 对应的编码函数也被拆分为两个函数avcodec_send_frame()和avcodec_receive_packet()。具体用法如下:

old:

avcodec_encode_video2(pCodecCtx, pPacket, pFrame, &got_picture);

new:

avcodec_send_frame(pCodecCtx, pFrame);
avcodec_receive_packet(pCodecCtx, pPacket);

更多函数和一些细微上的差别:

  1. ffmpeg新旧函数对比
  2. Qt/C++音视频开发50-不同ffmpeg版本之间的差异处理

数据结构

  • AVFormatContext:封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息。
  • AVInputFormat:解复用器对象,每种封装格式(例如FLV, MKV, MP4, AVI)对应一个该结构体。
  • AVOutputFormat:复用器对象,表示输出文件容器格式。
  • AVStream:视频文件中每个视频(音频)流对应一个该结构体。
  • AVCodecContext:编解码器上下文结构体,保存了视频(音频)编解码相关信息。
  • AVCodec:每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体。
  • AVPacket:存储一帧压缩编码数据。
  • AVFrame:存储一帧解码后像素(采样)数据。

如果上下文数据保存在解码器里面?
多路解码的时候数据肯定有冲突。

它们之间的关系:

在这里插入图片描述

结构体分析

AVFormatContext

在这里插入图片描述

常用的成员:

struct AVInputFormat* iformat; // 输入数据的封装格式
AVIOContext *pb; // 输入数据的缓存
unsigned int nb_streams; // 音视频流个数
AVStream** streams; // 音视频流
int64_t duration; // 时长(us)
int bit_rate; // 比特率(bps)
AVDictionary *metadata; // 元数据
AVInputFormat

成员变量:

const char *name; // 格式名列表.也可以分配一个新名字。
const char *long_name; // 格式的描述性名称,意味着比名称更易于阅读。
int flags;
// 可用的flag有: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.
const char *extensions; // 文件扩展名
const AVClass *priv_class; // 一个模拟类型列表.用来在probe的时候check匹配的类型。
struct AVInputFormat *next; // 用于把所有支持的输入文件容器格式连接成链表,便于遍历查找。
int priv_data_size; // 标示具体的文件容器格式对应的Context的大小。

函数:

int (*read_probe)(AVProbeData *);//判断一个给定的文件是否有可能被解析为这种格式。 给定的buf足够大,所以你没有必要去检查它,除非你需要更多 。
int (*read_header)(struct AVFormatContext *);//读取format头并初始化AVFormatContext结构体,如果成功,返回0。创建新的流需要调用avformat_new_stream。
int (*read_header2)(struct AVFormatContext *, AVDictionary **options);//新加的函数指针,用于打开进一步嵌套输入的格式。
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);//读取一个数据包并将其放在“pkt”中。 pts和flag也被设置。
int (*read_close)(struct AVFormatContext *);//关闭流。 AVFormatContext和Streams不会被此函数释放。
int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);
int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, int64_t *pos, int64_t pos_limit);//获取stream [stream_index] .time_base单位中的下一个时间戳。
int (*read_play)(struct AVFormatContext *);//开始/继续播放 - 仅当使用基于网络的(RTSP)格式时才有意义。
int (*read_pause)(struct AVFormatContext *);//暂停播放 - 仅当使用基于网络的(RTSP)格式时才有意义。
int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);//寻求时间戳ts。
int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);返回设备列表及其属性。
int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);//初始化设备能力子模块。
int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);//释放设备能力子模块。
AVStream

AVStream是存储每一个视频/音频流信息的结构体。
在这里插入图片描述

成员变量:

int index; // 标识该视频/音频流
AVCodecContext* codec; // 指向该音频/视频流的AVCodecContext(已被废弃,不推荐使用)
AVCodecParameters* codecpar; // 获得AVCodecParameters对象,用于替代AVCodecContext(推荐使用)
int64_t duration; // 视频长度
AVRational avg_frame_rate; // 视频平均帧率
// 其中,AVRational表示有理数,他有两个参数:avg_frame_rate.num是分子,avg_frame_rate.den是分母。
// 平均帧率 = avg_frame_rate.num / avg_frame_rate.den
AVCodecParameters

AVCodecParameters是FFmpeg库中的一个结构体,用于保存音视频流的基本参数信息。该结构体通常会在AVCodecContext中被填充并使用。

在这里插入图片描述

AVCodecContext

编码器上下文AVCodecContext是FFmpeg中用于描述编码器状态的结构体,包含了许多参数和配置选项,用于控制编码器的行为和性能。

常用成员变量:

enum AVMediaType codec_type; // 编解码器的类型(视频,音频...)
struct AVCodec *codec; // 采用的解码器AVCodec(H.264,MPEG2...)
int bit_rate; // 平均比特率
uint8_t *extradata;// 针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)
int extradata_size:
AVRational time_base; // 根据该参数,可以把PTS转化为实际的时间(单位为秒s)
int width, height; // 如果是视频的话,代表宽和高
int refs; // 运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)
int sample_rate; // 采样率(音频)
int channels; // 声道数(音频)
enum AVSampleFormat sample_fmt; // 采样格式
AVCodec

AVCodec是存储编解码器信息的结构体。

常用成员变量:

const char *name; // 编解码器的名字,比较短
const char *long_name; // 编解码器的名字,全称,比较长
enum AVMediaType type; // 指明了类型,是视频,音频,还是字幕
enum AVCodecID id; // 编解码器ID,不重复
const AVRational *supported_framerates; // 支持的帧率(仅视频)
const enum AVPixelFormat *pix_fmts; // 支持的像素格式(仅视频)
const int *supported_samplerates; // 支持的采样率(仅音频)
const enum AVSampleFormat *sample_fmts; // 支持的采样格式(仅音频)
const uint64_t *channel_layouts; // 支持的声道数(仅音频)
int priv_data_size; // 私有数据的大小
// 初始化编解码器静态数据,从avcodec_register()调用。
void (*init_static_data)(struct AVCodec *codec);

关键函数:

// 将数据编码到AVPacket
int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr);
// 解码数据到AVPacket
int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
// 关闭编解码器
int (*close)(AVCodecContext *);
// 刷新缓冲区。当seek时会被调用
void (*flush)(AVCodecContext *);
AVPacket

AVPacket是存储压缩编码数据相关信息的结构体,保存了解封装之后,解码之前的数据(仍然是压缩后的数据)和关于这些数据的一些附加信息,如显示时间戳(pts)、解码时间戳(dts)、数据时长、所在媒体流的索引等。

对于视频(Video)来说,AVPacket通常包含一个压缩的Frame,而音频(Audio)则有可能包含多个压缩的Frame。并且,一个Packet有可能是空的,不包含任何压缩数据,只含有side data(side data,容器提供的关于Packet的一些附加信息。例如,在编码结束的时候更新一些流的参数)。

在这里插入图片描述

关键成员变量:

uint8_t *data; // 指向压缩编码数据的指针
// 对于packed格式的数据(例如RGB24),会存到data[0]里面
// 对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]
int size; // data的大小
int64_t pts; // 显示时间戳
int64_t dts; // 解码时间戳
int stream_index; // 标识该AVPacket所属的视频/音频流
int flags; // packet标志位,比如是否关键帧等
int64_t pos; // 当前包在流中的位置,单位字节
int64_t duration; // 数据的时长,以所属媒体流的时间基准为单位,未知则值为默认值0
AVBufferRef *buf; // 用来管理data指针引用的数据缓存

关键函数:

函数定义解释
int av_read_frame(AVFormatContext *s, AVPacket *pkt);读取码流中的音频若干帧或者视频一帧,填充AVPacket
AVPacket *av_packet_alloc(void);分配AVPacket这个时候和buffer没有关系
void av_packet_free(AVPacket **pkt);释放AVPacket和_alloc对应
void av_init_packet(AVPacket *pkt);初始化AVPacket只是单纯初始化pkt字段
int av_new_packet(AVPacket *pkt, int size);给AVPacket的buf分配内存, 引用计数初始化为1
int av_packet_ref(AVPacket *dst, const AVPacket *src);从src复制一个AVPacket结构体到dst,增加引用计数
void av_packet_unref(AVPacket *pkt);注销一个AVPacket对象,减少引用计数,若引用计数变成0,则回收缓冲区内存
void av_packet_move_ref(AVPacket *dst, AVPacket *src);转移引用计数
AVPacket *av_packet_clone(const AVPacket *src);等于av_packet_alloc()+av_packet_ref()
AVFrame

AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息。

关键成员变量:

// 解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)
// 这个data变量是一个指针数组,对于视频,可以简单地理解为三个一维数组
uint8_t *data[AV_NUM_DATA_POINTERS]; 
// data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽。
int linesize[AV_NUM_DATA_POINTERS];
int width, height; // 视频帧宽和高
int nb_samples; // 音频的一个AVFrame中可能包含多个音频帧,在此标记包含了几个
int format; // 解码后原始数据类型(YUV420,YUV422,RGB24...)
int key_frame; // 是否是关键帧
enum AVPictureType pict_type; // 帧类型(I,B,P...)
AVRational sample_aspect_ratio; // 宽高比(16:9,4:3...)
int64_t pts; // 显示时间戳
int coded_picture_number; // 编码帧序号
int display_picture_number; // 显示帧序号

关键函数:

函数定义解释
AVFrame *av_frame_alloc(void);申请AVFrame结构体空间,同时会对申请的结构体初始化
void av_frame_free(AVFrame **frame);释放AVFrame的结构体空间
int av_frame_ref(AVFrame *dst, const AVFrame *src);对已有AVFrame的引用,增加引用计数
void av_frame_unref(AVFrame *frame);对frame释放引用,减少引用计数,若引用计数变成0,则释放data的空间
void av_frame_move_ref(AVFrame *dst, AVFrame *src);转移引用计数
int av_frame_get_buffer(AVFrame *frame, int align);建立AVFrame中的data内存空间,使用这个函数之前frame结构中的format、width、height必须赋值
AVFrame *av_frame_clone(const AVFrame *src);等于av_frame_alloc()+av_frame_ref()

数据结构之间的关系

AVFormatContext和AVInputFormat之间的关系
int avformat_open_input(AVFormatContext **ps, const char *filename,
                        AVInputFormat *fmt, AVDictionary **options)

参数说明:

  1. AVFormatContext **ps:格式化的上下文。要注意,如果传入的是一个AVFormatContext*的指针,则该空间须自己手动清理,若传入的指针为空,则FFmpeg会内部自己创建。
  2. const char *filename:传入的文件地址。支持http、RTSP以及普通的本地文件。地址最终会存入到AVFormatContext结构体当中。
  3. AVInputFormat *fmt, 指定输入的封装格式。一般传NULL,由FFmpeg自行探测。
  4. AVDictionary **options, 其它参数设置。它是一个字典,用于参数传递,不传则写NULL。

在这里插入图片描述

AVCodecContext和AVCodec之间的关系

AVCodecContext:编码器上下文结构体,用于存储音视频编解码器的参数和状态信息。它包含了进行音视频编解码所需的各种设置和配置,如编码器类型、编码参数、解码参数、输入输出格式等。每个音视频流在编解码过程中都需要一个对应的AVCodecContext来描述和控制编解码器的行为。在解码过程中,AVCodecContext用于接收解码后的音视频数据。在编码过程中,AVCodecContext用于传递待编码的音视频数据。

struct AVCodec *codec;

AVCodec:音视频编解码器结构体,用于定义特定的编解码器。它包含了编解码器的类型、名称、支持的音视频格式、编解码函数等。通过AVCodec结构体,可以查询和获取系统中可用的编解码器,并与AVCodecContext关联以进行音视频编解码操作。

int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr);

AVCodecContext是对AVCodec的实例化使用,用于配置和管理编解码器的参数和状态,而AVCodec则定义了编解码器的具体功能和操作。两者共同协作,实现音视频的编解码过程。在使用FFmpeg进行音视频编解码时,首先需要选择合适的AVCodec,然后为每个音视频流创建对应的AVCodecContext,并将它们关联起来。AVCodecContext提供了对编解码器的参数进行设置的接口,如编码器参数、解码器参数、输入输出格式等。然后,通过调用相关的编解码函数,使用AVCodecContext进行音视频数据的编解码操作。

三个问题:

  1. AVCodecContext和AVCodec之间的关系是一对多的吗?

不,AVCodecContext和AVCodec之间的关系不是一对多的,而是一对一的关系。

每个AVCodecContext实例对应一个特定的编解码器,而每个编解码器对应一个AVCodec结构体。这意味着在一个AVCodecContext中,只能与一个特定的AVCodec相关联。

在使用FFmpeg进行音视频编解码时,通常会为每个音视频流创建一个对应的AVCodecContext来描述和控制编解码器的行为。在这种情况下,每个AVCodecContext会与一个特定的AVCodec相关联,用于执行相应的音视频编解码操作。

请注意,虽然多个AVCodecContext可能使用相同的AVCodec结构体进行实例化,但每个AVCodecContext都有自己的状态和参数设置,因此在使用过程中它们是独立的。这意味着每个AVCodecContext都有自己的上下文和状态,不会相互影响。

  1. AVCodecContext和AVCodec之间的关系是否可以动态地改变?

在一般情况下,AVCodecContext和AVCodec之间的关系是静态的,即在创建AVCodecContext时,会指定它所使用的特定AVCodec。一旦AVCodecContext与特定的AVCodec相关联,通常情况下不能动态地改变它们之间的关系。

这是因为AVCodecContext的配置和状态是基于特定的编解码器,而不同的编解码器可能具有不同的参数和行为。因此,如果要更改AVCodecContext的编解码器,通常需要先释放旧的AVCodecContext,然后重新创建一个新的AVCodecContext并与新的AVCodec相关联。

需要注意的是,这种重新关联的操作可能涉及到重新设置和初始化AVCodecContext的参数,以适应新的编解码器。这可能包括重新配置编码参数、解码参数、输入输出格式等。

总结来说,一般情况下,AVCodecContext和AVCodec之间的关系是静态的,一旦关联,通常不能动态地改变它们之间的关系。如果需要更改编解码器,通常需要释放旧的AVCodecContext并重新创建一个新的AVCodecContext并与新的AVCodec相关联。

  1. avcodec_open2初始化的是AVCodec还是AVCodecContext?

avcodec_open2函数用于初始化和打开一个编解码器,并将其与给定的AVCodecContext相关联。因此,avcodec_open2函数初始化的是AVCodecContext。

具体来说,avcodec_open2函数会根据AVCodecContext中的配置信息找到对应的AVCodec,然后初始化该编解码器,并将其与AVCodecContext关联起来。这样,AVCodecContext就准备好进行音视频编解码操作了。

在调用avcodec_open2函数之前,需要确保AVCodecContext已经正确设置了所需的参数,例如编码器类型、输入输出格式、编解码参数等。avcodec_open2函数会根据这些参数初始化相应的编解码器,并将其与AVCodecContext相关联,以便后续的编解码操作。

需要注意的是,一旦调用了avcodec_open2函数,AVCodecContext的参数就不能再被修改,否则可能导致未定义的行为。因此,在调用该函数之前,应该确保AVCodecContext已经正确设置了所有必要的参数。

总结来说,avcodec_open2函数用于初始化和打开一个编解码器,并将其与给定的AVCodecContext相关联,以准备进行音视频编解码操作。

AVFormatContext, AVStream和AVCodecContext之间的关系

在这里插入图片描述

如何区别不同的码流?

  • AVMEDIA_TYPE_VIDEO:视频流
  • AVMEDIA_TYPE_AUDIO:音频流
// 查找最佳匹配的媒体流
video_index = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
audio_index = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

AVPacket 里面也有一个index的字段。

内存模型

从av_read_frame读取到一个AVPacket后怎么放入解码器队列?

从avcodec_recevice_frame读取到一个AVFrame后又怎么放入解压后的帧队列?

从现有的Packet拷贝一个新Packet的时候,有两种情况:

  1. 两个Packet的buf引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题。
  2. 两个Packet的buf引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy。

在这里插入图片描述

FFmpeg 内存模型:

在这里插入图片描述

在这里插入图片描述

对于多个AVPacket共享同一个缓存空间, FFmpeg使用的引用计数的机制(reference-count) :

  1. 初始化引用计数为0,只有真正分配AVBuffer的时候,引用计数初始化为1
  2. 当有新的Packet引用共享的缓存空间时, 就将引用计数+1
  3. 当释放了引用共享空间的Packet,就将引用计数-1
  4. 引用计数为0时,就释放掉引用的缓存空间AVBuffer

AVFrame也是采用同样的机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1562404.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

上位机图像处理和嵌入式模块部署(qmacvisual获取边界点)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 在图像处理中,解决了分割的问题之后,下面就是属性信息的提取。在这其中,有一种属性是非常重要的 ,那…

蓝桥杯-单片机基础12——基于2023年IIC代码实现开机次数记录(串行EEPROM存储器AT24C02)

蓝桥杯单片机组备赛指南请查看这篇文章:戳此跳转蓝桥杯备赛指南文章 本文章针对蓝桥杯-单片机组比赛开发板所写,代码可直接在比赛开发板上使用。 型号:国信天长4T开发板(绿板),芯片:IAP15F2K6…

libusb Qt使用记录

1.libusb 下载 ,选择编译好的二进制文件,libusb-1.0.26-binaries.7z libusb Activity 2. 解压 3. 在 Qt Widgets Application 或者 Qt Console Application 工程中导入库,Qt 使用的是 minggw 64编译器,所以选择libusb-MinGW-x64。…

在岸上是永远学不会游泳的

为了让各位技术宅的师傅们了解如何追女孩,花无缺表哥来投稿啦!!! 在岸上是永远也学不会游泳的,就算是最好的教练来教你也没用,因为你没有去实践。实践是快速学习的最佳手段,将这些方法运用到工…

深度学习理论基础(三)封装数据集及手写数字识别

目录 前期准备一、制作数据集1. excel表格数据2. 代码 二、手写数字识别1. 下载数据集2. 搭建模型3. 训练网络4. 测试网络5. 保存训练模型6. 导入已经训练好的模型文件7. 完整代码 前期准备 必须使用 3 个 PyTorch 内置的实用工具(utils): ⚫…

java数据结构与算法刷题-----LeetCode695. 岛屿的最大面积

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 深度优先遍历2. 广度优先 1. 深度优先遍历 这不是找最短路径&…

量化交易入门(三十八)CCI指标Python实现和回测

今天我们先单纯用CCI指标来完成策略的编写,后续我们会改进这个策略,将CCI指标和前面讲到的MACD和RSI相结合来优化,看看我们优化后的效果会不会更好。 一、量化策略 CCI指标在量化交易中的策略: 在以下情况下生成买入信号&#…

C# 排序的多种实现方式(经典)

一、 对数组进行排序 最常见的排序是对一个数组排序,比如: int[] aArray new int[8] { 18, 17, 21, 23, 11, 31, 27, 38 }; 1、利用冒泡排序进行排序: (即每个值都和它后面的数值比较,每次拿出最小值) s…

黄仁勋:我们有望在未来五到十年内见证完全由人工智能生成的游戏

黄仁勋:我们有望在未来五到十年内见证完全由人工智能生成的游戏 近日,英伟达(Nvidia)GPU技术大会上,英伟达首席执行官黄仁勋(Jensen Huang)对未来游戏产业的展望引发了业界的广泛关注。在一次与…

centos安装docker,docker安装centos进而安装宝塔

背景 由于工作需要自己有许多的web应用要部署在云服务器上。然而资金有限每年都要去阿里云,腾讯云,华为云买最便宜的服务器,这就意味着每年都要经历一次痛苦的环境安装项目部署的过程(nginx、mysql、elasticsearch、redis等等&am…

【Redis】Redis的类型及相关操作

一、常用的key操作命令 keys * 查看当前数据库的键值 ttl key 查看还有多少秒过期,-1表示永不过期,-2表示过期 del / unlink key 同样是删除,unlink是非阻塞删除,del则有可能导致阻塞 select dbindex 切换数据库 flushdb 清空…

CA根证书——https安全保障的基石

HTTPS通信中,服务器端使用数字证书来证明自己的身份。客户端需要验证服务器发送的证书的真实性。这就需要一个可信的第三方机构,即CA,来颁发和管理证书。CA根证书是证书颁发机构层次结构的顶级证书,客户端信任的所有证书都可以追溯…

并发编程之线程池的详细解析

线程池 线程池的优势: 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后线程创建石后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕&#xf…

【回眸】Tessy 单元测试软件使用指南(三)怎么打桩和指针赋值和测试

目录 前言 Tessy 如何进行打桩操作 普通桩 高级桩 手写桩 Tessy单元测试之指针相关测试注意事项 有类型的指针(非函数指针): 有类型的函数指针: void 类型的指针: 结语 前言 进行单元测试之后,但凡…

css-盒子阴影

1.box-shadow: 10px 20px 10px 10px blue; 参数对应顺序:上下,左右 ,模糊程度,颜色 ,阴影大小 2.box-shadow: 10px 20px 10px 20px blue,-10px -20px 10px 50px red; 负号就是相反方向 支持多个阴影 在后面加逗号 3…

dockerfile制作-pytoch+深度学习环境版

你好你好! 以下内容仅为当前认识,可能有不足之处,欢迎讨论! 文章目录 文档内容docker相关术语docker常用命令容器常用命令根据dockerfile创建容器dokerfile文件内容 docker问题:可能的原因和解决方法示例修改修改后的D…

谷粒商城——通过接口幂等性防止重复提交订单

如果用户向后端服务提交多次相同订单的提交服务,那么后端应该只生成一条订单记录。 有一些操作天然是幂等的,如查询操作和删除操作等。 幂等性实现 1.token机制(仅这个方法适用于订单的重复提交) 后端先生成1个令牌将其记录在R…

MATLAB绘制堆叠填充图--巧用句柄

MATLAB绘制堆叠填充图–巧用句柄 目录 MATLAB绘制堆叠填充图--巧用句柄1. 主要原理讲解1.1 主要函数1.2 句柄原理 2. 绘图示例2.1 准备数据2.2 绘制堆叠填充图-使用句柄控制图形属性2.3 设置填充颜色和样式2.4 添加标题和标签2.5 绘图效果 3. 结语 堆叠填充图是一种常见的数据可…

【2024年5月备考新增】《2024高项论文精华版(3)考试技巧》

3 考试技巧 3.1 考试难度 考试难度上,越是常见的题目、越是被大家预测的题目,阅卷就会更严格。 越是大家猜测不到的,越是小众的题目,阅卷严格程度就会低。 3.2 技巧 1、记住软考论文的目的,不是为了证明你的格式严谨…

中科驭数超低时延网络解决方案入选2023年度金融信创优秀解决方案

近日,由中国人民银行领导、中国金融电子化集团有限公司牵头组建的金融信创生态实验室发布「2023年度第三期金融信创优秀解决方案」,中科驭数超低时延网络解决方案从众多方案中脱颖而出,成功入选,代表了该方案的技术创新和金融实践…