文章目录
一、FFmpeg 音视频文件 封装格式相关函数
FFmpeg 封装格式相关的函数 :
- avformat_alloc_context : 初始化 AVFormatContext 结构体 , 该结构体就是 解复用器 的 上下文 ;
- avformat_free_context : 释放 AVFormatContext 结构体 ;
- avformat_open_input : 打开 输入文件 ,
- avformat_close_input : 关闭 输入文件 ,
- avformat_find_stream_info : 获取 输入文件 音视频信息 ,
- av_read_frame : 读取 音视频 数据包 ,
- avformat_seek_file : 根据 定位文件 ,
- av_seek_frame : 根据 定位文件 ,
1、FFmpeg 的 封装格式 与 解复用器
封装格式 是指 将 多种 媒体流 ( 如 音频流、视频流、字幕流 等 ) 封装到 一个 文件 或 流 的格式 , 如 : MP4 / AVI / FLV 等格式 ;
FFmpeg 解复用器 的 作用是 从 容器文件 的 封装格式 中提取 音频流、视频流、字幕流 , 用于后续处理 ;
解复用器 处理流程 :
- 从 容器文件 中 读取 数据 ;
- 提取 音频流 / 视频流 的 编码格式、码率、时间戳等信息 ;
- 将 提取 的 媒体流 传递给相应的 音视频解码器 进行 解码和播放 ;
解复用器 解封装 流程 如下 :
- 首先 , 调用 avformat_alloc_context 函数 , 初始化 解复用器 格式上下文 结构体 , 得到一个 AVFormatContext 结构体对象 ;
- 然后 , 调用 avformat_open_input 函数 , 打开 本地文件 或 网络文件 , 同时为 AVFormatContext 结构体对象 赋值 ;
- 再后 , 调用 avformat_find_stream_info 函数 , 获取相关的码流信息 , 将获取的信息 填充到 AVFormatContext 结构体中 ;
- 此时可 进行 FFmpeg 的音视频操作 , 可能涉及到 循环读取数据包 , 文件的定位跳转 等操作 ;
- 最后 , 程序执行结束后 , 调用 avformat_close_input 函数 , 关闭 解复用器 , 释放相关资源 ;
2、avformat_alloc_context 函数
avformat_alloc_context 函数 的 作用是 分配 和 初始化一个 AVFormatContext 结构体 , 该结构体用于 存储媒体格式信息 , 包括 输入输出流相关数据 / 文件格式 / 编解码器 等 ;
avformat_alloc_context 函数原型 :
AVFormatContext *avformat_alloc_context(void);
avformat_alloc_context 函数 先 在 堆内存 中分配 AVFormatContext 结构体 所需的内存 , 该结构体的 成员字段 初始化默认值 ;
如果 函数 执行成功 , 返回 指向 AVFormatContext 结构体对象的指针 , 如果 函数 执行失败 , 返回 NULL 值 ;
代码示例 : 在下面的代码中 , 调用 avformat_alloc_context 函数 初始化 AVFormatContext 结构体 , 判断函数返回值是否为空 来确定函数是否执行成功 ; 在 FFmpeg 代码逻辑执行完毕后 , 需要调用 avformat_free_context 函数 释放 AVFormatContext 结构体 ;
// 初始化 AVFormatContext 结构体
AVFormatContext *fmt_ctx = avformat_alloc_context();
// 判定 AVFormatContext 结构体是否初始化成功
if (!fmt_ctx) {
fprintf(stderr, "内存不足 AVFormatContext 初始化失败\n");
return -1;
}
// 执行 FFmpeg 其它逻辑
// 释放 AVFormatContext 结构体 资源
avformat_free_context(fmt_ctx);
3、avformat_free_context 函数
avformat_free_context 函数 的 作用是 用于 释放之前在 avformat_alloc_context 函数中分配的 AVFormatContext 结构体 及其相关资源 , 如果 在程序结束后不调用该函数 , 会出现堆内存泄漏问题 ;
avformat_free_context 函数原型 : 该函数用于 释放 AVFormatContext 结构体对象 以及 与之关联的 媒体流、编解码器、上下文格式 等 动态分配的内存 ;
void avformat_free_context(AVFormatContext *s);
代码示例可参考上个章节 ;
4、avformat_open_input 函数
avformat_open_input 函数作用 :
- 打开 媒体容器文件 ;
- 读取 媒体文件 的头信息 初始化 格式上下文结构体 AVFormatContext 对象 ;
avformat_open_input 函数原型 :
AVFormatContext *avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
-
函数参数 :
**AVFormatContext ps
参数 : 指向一个 AVFormatContext 指针的指针 , 这是一个二级指针 , 用于存放打开的文件格式上下文信息 ; 如果打开失败 , 该指针会被设置为 NULL ;*const char url
: 媒体文件 路径 或 URL ;*AVInputFormat fmt
: 可选参数 , 设置指定输入格式 , 如果设置为 NULL , FFmpeg 会自动检测 格式信息 ;**AVDictionary options
: 可选参数 , 指定打开文件时的选项 ;
-
函数返回值 : 返回 0 表示成功 , 返回 负值 表示错误 ;
代码示例 : 在下面的代码中 , 传入指向 AVFormatContext 指针的指针 , 传入 NULL 指针也可以 , FFmpeg 会自动初始化创建该结构体 ; 通过判定 avformat_open_input 返回值是否为 0 确定函数是否执行成功 , 文件打开成功返回 0 , 打开失败返回负数 ;
// 初始化 AVFormatContext 结构体
AVFormatContext *fmt_ctx = avformat_alloc_context();
// 判定 AVFormatContext 结构体是否初始化成功
if (!fmt_ctx) {
fprintf(stderr, "内存不足 AVFormatContext 初始化失败\n");
return -1;
}
// 打开文件
int ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
// 判定文件是否打开成功 , 打开成功返回 0 , 打开失败返回负数
if (ret < 0) {
fprintf(stderr, "打开媒体文件失败 %s\n", "input.mp4");
return ret;
}
// 执行 FFmpeg 其它逻辑
// 释放 AVFormatContext 相关资源
avformat_close_input(&fmt_ctx);
5、avformat_close_input 函数
avformat_close_input 函数 用于 关闭之前通过 avformat_open_input 打开的媒体文件并释放相关的资源 , 该函数可确保不会出现内存泄漏 ;
avformat_close_input 函数原型 :
void avformat_close_input(AVFormatContext **ps);
AVFormatContext **ps
参数 : 传入该参数后 , 会释放之前通过 avformat_open_input 函数 打开的 格式上下文 结构体 AVFormatContext 对象 ;
该函数的代码示例可以参考上一个章节代码 ;
6、avformat_find_stream_info 函数
avformat_find_stream_info 函数 用于 读取 媒体流 的 相关信息 并填充到 AVFormatContext 结构体中 , 包括 音频流、视频流、字幕等流的格式、编解码器信息 ;
使用场景 : 该函数通常在 打开媒体文件后调用 , 以获取更详细的流信息 ;
avformat_find_stream_info 函数原型 :
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
-
*AVFormatContext ic
参数 : 该参数 指向已打开的 AVFormatContext 结构体的指针 , 格式上下文结构体中包含了媒体文件的基本信息和流的结构 ; -
**AVDictionary options
参数 : 该参数为可选参数 , 使用 键值对 指定流信息检索过程中的选项 ; -
返回值 : 返回 0 表示成功 , 返回负值表示出错 , 返回的负值 表示具体的错误代码 ;
代码示例 :
// 初始化 AVFormatContext 结构体
AVFormatContext *fmt_ctx = avformat_alloc_context();
// 判定 AVFormatContext 结构体是否初始化成功
if (!fmt_ctx) {
fprintf(stderr, "内存不足 AVFormatContext 初始化失败\n");
return -1;
}
// 打开文件
int ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
// 判定文件是否打开成功 , 打开成功返回 0 , 打开失败返回负数
if (ret < 0) {
fprintf(stderr, "打开媒体文件失败 %s\n", "input.mp4");
return ret;
}
// 获取文件流信息
ret = avformat_find_stream_info(fmt_ctx, NULL);
// 判定获取文件流信息是否成功 , 成功返回 0 , 失败返回负数
if (ret < 0) {
fprintf(stderr, "没有找到文件流信息\n");
avformat_close_input(&fmt_ctx);
return ret;
}
// 执行 FFmpeg 其它逻辑
// 释放 AVFormatContext 相关资源
avformat_close_input(&fmt_ctx);
7、av_read_frame 函数
av_read_frame 函数 用于 从媒体文件中读取一帧数据 ;
av_read_frame 函数原型 :
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
-
*AVFormatContext s
: 已打开的 AVFormatContext 结构体的指针 , 其中包含了 媒体文件 的 基本信息 和 流结构信息 ; -
*AVPacket pkt
: 指向 AVPacket 结构体的指针 , 其中存放的是读取到的帧数据 , 这是没有解码的数据 , 不能用于播放 ; -
返回值 : 返回 0 表示读取成功 , 返回负值表示错误 , 返回的负值是错误码 ;
代码示例 :
// 初始化 AVFormatContext 结构体
AVFormatContext *fmt_ctx = avformat_alloc_context();
// 读取的数据包
AVPacket pkt;
// 判定 AVFormatContext 结构体是否初始化成功
if (!fmt_ctx) {
fprintf(stderr, "内存不足 AVFormatContext 初始化失败\n");
return -1;
}
// 打开文件
int ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
// 判定文件是否打开成功 , 打开成功返回 0 , 打开失败返回负数
if (ret < 0) {
fprintf(stderr, "打开媒体文件失败 %s\n", "input.mp4");
return ret;
}
// 获取文件流信息
ret = avformat_find_stream_info(fmt_ctx, NULL);
// 判定获取文件流信息是否成功 , 成功返回 0 , 失败返回负数
if (ret < 0) {
fprintf(stderr, "没有找到文件流信息\n");
avformat_close_input(&fmt_ctx);
return ret;
}
// 读取一帧数据
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
// 处理 pkt,例如解码、保存等
// 记得释放 pkt 中的资源
av_packet_unref(&pkt);
}
// 执行 FFmpeg 其它逻辑
// 释放 AVFormatContext 相关资源
avformat_close_input(&fmt_ctx);
8、avformat_seek_file 函数
avformat_seek_file 函数 用于在媒体文件中进行 时间跳转 , 该函数 可以 在音频或视频流中根据 微秒时间戳 进行精确的定位,常用于实现快进、快退等功能 ;
avformat_seek_file 函数原型 :
int avformat_seek_file(AVFormatContext *s, int stream_index, int min_ts, int ts, int max_ts, int flags);
-
*AVFormatContext s
参数 : 指向已打开的 AVFormatContext 结构体的指针 , 包含了媒体文件的基本信息 ; -
int stream_index
参数 : 指定 要进行定位的 流的索引 , 如果设置为 -1 , 则对所有流进行定位 , 包括音频流 / 视频流 / 字幕流 等同时定位 ; -
int min_ts
参数 : 设置 定位时间戳的最小值 , 防止过早的定位 , 该参数的单位是 微秒 ; 一般设置为 0 ; -
int ts
参数 : 定位的目标时间戳 , 将要跳转到的位置 , 单位 微秒 ; -
int max_ts
参数 : 设置 定位时间戳的最大值 , 防止定位的位置过于靠后 , 单位 微秒 ; 一般设置为 INT64_MAX ; -
int flags
: 定位选项 , 一般设置为 0 ; -
返回值 : 返回 0 表示定位成功 , 返回负值表示失败 , 该负值也是错误码 ;
-
使用示例 :
// 寻找指定时间戳的位置(例如 10 秒)
int64_t target_timestamp = 10 * AV_TIME_BASE; // 10 秒转换为微秒
if (avformat_seek_file(fmt_ctx, -1, 0, target_timestamp, INT64_MAX, 0) < 0) {
fprintf(stderr, "时间戳定位失败\n");
}
代码示例 :
// 初始化 AVFormatContext 结构体
AVFormatContext *fmt_ctx = avformat_alloc_context();
// 读取的数据包
AVPacket pkt;
// 判定 AVFormatContext 结构体是否初始化成功
if (!fmt_ctx) {
fprintf(stderr, "内存不足 AVFormatContext 初始化失败\n");
return -1;
}
// 打开文件
int ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
// 判定文件是否打开成功 , 打开成功返回 0 , 打开失败返回负数
if (ret < 0) {
fprintf(stderr, "打开媒体文件失败 %s\n", "input.mp4");
return ret;
}
// 获取文件流信息
ret = avformat_find_stream_info(fmt_ctx, NULL);
// 判定获取文件流信息是否成功 , 成功返回 0 , 失败返回负数
if (ret < 0) {
fprintf(stderr, "没有找到文件流信息\n");
avformat_close_input(&fmt_ctx);
return ret;
}
// 寻找指定时间戳的位置(例如 10 秒)
int64_t target_timestamp = 10 * AV_TIME_BASE; // 10 秒转换为微秒
if (avformat_seek_file(fmt_ctx, -1, 0, target_timestamp, INT64_MAX, 0) < 0) {
fprintf(stderr, "时间戳定位失败\n");
}
// 读取一帧数据
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
// 处理 pkt,例如解码、保存等
// 记得释放 pkt 中的资源
av_packet_unref(&pkt);
}
// 执行 FFmpeg 其它逻辑
// 释放 AVFormatContext 相关资源
avformat_close_input(&fmt_ctx);
9、av_seek_frame 函数
av_seek_frame 函数 用于 在 多媒体文件 中 精确 定位到指定的 数据帧 ;
av_seek_frame 函数原型 :
int av_seek_frame(AVFormatContext *s, int stream_index, int timestamp, int flags);
-
*AVFormatContext s
参数 : 指向已打开的 AVFormatContext 结构体的指针 , 其中包含了媒体文件的基本信息 ; -
int stream_index
参数 : 指定要进行定位的流的索引 , 设置为 -1,则对所有流进行定位 ; -
int64_t timestamp
参数 : 目标时间戳 , 表示希望跳转到的位置 , 单位 微秒 ; -
int flags
参数 : 定位选项 , 通常为 0 或 AVSEEK_FLAG_BACKWARD 等标志 , AVSEEK_FLAG_BACKWARD 表示 跳转将尝试找到前一个关键帧 ; -
返回值 : 返回 0 表示定位成功 , 返回负值表示失败 , 该负值也是错误码 ;
-
使用示例 :
// 寻找指定时间戳的位置(例如 10 秒)
int64_t target_timestamp = 10 * AV_TIME_BASE; // 10 秒转换为微秒
if (av_seek_frame(fmt_ctx, -1, target_timestamp, 0) < 0) {
fprintf(stderr, "定位失败\n");
}
完整代码示例 :
// 初始化 AVFormatContext 结构体
AVFormatContext *fmt_ctx = avformat_alloc_context();
// 读取的数据包
AVPacket pkt;
// 判定 AVFormatContext 结构体是否初始化成功
if (!fmt_ctx) {
fprintf(stderr, "内存不足 AVFormatContext 初始化失败\n");
return -1;
}
// 打开文件
int ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
// 判定文件是否打开成功 , 打开成功返回 0 , 打开失败返回负数
if (ret < 0) {
fprintf(stderr, "打开媒体文件失败 %s\n", "input.mp4");
return ret;
}
// 获取文件流信息
ret = avformat_find_stream_info(fmt_ctx, NULL);
// 判定获取文件流信息是否成功 , 成功返回 0 , 失败返回负数
if (ret < 0) {
fprintf(stderr, "没有找到文件流信息\n");
avformat_close_input(&fmt_ctx);
return ret;
}
// 寻找指定时间戳的位置(例如 10 秒)
int64_t target_timestamp = 10 * AV_TIME_BASE; // 10 秒转换为微秒
if (av_seek_frame(fmt_ctx, -1, target_timestamp, 0) < 0) {
fprintf(stderr, "定位失败\n");
}
// 读取一帧数据
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
// 处理 pkt,例如解码、保存等
// 记得释放 pkt 中的资源
av_packet_unref(&pkt);
}
// 执行 FFmpeg 其它逻辑
// 释放 AVFormatContext 相关资源
avformat_close_input(&fmt_ctx);