您好,登錄后才能下訂單哦!
本文是我的《FFMPEG Tips》系列的第三篇文章,上篇文章 介紹了如何提取整個音視頻碼流的媒體信息,包括:封裝格式、編碼格式、視頻的分辨率、幀率、碼率、音頻的采樣率、位寬、通道數等等,而本文則關注得更細一點,看看如何利用 ffmpeg 讀取碼流中每一幀的信息。
1. 碼流中每一幀的哪些信息值得關注 ?
[ ] 音頻幀還是視頻幀
[ ] 關鍵幀還是非關鍵幀
[ ] 幀的數據和大小
[ ] 時間戳信息
2. 為什么要關注這些信息 ?
[ ] 音頻幀還是視頻幀 -> 分別送入音頻/視頻×××
[ ] 關鍵幀還是非關鍵幀 -> 追幀優化
[ ] 幀的數據和大小 -> 取出幀的內容
[ ] 時間戳信息 -> 音視頻同步
3. 如何從 ffmpeg 取出這些信息 ?
ffmpeg 提供了一個函數 av_read_frame 來完成解封裝的過程,它會從碼流里面提取每一個音頻、視頻幀,它使用了結構體 AVPacket 來記錄每一幀的信息。
讀取一幀數據的代碼示例如下(ic 即為 AVFormatContext 對象,碼流的上下文句柄):
AVPacket avpkt; av_init_packet(&avpkt); while (!interrupt) { int ret = av_read_frame(ic, &avpkt); if (ret < 0) { break; } // processing } av_free_packet(&avpkt);
每循環一次,就從碼流中解封裝并且提取了一幀數據,并存放在了 AVPacket 結構體中。
3.1 如何判斷是音頻幀還是視頻幀
上一篇文章我們提到過,使用下面的方法,獲取碼流中的 video_stream_idx 和 audio_stream_idx
int video_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); int audio_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
那么,此時就派上用場了,每一個 AVPacket 都有一個成員變量:stream_index,由該成員變量即可判斷這個 Packet 到底是音頻還是視頻了:
if (avpkt.stream_index == video_stream_idx) { LOGD("read a video frame"); } else if (avpkt.stream_index == audio_stream_idx) { LOGD("read audio frame); }
3.2 如何判斷是否為關鍵幀
判斷是否為關鍵幀的方法也比較簡單,示例如下:
if (avpkt.flags & AV_PKT_FLAG_KEY) { LOGD("read a key frame"); }
3.3 如何獲取幀的數據和大小
幀的數據和大小直接定義在 AVPacket 結構體中,對應的成員變量如下:
// 壓縮編碼的數據,一幀音頻/視頻 uint8_t *data; // 數據的大小 int size;
3.4 如何獲取幀的時間戳信息
每一個幀都可能攜帶有 2 個時間戳信息,一個是解碼時間戳 dts,一個是顯示時間戳 pts,解碼時間戳告訴我們什么時候需要解碼,顯示時間戳告訴我們什么時候需要顯示,只有在碼流中存在 B 幀的情況下,這兩個時間戳才會不一致。
這些時間戳信息不一定存在于碼流中(取決于生產端),如果不存在,則其值為:AV_NOPTS_VALUE
一定要選擇正確地方式打印時間戳,時間戳是使用 long long 來表示的,即 int64_t,因此打印的時候,需要使用 “%lld” 來打印,例如:
while (!interrupt) { int ret = av_read_frame(player->ic, &avpkt); if (ret < 0) { break; } if (avpkt.stream_index == video_stream_idx) { LOGD("read video frame, timestamp = %lld \n”, avpkt.pts); } else if (avpkt.stream_index == audio_stream_idx) { LOGD("read audio frame, timestamp = %lld \n”, avpkt.pts); } }
由此,我們就可以通過這些 log 信息調試一下某一段音視頻流的時間戳是否正確,比如是否出現了時間戳的回滾和錯亂,則必然會導致播放端出現音視頻不同步或者顯示異常等情況。
4. 小結
關于如何使用 FFMPEG 如何讀取每一幀的信息就介紹到這兒了,文章中有不清楚的地方歡迎留言或者來信 lujun.hust@gmail.com 交流,關注我的新浪微博 @盧_俊 或者 微信公眾號 @Jhuster 獲取最新的文章和資訊。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。