java - 使用 JLayer 解码流式 mp3 数据时出现问题

标签 java audio mp3 signal-processing jlayer

我正在尝试使用 JLayer java lib 来解码 mp3 数据流。我有一个回调,当下一个 mp3 数据 block 从网络到达时,该回调会被异步调用。到达的每个 block 包含 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 block 的 pcm 缓冲区,您可以看到前 2 - 3 个缓冲区填充了零。有没有人对 JLayer 有任何经验,可以看到我的方法存在明显的问题?

最佳答案

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

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

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

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

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

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

整个解码管道引入了相当多的延迟。由于管道的延迟,并且比特储备借用机制进一步复杂化,正确在 MP3 内部进行查找并非易事。

关于java - 使用 JLayer 解码流式 mp3 数据时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16738918/

相关文章:

java - hibernate 还是 'Do-It-Yourself"?

java - JPQL:OR 不会按预期工作

audio - WinRT - 将音频渲染到不同的设备

c++ - libswresample : swr_convert() not producing enough samples

javascript - 使用 HTML-CSS-JS 在触摸事件上播放 MP3

java - 使用 java.sound.sampled.Clip 播放声音文件的正确方法是什么

JavaFX 绑定(bind)和属性更改

iphone - iPhone中的自定义音频播放器

java - 向后寻求处理无效的 MP3 header ?

c++ - 音频文件元数据的最佳库?