FFmpeg開發筆記(十四)FFmpeg音訊重取樣的快取

aqi00發表於2024-04-14
FFmpeg在很多地方都運用了快取機制,比如《FFmpeg開發實戰:從零基礎到短影片上線》一書的“3.3.2 對影片流重新編碼”介紹了編解碼的資料快取,不單是影片編碼過程和影片解碼過程有快取,甚至連音訊重取樣都用到了快取。

也就是說,重取樣函式swr_convert一次只會輸出指定長度的音訊資料,超出這個長度的資料被留在重取樣的快取當中。那麼在對一個音訊檔案轉換格式之時,有可能所有音訊幀都遍歷完了,重取樣快取裡面還儲存著剩餘未取走的音訊資料。此時要像對待影片編碼快取那樣,想辦法把剩下的音訊資料衝出來。
具體到程式碼實現上,在呼叫swr_convert函式之時,倒數第二個引數填NULL,表示輸入的資料內容為空;倒數第一個引數填0,表示輸入的資料大小為0。這便告訴取樣器,已經沒有要轉換的音訊了,請把快取中剩餘的資料衝出來吧。那麼swr_convert函式的返回值就是本次沖走的輸出資料大小,當返回值為0時,表示重取樣快取已經衝光了,再也沒有剩餘的資料了,此時才能結束音訊的格式轉換操作。
當然,對於常見的mp3和aac格式,它們每幀的長度是固定的,正常情況呼叫一次swr_convert函式即可輸出完整的音訊資料,無需另外處理重取樣快取。只有ogg、amr、wma等格式的每幀音訊長度不固定,才需要額外處理音訊的重取樣快取,於是對《FFmpeg開發實戰:從零基礎到短影片上線》一書第五章的重取樣程式碼改動如下。
開啟chapter05/swrmp3.c,把下面這行

swr_frame->nb_samples = audio_decode_ctx->frame_size;

改為下面幾行(因為ogg、amr和wma的frame_size為0,所以需要另外賦值):

swr_frame->nb_samples = audio_decode_ctx->frame_size;
if (swr_frame->nb_samples <= 0) {
    swr_frame->nb_samples = 512;
}

另外在輪詢資料包的迴圈結束之後,補充下面的重取樣快取沖刷程式碼,這樣新生成的音訊檔案才是完整的:

while (1) { // 沖走重取樣的快取(相容對ogg、amr等格式的重取樣)
    // 重取樣。也就是把輸入的音訊資料根據指定的取樣規格轉換為新的音訊資料輸出
    ret = swr_convert(swr_ctx, // 音訊取樣器的例項
                    // 輸出的資料內容和資料大小
                    swr_frame->data, swr_frame->nb_samples,
                    // 輸入內容填NULL、輸入大小填0表示沖走快取
                    NULL, 0);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "swr_convert frame occur error %d.\n", ret);
        return -1;
    } else if (ret == 0) { // 到末尾了
        break;
    }
    save_mp3_file(fp_out, swr_frame); // 把音訊幀儲存到MP3檔案
}

接著執行下面的編譯命令。

gcc swrmp3.c -o swrmp3 -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

編譯完成後執行以下命令啟動測試程式,期望把ring.ogg重取樣後儲存為MP3檔案。

./swrmp3 ../ring.ogg

程式執行完畢,發現控制檯輸出以下的日誌資訊,說明完成了對ogg檔案重取樣mp3音訊的操作。

Success open input_file ring.ogg.
audio_decode_ctx frame_size=0, sample_fmt=8, sample_rate=11025, nb_channels=1
audio_encode_ctx frame_size=1152, sample_fmt=6, sample_rate=44100, nb_channels=1
target audio file is output_swrmp3.mp3
Success resample audio frame as mp3 file.

然後開啟影音播放器可以正常播放output_swrmp3.mp3,表示上述程式碼正確實現了將ogg音訊資料重取樣再轉存MP3檔案的功能。

相關文章