音视频开发9 FFmpeg 解复用框架说明,重要API说明

news2024/9/24 21:17:12

一,播放器框架

二 常用音视频术语

容器/文件(Conainer/File): 即特定格式的多媒体文件, 比如mp4、flv、mkv等。
媒体流(Stream): 表示时间轴上的一段连续数据,如一 段 声音数据 、一段 视频数据 或一段 字幕数据 ,可以是压缩 的,也可以是非压缩的,压缩的数据需要关联特定的编解 码器(有些码流音频他是纯PCM)。一般对于 一个 mp4文件, 通过解复用器,就可以将mp4中的 视频流和 音频流,甚至字符流都分离出来。 
数据帧/数据包(Frame/Packet): 通常,一个媒体流是 由大量的数据帧组成的,对于压缩数据, 帧对应着编解码 器的最小处理单元 ,分属于不同媒体流的数据帧交错存储 于容器之中。
一般来说,压缩后的数据,我们认为是packet
解码后的数据,我们认为是frame的概念。
帧对应着编解码 器的最小处理单元 这句话的理解如下:
对于视频,一个frame实际上就是一张图片了
对于音频,一个frame,对于 aac 是1024个采样点为一帧,mp3 则是1152个采样点为一帧。
编解码器: 编解码器是以帧为单位实现压缩数据和原始数 据之间的相互转换的。

三 常用概念-复用器

四 常用概念-编解码器

五 FFmpeg库简介

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

六 FFmpeg函数简介

av_register_all():注册所有组件,4.0已经弃用

avdevice_register_all()对设备进行注册,比如V4L2等。

        
#include <libavdevice/avdevice.h>

/**
 * Initialize libavdevice and register all the input and output devices.
 */
void avdevice_register_all(void);

avformat_network_init();初始化网络库以及网络加密协议相关 的库(比如openssl)

#include <libavformat/avformat.h>

int avformat_network_init(void);

七 FFmpeg函数简介-封装格式相关

avformat_alloc_context();负责申请一个AVFormatContext 结构的内存,并进行简单初始化

#include <libavformat/avformat.h>

/**
 * Allocate an AVFormatContext.
 * avformat_free_context() can be used to free the context and everything
 * allocated by the framework within it.
 */
AVFormatContext *avformat_alloc_context(void);

## AVFormatContext:它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体, 	

其中比较重要的几个如下解释:

typedef struct AVFormatContext {
    const AVClass *av_class;   // 一个类,用于保存指向父对象的链接,用于日志记录
    struct AVInputFormat *iformat;  // 用于指定输入文件的格式以及文件读取的操作函数
    struct AVOutputFormat *oformat; // 用于指定输出文件的格式以及文件写入的操作函数
    void *priv_data;            // 指向 AVFormatContext(容器上下文)的私有数据
    AVIOContext *pb;            // 用于读取和写入媒体数据的 I/O 上下文
    int nb_streams;             // 流的数量,包括音频、视频、字幕等
    AVStream **streams;         // 指向 AVStream 结构体的指针,用于存储所有流的信息
    char *filename;             // 用于存储文件名的字符串
    int64_t start_time, duration;// 媒体文件的起始时间戳和持续时间
    int64_t bit_rate;           // 比特率,以 bit/s 计算
    uint8_t *buffer;            // 用于暂存数据的缓冲区
    int buffer_size;            // 缓冲区的大小
} AVFormatContext;


AVFormatContext 结构体的各个成员变量的作用详见以下介绍。

成员变量介绍

AVClass *av_class: 一个类,用于保存指向父对象的链接,用于日志记录;
AVClass是FFmpeg中libavutil库中的一个结构体,用于在FFmpeg中实现类及其对象的日志和调试功能。AVClass提供了一种标准的方式来管理类及其对象,在不同的库和插件之间提供了统一的日志记录和调试接口。

AVInputFormat *iformat:用于指定输入文件的格式以及文件读取的操作函数;
AVOutputFormat *oformat: 用于指定输出文件的格式以及文件写入的操作函数;

void *priv_data: 指向 AVFormatContext(容器上下文)的私有数据;
priv_data成员可以用于存储和传递特定协议下使用的私有数据,常见的使用场景是实现自定义输入或输出协议。

AVIOContext *pb:用于读取和写入媒体数据的 I/O 上下文;
AVIOContext 是libavformat库中一个表示访问媒体文件的I/O环境的结构体。它封装了对媒体文件的读取和写入操作,提供了和具体I/O操作系统相关的操作的抽象接口,实现了独立于实际操作系统的媒体文件访问接口。

int nb_streams: 流的数量,包括音频、视频、字幕等;
AVStream **streams:指向 AVStream 结构体的指针,用于存储所有流的信息;
AVStream是FFmpeg中libavformat库中的一个数据结构,用于表示媒体文件中的一个音频或视频流。在FFmpeg中,一个媒体文件通常包含多个音视频流,每个流对应着媒体文件中的一个轨道。AVStream通过存储音视频流的各种属性信息,方便解码和编码,对于多媒体处理和视频编辑有着至关重要的作用。
AVStream包含了一个媒体流的所有基本信息,如类型、编解码器、时间戳、时长、帧率、码率等等。常用的成员变量有:
char *filename: 用于存储文件名的字符串;

int64_t start_time: 媒体文件的起始时间戳;
int64_t duration: 媒体文件的持续时间;
int64_t bit_rate: 比特率,以 bit/s 计算;
uint8_t *buffer: 用于暂存数据的缓冲区;
int buffer_size: 缓冲区的大小。

avformat_free_context();释放该结构里的所有东西以及该 结构本身

#include <libavformat/avformat.h>

/**
 * Free an AVFormatContext and all its streams.
 * @param s context to free
 */
void avformat_free_context(AVFormatContext *s);

avformat_close_input();关闭解复用器。关闭后就不再需要 使用avformat_free_context 进行释放。

/**
 * Close an opened input AVFormatContext. Free it and all its contents
 * and set *s to NULL.
 */
void avformat_close_input(AVFormatContext **s);

avformat_open_input();打开媒体文件并获取媒体文件信息的函数

在前面,调用 avformat_alloc_context()方法中,我们申请了一个avformatcontext,这个context中现在只有框架,没有实际的内容。
avformat_open_input 方法的目的就是打开一个媒体文件,并将媒体文件信息,都存储在这个avformatcontext 中,
在打开这个文件的时候,我们当然要指定存储到那个 avformatcontext中,因此有了第一个参数,也是由于要给这个avformatcontext存储值,在c语言的语法中,第一个参数就是传递指针 ,又因为avformat_alloc_context 返回的本来就是一个 avformatcontext *,因此第一个参数就是 avformatcontext**
第二个参数是要打开的音影文件,或者媒体文件的URL。
第三个参数是可以指定该文件 用什么具体的 媒体文件输入格式 (AVInputFormat )来解析,一般设置为NULL,设置为NULL的意思是,根据文件的扩展名 自动 选择输入格式
如果我们自己填写了这个参数,那么这个参数会对  avformatcontext 中 的 iformat起作用。参见 结构体  avformatcontext  中的说明
    /**
     * The input container format.
     *
     * Demuxing only, set by avformat_open_input().
     */
    const struct AVInputFormat *iformat;
第四个参数:todo,这个怎么理解,有啥用?
avformat_open_input() 函数是用于打开媒体文件并获取媒体文件信息的函数,该函数定义在libavformat/avformat.h中。


该函数的参数含义如下:

ps:AVFormatContext结构体的指针。该参数用于存储打开的媒体文件的信息。当该函数成功返回时,AVFormatContext结构体中将存储媒体文件的相关信息。
url:要打开的媒体文件的URL。可以是本地文件路径,也可以是HTTP URL或其他协议的URL。
fmt:AVInputFormat结构体的指针,用于指定媒体文件的格式。如果该参数为NULL,则根据文件扩展名自动选择输入格式。
options:AVDictionary结构体的指针,用于传递打开媒体文件时的选项。



/**
 * Open an input stream and read the header. The codecs are not opened.
 * The stream must be closed with avformat_close_input().
 *
 * @param ps       Pointer to user-supplied AVFormatContext (allocated by
 *                 avformat_alloc_context). May be a pointer to NULL, in
 *                 which case an AVFormatContext is allocated by this
 *                 function and written into ps.
 *                 Note that a user-supplied AVFormatContext will be freed
 *                 on failure.
 * @param url      URL of the stream to open.
 * @param fmt      If non-NULL, this parameter forces a specific input format.
 *                 Otherwise the format is autodetected.
 * @param options  A dictionary filled with AVFormatContext and demuxer-private
 *                 options.
 *                 On return this parameter will be destroyed and replaced with
 *                 a dict containing options that were not found. May be NULL.
 *
 * @return 0 on success, a negative AVERROR on failure.
 *
 * @note If you want to use custom IO, preallocate the format context and set its pb field.
 */
int avformat_open_input(AVFormatContext **ps, const char *url,
                        const AVInputFormat *fmt, AVDictionary **options);

◼avformat_find_stream_info():获取音视频文件信息

当我们使用avformat_open_input打开一个文件后,下来就应该将这个文件的每个音视频流获取出来,就是通过avformat_find_stream_info方法完成的,因此第一个参数要传递 avformatcontext,第二个参数已经不在使用,直接填写NULL,就好

avformat_find_stream_info()函数是用于获取媒体文件中每个音视频流的详细信息的函数,包括解码器类型、采样率、声道数、码率、关键帧等信息。该函数定义在libavformat/avformat.h中。
函数原型为:

int avformat_find_stream_info(AVFormatContext *fmt_ctx, AVDictionary **options);

该函数的参数含义如下:

fmt_ctx:AVFormatContext结构体指针,表示媒体文件的格式上下文,其中包含已经打开的媒体文件的信息和媒体文件中每个音视频流的信息。
options:AVDictionary结构体指针,用于传递选项。目前已经不使用,传NULL即可。

到这一步,如果都没有错误的话,我们已经将一个音视频文件中的 视频流,音频流,字幕流,都分解出来了,那么我们下一步,我们可以通过av_read_frame(); 读取该文件流的数据了

av_read_frame(); 从文件中读取数据包,

int av_read_frame(AVFormatContext *s, AVPacket *pkt);
参数说明:
AVFormatContext *s   // 文件格式上下文,输入的AVFormatContext
AVPacket *pkt   // 这个值不能传NULL,必须是一个空间,输出的AVPacket
            // 返回值:return 0 is OK, <0 on error or end of file

ffmpeg中的av_read_frame()的作用是读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码(例如H.264中一帧压缩数据通常对应一个NAL)。
对于视频的编解码来说,要对数据进行解码,那么首先要获取视频帧的压缩数据。 av_read_frame()的作用就是获取视频的数据。
注:av_read_frame()获取视频的一帧,不存在半帧说法。但可以获取音频的若干帧。
说明①:av_read_frame()函数是ffmpeg新型的用法,旧用法之所以被抛弃,就是因为以前获取的数据可能不是完整的,而av_read_frame()保证了视频数据一帧的完整性。
说明②:查看API的改变可以看到,从2012-03-20开始,Deprecate av_read_packet(), use
av_read_frame()

返回流的下一帧。
*此函数返回存储在文件中的内容,但不验证解码器是否有有效帧。 它将把文件中存储的内容拆分为帧,并为每个调用返回一个帧。 它不会省略有效帧之间的无效数据,以便给解码器最大可能的解码信息。
如果pkt->buf为NULL,那么直到下一个av_read_frame()或直到avformat_close_input(),包都是有效的。
否则数据包将无限期有效。在这两种情况下,当不再需要包时,必须使用av_free_packet释放包。 对于视频,数据包只包含一帧。
对于音频,如果每个帧具有已知的固定大小(例如PCM或ADPCM数据),则它包含整数帧数。
如果音频帧有一个可变的大小(例如MPEG音频),那么它包含一帧。
在AVStream中,pkt->pts、pkt->dts和pkt->持续时间总是被设置为恰当的值。
time_base单元(猜测格式是否不能提供它们)。
如果视频格式为B-frames,pkt->pts可以是AV_NOPTS_VALUE,所以如果不解压缩有效负载,最好依赖pkt->dts。

/**
* 返回流的下一帧。
 * 此函数返回文件中存储的内容,不进行验证
 * 什么是解码器的有效帧。它会分裂什么是
 * 将文件存储为帧并为每次调用返回一个。它不会
 * 省略有效帧之间的无效数据,以便给解码器最大
 * 可用于解码的信息。
 *
 * 如果 pkt->buf 为 NULL,则数据包在下一次之前有效
 * av_read_frame() 或直到 avformat_close_input()。否则包
 * 无限期有效。在这两种情况下,必须使用以下命令释放数据包
 * av_free_packet 不再需要时。对于视频,数据包包含
 *正好一帧。对于音频,它包含整数个帧,如果每个
 * 帧具有已知的固定大小(例如 PCM 或 ADPCM 数据)。如果音频帧
 * 具有可变大小(例如 MPEG 音频),则它包含一帧。
 *
 * pkt->pts、pkt->dts 和 pkt->duration 始终设置为正确
 * AVStream.time_base 单位中的值(如果格式不能,则猜测
 * 提供它们)。 pkt->pts 可以是 AV_NOPTS_VALUE 如果视频格式
 * 有 B 帧,所以如果你没有,最好依靠 pkt->dts
 * 解压有效载荷。

 * @return 0 如果正常,< 0 错误或文件结束
 * 
 * s:输入的AVFormatContext
 * pkt:输出的AVPacket
 */
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
 关于 avpacket,这个struct,在本章后面的章节有详细的说明。

avformat_seek_file(); 定位文件

在做音视频数据分析的时候,经常会遇到这样的需求,每隔5分钟抽取一帧数据进行分析。

在做播放器开发的时候,也会遇到这种情况,就是拖动进度条跳转到某个位置进行播放。

如果直接用 av_read_frame() 不断读数据,读到第 5 分钟的 AVPacket 才开始处理,其他读出来的 AVPacket 丢弃,这样做会带来非常大的磁盘IO。

其实上面两种场景,都可以用同一个函数解决,那就是 avformat_seek_file(),这个函数类似于 Linux 的 lseek() ,设置文件的读取位置。

只不过 avformat_seek_file() 是用于音视频文件的。

/**
 * Seek to timestamp ts.
 * Seeking will be done so that the point from which all active streams
 * can be presented successfully will be closest to ts and within min/max_ts.
 * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL.
 *
 * If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in bytes and
 * are the file position (this may not be supported by all demuxers).
 * If flags contain AVSEEK_FLAG_FRAME, then all timestamps are in frames
 * in the stream with stream_index (this may not be supported by all demuxers).
 * Otherwise all timestamps are in units of the stream selected by stream_index
 * or if stream_index is -1, in AV_TIME_BASE units.
 * If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as
 * keyframes (this may not be supported by all demuxers).
 * If flags contain AVSEEK_FLAG_BACKWARD, it is ignored.
 *
 * @param s            media file handle
 * @param stream_index index of the stream which is used as time base reference
 * @param min_ts       smallest acceptable timestamp
 * @param ts           target timestamp
 * @param max_ts       largest acceptable timestamp
 * @param flags        flags
 * @return >=0 on success, error code otherwise
 *
 * @note This is part of the new seek API which is still under construction.
 */
int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);

参数解释如下:

1,AVFormatContext *s,已经打开的容器示例。

2,int stream_index,流索引,但是只有在 flags 包含 AVSEEK_FLAG_FRAME 的时候才是 设置某个流的读取位置。其他情况都只是把这个流的 time_base (时间基)作为参考。

3,int64_t min_ts,跳转到的最小的时间,但是这个变量不一定是时间单位,也有可能是字节单位,也可能是帧数单位(第几帧)。

4,int64_t ts,要跳转到的读取位置,单位同上。

5,int64_t max_ts,跳转到的最大的时间,单位同上,通常填 INT64_MAX 即可。

6,int flags,跳转的方式,有 4 个 flags,如下:

AVSEEK_FLAG_BYTE,按字节大小进行跳转。
AVSEEK_FLAG_FRAME,按帧数大小进行跳转。
AVSEEK_FLAG_ANY,可以跳转到非关键帧的读取位置,但是解码会出现马赛克。
AVSEEK_FLAG_BACKWARD,往 ts 的后面找关键帧,默认是往 ts 的前面找关键帧。
avformat_seek_file() 函数默认是把文件的读取位置,设置到离 ts 参数最近的关键帧的地方。

而且默认情况,是容器里面所有流的读取位置都会被设置,包括 音频流,视频流,字幕流。

只要流的 discard 属性小于 AVDISCARD_ALL 就会被设置。

AVStream.discard < AVDISCARD_ALL
min_ts 跟 max_ts 变量有一些设置的技巧。

如果是快进的时候,min_ts 可以设置得比 当前位置 大一点,例如加 2。 而 max_ts 可以填 INT64_MAX

min_ts = 当前位置 + 2
max_ts = INT64_MAX
+2 是为了防止某些情况,avformat_seek_file() 会把读取位置往后挪一点。

如果是后退的时候,min_ts 可以填 INT64_MIN,max_ts 可以设置得比 当前位置 小一点,例如减 2。

min_ts = INT64_MIN
max_ts = 当前位置 - 2
-2 是为了防止某些情况,avformat_seek_file() 会把读取位置往前挪一点。

当 flags 为 0 的时候,默认情况,是按时间来 seek 的,而时间基是根据 stream_index 来确定的。

如果 stream_index 为 -1 ,那 ts 的时间基就是 AV_TIME_BASE,

如果stream_index 不等于 -1 ,那 ts 的时间基就是 stream_index 对应的流的时间基。

这种情况,avformat_seek_file() 会导致容器里面所有流的读取位置都发生跳转,包括音频流,视频流,字幕流。

当 flags 包含 AVSEEK_FLAG_BYTE,ts 参数就是字节大小,代表 avformat_seek_file() 会把读取位置设置到第几个字节。用 av_read_frame() 读出来的 pkt 里面有一个字段 pos,代表当前读取的字节位置。可以用pkt->pos 辅助设置 ts 参数,

AVSEEK_FLAG_BYTE 是否是对所有流都生效,我后面测试一下再补充。

当 flags 包含 AVSEEK_FLAG_FRAME,ts 参数就是帧数大小,代表 avformat_seek_file() 会把读取位置设置到第几帧。这时候 stream_index 可以指定只设置某个流的读取位置,如果 stream_index 为 -1 ,代表设置所有的流。

当 flags 包含 AVSEEK_FLAG_ANY,那就代表 seek 可以跳转到非关键帧的位置,但是非关键帧解码会出现马赛克。如果不设置 AVSEEK_FLAG_ANY, 默认是跳转到离 ts 最近的关键帧的位置的。

当 flags 包含 AVSEEK_FLAG_BACKWARD,代表 avformat_seek_file() 在查找里 ts 最近的关键帧的时候,会往 ts 的后面找,默认是往 ts 的前面找关键帧。

提醒:AVSEEK_FLAG_BYTE ,AVSEEK_FLAG_FRAME,AVSEEK_FLAG_ANY 这 3 种方式,有些封装格式是不支持的。

下面通过一个例子来演示 avformat_seek_file() 函数的用法。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/u012117034/article/details/127760798

调用:
运行结果
可以看到,跳转之后,后面 av_read_frame() 读取到的 AVPacket 的 pts 跟 pos 都有很大的偏移了。

avformat_seek_file() 函数介绍完毕。

扩展知识:avformat_seek_file() 对应的旧版函数是 av_seek_frame()
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/u012117034/article/details/127760798

av_seek_frame():定位文件

功能 :该函数可以将音/视频seek到指定的位置。

参数说明:

AVFormatContext *s    // 封装格式上下文
int streamIndex    // 流的索引。默认值为-1,因为媒体文件中可能既包含视频又包含音频,可以通过streamIndex来指定究竟是以视频还是音频来移。
int64_t timestamp.     // 时间戳。你要移动到哪个时间位置。
int flag             // 标识位。表示我们移动的策略(究竟是向前移,还是向后移)。


参数 timestamp
时间戳以AVStream.time_base为单位,如果未指定流,则以AV_time_base为单位

PS:参数flag

#define AVSEEK_FLAG_BACKGROUND 1              ///<<Seek Background 往后移,
#define AVSEEK_FALG_BYTE              ///<<<seeking based on position in bytes 让时间戳 变成一个byte, 按照文件的大小位置跳到那个位置
#define AVSEEK_FLAG_ANY               ///<<<seek to any frame, even non-keyframes // 移动到任意帧的位置,不去找前面的关键帧,
#define AVSEEK_FLAG_FRAME             ///<<<seeking based on frame number // 找关键帧,一般与AVSEEK_FLAG_BACKGROUND一起使用

/**
 * Seek to the keyframe at timestamp.
 * 'timestamp' in 'stream_index'.
 *
 * @param s            media file handle
 * @param stream_index If stream_index is (-1), a default stream is selected,
 *                     and timestamp is automatically converted from
 *                     AV_TIME_BASE units to the stream specific time_base.
 * @param timestamp    Timestamp in AVStream.time_base units or, if no stream
 *                     is specified, in AV_TIME_BASE units.
 * @param flags        flags which select direction and seeking mode
 *
 * @return >= 0 on success
 */
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
                  int flags);

// 读取一帧数据
    AVPacket *packet = av_packet_alloc();
    for (;;) {
        int ret = av_read_frame(ic, packet);
        if (ret != 0) {
            LOGI("读取到结尾处");
            int pos = 20 * r2d(ic->streams[videoStream]->time_base);
            // 改变播放进度
            av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
            continue;
        }
        LOGI("streamIndex=%d, size=%d, pts=%lld, flag=%d",
             packet->stream_index,
             packet->size,
             packet->pts,
             packet->flags
        );
        av_packet_unref(packet);
    }

FFmpeg函数简介-封装格配解复用器上下文,整体流程图

当解封装完成,我们目前的进度是这样的

八 FFmpeg解码函数简介-解码器相关

那么我们下来就需要将从前面解封装得到的avpacket 进行处理。进行解码。 

首先,我们要指定一个解码器,告诉ffmpeg 代码,我用这个解码器来解码。方法有两种,avcodec_find_decoder() 和 avcodec_find_decoder_by_name();

avcodec_find_decoder():根据ID查找解码器

/**
 * Find a registered decoder with a matching codec ID.
 *
 * @param id AVCodecID of the requested decoder
 * @return A decoder if one was found, NULL otherwise.
 */
const AVCodec *avcodec_find_decoder(enum AVCodecID id);
详情请参考 enum AVCodecID,这个enum AVCodecID 太大了,这里写不下,用的时候要具体查看,我们举例说明一下,就好

    const AVCodec *codec;
    codec = avcodec_find_decoder(AV_CODEC_ID_AAC);


    const AVCodec *codec;
    codec = avcodec_find_decoder(AV_CODEC_ID_H264);

但是这里有一个问题,就是我们一般在解析一个文件的时候,并不知道这个文件的音频和视频用的什么编码,也就不知道用什么解码器解码比较好,因此,这里一种通用的写法是使用如下的api从得到流中 最合适的

int av_find_best_stream(AVFormatContext *ic,
                        enum AVMediaType type,
                        int wanted_stream_nb,
                        int related_stream,
                        const struct AVCodec **decoder_ret,
                        int flags);

avcodec_find_decoder_by_name():根据解码器名字

avcodec_alloc_context3(): 分配解码器上下文

#include <libavcodec/avcodec.h>

avcodec_open2(): 打开码器

avcodec_decode_video2():解码一帧视频数据

avcodec_decode_audio4():解码一帧音频数据

avcodec_send_packet(): 发送编码数据包

avcodec_receive_frame(): 接收解码后数据

avcodec_free_context():释放解码器上下文,包含了 avcodec_close()

avcodec_close():关闭解码器

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

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

相关文章

维护课堂纪律的重要性

在课堂上&#xff0c;老师们是否经常遇到这样的情况&#xff1a;孩子们交头接耳&#xff0c;小动作不断&#xff0c;甚至有人偷偷玩手机&#xff1f;这些行为长让老师感到头疼&#xff0c;但作为老师&#xff0c;是否思考过&#xff0c;维护课堂纪律的重要性究竟何在&#xff1…

生信网络学院|05月31日《SOLIDWORKS Manage 产品周期管理》

课程主题&#xff1a;SOLIDWORKS Manage 产品周期管理 课程时间&#xff1a;2024年05月31日 14:00-14:30 主讲人&#xff1a;付舰 生信科技 PLM实施顾问 1、SOLIDWORKS Manage介绍 2、周期流程管理 3、产品项目管理 4、项目会议管理 5、项目问题管理 安装腾讯会议客户端…

java配置文件解析yml/xml/properties文件

XML 以mybatis.xml:获取所有Environment中的数据库并连接session为例 import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException;import javax.xml.parsers.DocumentBuilder; impo…

第十二周 5.21面向对象的三大特性(封装、继承、多态)(二)

三、多态 1.理解: (1)多态:父类型的引用存储不同子类型的对象 父类类名 引用名 new 子类类名(); 引用 对象 父类型 子类型 …

视频太大怎么压缩变小 视频太大了怎么压缩

视频作为一种多媒体形式&#xff0c;在当今社会的重要性日益凸显&#xff0c;其应用范围广泛&#xff0c;影响力深远。 但是视频文件的大小也在不断增加&#xff0c;这给存储和传输带来了很大的困扰。那么&#xff0c;当视频文件过大时&#xff0c;我们该如何将其压缩变小呢&am…

lubuntu20.04安装和使用ROS Noetic Ninjemys

Noetic Ninjemys 最后一个ROS官方支持的第一代Noetic Ninjemys 为何选择Lubuntu 熟悉我博客的朋友知道&#xff0c;我的这些分享都没有官方经费支持&#xff0c;都是在自己和志同道合朋友们共同努力下&#xff0c;走到今天。 设备陈旧&#xff0c;只能选择对系统资源需求最少…

Common Lisp笔记

在计划学习函数式编程的时候&#xff0c;我一开始打算学习的是 F#。因为我朋友就是在 DTU 上的学&#xff0c;F# 就是 DTU&#xff08;丹麦理工&#xff09;开发的。但是由于 F# 和微软的 .NET 绑定&#xff0c;而在 macOS 上&#xff0c;目前版本的 .NET 的是有些问题的&#…

【NumPy】NumPy线性代数模块详解:掌握numpy.linalg的核心功能

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

软件测试人员如何规划自己的职业发展路线

在这个飞速发展的时代中&#xff0c;我们每个人都渴望在各自的领域里找到属于自己的一片天空&#xff0c;而对于那些致力于软件测试的朋友们而言&#xff0c;规划好自己的职业发展路线显得尤为重要。 一、明确目标 首先&#xff0c;我们需要确立一个明确的职业发展目标。对于软…

C# yolov8 TensorRT Demo

C# yolov8 TensorRT Demo 目录 效果 说明 项目 代码 下载 效果 说明 环境 NVIDIA GeForce RTX 4060 Laptop GPU cuda12.1cudnn 8.8.1TensorRT-8.6.1.6 版本和我不一致的需要重新编译TensorRtExtern.dll&#xff0c;TensorRtExtern源码地址&#xff1a;https://githu…

《爷爷的信》获短片金奖:电影新星的摇篮与短片的春天

随着上汽大众杯澳涞坞全球青年电影短片大赛金奖的公布&#xff0c;我们迎来了短片创作的一个崭新里程碑。这一赛事不仅为青年电影人搭建了一个展示才华的舞台&#xff0c;更预示着短片艺术即将迈入一个繁荣的新纪元。 澳涞坞集团此次大赛的举办&#xff0c;是对青年创作力量的…

美国西储大学(CRWU)轴承故障诊断——连续小波(CWT)变换

1.数据集介绍 2.代码 import random import matplotlib matplotlib.use(Agg) from scipy.io import loadmat import numpy as npdef split(DATA):step = 400;size = 1024;data = []for i in range(1, len(DATA) - size, step):data1 = DATA[i:i + size]data.append(data1)rand…

【NumPy】深入理解NumPy的cov函数:计算协方差矩阵的完整指南

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

全网首发!精选32个最新计算机毕设实战项目(附源码),拿走就用!

Hi 大家好&#xff0c;马上毕业季又要开始了&#xff0c;陆陆续续又要准备毕业设计了&#xff0c;有些学生轻而易举就搞定了&#xff0c;有些学生压根没有思路怎么做&#xff0c;可能是因为技术问题&#xff0c;也可能是因为经验问题。 计算机毕业相关的设计最近几年类型比较多…

基于半花青的近红外荧光材料

一、基于半花青的酯酶响应的光声探针&#xff1a; 参考文献&#xff1a;De Novo Design of Activatable Photoacoustic/Fluorescent Probes for Imaging Acute Lung Injury In Vivo | Analytical Chemistry (acs.org) 1.季铵盐的结构改造&#xff1a; 之前的基于半花青的近红外…

Linux网络_网络基础预备

文章目录 前言一、网络基础知识网络协议协议分层OSI七层模型TCP/IP五层(或四层)模型 认识IP地址认识MAC地址数据包封装和分用 前言 Linux系统编程已经告一段落&#xff0c;但是我们在学习LInux系统编程所积累的知识&#xff0c;将仍然与后面网络知识强相关&#xff0c;学习网络…

Nature plants|做完单细胞还可以做哪些下游验证实验

中国科学院分子植物科学中心与南方科技大学在《Nature Plants》期刊上(IF18.0)发表了关于苜蓿根瘤共生感知和早期反应的文章&#xff0c;该研究首次在单细胞水平解析了结瘤因子处理蒺藜苜蓿&#xff08;Medicago truncatula&#xff09;根系24小时内特异细胞类型的基因表达变化…

c++(四)

c&#xff08;四&#xff09; 运算符重载可重载的运算符不可重载的运算符运算符重载的格式运算符重载的方式友元函数进行运算符重载成员函数进行运算符重载 模板定义的格式函数模板类模板 标准模板库vector向量容器STL中的listmap向量容器 运算符重载 运算符相似&#xff0c;运…

上周暗网0day售卖情报一览

黑客声称以 1,700,000 美元出售 Outlook RCE 漏洞 0Day 令人担忧的是&#xff0c;一个名为“Cvsp”的威胁参与者宣布出售所谓的 Outlook 远程代码执行 (RCE) 漏洞 0day。这一所谓的漏洞旨在针对跨 x86 和 x64 架构的各种 Microsoft Office 版本&#xff0c;对全球用户构成重大安…

Facebook之魅:数字社交的体验

在当今数字化时代&#xff0c;Facebook作为全球最大的社交平台之一&#xff0c;承载着数十亿用户的社交需求和期待。它不仅仅是一个简单的网站或应用程序&#xff0c;更是一个将世界各地的人们连接在一起的社交网络&#xff0c;为用户提供了丰富多彩、无与伦比的数字社交体验。…