2013年9月22日 星期日

FFMPEG -- WMA 轉換為 AAC 並存檔為 MP4

台灣地區的網路線上廣播,一般都是採用微軟的方式,其方法主要有兩個重點
  • 透過 MMS(Microsoft Media Server) 協定來傳送音樂
  • 使用 WMA(Windows Media Audio) 的格式來封裝音樂



若想要使用 MMS+FFmpeg 播放網路廣播,可參考這裡

若想要錄音,便需要將 WMA 的音樂進行儲存,此處使用 ffmpeg 進行實作,將音樂先轉成PCM進行播放,接著將其轉為AAC後,存入MP4檔案,茲將實作時遇到的問題整理如下:

1. 使用 avcodec_decode_audio4() 將 WMA 解碼,並存至 AVFrame。
此處用法如下,並無問題。 
avcodec_decode_audio4(pAudioCodecCtx, pAVFrame1, &gotFrame, &AudioPacket);

2. 使用 avcodec_encode_audio2() 將 AVFrame 進行編碼。
int avcodec_encode_audio2 (  
 AVCodecContext * avctx,
AVPacket             * avpkt,
const AVFrame    * frame,
int * got_packet_ptr
)  
實作過程中,此處發生問題,出現的錯誤訊息如下:
more samples than frame size (avcodec_encode_audio2) 
其原因是因為 frame->nb_samples > avctx->frame_size ,摘錄 FFmpeg 對於 avcodec_encode_audio2() 的說明如下
AVFrame containing the raw audio data to be encoded. May be NULL when flushing an encoder that has the CODEC_CAP_DELAY capability set. If CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame can have any number of samples. If it is not set, frame->nb_samples must be equal to avctx->frame_size for all frames except the last. The final frame may be smaller than avctx->frame_size.
參考程式輸出的log,此時 nb_samples=14336, framesize=1024,的確符合上述說明的情況。
vRet=-22, Err=Invalid argument
data_size=57344,framesize=1024
linesize=57344 channels=2
nb_samples=14336, sample_rate=44100
[aac @ 0xb214600] more samples than frame size (avcodec_encode_audio2)

解決方式
其原則就是要讓每次呼叫 avcodec_encode_audio2(),其帶入的參數能夠滿足 frame->nb_samples <= avctx->frame_size 的條件。 因此我們自行將一個大的 frame 拆開成多個 frame,讓每個 frame->nb_samples 接小於 avctx->frame_size 。

程式碼可參考 AudioPlayer.m 內的函數 putAVPacketsIntoAudioQueue(),這邊僅列出實作時的幾個重點
1. 不要設定 AVCodecContext extradata,若設定 extradata,VLC在解碼時會出現下列錯誤訊息,實際原因可能是 extradata 與 rate, channel 要有正確的對應關係,這部分我並不清楚,因此直接略過,細節部分可參考 faad.c,找尋關鍵字 "Failed to initialize faad using extra data"
main debug: looking for decoder module: 32 candidates
faad error: Failed to initialize faad using extra data
avcodec debug: libavcodec initialized (interface 0x361e00)
avcodec error: cannot open codec (MPEG AAC Audio)
2. 當拆開 AVFrame 時,只需要改變 nb_samples 與 data 欄位 
3. 可以直接使用 avcodec_fill_audio_frame() 來進行複製部分 AVFrame 的動作。

Reference: