当我们打开一个多媒体文件时,FFmpeg 会用 AVCodecContext 结构体来存储文件的一些信息和参数,用于后续对文件的一系列操作。本篇就简单介绍下,如何使用 FFmpeg 打开和关闭一个多媒体文件。我们的操作步骤如下:
- 打开多媒体文件
- 探索多媒体文件更多信息
- 关闭多媒体文件
在这个过程中涉及到如下的函数:
- 打开:avformat_open_input
- 探索:avformat_find_stream_info
- 关闭:avformat_close_input
完整的示例代码如下:
#include <iostream>
extern "C"
{
#include <libavformat/avformat.h>
}
void test()
{
const AVInputFormat* pInputFormat = nullptr;
AVDictionary* pOptionDict = nullptr;
AVFormatContext* pFormatContex = nullptr;
// 打开多媒体文件
int err_num = avformat_open_input(&pFormatContex, "demo.mp4", pInputFormat, &pOptionDict);
printf("pFormatContex: %p\n", pFormatContex);
// 错误码处理
if (err_num < 0)
{
char error[128] = { 0 };
int ret = av_strerror(err_num, error, sizeof(errno));
if (ret < 0)
{
fprintf(stderr, "未知的错误!\n");
}
else
{
fprintf(stderr, "错误信息: %s\n", error);
}
return;
}
// 文件信息探测
avformat_find_stream_info(pFormatContex, nullptr);
// 关闭多媒体文件
avformat_close_input(&pFormatContex);
printf("pFormatContex: %p\n", pFormatContex);
}
int main()
{
test();
return 0;
}
程序输出结果:
pFormatContex: 000001F9AE09F900
pFormatContex: 0000000000000000
接下来详细讲解下这三个函数的作用和参数含义。
- avformat_open_input {#title-0} =================================
FFmpeg 中的 avformat_open_input 函数用于打开一个多媒体文件,并读取多媒体文件的头信息。
多媒体文件内的数据大致分为两部分,第一部分存储了文件的一些信息和参数,第二部存储文件的流数据。头信息指的是第一部分的内容。
该函数的声明如下:
int avformat_open_input(AVFormatContext **ps, const char *url, const AVInputFormat *fmt, AVDictionary **options);
- 第一个参数 ps 就是专门用于存储多媒体文件的信息的。该参数是个二级指针,这就是说,ps 的内存不需要我们申请,而是由 avformat_open_input 申请,并将读取到的头信息初始化 ps 结构。
- 第二个参数 url 指的是多媒体的文件路径。
- 第三个参数 fmt 是文件的格式。如果该参数为 nullptr,avformat_open_input 函数会自动去探测输入多媒体文件相关的信息,并将信息存储到 ps 中,如果设置了该参数,那么将会使用用户指定的 fmt 来设置 ps 中的信息。
- 第四个参数 options 从名字来看就是可选参数,它用于设置一些文件的额外参数。
对于第四个参数,我们暂时用不到,先不关心可以设置哪些参数,但是需要了解下,该参数如何设置。
void test()
{
AVDictionary* dict = nullptr;
/*
#define AV_DICT_MATCH_CASE 1 // Only get an entry with exact-case key match. Only relevant in av_dict_get().
#define AV_DICT_IGNORE_SUFFIX 2 // Return first entry in a dictionary whose first part corresponds to the search key, ignoring the suffix of the found key string. Only relevant in av_dict_get(). allocated with av_malloc() or another memory allocation function.
#define AV_DICT_DONT_STRDUP_VAL 8 // Take ownership of a value that's been allocated with av_malloc() or another memory allocation function.
#define AV_DICT_DONT_OVERWRITE 16 // Don't overwrite existing entries.
#define AV_DICT_APPEND 32 // If the entry already exists, append to it. Note that no delimiter is added, the strings are simply concatenated.
#define AV_DICT_MULTIKEY 64 // Allow to store several equal keys in the dictionary
*/
// 添加配置
av_dict_set(&dict, "Name", "Trump", AV_DICT_MATCH_CASE);
// 该版本的函数会将 int 转换为字符串之后再进行存储
av_dict_set_int(&dict, "Age", 100, AV_DICT_MATCH_CASE);
// 获得配置
AVDictionaryEntry* entry = av_dict_get(dict, "Name", nullptr, AV_DICT_MATCH_CASE);
printf("Key: %s Value: %s\n", entry->key, entry->value);
entry = av_dict_get(dict, "Age", nullptr, AV_DICT_MATCH_CASE);
printf("Key: %s Value: %s\n", entry->key, entry->value);
// 元素个数
printf("Count: %d\n", av_dict_count(dict));
// 销毁字典
av_dict_free(&dict);
}
- avformat_find_stream_info {#title-1} =======================================
函数 avformat_find_stream_info 用于探索文件更多的信息。我们前面提到的 avformat_open_input 函数可以读取多媒体文件头信息,从而获得输入文件的信息。那么,为什么还要用 avformat_find_stream_info 函数呢?
如果所有的多媒体文件都把信息和参数写到头,那我们就用 avformat_open_input 就可以了,但是有些格式的多媒体文件中,有些信息,或者我们后续操作需要的信息并没有放到头位置,这就导致了 avformat_find_stream_info 函数无法获得需要的信息。此时就可以使用 avformat_find_stream_info 函数对输入的多媒体文件进行更深入的读取、探索,从而获得这些我们需要的必要信息。
所以,使用时,一般就在 avformat_open_input 函数调用之后,接着调用 avformat_find_stream_info 函数,从而获得更为详细、有用的多媒体文件信息,便于后期对该文件的操作。
函数的声明如下:
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
注意:第一个参数为 avformat_open_input 调用成功之后的 AVFormatContext 类型指针,第二个参数可选参数,不需要设置的话,可以设置为 nullptr。
- avformat_close_input {#title-2} ==================================
函数 avformat_close_input 的作用就是在最后释放 AVFormatContext 指针占用的内存,函数声明如下:
void avformat_close_input(AVFormatContext **s);
注意该函数传递的是 AVFormatContext 类型的二级指针,即:当 avformat_close_input 释放内存之后,会将传递进来的指针设置为 nullptr,避免悬挂指针(野指针)问题。