我正在尝试从解码的 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 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 框架提供的工具构建我的第一个应用程序(我我正在使用奇巧)。任何帮助将不胜感激。
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 流,还是取决于解码器的实现?