如何在Java中播放Opus编码的音频?

如何在Java中播放Opus编码的音频?,java,audio,ogg,opus,jitsi,Java,Audio,Ogg,Opus,Jitsi,当播放解码后的音频时,我设法产生了各种各样的声音,从咕噜声到尖叫声再到恶魔般的圣歌。其中最接近的声音类似于快进播放,播放时间仅为15秒左右。我尝试了解码和AudioSystem API方法的大量参数组合,但似乎没有任何效果 那么,是什么导致了这种音频失真呢 此文件的Opusinfo显示以下内容: Processing file "test.opus"... New logical stream (#1, serial: 00002c88): type opus Encoded with lib

当播放解码后的音频时,我设法产生了各种各样的声音,从咕噜声到尖叫声再到恶魔般的圣歌。其中最接近的声音类似于快进播放,播放时间仅为15秒左右。我尝试了解码和AudioSystem API方法的大量参数组合,但似乎没有任何效果

那么,是什么导致了这种音频失真呢

此文件的Opusinfo显示以下内容:

Processing file "test.opus"...

New logical stream (#1, serial: 00002c88): type opus
Encoded with libopus 1.1
User comments section follows...
     ENCODER=opusenc from opus-tools 0.1.9
Opus stream 1:
    Pre-skip: 356
    Playback gain: 0 dB
    Channels: 1
    Original sample rate: 44100Hz
    Packet duration:   20.0ms (max),   20.0ms (avg),   20.0ms (min)
    Page duration:   1000.0ms (max),  996.8ms (avg),  200.0ms (min)
    Total data length: 1930655 bytes (overhead: 1.04%)
    Playback length: 4m:09.173s
    Average bitrate: 61.99 kb/s, w/o overhead: 61.34 kb/s
Logical stream 1 ended
该文件使用VLC正确回放

要解码文件,我尝试使用以下库:

  • VorbisJava()-从OGG容器中提取帧

  • LibJitsi()-它有一个Opus的JNI包装器,用于解码Opus帧

下面是SSCCE
package me.justinb.mediapad.audio;
导入org.gagravarr.ogg.OggFile;
导入org.gagravarr.ogg.OggPacket;
导入org.jitsi.impl.neomedia.codec.audio.opus.opus;
导入javax.sound.sampled.*;
导入java.io.*;
导入java.nio.ByteBuffer;
公营职业球员{
私有静态int缓冲区_SIZE=1024*1024;
专用静态int输入\ u比特率=48000;
专用静态int输出\ u比特率=44100;
私人OggFile OggFile;
私人长人口国家;
专用ByteBuffer decodeBuffer=ByteBuffer.allocate(缓冲区大小);
私有AudioFormat AudioFormat=新的AudioFormat(输出比特率,16,1,真,假);
公共静态void main(字符串[]args){
试一试{
OpusAudioPlayer OpusAudioPlayer=新的OpusAudioPlayer(新文件(“test.opus”);
opusAudioPlayer.play();
}捕获(IOE异常){
e、 printStackTrace();
}
}
公共播放器(文件audioFile)引发IOException{
oggFile=新oggFile(新文件输入流(音频文件));
opusState=Opus.decoder\u create(输入比特率,1);
System.out.println(“音频格式:“+audioFormat”);
}
专用字节[]解码(字节[]packetData){
int frameSize=Opus.decoder_get_nb_samples(opusState,packetData,0,packetData.length);
int decodedSamples=Opus.decode(opusState,packetData,0,packetData.length,decodeBuffer.array(),0,frameSize,0);
if(解码样本<0){
System.out.println(“解码错误:+decodedSamples”);
decodeBuffer.clear();
返回null;
}
decodeBuffer.position(decodedSamples*2);//每个样本2字节
decodeBuffer.flip();
byte[]decodedData=新字节[decodeBuffer.remaining()];
decodeBuffer.get(decodedData);
decodeBuffer.flip();
System.out.println(String.format(“编码帧大小:%d字节”,packetData.length));
System.out.println(String.format(“解码帧大小:%d字节”,decodedData.length));
System.out.println(String.format(“解码的%d个样本”,解码的样本));
返回解码数据;
}
公共游戏{
int totalDecodedBytes=0;
试一试{
SourceDataLine speaker=AudioSystem.getSourceDataLine(audioFormat);
OggPacket nextPacket=oggFile.getPacketReader().getNextPacket();
//移动到流的开头
而(!nextPackage.isBeginingofStream()){
nextPacket=oggFile.getPacketReader().getNextPacket();
}
speaker.open();
演讲者。开始();
while(nextPacket!=null){
//解码每个包
字节[]decodedData=decode(nextPacket.getData());
if(decodedData!=null){
//将数据包写入SourceDataLine
speaker.write(decodedata,0,decodedata.length);
totalDecodedBytes+=decodedData.length;
}
nextPacket=oggFile.getPacketReader().getNextPacket();
}
扬声器。排水管();
演讲者。关闭();
System.out.println(String.format(“解码为%d字节”,totalDecodedBytes));
}捕获(例外e){
e、 printStackTrace();
}
}
}

我的问题似乎是由VorbisJava中的一个bug引起的。我现在使用的是J-Ogg,它处理容器解析时没有任何问题。我肯定有人会觉得这很有用

这是演示如何在Java中播放Opus编码音频的最后一段代码:

package me.justinb.mediapad.audio;

import de.jarnbjo.ogg.FileStream;
import de.jarnbjo.ogg.LogicalOggStream;
import org.jitsi.impl.neomedia.codec.audio.opus.Opus;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.Collection;

public class OpusAudioPlayer {
    private static int BUFFER_SIZE = 1024 * 1024;
    private static int INPUT_BITRATE = 48000;
    private static int OUTPUT_BITRATE = 48000;

    private FileStream oggFile;
    private long opusState;

    private ByteBuffer decodeBuffer = ByteBuffer.allocate(BUFFER_SIZE);

    private AudioFormat audioFormat = new AudioFormat(OUTPUT_BITRATE, 16, 1, true, false);

    public static void main(String[] args) {
        try {
            OpusAudioPlayer opusAudioPlayer = new OpusAudioPlayer(new File("test.opus"));
            opusAudioPlayer.play();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public OpusAudioPlayer(File audioFile) throws IOException {
        oggFile = new FileStream(new RandomAccessFile(audioFile, "r"));
        opusState = Opus.decoder_create(INPUT_BITRATE, 1);
    }

    private byte[] decode(byte[] packetData) {
        int frameSize = Opus.decoder_get_nb_samples(opusState, packetData, 0, packetData.length);
        int decodedSamples = Opus.decode(opusState, packetData, 0, packetData.length, decodeBuffer.array(), 0, frameSize, 0);
        if (decodedSamples < 0) {
            System.out.println("Decode error: " + decodedSamples);
            decodeBuffer.clear();
            return null;
        }
        decodeBuffer.position(decodedSamples * 2); // 2 bytes per sample
        decodeBuffer.flip();

        byte[] decodedData = new byte[decodeBuffer.remaining()];
        decodeBuffer.get(decodedData);
        decodeBuffer.flip();
        return decodedData;
    }

    public void play() {
        int totalDecodedBytes = 0;
        try {
            SourceDataLine speaker = AudioSystem.getSourceDataLine(audioFormat);
            speaker.open();
            speaker.start();
            for (LogicalOggStream stream : (Collection<LogicalOggStream>) oggFile.getLogicalStreams()) {
                byte[] nextPacket = stream.getNextOggPacket();
                while (nextPacket != null) {
                    byte[] decodedData = decode(nextPacket);
                    if(decodedData != null) {
                        // Write packet to SourceDataLine
                        speaker.write(decodedData, 0, decodedData.length);
                        totalDecodedBytes += decodedData.length;
                    }
                    nextPacket = stream.getNextOggPacket();
                }
            }
            speaker.drain();
            speaker.close();
            System.out.println(String.format("Decoded to %d bytes", totalDecodedBytes));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package me.justinb.mediapad.audio;
导入de.jarnbjo.ogg.FileStream;
导入de.jarnbjo.ogg.logicalogstream;
导入org.jitsi.impl.neomedia.codec.audio.opus.opus;
导入javax.sound.sampled.AudioFormat;
导入javax.sound.sampled.AudioSystem;
导入javax.sound.sampled.SourceDataLine;
导入java.io.File;
导入java.io.IOException;
导入java.io.RandomAccessFile;
导入java.nio.ByteBuffer;
导入java.util.Collection;
公营职业球员{
私有静态int缓冲区_SIZE=1024*1024;
专用静态int输入\ u比特率=48000;
专用静态int输出\ u比特率=48000;
私有文件流日志文件;
私人长人口国家;
专用ByteBuffer decodeBuffer=ByteBuffer.allocate(缓冲区大小);
私有AudioFormat AudioFormat=新的AudioFormat(输出比特率,16,1,真,假);
公共静态void main(字符串[]args){
试一试{
OpusAudioPlayer OpusAudioPlayer=新的OpusAudioPlayer(新文件(“test.opus”);
opusAudioPlayer.play();
}捕获(IOE异常){
e、 printStackTrace();
}
}
公共播放器(文件audioFile)引发IOException{
oggFile=newfilestream(newrandomAccessFile(音频文件,“r”));
opusState=Opus.decoder\u create(输入比特率,1);
}
专用字节[]解码(字节[]packetData){
int frameSize=Opus.decoder_get_nb_samples(opusState,packetData,0,packetData.length);
int decodedSamples=Opus.decode(opusState,packetData,0,packetData.length,decodeBuffer.array(),0,frameSize,0);
package me.justinb.mediapad.audio;

import de.jarnbjo.ogg.FileStream;
import de.jarnbjo.ogg.LogicalOggStream;
import org.jitsi.impl.neomedia.codec.audio.opus.Opus;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.Collection;

public class OpusAudioPlayer {
    private static int BUFFER_SIZE = 1024 * 1024;
    private static int INPUT_BITRATE = 48000;
    private static int OUTPUT_BITRATE = 48000;

    private FileStream oggFile;
    private long opusState;

    private ByteBuffer decodeBuffer = ByteBuffer.allocate(BUFFER_SIZE);

    private AudioFormat audioFormat = new AudioFormat(OUTPUT_BITRATE, 16, 1, true, false);

    public static void main(String[] args) {
        try {
            OpusAudioPlayer opusAudioPlayer = new OpusAudioPlayer(new File("test.opus"));
            opusAudioPlayer.play();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public OpusAudioPlayer(File audioFile) throws IOException {
        oggFile = new FileStream(new RandomAccessFile(audioFile, "r"));
        opusState = Opus.decoder_create(INPUT_BITRATE, 1);
    }

    private byte[] decode(byte[] packetData) {
        int frameSize = Opus.decoder_get_nb_samples(opusState, packetData, 0, packetData.length);
        int decodedSamples = Opus.decode(opusState, packetData, 0, packetData.length, decodeBuffer.array(), 0, frameSize, 0);
        if (decodedSamples < 0) {
            System.out.println("Decode error: " + decodedSamples);
            decodeBuffer.clear();
            return null;
        }
        decodeBuffer.position(decodedSamples * 2); // 2 bytes per sample
        decodeBuffer.flip();

        byte[] decodedData = new byte[decodeBuffer.remaining()];
        decodeBuffer.get(decodedData);
        decodeBuffer.flip();
        return decodedData;
    }

    public void play() {
        int totalDecodedBytes = 0;
        try {
            SourceDataLine speaker = AudioSystem.getSourceDataLine(audioFormat);
            speaker.open();
            speaker.start();
            for (LogicalOggStream stream : (Collection<LogicalOggStream>) oggFile.getLogicalStreams()) {
                byte[] nextPacket = stream.getNextOggPacket();
                while (nextPacket != null) {
                    byte[] decodedData = decode(nextPacket);
                    if(decodedData != null) {
                        // Write packet to SourceDataLine
                        speaker.write(decodedData, 0, decodedData.length);
                        totalDecodedBytes += decodedData.length;
                    }
                    nextPacket = stream.getNextOggPacket();
                }
            }
            speaker.drain();
            speaker.close();
            System.out.println(String.format("Decoded to %d bytes", totalDecodedBytes));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}