using System; using System.Threading; using System.Threading.Tasks; namespace Discord.Audio.Streams { /// Converts Opus to PCM public class OpusDecodeStream : AudioOutStream { public const int SampleRate = OpusEncodeStream.SampleRate; private readonly AudioStream _next; private readonly OpusDecoder _decoder; private readonly byte[] _buffer; private bool _nextMissed; private bool _hasHeader; public OpusDecodeStream(AudioStream next) { _next = next; _buffer = new byte[OpusConverter.FrameBytes]; _decoder = new OpusDecoder(); } /// Header received with no payload. public override void WriteHeader(ushort seq, uint timestamp, bool missed) { if (_hasHeader) throw new InvalidOperationException("Header received with no payload."); _hasHeader = true; _nextMissed = missed; _next.WriteHeader(seq, timestamp, missed); } /// Received payload without an RTP header. public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) { if (!_hasHeader) throw new InvalidOperationException("Received payload without an RTP header."); _hasHeader = false; if (!_nextMissed) { count = _decoder.DecodeFrame(buffer, offset, count, _buffer, 0, false); await _next.WriteAsync(_buffer, 0, count, cancelToken).ConfigureAwait(false); } else if (count > 0) { count = _decoder.DecodeFrame(buffer, offset, count, _buffer, 0, true); await _next.WriteAsync(_buffer, 0, count, cancelToken).ConfigureAwait(false); } else { count = _decoder.DecodeFrame(null, 0, 0, _buffer, 0, true); await _next.WriteAsync(_buffer, 0, count, cancelToken).ConfigureAwait(false); } } public override async Task FlushAsync(CancellationToken cancelToken) { await _next.FlushAsync(cancelToken).ConfigureAwait(false); } public override async Task ClearAsync(CancellationToken cancelToken) { await _next.ClearAsync(cancelToken).ConfigureAwait(false); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) _decoder.Dispose(); } } }