【FFmpeg】解封装 ③ ( 解封装代码实战 | 分析 avformat_find_stream_info 函数的作用 | 获取视频文件码率 | 分析媒体流数据 )
文章目录
FFmpeg 4.0 版本源码地址 :
- GitHub : https://github.com/FFmpeg/FFmpeg/tree/release/4.0
- GitCode : https://gitcode.com/gh_mirrors/ff/FFmpeg/tree/release/4.0
- FFmpeg/libavcodec/avpacket.c 源码 : https://gitcode.com/gh_mirrors/ff/FFmpeg/blob/release/4.0/libavcodec/avpacket.c
- FFmpeg/libavformat/avformat.h 源码 : https://gitcode.com/gh_mirrors/ff/FFmpeg/blob/release/4.0/libavformat/avformat.h
一、AVFormatContext 结构体的视频参数信息
1、回顾总结
在上一篇博客 【FFmpeg】解封装 ② ( 解封装代码实战 | Qt 项目搭建 | Qt 可执行程序运行环境分析 | FFmpeg 函数错误码处理 | 获取视频文件的参数信息 | 获取文 码流个数/播放时长 ) 中 , 介绍了
- 调用 avformat_open_input 函数 打开 视频文件 ,
- mp4 格式的文件 , 仅调用该函数 , 即可获取完整的 视频参数信息 ;
- flv 格式的文件 , 无法通过该函数获取所有参数信息 ;
- 调用 avformat_find_stream_info 函数 分析媒体流详细参数信息 ,
- flv 格式的文件 , 需要额外调用该函数 , 才能获取所有的码流参数信息 ;
- 遇到 FFmpeg 函数执行失败时 , 可 调用 av_strerror 函数 , 将错误码转为文字描述信息 ;
- 获取 AVFormatContext 中的参数信息 :
- url 字段 是 文件路径或地址 ;
- nb_streams 字段 是 码流个数 ;
- duration 字段 是 播放时长 , 单位 微秒 ;
2、AVFormatContext 的常用字段
下面是 AVFormatContext 结构体 中 的常用字段 , 每个字段设置了中文注释 ;
typedef struct AVFormatContext {
/**
* AVFormatContext.streams 中的元素数量。
*/
unsigned int nb_streams;
/**
* 文件中所有流的列表
*/
AVStream **streams;
/**
* 输入或输出 URL。与旧的 filename 字段不同,此字段没有
* 长度限制。
*/
char *url;
/**
* 流的持续时间,单位为 AV_TIME_BASE 分数秒
*/
int64_t duration;
/**
* 总流比特率(比特/秒)
*/
int64_t bit_rate;
}
3、对比 avformat_find_stream_info 函数的作用
① 文件准备
将 FLV / MP4 / TS 格式的三个视频文件 , 拷贝到 源码编译 输出目录中 , 这也是 执行程序 的 操作目录 ,

将视频放在 上述目录 " D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug " 中 , 在 项目设置 中 的 命令行参数 CommandLine arguments 中配置 文件的 相对路径 即可 ;

② MP4 格式
MP4 格式的 视频文件 将文件的 元数据信息 集中存储在文件的头部 中 , 这些元数据有 : 视频流信息、音频流信息、时长、比特率、分辨率 等 ;
当调用 avformat_open_input 函数 打开 MP4 文件时 , FFmpeg 会读取 视频文件的头部数据 , 并从中提取出所有的流信息 ;
由于这些信息在文件头部就已经完整 , 后续无需 调用 avformat_find_stream_info 函数 解析流内容 ;
在 【FFmpeg】解封装 ② ( 解封装代码实战 | Qt 项目搭建 | Qt 可执行程序运行环境分析 | FFmpeg 函数错误码处理 | 获取视频文件的参数信息 | 获取文 码流个数/播放时长 ) 四、获取视频文件的参数信息 3、代码示例 代码的基础上 , 将 avformat_find_stream_info 函数注释掉 , 发现 不调用 avformat_find_stream_info 函数 仍然可以打印出完整的 视频参数信息 ;

③ FLV 格式
FLV 格式的文件 是 专门为 流媒体传输 而设计的 文件格式 , FLV 文件的 元数据结构较简单 , 文件头 只包含 文件类型、版本号、音视频标志等 基本信息 , 媒体流 的 参数信息 存储在 FLV 文件的后续数据包 ;
媒体流 的 码率、分辨率、时长 等 参数 , 都不在 FLV 头文件中 , 如果要获取这些信息 , 需要 解析 多个数据包 后才能获取到所有信息 ;
调用 avformat_find_stream_info 函数 就是 读取 后续数据包信息 , 解析媒体流的 一系列参数 ;
在上面的代码基础上 , 将命令行参数设置为 test.flv , 解析 FLV 格式的视频文件 ;

调用 avformat_open_input 函数 后 , 只能解析头文件 , 头文件没有媒体流的参数信息 ,
Input #0, flv, from 'test.flv':
Duration: N/A, start: 0.000000, bitrate: N/A

将 avformat_find_stream_info 函数调用的注释打开 , 再次执行应用 , 对 FLV 格式的视频文件进行解封装 , 此时可以完整的打印出 FLV 文件的 媒体流 详细参数信息 ;

二、获取视频文件码率
1、媒体流的比特率
AVFormatContext 结构体 的 int64_t bit_rate 字段 是 视频文件 或 音频文件 的 媒体流 的 比特率 , 单位是 bps 比特每秒 , 比特率 反映 媒体流的 质量 和 压缩程度 ;
typedef struct AVFormatContext {
/**
* 总流比特率(比特/秒),如果不可用则为 0。
* 如果文件大小和持续时间已知,请勿直接设置它,因为 FFmpeg 可以自动计算。
*/
int64_t bit_rate;
} AVFormatContext;
bit_rate 字段 是一个 64 位的整数 , 其 单位是 比特每秒 bit/s , 该值是文件的 总比特率 , 通常在多媒体文件的头部 会有这个信息 , 这个比特率是对整个文件或流的压缩率的一个度量 ;
- 对于视频文件 , 比特率 通常指的是 视频流 和 音频流 的 总比特率 ;
- 对于流媒体 , 比特率 表示 媒体流 的 传输比特率 ;
比特率 只是一个大致的估计值 , 尤其是在网络宽带不稳定的的情况下 , 网络流的比特率 不是精确的值 ;
获取 视频总比特率代码 :
// 8. 获取视频文件的总比特率 , 单位为bps
printf("\nbit_rate : %lld bps\n",(int64_t)(fmt_ctx->bit_rate));
执行结果 :
bit_rate : 286624 bps

2、比特率 与 存储单位 的 换算基数
比特率 的 单位 是 比特每秒 bps ,
按照理论来说 , 比特率应该按照 1024 进行换算 , 1 Kbps = 1024 bps ;
比特率 主要是用于描述 网络传输速度 和 信号带宽 , 一般 遵循 国际单位制 , 而国际单位制 规定 : 单位之间的换算使用 1000 , 因此 在实际使用时 , 一般都按照如下进行单位换算 :
- 1 Kbps = 1000 bps
- 1 Mbps = 1000 Kbps = 1,000,000 bps
- 1 Gbps = 1000 Mbps = 1,000,000,000 bps
与之相对的是 存储单位的换算 , 在计算机存储和内存的容量度量中 , 通常使用 1024 作为换算基数 ;
- 1 KiB (Kibibyte) = 1024 bytes
- 1 MiB (Mebibyte) = 1024 KiB = 1,048,576 bytes
- 1 GiB (Gibibyte) = 1024 MiB = 1,073,741,824 bytes
3、常见的音视频比特率
常见的音视频比特率 :
- 音频 比特率 : 通常 以 Kbps 为单位 , 音频的 比特率 越高 , 音质通常越好 , 但文件或数据流的大小也会增加 ; 常见的 MP3 音频比特率为 128 Kbps、192 Kbps、256 Kbps、320 Kbps ;
- 视频 比特率 : 视频 比特率 表示 每秒钟传输的视频数据量 , 通常以 Kbps 或 Mbps 为单位 ; 视频比特率越高 , 视频的画质通常越好 , 但文件的大小也会增大 ;
- 1000 Kbps 是 低清视频 ;
- 2500 Kbps 是 标清视频 ;
- 5000 Kbps 是 高清 720p 视频 ;
- 8000 Kbps 是 全高清 1080p 视频 ;
三、分析媒体流数据
1、媒体流遍历
在 AVFormatContext 结构体 中 unsigned int nb_streams 成员 是 媒体流个数 , AVStream **streams 是 媒体流 的实际数据 , 每个 AVStream 都表示一个媒体流 , 可参考本博客 第一章 的 AVFormatContext 结构体 源码 ;
AVStream 媒体流 可以是 :
- 视频流
- 音频流
- 字幕流
使用下面的方式 , 可以遍历 视频文件 的 媒体流 ;
AVFormatContext *fmt_ctx = NULL
// ... 初始化 AVFormatContext 结构体数据
for (uint32_t i = 0; i < fmt_ctx->nb_streams; i++){
// 解析的第 i 个媒体流 , 可能是 视频流 / 音频流 / 字幕流
AVStream *in_stream = fmt_ctx->streams[i];
// 解析 AVStream 操作
}
2、AVStream 中的 codecpar 成员
AVStream 结构体 中的 AVCodecParameters *codecpar 成员 是一个指向 AVCodecParameters 结构体的指针 , 用于 描述媒体流的编解码器相关参数 ;
typedef struct AVStream {
/**
* 与此流关联的编解码器参数。在调用 avformat_new_stream() 时由 libavformat 分配,
* 并在调用 avformat_free_context() 时由 libavformat 释放。
*
* - 解复用:在流创建时或调用 avformat_find_stream_info() 时由 libavformat 填充。
* - 复用:在调用 avformat_write_header() 之前由调用方填充。
*/
AVCodecParameters *codecpar;
} AVStream;
AVCodecParameters 是一个 轻量级的结构体 , 仅存储描述媒体流的静态属性 , 如 : 编码格式、分辨率、采样率 等 ;
AVCodecParameters 结构体中 不包含运行时的状态信息 , 不依赖具体的 解码器 或 编码器 实现 , 比 AVCodecContext 结构体 更适合 作为 描述媒体流 的 独立结构体 ;
AVStream 的 AVCodecContext *codec 成员 , 已经被标记为废弃状态 , 不推荐使用 , 仅用于兼容老旧代码 ;
typedef struct AVStream {
/**
* @deprecated use the codecpar struct instead
*/
attribute_deprecated
AVCodecContext *codec;
} AVStream;
3、AVCodecParameters 结构体
AVCodecParameters 结构体 定义在 libavcodec/avcodec.h 头文件 中 , 该结构体中 的成员分为如下几类 :
- 通用参数 : 媒体类型 codec_type , 编解码器 ID codec_id , 编码器标记 codec_tag , 比特率 bit_rate ;
- 视频流参数 : 视频宽高 width height , 像素格式 format , 画面宽高比 sample_aspect_ratio ;
- 音频流参数 : 音频采样率 sample_rate , 声道数 channels , 采样位数 format , 每帧采样数 frame_size ;
- 字幕流参数 : 字幕宽高 width height , 字幕像素格式 format ;
注意 : 某些字段 , 多种媒体流使用会有不同的含义 ;
typedef struct AVCodecParameters {
enum AVMediaType codec_type; /**< 媒体类型 (如音频、视频、字幕等) */
enum AVCodecID codec_id; /**< 编解码器 ID (如 H.264, AAC) */
uint32_t codec_tag; /**< 编解码器标记 (格式标识符,如 avc1, mp4a) */
uint8_t *extradata; /**< 附加数据 (如 SPS/PPS 信息) */
int extradata_size; /**< 附加数据大小 (单位: 字节) */
int format; /**< 像素格式 (视频) 或采样格式 (音频) */
int bit_rate; /**< 比特率 (单位: bit/s) */
int bits_per_coded_sample; /**< 每编码样本位数 */
int bits_per_raw_sample; /**< 每原始样本位数 */
int profile; /**< 编解码器配置 (如 H.264 的 High Profile) */
int level; /**< 编解码器级别 (如 H.264 的 Level 4.1) */
// 视频相关字段
int width; /**< 视频帧宽度 (单位: 像素) */
int height; /**< 视频帧高度 (单位: 像素) */
AVRational sample_aspect_ratio; /**< 显示宽高比 (SAR) */
enum AVFieldOrder field_order; /**< 字段顺序 (如逐行、隔行) */
int color_range; /**< 颜色范围 (如 MPEG 或 JPEG 范围) */
int color_primaries; /**< 颜色原色 */
int color_trc; /**< 颜色传递特性 */
int color_space; /**< 颜色空间 (如 BT.601, BT.709) */
int chroma_location; /**< 色度位置 */
int video_delay; /**< 视频延迟 (单位: 帧) */
// 音频相关字段
int channel_layout; /**< 声道布局 (如立体声、5.1 声道) */
int channels; /**< 声道数 */
int sample_rate; /**< 音频采样率 (单位: Hz) */
int block_align; /**< 对齐块大小 */
int frame_size; /**< 每帧采样点数 */
int initial_padding; /**< 解码器初始填充样本数 */
int trailing_padding; /**< 解码器尾部填充样本数 */
int seek_preroll; /**< 回放时需要的预卷样本数 */
} AVCodecParameters;
4、媒体流类型
在 AVStream 结构体 中 的 AVCodecParameters *codecpar 结构体成员 中 的 enum AVMediaType codec_type 枚举成员 表示的是 媒体流类型 ;
AVMediaType 枚举 表示 媒体流类型 , 该枚举 定义在 libavutil/avutil.h 文件中 ;
下面是 AVMediaType 枚举 的原型 :
typedef enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, /**< 未知类型 */
AVMEDIA_TYPE_VIDEO, /**< 视频 */
AVMEDIA_TYPE_AUDIO, /**< 音频 */
AVMEDIA_TYPE_DATA, /**< 数据 (通常为未解码的补充数据) */
AVMEDIA_TYPE_SUBTITLE, /**< 字幕 */
AVMEDIA_TYPE_ATTACHMENT, /**< 附件 (如字体或图片) */
AVMEDIA_TYPE_NB /**< 媒体类型的数量 (仅用于计数) */
} AVMediaType;
- AVMEDIA_TYPE_UNKNOWN : 未知 媒体流 类型 , 如果没有识别出流的类型就会设置 该枚举选项 ;
- AVMEDIA_TYPE_VIDEO : 视频流 类型 , 包括编码的或未编码的 视频帧数据 , 如 : H.264 ;
- AVMEDIA_TYPE_AUDIO : 音频流 类型 , 包括各种音频编码格式 , 如 : acc / mp3 / pcm ;
- AVMEDIA_TYPE_DATA : 数据流 类型 , 包含 未解码 或 额外的非音视频数据 , 如 : 元数据 / 统计信息 ;
- AVMEDIA_TYPE_SUBTITLE : 字幕流 类型 , 包含 ASS / SRT 文字字幕 或 图片字幕 ;
- AVMEDIA_TYPE_ATTACHMENT : 附件流 类型 , 通常为媒体文件中包含的 字体 或 缩略图 等 外部资源 ;
- AVMEDIA_TYPE_NB : 媒体类型的数量 , 用于标记 AVMediaType 中的枚举条目总数 ;
5、判断媒体流类型
核心代码示例 : 在下面的代码中 , 遍历 AVFormatContext 中的 AVStream **streams 成员 , 这是所有的媒体流 , 通过判断 AVStream 的 codecpar->codec_type 成员 , 判定媒体流的类型 ;
// 9. 遍历媒体流
for (uint32_t i = 0; i < fmt_ctx->nb_streams; i++)
{
// 获取第 i 个媒体流 , 该媒体流可以是 音频流、视频流、字幕流
AVStream *in_stream = fmt_ctx->streams[i];
if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type) // 解析音频流参数
{
printf("\n\n==== Audio info ===\n\n");
}
else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) //解析视频流参数
{
printf("\n\n==== Vedio info ===\n\n");
}
}
执行结果如下 :

更多推荐



所有评论(0)