2016-09-27 43 views
0

我正在尝试编写一个Android应用程序(使用Xamarin),在该应用程序中我可以录制并播放.opus文件。我以前从未使用Opus,所以请耐心等待...我如何找到用Concentus解压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需求是为了解压缩到正确的帧大小,因为帧大小必须与压缩过程中使用的帧大小相同(如果我理解正确的话)?

回答

2

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

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

编辑:因为我需要去反正我已经上Oggfile解析器包开始工作,虽然只有解码器迄今实现:https://www.nuget.org/packages/Concentus.Oggfile

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

+0

哇,非常感谢,我弄清楚我需要一个容器,并开始跋涉规格,一路上有很多困惑:)这为我节省了很多工作。感谢10e6! –