JLayer всегда выдает исключение ArrayIndexOutOfBounds
Я использую JLayer для декодирования MP3 и сохранения его в формате PCM Data. Однако, независимо от того, какой файл 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)
2 ответа
Вам нужно позвонить Bitstream.closeFrame()
между каждым звонком readFrame()
- так
while ((frameHeader = bs.readFrame()) != null) {
short[] pcm = getPCM(frameHeader, bs);
bs.closeFrame();
.. more ..
Судя по всему, вы пытаетесь, чтобы декодер снова и снова декодировал один и тот же кадр. Это не будет работать, BitStream имеет внутреннее состояние (указатель на следующий бит для чтения), которое становится недействительным после декодирования кадра. Исключение указывает, что он пытается прочитать данные о емкости буферов.
Вы должны вызывать decodeFrame() только один раз для каждого кадра.
Редактировать: ваша трассировка стека не показывает, где вы на самом деле читаете данные. Поскольку исключение находится в методе рисования компонента, я предполагаю, что вы читаете фрейм в другом потоке или в другом месте.
Также обратите внимание на метод getPCM(): этот код никогда не будет работать. Вы не можете создать новый экземпляр декодера для каждого кадра - это просто не будет работать, потому что декодер имеет много внутреннего состояния, которое необходимо и зависит от предыдущего кадра (ов). Вы должны создать декодер только один раз и повторно использовать его для всех кадров.
Посмотрите на источник javazoom.jl.player.Player - он показывает правильный цикл воспроизведения в своем методе play (int) (его длина всего несколько строк, но вы должны придерживаться показанного там порядка вызовов, в противном случае вы будете разрушить надлежащее состояние с помощью декодера и объектов, удерживаемых декодером).