FFmpeg 详解

news/2024/6/16 10:06:04/文章来源:https://blog.csdn.net/ProgramNovice/article/details/137037528

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.luyixian.cn/news_show_1033403.aspx

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

相关文章

安全SCDN的威胁情报库对DDOS防护有什么好处

目前网络攻击事件频频发生,DDoS(分布式拒绝服务)攻击已成为各种企业(小到区域性小公司大到各种跨国公司)的主要威胁,DDoS 攻击可能会对企业造成重大损害和破坏,比如对目标公司的业务造成产生不利…

什么是ISP住宅IP?相比于普通IP它的优势是什么?

什么是ISP住宅IP? ISP住宅IP是指由互联网服务提供商(ISP)分配给住宅用户的IP地址。它是用户在家庭网络环境中连接互联网的标识符,通常用于上网浏览、数据传输等活动。ISP住宅IP可以是动态分配的,即每次连接时都可能会…

HarmonyOS 应用开发之PageAbility组件

PageAbility组件概述 PageAbility是包含UI、提供展示UI能力的应用组件,主要用于与用户交互。 开发者通过DevEco Studio开发平台创建PageAbility时,DevEco Studio会自动创建相关模板代码。PageAbility相关能力通过单独的featureAbility实现,…

每天五分钟深度学习:神经网络和深度学习有什么样的关系?

本文重点 神经网络是一种模拟人脑神经元连接方式的计算模型,通过大量神经元之间的连接和权重调整,实现对输入数据的处理和分析。而深度学习则是神经网络的一种特殊形式,它通过构建深层次的神经网络结构,实现对复杂数据的深度学习…

mongodb sharding分片模式的集群数据库,日志治理缺失导致写入数据库报错MongoWriteConcernException的问题总结(下)

一、接着上文 上文介绍了mongodb sharding的分片集群搭建,本文侧重于讲述日志治理。 这里使用linux自带的日志治理工具logrotate,无论是哪个端口的进程,其日志治理方式类似。 查看/data目录下的文件大小, du -hs *二、Logrota…

Docker_介绍

Docker概念介绍 1. Docker架构 从上图得知,Docker分为三部分,客户端,Docker服务端,仓库。 1.1 客户端 平常我们使用的就是客户端,通过客户端命令和Docker引擎进行交互 Docker 是一个客户端-服务器(C/S&…

安卓Termux系统安装MariaDB结合内网穿透实现公网连接本地数据库

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备,尽管最初并非设计为服务器,但是随着技术的进步我们可以将Android配置为生产力工具,变成一个随身…

Docker Desktop 在 Windows 上的安装和使用

目录 1、安装 Docker Desktop 2、使用 Docker Desktop (1)运行容器 (2)查看容器信息 (3)数据挂载 Docker Desktop是Docker的官方桌面版,专为Mac和Windows用户设计,提供了一个简…

钉钉服务端API报错 43008 参数需要multipart类型

钉钉服务端API报错 43008 参数需要multipart类型 problem 使用媒体文件上传接口,按照文档输入参数,结果返回报错 # 参数 {"access_token": "xxx""type": "image","media": "/Users/xxx/xxx/s…

GROBID库文献解析

1. 起因 由于某些原因需要在大量的文献中查找相关内容,手动实在是太慢了,所以选择了GROBID库进行文献批量解析 2. GROBID介绍 GROBID是一个机器学习库,用于将PDF等原始文档提取、解析和re-structuring为结构化的XML/TEI编码文档&#xff0…

2024年MathorCup数学建模思路A题思路分享

文章目录 1 赛题思路2 比赛日期和时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间:2024…

KingbaseV8 数据库迁移

1、打开数据迁移工具 启动迁移系统路径:安装位置\KESRealPro\V008R006C006B0021\ClientTools\guitools\KDts\KDTS-WEB\bin 3、访问到KingbaseDTS 数据库迁移工具 http://localhost:8080 默认账号密码如下: 账号:admin 密码:123…

创建第一个Electron程序

前置准备 创建一个文件夹,如: electest进入文件夹,初始化npm npm init -y 安装electron依赖包 注,这里使用npm i -D electron会特别卡,哪怕换成淘宝源也不行。可以使用下面方式安装。 首先,安装yarn npm i -g yarn 随…

快速入门Linux,Linux岗位有哪些?(一)

文章目录 Linux与Linux运维操作系统?操作系统图解 认识LinuxLinux受欢迎的原因什么是Linux运维Linux运维岗位Linux运维岗位职责Linux运维架构师岗位职责Linux运维职业发展路线计算机硬件分类运维人员的三大核心职责 运维人员工作(服务器)什么…

注册接口和前置SQL及数据生成及封装

注册接口 演示注册接口的三步操作:【注册流程逻辑】 第一步:发送注册短信验证码接口请求 请求方法: put 请求地址:http://shop.lemonban.com:8107/user/sendRegisterSms 请求参数:{“mobile”:“13422337766”} 请求头…

蓝桥杯刷题day13——乘飞机【算法赛】

一、问题描述 等待登机的你看着眼前有老有小长长的队伍十分无聊,你突然想要知道,是否存在两个年龄相仿的乘客。每个乘客的年龄用一个 0 到 36500 的整数表示,两个乘客的年龄相差 365 以内就认为是相仿的。 具体来说,你有一个长度…

iOS - Runloop介绍

文章目录 iOS - Runloop介绍1. 简介1.1 顾名思义1.2. 应用范畴1.3. 如果没有runloop1.4. 如果有了runloop 2. Runloop对象3. Runloop与线程4. 获取Runloop对象4.1 Foundation4.2 Core Foundation4.3 示例 5. Runloop相关的类5.1 Core Foundation中关于RunLoop的5个类5.2 CFRunL…

Vue中的一些指令与计算方法

语法 插值语法 HTML的双标签内容中使用,在{{}}之内书写JS代码 属性语法 1.v-bind或: 2.:属性名"值"或v-bind"值" 事件语法 v-on或 v-on:事件名"方法名"或事件名"方法名" 选项 选项:可选的配置项——官方…

3D分割项目 | 基于Pytorch+3DUnet实现的3D体积语义分割算法

项目应用场景 用于 3D 体积语义分割场景,适用于各种物体的 3D 语义分割,比如大米、大豆的体积分割等 项目效果: 项目流程 > 具体参见项目内README.md (1) 安装 conda install -c conda-forge mamba mamba create -n pytorch-3dunet -c p…

OpenHarmony如何模拟搭建本地http静态服务

简介 本文是在基于OpenHarmony 4.0的基础上,介绍了一种编写一个前端http静态服务的思路. 方案设计 在OpenHarmony上,如果想要访问本地网页。有两种方案 u 方案一:使用file协议,将html放至entry/src/main/resource/rawfile下&#…