音视频开发系列(10)——FFmpeg常用Api
在这个示例中,首先从 AVFormatContext 中获取特定流的 AVCodecParameters,然后使用 avcodec_alloc_context3 分配内存并返回一个新的 AVCodecContext,最后使用 avcodec_parameters_to_context 将 AVCodecParameters 中的参数转换到 AVCodecContext 中。avformat_fin
1.avformat_open_input
avformat_open_input 是 FFmpeg 中的一个重要函数,用于打开输入的音视频文件并读取相关的媒体文件信息。该函数的原型如下:
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
该函数接受四个参数:
ps:指向一个指针的指针,该指针指向已经分配了内存的 AVFormatContext 结构体。该结构体用于保存打开的音视频文件的相关信息,例如媒体格式、时长、帧率等。
url:表示要打开的音视频文件的路径。该参数可以是一个本地文件路径或网络流的 URL。
fmt:表示指定要使用的输入格式,如果传入 NULL,则会自动检测输入文件的格式。
options:表示一些可选的参数,可以传入 NULL。
在函数调用过程中,avformat_open_input 会做以下工作:
调用 avformat_alloc_context 函数,分配一个 AVFormatContext 结构体,并将其指针赋值给 *ps。
调用 avformat_open_input 函数,打开指定的音视频文件,并将媒体文件的相关信息读取到 *ps 中。
返回一个整数值,表示打开文件的状态。如果返回的是非零值,表示打开文件失败,可以通过错误码来进一步判断失败的原因。
在使用 avformat_open_input 函数时,需要注意以下几点:
要先分配一个 AVFormatContext 结构体,并将其指针传递给函数。
如果需要自动检测输入文件的格式,可以将 fmt 参数设置为 NULL。
打开的媒体文件必须是有效的,否则函数会返回一个错误码。
在函数调用结束后,可以从 AVFormatContext 结构体中获取打开文件的相关信息,例如媒体格式、时长、帧率等。
2.avformat_find_stream_info
用于在一个打开的AVFormatContext中查找所有流的相关信息,例如编解码器参数、时长、帧率、分辨率等等。该函数可以在avformat_open_input之后被调用,一般在打开输入文件后会调用该函数以获取输入文件的流信息。
函数的声明为:
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
参数说明:
ic:打开的AVFormatContext结构体指针。
options:AVDictionary类型的选项参数,可以为NULL。
函数返回值为非负整数,表示成功获取的流的数量,如果没有成功获取,则返回负数,例如AVERROR(EINVAL)表示参数不合法,AVERROR_EOF表示已经到达输入文件的结尾,AVERROR(EAGAIN)表示需要再次调用该函数。
函数执行过程中会尝试打开编解码器,分配编解码器上下文,分配AVStream结构体等,因此该函数的执行是相对耗时的。
详细解释avformat_find_stream_info耗时的原因
avformat_find_stream_info是FFmpeg中相对比较耗时的函数之一,其耗时主要源于以下几个方面:
需要解码器进行解码
avformat_find_stream_info会尝试打开编解码器,并对输入文件的流进行解码,以获取流的相关信息。对于视频流,需要解码出关键帧才能获取其分辨率、帧率等信息;对于音频流,需要解码出至少一帧音频才能获取采样率、声道数等信息。
需要分配各种结构体
在解码过程中,需要分配各种结构体,例如AVStream结构体、AVCodecContext结构体等,用于保存解码出的信息,这些结构体的分配和初始化都需要耗费一定的时间和内存。
需要解析容器格式
输入文件的格式可能是MP4、AVI、MKV等多种容器格式之一,avformat_find_stream_info需要解析容器格式,以确定其中包含的音视频流、字幕流等信息。
需要从多个数据源中读取数据
输入文件可能是本地磁盘文件、网络文件或者其他数据源,avformat_find_stream_info需要从这些数据源中读取数据,并对数据进行解析和处理。
综上所述,avformat_find_stream_info的耗时主要源于解码器的解码、分配结构体、解析容器格式以及数据读取等方面。由于其需要执行多种操作,因此执行时间相对较长。
为什么MediaExtractor为什么速度比avformat_find_stream_info方法快?
因:
轻量级:MediaExtractor是Android提供的一个轻量级的API,它的主要作用是提取媒体文件的基本信息,而不像FFmpeg一样具有完整的解码功能。因此,MediaExtractor的实现相对简单,可以快速地获取媒体文件的信息。
硬件加速:在Android平台上,MediaExtractor常常和MediaCodec一起使用,可以利用硬件加速来提高视频解码的效率。相比于软件解码,硬件解码可以更快地完成视频解码,因此可以提高解码速度。
内存分配:在FFmpeg中,avformat_find_stream_info方法需要对整个媒体文件进行遍历,并分析每个数据包的信息,因此需要分配大量的内存空间,这会影响解码速度。而MediaExtractor的实现相对简单,可以避免过多的内存分配。
总之,MediaExtractor相对于FFmpeg中的avformat_find_stream_info方法,具有更高的解析速度和更低的内存占用,这也是Android开发者更倾向于使用MediaExtractor的原因之一。
3.m_pInputAVFmtCtx->streams[i]->codecpar
int index = 0;
for(int i = 0; i < m_pInputAVFmtCtx->nb_streams; i++)
{
if(m_pInputAVFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|| m_pInputAVFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
AVMediaType codecType = m_pInputAVFmtCtx->streams[i]->codecpar->codec_type;
AVCodecID codecId = m_pInputAVFmtCtx->streams[i]->codecpar->codec_id;
if(GetDecoder(i) == NULL
|| (codecType == AVMEDIA_TYPE_AUDIO && m_pInputAVFmtCtx->streams[i]->codecpar->sample_rate == 0)
|| (codecType == AVMEDIA_TYPE_VIDEO && codecId == AV_CODEC_ID_PNG)
|| (codecType == AVMEDIA_TYPE_VIDEO && codecId == AV_CODEC_ID_MJPEG)
|| (codecType == AVMEDIA_TYPE_VIDEO && codecId == AV_CODEC_ID_GIF)
|| (codecType == AVMEDIA_TYPE_VIDEO && codecId == AV_CODEC_ID_LJPEG)
|| (codecType == AVMEDIA_TYPE_VIDEO && codecId == AV_CODEC_ID_MJPEGB))
{
m_ignoreIndexs[m_ignoreIndexCount++] = index;
}
}
index++;
}
**streams[i]->codecpar获取的是AVCodecParameters结构体,用于描述一个媒体流中的音频或视频编解码器参数,包括编解码器ID、媒体类型、采样率、分辨率、比特率等信息。**在FFmpeg中,AVCodecParameters可以作为输入或输出参数,方便解码或编码。
m_pInputAVFmtCtx->streams[i]->codecpar->codec_type
指的是解码器的类型。在这段代码中,通过访问 AVStream 结构体中的 codecpar 成员获取了对应媒体流的解码器参数,其中包括了解码器的类型 codec_type。这个值可以是 AVMEDIA_TYPE_VIDEO、AVMEDIA_TYPE_AUDIO、AVMEDIA_TYPE_SUBTITLE 等等。根据不同的解码器类型,会有不同的解码器来进行处理。
m_pInputAVFmtCtx->streams[i]->codecpar->codec_id
codec_id 指的是解码器(或编码器)的 ID。每个编解码器都有一个唯一的 ID,用于标识该编解码器。在 FFmpeg 中,codec_id 是一个 AVCodecID 类型的枚举,它包含了所有支持的编解码器的 ID。
4.avcodec_find_decoder
作用是根据输入的编码ID查找对应的解码器,返回解码器的指针。其函数原型如下:
AVCodec* avcodec_find_decoder(enum AVCodecID id);
其中,id是要查找的编码ID,返回值是对应的解码器指针,如果没有找到则返回NULL。
在使用该函数时,需要先调用av_register_all()注册FFmpeg的编解码器。avcodec_find_decoder函数的内部实现是遍历已注册的所有编解码器,比对编码ID是否匹配,找到匹配的解码器则返回其指针。
使用该函数的示例代码如下:
// 注册FFmpeg的编解码器
av_register_all();
// 查找H.264解码器
AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if(codec == NULL)
{
// 没有找到解码器
return -1;
}
// 创建解码器上下文
AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
if(codec_ctx == NULL)
{
// 分配解码器上下文失败
return -1;
}
// 初始化解码器上下文
if(avcodec_open2(codec_ctx, codec, NULL) < 0)
{
// 初始化解码器上下文失败
avcodec_free_context(&codec_ctx);
return -1;
}
// 使用解码器解码数据...
...
// 释放解码器上下文
avcodec_free_context(&codec_ctx);
以上示例代码先使用avcodec_find_decoder函数查找H.264解码器,然后创建解码器上下文、初始化解码器上下文,并最终使用解码器解码数据。
5.avcodec_alloc_context3
用于创建 AVCodecContext 结构体,也就是编码器和解码器的上下文。它会自动分配空间,并设置默认值,返回创建好的结构体的指针。
在使用 FFmpeg 进行音视频处理时,常常需要使用 AVCodecContext 结构体,以获取音视频流的相关信息,以及为音视频编解码做好准备。使用 avcodec_alloc_context3 函数可以方便地创建 AVCodecContext 结构体,并自动设置一些默认值,避免了手动设置参数的繁琐工作。
函数声明如下:
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
其中,codec 参数是对应的编码器或解码器,可以通过 avcodec_find_encoder 或 avcodec_find_decoder 等函数来获取。
函数返回值是指向 AVCodecContext 结构体的指针,该结构体已经分配好内存,并设置好默认值。
需要注意的是,使用完 AVCodecContext 结构体之后,需要手动释放内存,可以通过 avcodec_free_context 函数来完成。
6.avcodec_parameters_to_context
avcodec_parameters_to_context 方法用于将 AVCodecParameters 转换为 AVCodecContext。
在 FFmpeg 中,AVCodecParameters 保存了一个流的编码参数,例如视频流的帧率、分辨率、像素格式等信息,音频流的采样率、声道数、音频格式等信息。而 AVCodecContext 保存了一个解码器的上下文信息,例如解码器的参数、状态等信息。
在解码过程中,通常需要将 AVCodecParameters 转换为 AVCodecContext,然后再进行解码。这就是 avcodec_parameters_to_context 方法的作用。
该方法将 AVCodecParameters 结构体中的编码参数赋值给 AVCodecContext 结构体中的解码参数。这些参数包括像素格式、帧率、音频采样率、音频格式等。同时,该方法也会为 AVCodecContext 结构体中的其他参数设置默认值。
调用该方法的代码示例:
AVCodecParameters *codecpar = avformat_context->streams[stream_index]->codecpar;
AVCodecContext *codec_ctx = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(codec_ctx, codecpar);
在这个示例中,首先从 AVFormatContext 中获取特定流的 AVCodecParameters,然后使用 avcodec_alloc_context3 分配内存并返回一个新的 AVCodecContext,最后使用 avcodec_parameters_to_context 将 AVCodecParameters 中的参数转换到 AVCodecContext 中。
7.av_guess_frame_rate
av_guess_frame_rate()函数来猜测媒体文件的帧率,以便为视频文件提供正确的播放速度,并在需要的时候对音视频同步进行调整。
av_guess_frame_rate 函数接受三个参数:
AVStream *stream; // 输入视频流
AVCodecContext *codec; // 解码器上下文
AVFrame *frame; // 视频帧
函数的作用是根据输入视频流、解码器上下文以及视频帧的时间戳(PTS)推断出每秒帧率(fps),返回一个 AVRational 类型的值。
AVRational 是 FFmpeg 中表示有理数的结构体,其中 num 表示分子,den 表示分母。所以该函数返回的结果可以理解为帧数(分子)和时间(分母)的比值,即每秒钟有多少帧。
8.avcodec_open2
avcodec_open2() 是 FFmpeg 中用于打开编码器或解码器的函数之一。它的原型如下:
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
其中,avctx 是编解码器上下文结构体,存储了编解码器的所有参数;codec 是要打开的编码器或解码器的指针;options 是一个字典结构体,用于设置编解码器的选项。
该函数的作用是初始化编解码器上下文,并打开相应的编解码器。它会根据 codec 参数中指定的编解码器来设置 avctx 中的各项参数,例如像素格式、图像大小、音频采样率、声道数等。如果 codec 参数为 NULL,则 avctx 中必须提供完整的编解码器信息。
函数返回值为 0 表示成功,其他值则表示错误。如果函数返回一个错误值,则必须通过调用 avcodec_close() 函数来释放编解码器资源。
需要注意的是,在 FFmpeg 4.0 版本以前,函数名为 avcodec_open(),而在 4.0 版本以后,该函数已被弃用,应使用 avcodec_open2() 替代。
9.av_init_packet
一个AVPacket结构体代表了音视频编解码器中传输的数据包,其中包含了数据、时间戳、时长、帧类型等信息。在使用AVPacket之前,需要先使用av_init_packet进行初始化。
具体来说,av_init_packet函数的作用有两个:
将AVPacket结构体中的所有字段都初始化为0或者默认值。
将AVPacket结构体中的引用计数初始化为1。
使用av_init_packet函数的代码如下:
AVPacket packet;
av_init_packet(&packet);
这样,就可以使用初始化后的packet结构体了,包括对其成员变量的操作,例如添加数据、时间戳等。
10.av_read_frame
av_read_frame()是FFmpeg库中用于读取媒体文件中每个AVPacket的函数。每个AVPacket代表了一个音视频帧或者一段压缩后的数据。它的函数原型如下:
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
其中s参数代表了已经打开的媒体文件的AVFormatContext,pkt参数是一个指向AVPacket结构体的指针,函数会将读取的AVPacket信息存储在该结构体中。
在调用av_read_frame()函数时,会从文件中读取下一个AVPacket并存储在pkt参数所指向的AVPacket结构体中。读取成功后,函数返回0。如果到了文件结尾,函数返回AVERROR_EOF。如果出现了其他错误,函数返回一个负数,表示读取失败的错误码。
需要注意的是,使用av_read_frame()函数后,需要在使用完AVPacket后使用av_packet_unref()函数释放AVPacket中的内存资源。另外,为了避免内存泄漏,在不再需要使用AVPacket时,应该使用av_packet_free()释放整个AVPacket结构体所占用的内存资源。
11.av_seek_frame
av_seek_frame(m_pInputAVFmtCtx, -1, time * (AV_TIME_BASE / 1000), AVSEEK_FLAG_BACKWARD);
这行代码是使用FFmpeg库中的av_seek_frame函数进行媒体流的定位(seek)。它的参数包括:
m_pInputAVFmtCtx: AVFormatContext类型的指针,表示要定位的媒体文件的封装格式上下文。
-1: 表示要在整个媒体文件中进行定位,而不是特定的媒体流。
time * (AV_TIME_BASE / 1000): 表示要定位的时间点,以微秒为单位,即time乘以AV_TIME_BASE / 1000。
AVSEEK_FLAG_BACKWARD: 表示要向后定位,即寻找最接近指定时间点之前的关键帧。
这行代码的作用是在媒体文件中定位到最接近指定时间点之前的关键帧。在视频播放等应用场景中,通常需要根据用户的拖动或跳转操作,快速地定位到特定时间点,以实现快速切换或跳转的功能。av_seek_frame函数提供了在媒体文件中快速定位到指定时间点的功能。
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);
其中,参数解释如下:
s:指向AVFormatContext结构体的指针,表示要进行跳转的媒体文件的封装格式上下文。
stream_index:表示要进行跳转的媒体流的索引,如果该值为-1,则表示跳转到任意媒体流的指定时间位置。
timestamp:表示要跳转到的时间位置,单位为媒体流中的时间基,通常是以微秒为单位。
flags:表示跳转的标志,可以是以下标志的组合:
AVSEEK_FLAG_BACKWARD: 用于指定向后(向时间戳减小的方向)查找;
AVSEEK_FLAG_BYTE: 用于指定根据字节位置进行查找;
AVSEEK_FLAG_ANY: 用于指定可以查找任意的关键帧,但是不一定是最接近指定时间戳的关键帧;
AVSEEK_FLAG_FRAME: 用于指定查找的目标是指定时间戳的关键帧。
跳转完成后,可以通过avcodec_flush_buffers函数刷新解码器的缓存,以确保从正确的位置开始解码。
12.avformat_alloc_output_context2
avformat_alloc_output_context2 是 FFmpeg 中一个用于创建输出格式上下文 AVFormatContext 的函数。它的函数原型如下:
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename);
ctx : 指向 AVFormatContext 指针的指针,当调用成功时,将为该指针分配内存,并将指向的指针指向新分配的 AVFormatContext。调用者必须在调用此函数之前将其初始化为 NULL,以便在发生错误时释放内存。
oformat : 指向要使用的输出格式的 AVOutputFormat 结构体的指针。如果该参数为 NULL,则由 format_name 参数指定的格式名称将用于查找输出格式。
format_name : 要使用的输出格式的名称。如果 oformat 参数不为 NULL,则忽略此参数。
filename : 要打开的输出文件名,如果不是文件输出,可以将其设置为 NULL。
此函数用于分配一个新的 AVFormatContext 上下文,用于存储输出媒体的相关信息。可以通过 oformat 参数来指定输出媒体的格式,如果没有指定,则通过 format_name 参数来查找输出媒体格式,如果都没有指定,FFmpeg 将根据输出文件名的扩展名自动确定输出媒体的格式。可以将 filename 设置为 NULL,以指定输出媒体不是文件,而是通过其他方法(如 RTP)进行输出。
如果调用成功,将分配一个新的 AVFormatContext 上下文,并将其分配给 ctx 参数指向的指针。此时,该上下文将包含与输出媒体有关的信息,例如文件格式、编解码器、封装格式等。调用者应该将这些信息填充完整,并在完成输出操作后释放分配的上下文。
如果分配过程失败,则会自动释放先前分配的所有资源,并将 ctx 参数指向的指针设置为 NULL。
举个例子,以下代码演示了如何使用 avformat_alloc_output_context2 分配一个新的 AVFormatContext 上下文:
AVFormatContext *format_ctx = NULL;
AVOutputFormat *output_format = NULL;
const char *filename = "output.mp4";
// 尝试查找输出格式
output_format = av_guess_format("mp4", NULL, NULL);
if (!output_format) {
fprintf(stderr, "Unable to find output format\n");
return -1;
}
// 分配输出格式上下文
if (avformat_alloc_output_context2(&format_ctx, output_format, NULL, filename) < 0) {
fprintf(stderr, "Unable to allocate output context\n");
return -1;
}
// 填充输出格式上下文的其他信息
...
12.avcodec_parameters_from_context
ret = avcodec_parameters_from_context(pStream->codecpar, pCodecCtx);
avcodec_parameters_from_context这个API是FFmpeg库中的一个编解码器相关的函数,它的主要作用是:
从一个已经存在的AVCodecContext中拷贝编解码相关的参数,生成一个新的AVCodecParameters结构体。
具体来说,它会:
- 分配一个新的AVCodecParameters结构体
- 从传入的AVCodecContext中复制编解码相关的配置参数,例如codec_id、codec_type、格式相关的参数等到新分配的AVCodecParameters中
- 返回这个新的包含了参数的AVCodecParameters
这个函数的典型用途有:
-
在不解码的情况下,获取媒体流的编解码信息参数。因为AVCodecContext在解码时会分配内存,调用这个函数可以避免不必要的内存分配。
-
将解码器上下文(AVCodecContext)中的参数复制到其他地方,例如在重启解码器时保留参数配置信息。
-
在只读的情况下访问编解码信息,因为AVCodecParameters是一个只读的结构体。
所以简单来说,这个函数的作用就是用来保存和复用已经配置好的编解码信息参数,主要用于避免重复的编码器配置过程。
13.av_init_packet
av_init_packet(&m_avPacket);
av_init_packet方法是FFmpeg中AVPacket相关操作的一个初始化函数。
其主要功能是:
-
分配一个新的AVPacket结构体。
-
将AVPacket的字段设置为默认值。
相关的默认值设置有:
-
pts、dts时间戳设置为AV_NOPTS_VALUE,表示不存在或者无效。
-
duration设置为0。
-
pos设置为-1。
-
flags设置为0。
-
stream_index设置为0。
-
buf等内存相关字段设置为空。
这样就创建了一个干净、空白的数据包,所有字段都被初始化成了默认初始状态。
这避免使用未初始化的AVPacket出现问题。
之后就可以通过av_packet_alloc等相关方法对这个包进行内存分配,填充数据,来准备好一个用于解封装或封装的AVPacket。
所以总体上,av_init_packet主要用于获得一个初始化状态的、空白的AVPacket,常被用来作为数据包处理过程的第一步。
14.avcodec_alloc_context3() 和 avformat_alloc_context()区别
avcodec_alloc_context3() 和 avformat_alloc_context() 这两个函数虽然都返回 AVCodecContext 结构体指针,但是用途上有一些差异:
-
avcodec_alloc_context3() 主要用于分配一个纯编解码器上下文,只包含编解码信息,不包含容器封装信息。
-
avformat_alloc_context() 主要用于分配一个和文件格式相关的编解码上下文,同时会分配一些字段包含格式信息,比如格式名称,IO上下文等。
主要区别在于:
-
avcodec_alloc_context3() 分配的上下文一般会传递给avcodec_open2等编解码器打开函数使用。
-
avformat_alloc_context() 分配的上下文会传递给avformat_open_input等打开媒体文件函数使用。
另外一点细微区别:
- avformat_alloc_context() 会初始化一些容器的上下文字段。
- avcodec_alloc_context3() 不会特别初始化,上下文字段为随机值。
所以简单来说:
-
如果只处理编解码,不涉及文件封装格式,使用avcodec_alloc_context3()。
-
如果需要处理文件和流,需要格式信息,使用avformat_alloc_context()。
15.AVCodecContext 和 AVCodec
AVCodecContext 和 AVCodec 是描述FFmpeg编解码器相关信息的两个关键数据结构,两者的主要区别是:
AVCodec:
编码或解码算法的抽象表示,包含编解码器类型、名称、支持的像素格式等信息,但没有具体的编解码参数设置。
主要字段包括:
- name - 编解码器描述名
- type - 媒体类型(视频、音频等)
- id - 编解码器ID标识
- capabilities - 支持的capabilities
- pix_fmts/sample_fmts - 支持的像素/样本格式
AVCodecContext:
具体的编解码器实例上下文,包含具体的编码或解码参数和设置。
主要字段包括:
- codec - 对应的AVCodec
- codec_type - 媒体类型
- codec_id - 编解码器ID
- width/height - 视频宽高
- sample_rate - 音频采样率
- channels - 声道数
- bit_rate - 比特率
- pix_fmt - 像素格式
- time_base - 时间基
- opts - 编解码器选项
简单来说:
- AVCodec 描述编解码器信息
- AVCodecContext 表示编解码器的具体实例
两者关系像是类和对象的关系,AVCodec是抽象的类,AVCodecContext是根据它创建出来的具体对象。
16.av_packet_unref
用于对AVPacket进行初始化/无引用化。它会释放packet中的数据缓冲区,并重置字段。
主要逻辑是:
调用av_buffer_unref释放packet中data字段引用的内存
重置一些packet字段,如size、pts、dts等置0
将一些指针字段如data、side_data等置为空
这样可以在packet重复使用时,避免旧数据的干扰。
17.avcodec_send_packet
avcodec_send_packet是FFmpeg中AVCodec的编码/解码API中的一个方法,它的作用是:
向编解码器发送压缩编码后的媒体数据包进行解码。
主要的逻辑是:
-
根据编解码器类型,调用相应的send_packet函数指针,发送packet
-
如果编解码器内部缓冲区满了会返回EAGAIN错误
-
将packet标记为已使用状态
-
刷新编解码器内部状态
-
返回发送结果
这个函数和avcodec_receive_frame组合使用,一个典型的解码过程是:
-
发送压缩编码后的packet给解码器 - avcodec_send_packet
-
从解码器接收解码后的frame - avcodec_receive_frame
-
回到第1步重复发送packet,直到全部packet处理完。
所以这个函数的主要作用就是把压缩编码的packet传入编解码器开始解码过程。
18.av_frame_get_buffer
av_frame_get_buffer是FFmpeg中AVFrame操作的一个重要方法,它的主要作用是:
为AVFrame分配数据缓冲区和初始化AVFrame字段。
其主要过程是:
- 计算所需的缓冲区大小
- 根据AVFrame参数分配内存缓冲区
- 将分配的缓冲区地址和行字节距等信息填充到AVFrame相关字段
- 创建AVBufferRef,用于缓冲区的内存管理
- 进行一些参数的初始化操作
使用这个函数后,AVFrame就拥有了可供编码/解码处理的内存缓冲区。
不再需要手动分配和初始化,简化了AVFrame的使用。
av_frame_get_buffer负责内存分配和数据准备,是FFmpeg操作AVFrame一个常用的函数。
总的来说,主要功能和用途就是自动地为AVFrame分配需要的内存缓冲区。
19.avfilter_graph_create_filter
int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
const char *name, const char *args, void *opaque,
AVFilterGraph *graph_ctx);
其中主要参数作用如下:
filt_ctx: 输出参数,返回创建的AVFilterContext对象指针地址
filt: 要创建的filter类型(通过avfilter_get_by_name获得)
name: 创建的filter实例名
args: 创建filter时传入的参数
opaque: 回调函数的参数
graph_ctx: filter graph的上下文
该函数会根据传入的AVFilter类型,在指定的filter graph中新建一个该filter的实例,并使用args作为参数初始化这个filter上下文。
创建成功后,会通过filt_ctx返回这个新建的过滤器上下文。
这使得我们可以便捷地通过filter的名称和参数来在graph中动态添加filter,而不需要手动创建上下文并关联链接。
所以简单来说,这个API的主要作用就是动态在filter graph中添加指定类型和参数的filter实例。
FFmpeg的avfilter框架中包含各种不同类型的过滤器,主要可以分为以下几大类:
- 输入输出过滤器:
- abuffer - 音频数据的缓冲输入过滤器
- buffer - 视频帧缓冲的输入过滤器
- abuffersink - 音频数据输出过滤器
- buffersink - 视频帧输出过滤器
- 音频过滤器:
- aformat - 音频格式转换
- aresample - 音频重采样
- volume - 音量控制
- anequalizer - 音频均衡器
- atempo - 音频速度控制
- chorus, flanger 等音效滤波器
- 视频过滤器:
- format - 视频像素格式转换
- crop - 视频裁剪
- scale - 视频缩放
- transpose - 视频旋转/翻转
- overlay - 视频叠加
- deshake, tblur 等视频特效滤波器
- 编码/解码过滤器:
- aac/ac3/flac 编码器
- h264/hevc 视频编码器
- vpx/prores 视频解码器
此外,avfilter中还有一些通用型的过滤器,如concat合并滤波器,trim截取滤波器,setpts时间戳设置滤波器等。
avfilter提供了非常丰富和全面的媒体处理能力,可以灵活配置来达到各种效果。
20.av_get_default_channel_layout
av_get_default_channel_layout是FFmpeg中一个获取默认通道布局的API。
该函数的原型为:
uint64_t av_get_default_channel_layout(int nb_channels);
它根据输入的声道数nb_channels来返回一个默认的通道布局掩码。
FFmpeg里通道布局使用一个64位的整数来表示,每个比特位表示一个通道的存在。
举几个例子:
-
对于立体声(2个声道),返回值是AV_CH_LAYOUT_STEREO,二进制为3。表示有front left和front right两个通道。
-
对于5.1声道,返回值是AV_CH_LAYOUT_5POINT1,二进制为63。表示有front left/right, center, LFE, back left/right 6个通道。
-
对于7.1声道,返回值是AV_CH_LAYOUT_7POINT1,二进制为127。
通过返回默认布局掩码,这个API使得应用程序可以不需要关心具体的掩码数值,直接使用默认布局来简化操作。
另外需要注意,如果传入的通道数不是标准的声道布局类型,则返回值可能没有实际意义,需要应用程序额外判断。
所以总的来说,av_get_default_channel_layout可以快速获取常用通道数下的默认通道布局,简化和规范化音频参数设置过程。
21.av_packet_rescale_ts是FFmpeg中一个用于对AVPacket中的时间戳进行转换的API。
其函数原型是:
void av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb);
主要参数含义:
- pkt: 要转换时间戳的AVPacket
- src_tb: pkt当前时间戳的时间基
- dst_tb: 要转换到的新时间基
这个函数的主要作用是将pkt中的pts和dts时间戳从当前的src_tb时间基转换到dst_tb新的时间基。
转换核心公式是:
新时间戳 = 老时间戳 * (新时间基分子/新时间基分母) / (老时间基分子/老时间基分母)
进行放缩换算。比如将时间戳从1/90000秒基转换为1/48000秒基。
时间戳转换对于过滤器链处理AVPacket,以及文件封装过程中时间基转换都非常重要。
这个API简化了复杂的时间戳计算,只需要给定源和目标时间基,轻松完成AVPacket时间戳的转换。
所以,av_packet_rescale_ts通过时间基转换改变AVPacket中的pts/dts时间戳,是FFmpeg中时间处理的一个核心工具。av_packet_rescale_ts
22.ffmpeg需要了解的结构体
关于 FFmpeg 需要了解的几个结构体:
AVFormatContext:解封装功能的结构体,包含文件名、音视频流、时长、比特率等信息;
AVCodecContext:编解码器上下文,编码和解码时必须用到的结构体,包含编解码器类型、视频
宽高、音频通道数和采样率等信息;
AVCodec:存储编解码器信息的结构体;
AVStream:存储音频或视频流信息的结构体;
AVPacket:存储音频或视频编码数据;
AVFrame:存储音频或视频解码数据(原始数据);
更多推荐
所有评论(0)