Как найти правильный размер буфера для распаковки файла Opus с помощью Concentus

Я пытаюсь написать приложение для 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);
            }
        }
    }

Глядя на thisPacketSize, я вижу, что это переменная.

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

Моя проблема в том, что... как я могу определить, какой размер compressedPacket должен быть для распаковки в правильный размер кадра, учитывая, что размер кадра должен быть таким же, как тот, который используется при сжатии (если я правильно понимаю)?


person Frauke    schedule 27.09.2016    source источник


Ответы (1)


Я думаю, что вы просите механизм пакетирования. Настоящая проблема здесь заключается в том, что Concentus до сих пор работает только с необработанными пакетами Opus, но не имеет кода для фактического анализа контейнера, в котором находятся пакеты (файл .opus на самом деле является файлом медиаконтейнера Ogg, содержащим поток данных opus). Итак, если вы действительно хотите читать/писать формат .opus, вам придется реализовать программу чтения/записи для файлов Ogg или подождать, пока я действительно реализую ее в пакете Concentus (извините за это).

NVorbis имеет собственный синтаксический анализатор Ogg, который можно модифицировать для этой цели. Или, если вас не волнует совместимость с другими программами, вы можете реализовать свой собственный базовый пакетизатор, записав 2-байтовое поле длины пакета перед каждым блоком закодированных данных Opus.

редактировать: поскольку мне все равно нужно было добраться до него, я начал работу над пакетом синтаксического анализатора Oggfile, хотя пока реализован только декодер: https://www.nuget.org/packages/Concentus.Oggfile

edit2: теперь реализован кодировщик Opusfile ;-)

person Logan S.    schedule 01.10.2016
comment
Вау, большое спасибо за это, я понял, что мне нужен контейнер, и начал ковыряться в спецификациях, с большой путаницей по пути :) Это экономит мне очень много работы. Спасибо 10e6! - person Frauke; 03.10.2016