我试图使用JLayer java lib来解码mp3数据流。我有一个回调,当下一个mp3数据块从网络到达时,它被异步调用。每个到达的块包含4个byte[]
格式的mp3帧。此数据传递到short[] decode(byte[] mp3_data)
进行解码,输出为short[]
pcm音频缓冲区。使用concatArray()
方法将缓冲区附加到while循环内部,直到所有mp3帧都用完为止。我遇到的问题是前2帧或有时3帧数据返回一个填充零的pcm缓冲区,其中最后2或1返回有效的16位音频值。
public short[] decode(byte[] mp3_data) throws IOException {
SampleBuffer output = null;
InputStream inputStream = new ByteArrayInputStream(mp3_data);
short[] pcmOut = {};
try {
Bitstream bitstream = new Bitstream(inputStream);
Decoder decoder = new Decoder();
boolean done = false;
int i = 0;
while (! done) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
done = true;
} else {
output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
short[] next = output.getBuffer();
pcmOut = concatArrays(pcmOut, next);
}
bitstream.closeFrame();
i++;
}
return pcmOut;
} catch (BitstreamException e) {
throw new IOException("Bitstream error: " + e);
} catch (DecoderException e) {
Log.w(LOG_TAG, "Decoder error", e);
}
return null;
}
short[] concatArrays(short[] A, short[] B) {
int aLen = A.length;
int bLen = B.length;
short[] C= new short[aLen+bLen];
System.arraycopy(A, 0, C, 0, aLen);
System.arraycopy(B, 0, C, aLen, bLen);
return C;
}
LOG OUTPUT
Frame 0 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 1 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 2 len: 2304, First 10 samples: [-4128, -4158, -4252, -3934, -4452, -3775, -4799, -3762, -5430, -4092]
Frame 3 len: 2304, First 10 samples: [-18050, -19711, -18184, -19753, -18143, -19595, -17046, -18362, -14773, -15933]
Frame 0 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 1 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 2 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 3 len: 2304, First 10 samples: [2455, 2345, 5253, 5129, 6716, 6442, 7475, 6866, 8461, 7444]
Frame 0 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 1 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 2 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 3 len: 2304, First 10 samples: [951, 1322, 1497, 1929, 1615, 2198, 1320, 2134, 1040, 2114]
Frame 0 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 1 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 2 len: 2304, First 10 samples: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Frame 3 len: 2304, First 10 samples: [-10213, -9578, -11691, -10867, -13686, -12770, -14837, -13874, -15619, -14574]
正如您所看到的,为每个4帧mp3块打印出pcm缓冲区,您可以看到前2-3个缓冲区都填充了零。有没有人与JLayer有任何关系,他们可以看到我的方法明显的问题?
答案 0 :(得分:3)
是什么问题?首先,很多mp3会显然以沉默开始。其次,由于PCM合成的性质,填充多相合成滤波器组需要一段时间,因此第一个样本非常可能是零,合成滤波器开始时其全部为零。银行。
查看整个帧以确定其是否为静音,而不是10个样本。
编辑:你显然不熟悉MP3内部的工作原理,所以我将详细介绍一下基础知识。MP3帧包含标题字(讲述比特率,采样率和立体声类型)以及一些控制信息。该框架的大部分仅包含打包数据。与谈到MP3时所暗示的内容相反,打包数据并不完全属于那个单帧。帧可以从其前任“借用”打包数据空间,并且它还可以携带属于下一帧的数据。 CBR(恒定比特率)只是告诉所有帧具有相等的大小,但是由于从先前帧的借用,特别复杂的帧可以通过从前一帧中借用空间来分配更多比特(该决定)由编码器在创建流时创建)。 VBR只是增加了额外的可能性来改变帧大小,技术上CBR流已经能够为每帧分配可变数量的比特,只是在比VBR更严格的限制内。
为了将解码与不均匀分配的帧数据解耦,解码器将每帧接收到的打包数据馈送到称为“比特储备”的FIFO缓冲区,该缓冲区基本上要记住从先前帧借来的所有数据都要记住,直到它为止。请求解码管道。
然后对来自比特储备的数据进行霍夫曼解码,通过一些复杂的数学处理来产生时频采样。为了将它们转换成PCM,将它们馈入合成滤波器。合成滤波器记住固定时间段内的每个时间频率样本(技术上步骤,挂钟时间随采样率而变化)到其“银行”中的过去(每次 - 频率样本影响多个PCM样本),其中最老的样本被最新推出。
整个解码流程引入了相当多的延迟。正确地在MP3 中寻找 是非常重要的,因为管道延迟并且由于bitreserve借用机制而进一步复杂化。
答案 1 :(得分:0)
我使用JLayer进行了一些mp3解码,我只是面对同样的问题:对于每一帧我都会得到很多零,然后是几个非零的pcm样本。
我想decodeFrame()方法应该返回解码的真实pcm样本,因为它 已经处理,重新量化,霍夫曼解码,多相重新合成为我编码。
这样,总的pcm样本应该更多,所以我决定剥离所有pcm零样本,并以wav格式写出样本。我知道它有点'怪异'但是......现在听起来真的应该!! [/ p>
我解码的歌曲是CBR格式,单声道只是为了让这些东西更简单。
我认为也许所有这些零都与bit-reservoir有关,所以如果使用的歌曲和psycoacustic模型并不真正需要它们,那么它们将被设置为零。然后我做了其他测试。
我所争论的是,如果每个第3层帧在2304 pcm样本中被解码,则在单声道歌曲中可能只有前半部分非零,而秒半部分全部为零。 但是,如果我使用立体声MP3 ...几乎所有的样本都是非零的,除了很明显在歌曲的开头。
所以看起来这个'问题'只出现在单声道编码的mp3中。 使用stero mp3我可以获得所有正确的pcm样本,在单声道mp3中我只需要获得每帧解码的pcm样本的前半部分。
但这不是浪费音频压缩算法的空间吗?也许我还在失去一些东西......
希望这有点帮助...
修改强>
我可以看到,频道在帧中交错:对于2声道mp3,解码的2304 pcm样本是:
L [0],R [0],L [1],R [1],L [2],R [2],.......,L [1152],R [1152]
现在生成的ouptut wav文件听起来比以前好多了。