2

我正在尝试使用 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;
    }

日志输出

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 有任何可以看出我的方法存在明显问题的经验?

4

2 回答 2

3

有什么问题?首先,许多 mp3 显然会以静音开头。其次,由于 PCM 合成的性质,填充多相合成滤波器组需要一段时间,因此第一个样本可能是零,合成滤波器开始时在其 16 个组中全为零。

查看整个帧以确定其是否静音,而不是 10 个样本。

编辑:您显然不熟悉 MP3 内部的工作原理,所以我将详细介绍一下基础知识。

MP3 帧包含标题字(说明比特率、采样率和立体声类型)和一些控制信息。帧的大部分仅由打包数据组成。与谈到 MP3 时所暗示的相反,打包的数据并不完全属于那个单一的帧。帧可以从其前身“借用”打包数据空间,并且它还可以携带属于后续帧的数据。CBR(恒定比特率)只是告诉所有帧的大小相同,但是由于从之前的帧中借用,特别复杂的帧可以通过从前面的帧中借用空间来分配更多的位(这个决定是由编码器在创建流时做出的)。VBR 只是增加了改变帧大小的额外可能性,从技术上讲,CBR 流已经能够为每帧分配可变数量的比特,只是在比 VBR 更严格的限制范围内。

为了将解码与分配不均的帧数据分离,解码器将它接收到的每帧打包数据馈送到称为“位保留”的 FIFO 缓冲区,该缓冲区基本上负责记住从先前帧借来的所有数据,直到它被请求解码管道。

然后来自比特储备的数据被霍夫曼解码,通过一些复杂的数学处理以产生时频样本。为了将它们转换为 PCM,它们被送入合成滤波器。合成滤波器在其“银行”中记住每个时频样本一段固定的时间(从技术上讲挂钟时间随采样率而变化)(每个时频样本影响多个 PCM 样本) ,最旧的被最新的推出。

整个解码管道引入了相当多的延迟。由于流水线的延迟,并且由于 bitreserve 借用机制而进一步复杂化,因此在MP3中正确查找并非易事。

于 2013-05-24T15:57:10.760 回答
0

我一直在使用 JLayer 进行 mp3 解码,但我正面临同样的问题:对于每一帧,我得到很多零,然后是几个非零 pcm 样本。

我想 decodeFrame() 方法应该返回解码的真实 pcm 样本,因为它已经为我处理、重新量化、霍夫曼解码、多相重新合成了编码。

这样,总 pcm 样本比它们应该的要多,所以我决定剥离所有 pcm 零样本,并以 wav 格式写出样本。我知道这有点“奇怪”,但是..现在听起来真的应该!

我解码的歌曲是 CBR 格式,单声道只是为了让内容更简单。

我想也许所有这些零都与比特水库有关,所以如果歌曲和使用的心理声学模型并不真正需要它们,它们就会被设置为零。然后我做了其他测试。

我的论点是,如果每个第 3 层帧都以 2304 pcm 样本解码,那么在一首单声道歌曲中,可能只有前半部分非零,而后半部分全为零。但是如果我使用立体声 mp3……几乎所有的样本都是非零的,除非很明显在歌曲的开头。

所以似乎这个“问题”只出现在单声道编码的 mp3 中。使用立体声 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 文件生成的声音现在比以前好得多。

于 2013-06-05T10:16:37.370 回答