JLayer всегда выдает исключение ArrayIndexOutOfBounds

Я использую JLayer для декодирования MP3 и сохраняю его как данные PCM. Однако независимо от того, какой файл MP3 я использую, я всегда получаю ArrayIndexOutOfBoudnsException с индексом 443. Я слышал, что это происходит только с определенными MP3, но это происходит в каждом кадре с каждым MP3, который я играю. Вот мой код:

private short[] getPCM(Header frameHeader, Bitstream bs) {
    short[] pcm = null;
    try {
        Decoder d = new Decoder();
        SampleBuffer buffer = (SampleBuffer) d.decodeFrame(frameHeader, bs);
        pcm = buffer.getBuffer();
    } catch (ArrayIndexOutOfBoundsException | DecoderException e) {
        System.err.println("JLayer, stap it");
    }
    return pcm;
}

И метод вызывается с этим кодом:

while ((frameHeader = bs.readFrame()) != null) {
    short[] pcm = getPCM(frameHeader, bs);

    for(short i : pcm){
        try {
            os.write(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }       
}

Трассировки стека:

java.lang.ArrayIndexOutOfBoundsException: 433
at javazoom.jl.decoder.Bitstream.get_bits(Unknown Source)
at javazoom.jl.decoder.LayerIIIDecoder.decode(Unknown Source)
at javazoom.jl.decoder.LayerIIIDecoder.decodeFrame(Unknown Source)
at javazoom.jl.decoder.Decoder.decodeFrame(Unknown Source)
at com.dentonposs.Canvas.getPCM(Canvas.java:70)
at com.dentonposs.Canvas.paintComponent(Canvas.java:48)
at javax.swing.JComponent.paint(Unknown Source)
at javax.swing.JComponent.paintChildren(Unknown Source)
at javax.swing.JComponent.paint(Unknown Source)
at javax.swing.JLayeredPane.paint(Unknown Source)
at javax.swing.JComponent.paintChildren(Unknown Source)
at javax.swing.JComponent.paintToOffscreen(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
at javax.swing.RepaintManager.paint(Unknown Source)
at javax.swing.JComponent.paint(Unknown Source)
at java.awt.GraphicsCallback$PaintCallback.run(Unknown Source)
at sun.awt.SunGraphicsCallback.runOneComponent(Unknown Source)
at sun.awt.SunGraphicsCallback.runComponents(Unknown Source)
at java.awt.Container.paint(Unknown Source)
at java.awt.Window.paint(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.access$700(Unknown Source)
at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

person gameboyprinter    schedule 10.11.2013    source источник
comment
Опубликуйте трассировку стека и укажите, из какой строки выброшено исключение.   -  person chrylis -cautiouslyoptimistic-    schedule 10.11.2013
comment
Ваш код в блоке catch не будет для вас очень информативным или полезным. Лучше было бы иметь e.printStackTrace(); внутри блока catch. Редактировать: @chrylis: с его текущим кодом, как написано, он никогда не увидит трассировку стека. Надеюсь, он сможет это сделать и опубликует, если внесет изменения, которые я рекомендовал.   -  person Hovercraft Full Of Eels    schedule 10.11.2013
comment
У меня там была трассировка стека, я не знаю, почему я удалил ее, когда копировал код.   -  person gameboyprinter    schedule 10.11.2013
comment
@HovercraftFullOfEels Я добавил его, а затем вставил вывод трассировки стека. Ничего не изменилось.   -  person gameboyprinter    schedule 11.11.2013


Ответы (2)


Вам нужно вызывать Bitstream.closeFrame() между каждым вызовом readFrame() - так что

while ((frameHeader = bs.readFrame()) != null) {
    short[] pcm = getPCM(frameHeader, bs);

    bs.closeFrame();

    .. more ..
person greg-449    schedule 11.11.2013

Судя по всему, вы пытаетесь заставить декодер декодировать один и тот же кадр снова и снова. Это не сработает, BitStream имеет внутреннее состояние (указатель на следующий бит для чтения), которое становится недействительным после декодирования кадра. Исключение указывает, что он пытается прочитать емкость буферов.

Вы должны вызывать decodeFrame() только один раз для каждого кадра.

Изменить: ваша трассировка стека не показывает, где вы на самом деле читаете данные. Поскольку исключение находится в методе рисования компонента, я предполагаю, что вы прочитали кадр в другом потоке или где-либо еще.

Кроме того, глядя на ваш метод getPCM(): этот код никогда не будет работать. Вы не можете создать новый экземпляр декодера для каждого кадра — это просто не будет работать, потому что декодер имеет много внутреннего состояния, которое необходимо и зависит от предыдущего кадра(ов). Вы должны создать декодер только один раз и повторно использовать его для всех кадров.

Взгляните на источник javazoom.jl.player.Player - он показывает правильный цикл воспроизведения в своем методе play(int) (всего несколько строк, но вы должны придерживаться показанного там порядка вызовов, иначе вы будете разрушить правильное состояние в декодере и объектах, удерживаемых декодером).

person Durandal    schedule 10.11.2013
comment
Но я вызываю decodeFrame только один раз. Разве (frameHeader = bs.readFrame()) не следует читать следующий кадр? - person gameboyprinter; 11.11.2013
comment
@ blendmaster345 Трудно сказать, что и в каком порядке вы вызываете, поскольку вы четко не показываете, где в стеке вызовов выполняются фрагменты кода (вы, вероятно, найдете это очевидным, но мы можем видеть только то, что вы нам показываете здесь). См. мое редактирование для некоторых дополнительных указателей. Придерживайтесь порядка вызовов, показанного в примере кода, поставляемого с jlayer, и ваши проблемы должны исчезнуть. - person Durandal; 11.11.2013
comment
Это не решило проблему, но помогло с другой, которая возникла, так что спасибо. - person gameboyprinter; 12.11.2013