溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

FFMPEG Tips (3) 如何讀取每一幀的信息

發(fā)布時間:2020-07-12 11:33:32 來源:網(wǎng)絡(luò) 閱讀:14328 作者:Jhuster 欄目:開發(fā)技術(shù)

本文是我的《FFMPEG Tips》系列的第三篇文章,上篇文章 介紹了如何提取整個音視頻碼流的媒體信息,包括:封裝格式、編碼格式、視頻的分辨率、幀率、碼率、音頻的采樣率、位寬、通道數(shù)等等,而本文則關(guān)注得更細(xì)一點,看看如何利用 ffmpeg 讀取碼流中每一幀的信息。


1.  碼流中每一幀的哪些信息值得關(guān)注 ?


[ ]  音頻幀還是視頻幀

[ ]  關(guān)鍵幀還是非關(guān)鍵幀

[ ]  幀的數(shù)據(jù)和大小

[ ]  時間戳信息


2.  為什么要關(guān)注這些信息 ?


[ ]  音頻幀還是視頻幀 -> 分別送入音頻/視頻×××

[ ]  關(guān)鍵幀還是非關(guān)鍵幀 -> 追幀優(yōu)化

[ ]  幀的數(shù)據(jù)和大小 -> 取出幀的內(nèi)容

[ ]  時間戳信息 -> 音視頻同步


3. 如何從 ffmpeg 取出這些信息 ?


ffmpeg 提供了一個函數(shù) av_read_frame 來完成解封裝的過程,它會從碼流里面提取每一個音頻、視頻幀,它使用了結(jié)構(gòu)體 AVPacket 來記錄每一幀的信息。


讀取一幀數(shù)據(jù)的代碼示例如下(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);


每循環(huán)一次,就從碼流中解封裝并且提取了一幀數(shù)據(jù),并存放在了 AVPacket 結(jié)構(gòu)體中。


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 如何判斷是否為關(guān)鍵幀


判斷是否為關(guān)鍵幀的方法也比較簡單,示例如下:


if (avpkt.flags & AV_PKT_FLAG_KEY) {
    LOGD("read a key frame");
}


3.3 如何獲取幀的數(shù)據(jù)和大小


幀的數(shù)據(jù)和大小直接定義在 AVPacket 結(jié)構(gòu)體中,對應(yīng)的成員變量如下:


// 壓縮編碼的數(shù)據(jù),一幀音頻/視頻
uint8_t *data;

// 數(shù)據(jù)的大小
int size;


3.4 如何獲取幀的時間戳信息


每一個幀都可能攜帶有 2 個時間戳信息,一個是解碼時間戳 dts,一個是顯示時間戳 pts,解碼時間戳告訴我們什么時候需要解碼,顯示時間戳告訴我們什么時候需要顯示,只有在碼流中存在 B 幀的情況下,這兩個時間戳才會不一致。


這些時間戳信息不一定存在于碼流中(取決于生產(chǎn)端),如果不存在,則其值為: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 信息調(diào)試一下某一段音視頻流的時間戳是否正確,比如是否出現(xiàn)了時間戳的回滾和錯亂,則必然會導(dǎo)致播放端出現(xiàn)音視頻不同步或者顯示異常等情況。


4. 小結(jié)


關(guān)于如何使用 FFMPEG 如何讀取每一幀的信息就介紹到這兒了,文章中有不清楚的地方歡迎留言或者來信 lujun.hust@gmail.com 交流,關(guān)注我的新浪微博 @盧_俊 或者 微信公眾號 @Jhuster 獲取最新的文章和資訊。

FFMPEG Tips (3) 如何讀取每一幀的信息

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI