Pow*_*der 5 java audio android android-mediacodec
我想对音频文件进行一些处理而不播放它们,只是数学。我怀疑我的做法是否正确,并且有几个问题。我读了一些示例,但大多数都是关于视频流的,根本没有处理原始数据。
我准备了一个 mp3 文件,它有 2 个相同的通道,即它是立体声的,但左右声道相同。解码后,我期望获得具有相同数字对的缓冲区,因为 PCM-16 交替存储通道样本,例如 { L R L R L R...},对吗?例如:
{ 105 105 601 601 -243 -243 -484 -484...}。
但我得到了一对接近但不相等的数字:
{ -308 -264 -1628 -1667 -2568 -2550 -4396 -4389}
mp3 算法是否对相同的值进行不同的编码?为什么?
我想处理 1024 个样本包中的数据。如果没有足够的样本用于另一个包,我想保存其余的直到下一批原始数据(参见mExcess代码)。是否能保证订单得以保留?
我曾经将“样本”理解为音频数据的每个值。这里我看到了MediaExtractor::readSampleData和MediaExtractor::advance方法。第一个返回约 2000 个值,在第二个的描述中表示“前进到下一个样本”。这只是命名的重叠吗?我看到了几个示例,其中这些方法在循环中成对调用。我的用法正确吗?
这是我的代码:
public static void foo(String filepath) throws IOException {
final int SAMPLES_PER_CHUNK = 1024;
MediaExtractor mediaExtractor = new MediaExtractor();
mediaExtractor.setDataSource(filepath);
MediaFormat mediaFormat = mediaExtractor.getTrackFormat(0);
mediaExtractor.release();
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
mediaFormat.setString(MediaFormat.KEY_FRAME_RATE, null);
String codecName = mediaCodecList.findDecoderForFormat(mediaFormat);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 0); // MediaCodec crashes with JNI
// error if FRAME_RATE is null
MediaCodec mediaCodec = MediaCodec.createByCodecName(codecName);
mediaCodec.setCallback(new MediaCodec.Callback() {
private MediaExtractor mExtractor;
private short[] mExcess;
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
if (mExtractor == null) {
mExtractor = new MediaExtractor();
try {
mExtractor.setDataSource(filepath);
mExtractor.selectTrack(0);
} catch (IOException e) {
e.printStackTrace();
}
mExcess = new short[0];
}
ByteBuffer in = codec.getInputBuffer(index);
in.clear();
int sampleSize = mExtractor.readSampleData(in, 0);
if (sampleSize > 0) {
boolean isOver = !mExtractor.advance();
codec.queueInputBuffer(
index,
0,
sampleSize,
mExtractor.getSampleTime(),
isOver ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
} else {
int helloAmaBreakpoint = 1;
}
}
@Override
public void onOutputBufferAvailable(
MediaCodec codec,
int index,
MediaCodec.BufferInfo info) {
ByteBuffer tmp = codec.getOutputBuffer(index);
if (tmp.limit() == 0) return;
ShortBuffer out = tmp.order(ByteOrder.nativeOrder()).asShortBuffer();
// Prepend the remainder from previous batch to the new data
short[] buf = new short[mExcess.length + out.limit()];
System.arraycopy(mExcess, 0, buf, 0, mExcess.length);
out.get(buf, mExcess.length, out.limit());
final int channelCount
= codec.getOutputFormat().getInteger(MediaFormat.KEY_CHANNEL_COUNT);
for (
int offset = 0;
offset + SAMPLES_PER_CHUNK * channelCount < buf.length;
offset += SAMPLES_PER_CHUNK * channelCount) {
double[] x = new double[SAMPLES_PER_CHUNK]; // left channel
double[] y = new double[SAMPLES_PER_CHUNK]; // right channel
switch (channelCount) {
case 1: // if 1 channel then make 2 identical arrays
for (int i = 0; i < SAMPLES_PER_CHUNK; ++i) {
x[i] = (double) buf[offset + i];
y[i] = (double) buf[offset + i];
}
break;
case 2: // if 2 channels then read values alternately
for (int i = 0; i < SAMPLES_PER_CHUNK; ++i) {
x[i] = (double) buf[offset + i * 2];
y[i] = (double) buf[offset + i * 2 + 1];
}
break;
default:
throw new IllegalStateException("No algorithm for " + channelCount + " channels");
}
/// ... some processing ... ///
}
// Save the rest until next batch of raw data
int samplesLeft = buf.length % (SAMPLES_PER_CHUNK * channelCount);
mExcess = new short[samplesLeft];
System.arraycopy(
buf,
buf.length - samplesLeft,
mExcess,
0,
samplesLeft);
codec.releaseOutputBuffer(index, false);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) > 0) {
codec.stop();
codec.release();
mExtractor.release();
}
}
@Override
public void onError(MediaCodec codec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
}
});
mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT);
mediaCodec.configure(mediaFormat, null, null, 0);
mediaCodec.start();
}
Run Code Online (Sandbox Code Playgroud)
也欢迎快速代码审查。
我完全确定为什么它会以这种方式编码,但我认为小的差异在预期的容差范围内。请记住,mp3 是一种有损编解码器,只要声音表示足够接近,解码器的输出值就不会与输入相同。但这并不能说明为什么这两个渠道最终会出现微妙的不同。
是的,解码帧的各个顺序将是相同的。确切的值不会匹配,但声音应该相似。
在 MediaExtractor 中,样本是一个编码的数据包,您应该将其提供给解码器。对于 mp3,这通常是 1152 个样本(每个通道)。