繁体   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