FFmpeg 详解

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也是采用同样的机制。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Day5-

Hive 窗口函数 案例 需求:连续三天登陆的用户数据 步骤: -- 建表 create table logins (username string,log_date string ) row format delimited fields terminated by ; -- 加载数据 load data local inpath /opt/hive_data/login into table log…

商场促销--策略模式

1.1 商场收银软件 package com.lhx.design.pattern.test;import java.util.Scanner;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式…

聊聊测试用例评审流程

测试人员将需求熟悉完成后,开始编写相应的测试用例,待测试用例编写完成后只是测试用例完成前的第一步,后边的流程需要组织线上或线下评审会议等等。 首先要了解测试用例评审的最终目的是什么:提高测试用例的质量和覆盖率&#xff…

利用Node.js实现拉勾网数据爬取

引言 拉勾网作为中国领先的互联网招聘平台,汇集了丰富的职位信息,对于求职者和人力资源专业人士来说是一个宝贵的数据源。通过编写网络爬虫程序,我们可以自动化地收集这些信息,为求职决策和市场研究提供数据支持。Node.js以其非阻…

【Frida】【Android】08_爬虫之网络通信库okhttp3

🛫 系列文章导航 【Frida】【Android】01_手把手教你环境搭建 https://blog.csdn.net/kinghzking/article/details/136986950【Frida】【Android】02_JAVA层HOOK https://blog.csdn.net/kinghzking/article/details/137008446【Frida】【Android】03_RPC https://bl…

【Spring】分别基于XML、注解和配置类实现Spring的IOC(控制反转)

目录 1、理解loC是什么 2、基于XML实现Spring的IOC(这种方式已经不怎么使用了) 3、基于注解实现Spring的IOC 4、基于javaConfig实现Spring的IOC 5、总结 1、理解loC是什么 lOC:lnversion of Control 控制反转,简称就是 IOC 控…

如何使用极狐GitLab Maven 仓库?

本文作者:徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了如何使用极狐GitLa…

Yarn与Zookeeper的介绍

Yarn--三大调度策略 FIFO(先进先出): 目前几乎已经没有人使用了. 类似于: 单行道. 好处: 每个计算任务能独享集群100%的资源. 弊端: 不能并行执行, 如果大任务过多, 会导致小任务执行时间过长. Capacity(容量调度): 我们用…

入门教程:Windows搭建C语言和EasyX开发环境

🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 如果对你…

【论文阅读】TimesNet: Temporal 2D-Variation Modeling for General Time Series Analysis

TimesNet: Temporal 2D-Variation Modeling for General Time Series Analysis 引用: Wu H, Hu T, Liu Y, et al. Timesnet: Temporal 2d-variation modeling for general time series analysis[C]//The eleventh international conference on learning representa…

学习 MongoDB:打开强大的数据库技术大门

一、基本概念 MongoDB 是一个基于分布式文件存储的文档数据库,由 C 语言编写。它旨在为 Web 应用提供可扩展的高性能数据存储解决方案。 相信MySQL我们非常的熟悉,那么MySQL的表结构与MongoDB的文档结构进行类比的话可能更好理解MongoDB。 MySQL的数据…

联通iccid 19转20 使用luhn 算法的计算公式

联通iccid 19转20 使用luhn 算法的计算公式 第一次对接iccid 才知道 使用的是luhn 算法 19转20位 文章来源于 文章来源 当时也是一脸懵逼 的状态,然后各种chatgpt 寻找,怎么找都发现不对,最后看到这片java的文章实验是正确的,因…

ubuntu生成core文件的设置方法

core文件可以很方便的帮助我们查找程序挂的问题,因此在程序挂后生成core文件的很有必要的。 1、查看ulimit参数 ulimit -a 如果core file size是0 需要设置其最大容量。按下面步骤操作 2、设置core文件大小,两种方法 临时生效,重启设备失…

WPF学习笔记-FlowDocument流文档基础知识和基本操作

文章目录 概述一、块元素和内联元素1.1 块元素(Block类)1.2 内联元素(Inline类)二、Paragraph元素2.1 基本属性设置2.2 将内联元素Inline添加到Inlines中2.3 设置中西文字体不一样 三、Table元素3.1 添加新的Table3.2 添加列3.3 添…

采用C#.net6.0+Vue,Ant-Design技术开发的一套大型医院手术麻醉信息系统源码,系统成熟,运行稳定

手术麻醉信息系统源码,C#手麻系统源码,自主版权应用案例(适合上项目) 手术麻醉信息系统可以实现手术室监护仪、麻醉机、呼吸机、输液泵等设备输出数据的自动采集,采集的数据能据如实准确地反映患者生命体征参数的变化&…

前端、后端上传文件到OSS,简明记录

前端、后端上传文件到OSS,简明记录 上传文件到oss的方式: **后端上传:**文件先要从页面上传到后端存起来,再通过后端发送到oss,然后后端将存起来的文件删除(当然可以不删)。 **前端上传&…

Unix 网络编程, Socket 以及bind(), listen(), accept(), connect(), read()write()五大函数简介

Unix网络编程是针对类Unix操作系统(包括Linux、BSD以及其他遵循POSIX标准的操作系统)进行网络通信开发的技术领域。网络编程涉及创建和管理网络连接、交换数据以及处理不同层次网络协议栈上的各种网络事件。在Unix环境中,网络编程通常涉及到以…

华为ensp中ospf多区域管理 原理及配置命令(详解)

作者主页:点击! ENSP专栏:点击! ————前言———— OSPF 多区域的主要作用是缩小链路状态数据库和路由表的规模,减少路由更新的频率,提高网络的可扩展性,实现路由过滤和路由汇总&#xff0…

交通标志识别项目 | 基于Tensorflow+SSD实现道路交通标志识别

项目应用场景 面向智能驾驶或自动驾驶场景道路道路交通标志的识别,在交通标志识别的基础上为下一步的智能决策提供前提 项目效果: 项目细节 > 具体参见项目 README.md (1) 安装依赖 Python3.5、TensorFlow v0.12.0、Pickle、OpenCV-Python、Matplotl…

如何利用CSS实现文字滚动效果

1. 使用CSS3的animation属性 CSS3的animation属性可以让元素在一段时间内不停地播放某个动画效果。我们可以利用这个特性来实现文字滚动效果。 我们需要定义一个包含所有需要滚动的文本的容器元素。比如&#xff1a; <div class"scroll-container"><p>…