如何使用Java播放Opus编码的音频? [英] How do I play Opus encoded audio in Java?

查看:135
本文介绍了如何使用Java播放Opus编码的音频?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当播放解码的音频时,我设法产生了各种声音,从g吟到尖叫声再到恶魔般的颂歌.最接近的声音类似于快进播放,并且播放仅持续约15秒.我尝试将解码和AudioSystem API方法的参数组合在一起,似乎没有任何效果.

When playing back the decoded audio, I've managed to produce a variety of sounds from gurgling to screeching to demonic chants. The closest of which sounds similar to being played in fast-forward and playback only lasts about 15 seconds. I've tried with a large combination of parameters for the decoding and AudioSystem API methods, nothing seems to be working.

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

So, what is causing this audio distortion?

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

Opusinfo for this file shows the following:

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可以正确播放此文件.

This file plays back correctly using VLC.

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

To decode the file I'm attempting to use the following libraries:

  • VorbisJava (https://github.com/Gagravarr/VorbisJava/) - To pull the frames from the OGG container

LibJitsi( https://jitsi.org/Projects/LibJitsi )-具有用于Opus的JNI包装器,用于解码作品帧

LibJitsi (https://jitsi.org/Projects/LibJitsi) - Which has a JNI wrapper for Opus, used to decode the opus frames

package me.justinb.mediapad.audio;

import org.gagravarr.ogg.OggFile;
import org.gagravarr.ogg.OggPacket;
import org.jitsi.impl.neomedia.codec.audio.opus.Opus;
import javax.sound.sampled.*;
import java.io.*;
import java.nio.ByteBuffer;

public class OpusAudioPlayer {
    private static int BUFFER_SIZE = 1024 * 1024;
    private static int INPUT_BITRATE = 48000;
    private static int OUTPUT_BITRATE = 44100;
    private OggFile 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 OggFile(new FileInputStream(audioFile));
        opusState = Opus.decoder_create(INPUT_BITRATE, 1);
        System.out.println("Audio format: " + audioFormat);
    }

    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();
        System.out.println(String.format("Encoded frame size: %d bytes", packetData.length));
        System.out.println(String.format("Decoded frame size: %d bytes", decodedData.length));
        System.out.println(String.format("Decoded %d samples", decodedSamples));
        return decodedData;
    }

    public void play() {
        int totalDecodedBytes = 0;
        try {
            SourceDataLine speaker = AudioSystem.getSourceDataLine(audioFormat);
            OggPacket nextPacket = oggFile.getPacketReader().getNextPacket();
            // Move to beginning of stream
            while ( !nextPacket.isBeginningOfStream()) {
                nextPacket = oggFile.getPacketReader().getNextPacket();
            }
            speaker.open();
            speaker.start();
            while(nextPacket != null) {
                // Decode each packet
                byte[] decodedData = decode(nextPacket.getData());
                if(decodedData != null) {
                    // Write packet to SourceDataLine
                    speaker.write(decodedData, 0, decodedData.length);
                    totalDecodedBytes += decodedData.length;
                }
                nextPacket = oggFile.getPacketReader().getNextPacket();
            }
            speaker.drain();
            speaker.close();
            System.out.println(String.format("Decoded to %d bytes", totalDecodedBytes));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

推荐答案

我的特定问题似乎是由VorbisJava中的错误引起的.我现在正在使用J-Ogg,它可以毫无问题地处理容器解析.我敢肯定有人会觉得这很有用.

My particular issue seemed to be caused by a bug in VorbisJava. I'm now using J-Ogg which is handling the container parsing without any problems. I'm certain someone will find this useful.

这是最后的代码,显示了如何以Java播放Opus编码的音频:

This is the final code which shows how to play Opus encoded audio in Java:

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();
        }
    }
}

这篇关于如何使用Java播放Opus编码的音频?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆