我試圖從解碼的mp4緩衝區中獲取PCM樣本作進一步處理。我第一次提取記錄手機的攝像頭應用程序的視頻文件中的音頻軌道,並且我確信,當我得到了'音頻/ MP4的MIME鍵被選中的音軌:如何從MediaCodec解碼器的輸出中提取PCM樣本
MediaExtractor extractor = new MediaExtractor();
try {
extractor.setDataSource(fileUri.getPath());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int numTracks = extractor.getTrackCount();
for(int i =0; i<numTracks; ++i) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
//Log.d("mime =",mime);
if(mime.startsWith("audio/")) {
extractor.selectTrack(i);
decoder = MediaCodec.createDecoderByType(mime);
decoder.configure(format, null, null, 0);
//getSampleCryptoInfo(MediaCodec.CryptoInfo info)
break;
}
}
if (decoder == null) {
Log.e("DecodeActivity", "Can't find audio info!");
return;
}
decoder.start();
在那之後,我通過軌道迭代,供給的編解碼器編碼的接入單元的流,並且拉動解碼存取單元到一個字節緩衝區(這是代碼我從視頻再生渲染例如張貼在這裏https://github.com/vecio/MediaCodecDemo):
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
BufferInfo info = new BufferInfo();
boolean isEOS = false;
while (true) {
if (!isEOS) {
int inIndex = decoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
// We shouldn't stop the playback at this point, just pass the EOS
// flag to decoder, we will get it again from the
// dequeueOutputBuffer
Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
extractor.advance();
}
}
}
int outIndex = decoder.dequeueOutputBuffer(info, 10000);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
break;
default:
ByteBuffer buffer = outputBuffers[outIndex];
// How to obtain PCM samples from this buffer variable??
decoder.releaseOutputBuffer(outIndex, true);
break;
}
// All decoded frames have been rendered, we can stop playing now
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
break;
}
}
該代碼似乎迄今沒有錯誤的工作,但我目前堅持試圖找出如何獲取從PCM的PCM樣本teBuffer正在取出輸出緩衝區的值。我想我可以假設,因爲我正在處理16位立體聲音頻文件,交錯方案中至少應該有兩個字節......但是我不確定是否與此相鄰,因此要明確檢索PCM樣本來自這個字節流。有誰知道如何從MediaCodec API獲取這些信息?
我已經閱讀了一些使用ffmpeg或openSL的替代方案,但由於我是Android編程的新手,我希望避免使用基於c的API的複雜性,並僅使用由提供的工具構建我的第一個應用程序Android框架(我正在使用KitKat)。任何幫助將不勝感激。
UPDATE:我能夠提取PCM樣本,我假設這麼做的方式以及@ marcone指出的方式。要做到這一點,我增加了緩衝區分配如下這些行:
byte[] b = new byte[info.size-info.offset];
int a = buffer.position();
buffer.get(b);
buffer.position(a);
終於寫出由字節數組到一個文件:
f.write(b,0,info.size-info.offset);
我與現在正在處理的問題是:
解碼後的音頻樣本與iZotope完成的mp4音軌的解碼不完全匹配。波形文件大小有48個樣本不匹配,解碼信號有2112個樣本延遲。我現在的問題是:所有的mp4解碼器都會產生相同的輸出PCM流,還是取決於解碼器的實現?
緩衝區中的數據由交錯採樣組成(除非它是單聲道的,在這種情況下,顯然不會有交織)。對於mp3,輸出樣本幾乎保證爲16位(兼容設備必須假設它們是CTS測試),因此您可以使用例如ByteBuffer.getShort()來讀取它們。 – marcone
謝謝marcone,會試試看,並讓你知道結果。雖然我假設輸出是未壓縮的PCM,但不是mp3 ...,我錯過了什麼嗎? – jimijazz
它確實有效,但現在我正面臨着一些額外樣本和延遲的問題,因爲我已經包含在上面的編輯中。 – jimijazz