2014-03-26 130 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

回答

相关问题