AVFormatContext 再分析
说明 :将 avfromatContext 的变量依次打印分析,根据ffmpeg 给的说明,猜测,结合网上的文章字节写测试代码分析。
从常用到不常用依次分析
1. unsigned int nb_streams;
代表 avfromatContext 中 AVStream **streams 的个数
/**
* Number of elements in AVFormatContext.streams.
*
* Set by avformat_new_stream(), must not be modified by any other code.
*/
unsigned int nb_streams;
//打印在解复用时候,avformatContext的主要参数
void PrintfDeMuxingAVFormatContextMainParamter(AVFormatContext* avformatContext) {if (avformatContext == NULL) {cout << "func PrintfDeMuxingAVFormatContextMainParamter error because AVFormatContext == nullptr " << endl;return;}cout << "avformat中有的avstream的个数为:avformatContext->nb_streams = " << avformatContext->nb_streams << endl;}
2. int64_t bit_rate;
在 AVFormatContext
结构体中,bit_rate
字段表示媒体文件的全局平均比特率(单位为 bps,即 bits per second)
/**
* Total stream bitrate in bit/s, 0 if not
* available. Never set it directly if the file_size and the
* duration are known as FFmpeg can compute it automatically.
*/
int64_t bit_rate;
2.1 含义再说明1
从说明中可以看出来,代表的是所有 stream 的 平均比特率。啥意思呢?比如这是一个mp4文件,既有视频也有音频,我们假设有一个视频两个音频(粤语和普通话),假设视频是300kbps,普通话音频是128kbps,粤语音频是100kbps。那么 AVFormatContext
中的 bit_rate就应该等于 300 +128+100 = 628kbps,注意单位是kbps。
// 遍历所有流,计算总比特率
int64_t total_bit_rate = 0;
for (int i = 0; i < avformat_ctx->nb_streams; i++) {AVStream *stream = avformat_ctx->streams[i];total_bit_rate += stream->codecpar->bit_rate;
}
2.2 含义再说明2,在TS下无效
0 if not available
这个意思是说,这个值有可能是0,代表不可使用。
部分封装格式(如 TS 流)可能不记录全局比特率,此时 bit_rate
字段无效
2.3 含义再说明3,当文件大小和duration都知道的时候,user不要设置,ffmepg会自动计算
Never set it directly if the file_size and the duration are known as FFmpeg can compute it automatically.
2.5 动态码率场景
VBR 编码的文件中,该字段仅代表平均值,无法反映瞬时码率波动
CBR 是 (恒定比特率)
VBR(可变比特率),
mp4文件只能是 VBR。
也就是说:如果我们解析的是mp4文件,那么这个值是平均值。无法反映瞬时码率波动
2.6 典型应用场景
- 带宽估算:
结合容器和流的比特率,判断网络传输是否满足实时播放需求56。 - 文件分析工具:
统计媒体文件的码率分布,辅助编码参数优化6。
3. int64_t duration
只能用于解复用,
是 AVFormatContext 结构体的关键成员,用于表示 媒体文件的总时长。其数据类型为 int64_t
,单位为 AV_TIME_BASE(即微秒的倒数,通常为 1,000,000)。
该字段在 成功解析媒体文件流信息后(调用 avformat_find_stream_info()
)才会被正确赋值
/**
* Duration of the stream, in AV_TIME_BASE fractional
* seconds. Only set this value if you know none of the individual stream
* durations and also do not set any of them. This is deduced from the
* AVStream values if not set.
*
* Demuxing only, set by libavformat.
*/
int64_t duration;
主要使用场景,通过 duration 计算文件时长。
换算为秒:
double duration_sec = (double)avformatContext->duration / AV_TIME_BASE;
转换为时分秒格式:
int64_t total_us = avformatContext->duration + 5000; // 四舍五入修正
int hours = total_us / (3600 * AV_TIME_BASE);
int mins = (total_us % (3600 * AV_TIME_BASE)) / (60 * AV_TIME_BASE);
int secs = (total_us % (60 * AV_TIME_BASE)) / AV_TIME_BASE;
常见问题与解决方案
问题 | 原因 | 解决方案 |
---|---|---|
返回负值或极大值 | 未正确解析流信息或文件不完整 | 调用 avformat_find_stream_info() 前设置 max_analyze_duration 参数 |
单位换算错误 | 未使用 AV_TIME_BASE 进行转换 | 确保除以 AV_TIME_BASE (或使用 av_rescale_q() 函数) |
时间精度丢失 | 直接截断未四舍五入 | 添加 5000 微秒偏移(如 +5000 )后再计算 |
AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, filename, NULL, NULL); // 打开文件
fmt_ctx->max_analyze_duration = 5 * AV_TIME_BASE; // 限制解析时长避免卡顿
avformat_find_stream_info(fmt_ctx, NULL); // 解析流信息if (fmt_ctx->duration != AV_NOPTS_VALUE) {int64_t duration = fmt_ctx->duration + 5000; // 修正精度int hours = duration / (3600 * AV_TIME_BASE);int mins = (duration % (3600 * AV_TIME_BASE)) / (60 * AV_TIME_BASE);int secs = (duration % (60 * AV_TIME_BASE)) / AV_TIME_BASE;printf("Duration: %02d:%02d:%02d\n", hours, mins, secs);
} else {printf("Duration unavailable\n");
}
avformat_close_input(&fmt_ctx); // 释放资源
适用场景
- 媒体信息分析工具:如
ffprobe
使用该字段输出文件时长。 - 播放器开发:用于显示进度条总时长。
- 流媒体处理:结合
AVStream
中各流时长进行同步控制。
注意:在网络流或实时流中,duration
可能无法获取(值为 AV_NOPTS_VALUE
),需动态计算
cout << "avformat中duration为:avformatContext->duration = " << avformatContext->duration << endl;double duration_sec = (double)avformatContext->duration / AV_TIME_BASE;
cout << "avformat中秒数为:duration_sec = " << duration_sec << endl;if (avformatContext->duration != AV_NOPTS_VALUE) {int64_t duration = avformatContext->duration + 5000; // 修正精度int hours = duration / (3600 * AV_TIME_BASE);int mins = (duration % (3600 * AV_TIME_BASE)) / (60 * AV_TIME_BASE);int secs = (duration % (60 * AV_TIME_BASE)) / AV_TIME_BASE;printf("Duration: %02d:%02d:%02d\n", hours, mins, secs);
}else {printf("Duration unavailable\n");
}avformat中duration为:avformatContext->duration = 60024000
avformat中秒数为:duration_sec = 60.024
Duration: 00:01:00
4. char *url;
/**
* input or output URL. Unlike the old filename field, this field has no
* length restriction.
*
* - demuxing: set by avformat_open_input(), initialized to an empty
* string if url parameter was NULL in avformat_open_input().
* - muxing: may be set by the caller before calling avformat_write_header()
* (or avformat_init_output() if that is called first) to a string
* which is freeable by av_free(). Set to an empty string if it
* was NULL in avformat_init_output().
*
* Freed by libavformat in avformat_free_context().
*/
char *url;
和之前的filename不同,url是没有长度限制的。
在解码时,通过 avformat_open_input 方法 会将url 记录到 AVFormatContext ,可能会nullptr。
在编码时,需要在 调用 avformat_write_header 方法之前设置。
char * url = avformatContext->url;cout << "avformat中duration为 url = " << url << endl;结果为:avformat中duration为 url = ./120/400_300_25.mp4
5. int64_t start_time;
/**
* Position of the first frame of the component, in
* AV_TIME_BASE fractional seconds. NEVER set this value directly:
* It is deduced from the AVStream values.
*
* Demuxing only, set by libavformat.
*/
int64_t start_time;
组件第一帧的位置,以AV_TIME_BASE 为单位。
切勿直接设置此值:它是从AVStream值推断出来的。
这玩意有啥用呢?表示该avformatContext 第一帧的开始时间,那么应该都是0。
可能的点:todo
如果我们从文件的中间位置读取的,那么这个值就不是0?
在网络流的时候用?
int64_t starttime = avformatContext->start_time;cout << "avformat中duration为 starttime = " << starttime << endl;avformat中duration为 starttime = 0
6. 接下来都是非重点 AVCodec* audio_codec;
/**
* Forced audio codec.
* This allows forcing a specific decoder, even when there are multiple with the same codec_id.
* Demuxing: Set by user
*/
AVCodec *audio_codec;
这里从翻译来看,意思是该变量是为了 音频的编解码。
允许在解码的时候,允许强制使用特定的解码器,即使存在多个具有相同codec_id的解码器
/**
* Forced audio codec.
* This allows forcing a specific decoder, even when there are multiple with the same codec_id.
* Demuxing: Set by user
* AVCodec* audio_codec;
* 在 音频 编解码器 的时候使用,
* 在解复用的时候,允许强制使用特定的解码器,即使存在多个具有相同codec_id的解码器
* 我们使用test02测试
*/
avformatContext->audio_codec;cout << "avformatContext->audio_codec = " << avformatContext->audio_codec << endl;
* 在 音频 编解码器 的时候使用,
* 在解复用的时候,允许强制使用特定的解码器,即使存在多个具有相同codec_id的解码器
AVCodec* audioavcodec = avformatContext->audio_codec;if (audioavcodec == nullptr) {cout << "audioavcodec == nullptr" << endl;}else {cout << "audioavcodec != nullptr audioavcodec->id = " << audioavcodec->id << endl;}log 为:audioavcodec == nullptr