說明 :將 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