簡體   English   中英

如何從MediaCodec解碼器的輸出中提取PCM樣本

[英]How to extract PCM samples from MediaCodec decoder's output

我正在嘗試從解碼的mp4緩沖區中獲取PCM樣本以進行進一步處理。 我首先要從用手機的相機應用程序錄制的視頻文件中提取音軌,並且確保在獲得“ audio / mp4” mime鍵時已選擇音軌:

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();

之后,我遍歷該軌道,向編解碼器提供編碼后的訪問單元流,然后將解碼后的訪問單元拉入ByteBuffer(這是我從發布在此處的視頻渲染示例中回收的代碼), 網址為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;
    }
}

到目前為止,該代碼似乎沒有任何錯誤,但是我目前仍在嘗試找出如何從采用輸出緩沖區值的ByteBuffer中獲取PCM樣本。 我猜想我可以假設,因為我正在使用16位立體聲音頻文件,所以在交錯方案中應該至少有兩個字節...但是我不太確定是否要這樣做,因此可以明確地檢索PCM樣本。從此字節流。 有人知道如何從MediaCodec API中獲得這些信息嗎?

我已經閱讀了使用ffmpeg或openSL的幾種替代方法,但是由於我是Android編程的新手,所以我希望避免使用基於c的API的麻煩,並且僅使用Android框架提供的工具來構建我的第一個應用程序(我正在使用KitKat)。 任何幫助將不勝感激。

更新 :我能夠提取PCM樣本,我假設的提取方式以及way @ 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流,還是取決於解碼器的實現?

我發現延遲是由AAC編碼啟動和剩余時間引起的,如下所示:

https://developer.apple.com/library/mac/documentation/quicktime/qtff/QTFFAppenG/QTFFAppenG.html

在我的情況下,啟動時間始終是2112個樣本,其余時間自然取決於音頻大小而變化。

我知道問題已經解決了。 但是MediaCodec在當前代碼中同步使用,目前已不推薦使用。 我從這個問題中學到了東西,並且通過MediaCodec的異步使用做了同樣的事情。 只需發布github鏈接,以便以后對他人有所幫助。

Github異步實現: 鏈接

僅供參考 :暫時使用的音頻播放器只是從其他線程復制粘貼。 簡而言之。 我會在有時間的時候更新它。 代碼也位於Kotlin中(仍然很容易理解)

請查看Async鏈接以獲取官方MediaCodec文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM