我试图从解码的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