java - MediaCodec和MediaExtractor的理解
问题描述
我想对音频文件进行一些处理而不播放它们,只是数学。我怀疑我是否做得对并且有几个问题。我读了一些例子,但其中大部分是关于视频流的,根本没有处理原始数据。
我准备了一个 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();
}
也欢迎快速代码审查。
解决方案
我完全确定它为什么会这样编码,但我认为小的差异在预期的公差范围内。请记住,mp3 是一种有损编解码器,只要声音表示足够接近,解码器的输出值就不会与输入相同。但这并不能说明为什么这两个渠道最终会略有不同。
是的,解码帧的各个顺序将是相同的。确切的值不匹配,但它的声音应该相似。
在 MediaExtractor 中,样本是一个编码数据包,您应该将其提供给解码器。对于 mp3,这通常是 1152 个样本(每个通道)。
推荐阅读
- python - TypeError:“int”对象在训练神经网络时不可迭代
- javascript - 如何确保属性的值是预定义对象中的值之一?
- javascript - 如何从该对象内部的函数访问数组对象的属性
- c - Clion/Compiler 无法运行特定项目
- java - 在消费期间记录 InputStream
- r - 使用动态网络可视化更改节点颜色
- flutter - 如何在颤振上使用 ARCore 测量对象
- reactjs - Kenburns 影响未定义的错误 - React Native
- karate - 空手道如何使用 if 条件附加参数
- opencv - 使用 pytesseracct 将图像转换为文本