2014-03-26 35 views
8

我試圖從解碼的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流,還是取決於解碼器的實現?

+0

緩衝區中的數據由交錯採樣組成(除非它是單聲道的,在這種情況下,顯然不會有交織)。對於mp3,輸出樣本幾乎保證爲16位(兼容設備必須假設它們是CTS測試),因此您可以使用例如ByteBuffer.getShort()來讀取它們。 – marcone

+0

謝謝marcone,會試試看,並讓你知道結果。雖然我假設輸出是未壓縮的PCM,但不是mp3 ...,我錯過了什麼嗎? – jimijazz

+0

它確實有效,但現在我正面臨着一些額外樣本和延遲的問題,因爲我已經包含在上面的編輯中。 – jimijazz

回答

相關問題