最近在調試網上下載的android ffmpeg播放器的源代碼。因爲編譯ffmpeg過程中,覺得很麻煩,因此就沒有繼續編譯SDL庫了。打算在Java層使用Bitmap顯示視頻幀,在C層使用OpenSLES播放音頻幀。
將網上的相關源代碼整合到一塊後,發現音視頻解碼,都沒有錯誤。並且視頻可以播放出來,但是聲音聲調不對,主要還有雜音。試了網上的一些方法也不行。最後自己嘗試性地改了一個變量。雜音就消失了,但是聲調還不對。
網上的解碼音頻數據包,然後將解碼後的原始數據push到音頻緩衝區的源代碼中有以下幾行代碼
//音頻數據解碼
len = avcodec_decode_audio4(aCodecCtx, frame, &got_frame, &packet);
//得到解碼音頻數據的大小
data_size = av_samples_get_buffer_size( NULL, aCodecCtx->channels,
frame->nb_samples,aCodecCtx->sample_fmt, 1);
//將解碼的音頻數據push到音頻緩衝區
memcpy(stream, (uint8_t*)(frame->data[0]), data_size]);
我將 memcpy(stream, (uint8_t*)(frame->data[0]), data_size]);
改爲 memcpy(stream, (uint8_t*)(frame->data[0]), frame->linesize[0]); 後雜音就消失了。但是聲音明顯比正常的要快。
這個時候,感覺再去網上找方法也沒什麼用了。必須老老實實地理解AVFrame結構體中,各個字段的具體意思和PCM的相關知識點。
後來知道了雜音和聲調不對的原因了。
雜音是因爲avcodec_decode_audio4(aCodecCtx, frame, &got_frame, &packet)解碼後的兩個聲道的音頻數據分別存放在frame->data[0]和frame->data[1]中,而 frame->linesize[0]和 frame->linesize[1] 分別是前兩者的大小。(我用來調試的視頻文件,音頻部分是雙聲道的)。而data_size表示的是兩個聲道的音頻數據的總和。 因此memcpy(stream, (uint8_t*)(frame->data[0]), data_size])將一大段錯誤的數據push到緩衝區了。
聲音明顯比正常的要快的原因是,memcpy(stream, (uint8_t*)(frame->data[0]), frame->linesize[0])只是push了一個聲道的音頻數據,而創建OpenSLES的播放器是卻設置成了雙 聲道。format_pcm.numChannels = aCodecCtx->channels(實際上爲2)。兩個通道播放一個通道的數據,自然變快了。爲儘快播放出正常的聲音,將播放器聲道數設爲1,此時聽到了正常的音樂。
當然最正確的方法是正確地將編碼後的音頻數據,轉換爲正確的PCM格式。這樣就可以雙聲道播放了。
小結一下:遇到不是程序本身存在的問題時。還需要從原理入手,學習相關的知識,這樣才能夠真正理解一段代碼的工作流程和原理。