C# 如何找到正确的缓冲区大小,以便使用Concentus解压缩Opus文件

C# 如何找到正确的缓冲区大小,以便使用Concentus解压缩Opus文件,c#,xamarin.android,opus,C#,Xamarin.android,Opus,我正在尝试编写一个Android应用程序(使用Xamarin),在其中我可以录制并播放.opus文件。我以前从未使用过Opus,所以请容忍我 我正在使用Concentus NuGet软件包压缩/解压缩音频。Concentus的示例代码建议记录以下内容: // Initialize OpusEncoder encoder = OpusEncoder.Create(48000, 1, OpusApplication.OPUS_APPLICATION_AUDIO); encoder.Bitrate =

我正在尝试编写一个Android应用程序(使用Xamarin),在其中我可以录制并播放.opus文件。我以前从未使用过Opus,所以请容忍我

我正在使用Concentus NuGet软件包压缩/解压缩音频。Concentus的示例代码建议记录以下内容:

// Initialize
OpusEncoder encoder = OpusEncoder.Create(48000, 1, OpusApplication.OPUS_APPLICATION_AUDIO);
encoder.Bitrate = 12000;

// Encoding loop
short[] inputAudioSamples
byte[] outputBuffer[1000];
int frameSize = 960;

int thisPacketSize = encoder.Encode(inputAudioSamples, 0, frameSize, outputBuffer, 0, outputBuffer.Length); // this throws OpusException on a failure, rather than returning a negative number
    private void RecordAudio(string filename)
    {
        _outputStream = File.Open(filename, FileMode.OpenOrCreate);
        _binaryWriter = new BinaryWriter(_outputStream);
        OpusEncoder encoder = OpusEncoder.Create(AudioFormat.SampleRate, 1, OpusApplication.OPUS_APPLICATION_AUDIO);
        encoder.Bitrate = 12000;

        int frameSize = AudioFormat.SampleRate * 20 / 1000;
        short[] audioBuffer = new short[frameSize];
        byte[] outputBuffer = new byte[1000];

        _audioRecorder = new AudioRecord(
          // Hardware source of recording.
          AudioSource.Mic,
          // Frequency
          AudioFormat.SampleRate,
          // Mono or stereo
          AudioFormat.ChannelIn,
          // Audio encoding
          AudioFormat.Encoding,
          // Length of the audio clip.
          audioBuffer.Length
        );
        _audioRecorder.StartRecording();

        _isRecording = true;
        while (_isRecording)
        {
            try
            {
                // Keep reading the buffer while there is audio input.
                _audioRecorder.Read(audioBuffer, 0, audioBuffer.Length);

                int thisPacketSize = encoder.Encode(audioBuffer, 0, frameSize, outputBuffer, 0, outputBuffer.Length); // this throws OpusException on a failure, rather than returning a negative number
                Debug.WriteLine("Packet size = " + thisPacketSize);
                // Write to the audio file.
                _binaryWriter.Write(outputBuffer, 0, thisPacketSize);
            }
            catch (System.Exception ex)
            {
                Console.Out.WriteLine(ex.Message);
            }
        }
    }
使用此信息,我创建了以下录制方法:

// Initialize
OpusEncoder encoder = OpusEncoder.Create(48000, 1, OpusApplication.OPUS_APPLICATION_AUDIO);
encoder.Bitrate = 12000;

// Encoding loop
short[] inputAudioSamples
byte[] outputBuffer[1000];
int frameSize = 960;

int thisPacketSize = encoder.Encode(inputAudioSamples, 0, frameSize, outputBuffer, 0, outputBuffer.Length); // this throws OpusException on a failure, rather than returning a negative number
    private void RecordAudio(string filename)
    {
        _outputStream = File.Open(filename, FileMode.OpenOrCreate);
        _binaryWriter = new BinaryWriter(_outputStream);
        OpusEncoder encoder = OpusEncoder.Create(AudioFormat.SampleRate, 1, OpusApplication.OPUS_APPLICATION_AUDIO);
        encoder.Bitrate = 12000;

        int frameSize = AudioFormat.SampleRate * 20 / 1000;
        short[] audioBuffer = new short[frameSize];
        byte[] outputBuffer = new byte[1000];

        _audioRecorder = new AudioRecord(
          // Hardware source of recording.
          AudioSource.Mic,
          // Frequency
          AudioFormat.SampleRate,
          // Mono or stereo
          AudioFormat.ChannelIn,
          // Audio encoding
          AudioFormat.Encoding,
          // Length of the audio clip.
          audioBuffer.Length
        );
        _audioRecorder.StartRecording();

        _isRecording = true;
        while (_isRecording)
        {
            try
            {
                // Keep reading the buffer while there is audio input.
                _audioRecorder.Read(audioBuffer, 0, audioBuffer.Length);

                int thisPacketSize = encoder.Encode(audioBuffer, 0, frameSize, outputBuffer, 0, outputBuffer.Length); // this throws OpusException on a failure, rather than returning a negative number
                Debug.WriteLine("Packet size = " + thisPacketSize);
                // Write to the audio file.
                _binaryWriter.Write(outputBuffer, 0, thisPacketSize);
            }
            catch (System.Exception ex)
            {
                Console.Out.WriteLine(ex.Message);
            }
        }
    }
查看
这个packetsize
,我可以看到它是可变的

Concentus建议使用以下代码进行解码:

OpusDecoder decoder = OpusDecoder.Create(48000, 1);

// Decoding loop
byte[] compressedPacket;
int frameSize = 960; // must be same as framesize used in input, you can use OpusPacketInfo.GetNumSamples() to determine this dynamically
short[] outputBuffer = new short[frameSize];

int thisFrameSize = _decoder.Decode(compressedPacket, 0, compressedPacket.Length, outputBuffer, 0, frameSize, false);
我最初的播放想法如下:

    void PlayAudioTrack(string filename)
    {
        _inputStream = File.OpenRead(filename);
        _inputStream.Position = _recording.CurrentPosition - _recording.CurrentPosition % AudioFormat.BytesPerSample;
        OpusDecoder decoder = OpusDecoder.Create(AudioFormat.SampleRate, 1);

        int frameSize = AudioFormat.SampleRate * 20 / 1000;

        _audioTrack = new AudioTrack(
          // Stream type
          Android.Media.Stream.Music,
          // Frequency
          AudioFormat.SampleRate,
          // Mono or stereo
          AudioFormat.ChannelOut,
          // Audio encoding
          AudioFormat.Encoding,
          // Length of the audio clip.
          frameSize,
          // Mode. Stream or static.
          AudioTrackMode.Stream);

        _audioTrack.SetPositionNotificationPeriod(AudioFormat.SampleRate / 30);
        _audioTrack.PeriodicNotification += _audioTrack_PeriodicNotification;
        _audioTrack.Play();

        short[] audioBuffer = new short[frameSize];

        int bytesRead = 0;
        do
        {
            try
            {
                byte[] compressedPacket = new byte[???];
                bytesRead = _inputStream.Read(compressedPacket, 0, compressedPacket.Length);
                int thisFrameSize = decoder.Decode(compressedPacket, 0, compressedPacket.Length, audioBuffer, 0, frameSize, false);
                Debug.WriteLine("Frame size = " + thisFrameSize);
                _audioTrack.Write(audioBuffer, 0, bytesRead);
            }
            catch (System.Exception)
            {
                bytesRead = 0;
            }
        } while (bytesRead > 0 && _audioTrack.PlayState == PlayState.Playing);

        if (!(_audioTrack.PlayState == PlayState.Stopped))
        {
            RaisePlaybackCompleted(this, new EventArgs());
        }
    }

我的问题是。。。考虑到帧大小必须与压缩过程中使用的帧大小相同(如果我理解正确的话),我如何确定压缩包需要多大的大小才能解压缩到正确的帧大小?

我想你需要的是一种打包机制。这里真正的问题是Concentus到目前为止只对原始Opus数据包进行操作,但没有代码来实际解析数据包所在的容器(一个.Opus文件实际上是一个包含Opus数据流的Ogg媒体容器文件)。因此,如果您想真正读/写.opus格式,您必须为Ogg文件实现一个读/写器,或者等待我在Concentus包中实际实现一个(很抱歉)

NVorbis有一个本机Ogg解析器,可以为此进行修改。或者,如果您不关心与其他程序的兼容性,您可以通过在每个编码Opus数据块之前写入一个2字节的数据包长度字段来实现自己的基本打包器

编辑:因为我无论如何都需要开始,我已经开始了一个Oggfile解析器包的工作,尽管到目前为止只实现了解码器:


edit2:Opusfile编码器现在已实现;-)

我想你要求的是一个打包机制。这里真正的问题是Concentus到目前为止只对原始Opus数据包进行操作,但没有代码来实际解析数据包所在的容器(一个.Opus文件实际上是一个包含Opus数据流的Ogg媒体容器文件)。因此,如果您想真正读/写.opus格式,您必须为Ogg文件实现一个读/写器,或者等待我在Concentus包中实际实现一个(很抱歉)

NVorbis有一个本机Ogg解析器,可以为此进行修改。或者,如果您不关心与其他程序的兼容性,您可以通过在每个编码Opus数据块之前写入一个2字节的数据包长度字段来实现自己的基本打包器

编辑:因为我无论如何都需要开始,我已经开始了一个Oggfile解析器包的工作,尽管到目前为止只实现了解码器:


edit2:Opusfile编码器现在已实现;-)

哇,非常感谢你这么做,我确实发现我需要一个容器,开始艰难地通过规范,一路上有很多困惑:)这为我节省了大量的工作。谢谢你!哇,非常感谢你这么做,我确实发现我需要一个容器,开始艰难地通过规范,一路上有很多困惑:)这为我节省了大量的工作。谢谢你!