using System; using System.Threading; using System.Threading.Tasks; namespace Discord.Audio.Streams { /// Wraps data in an RTP frame public class RTPWriteStream : AudioOutStream { private readonly AudioStream _next; private readonly byte[] _header; protected readonly byte[] _buffer; private uint _ssrc; private ushort _nextSeq; private uint _nextTimestamp; private bool _hasHeader; public RTPWriteStream(AudioStream next, uint ssrc, int bufferSize = 4000) { _next = next; _ssrc = ssrc; _buffer = new byte[bufferSize]; _header = new byte[24]; _header[0] = 0x80; _header[1] = 0x78; _header[8] = (byte)(_ssrc >> 24); _header[9] = (byte)(_ssrc >> 16); _header[10] = (byte)(_ssrc >> 8); _header[11] = (byte)(_ssrc >> 0); } public override void WriteHeader(ushort seq, uint timestamp, bool missed) { if (_hasHeader) throw new InvalidOperationException("Header received with no payload"); _hasHeader = true; _nextSeq = seq; _nextTimestamp = timestamp; } public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) { cancelToken.ThrowIfCancellationRequested(); if (!_hasHeader) throw new InvalidOperationException("Received payload without an RTP header"); _hasHeader = false; unchecked { _header[2] = (byte)(_nextSeq >> 8); _header[3] = (byte)(_nextSeq >> 0); _header[4] = (byte)(_nextTimestamp >> 24); _header[5] = (byte)(_nextTimestamp >> 16); _header[6] = (byte)(_nextTimestamp >> 8); _header[7] = (byte)(_nextTimestamp >> 0); } Buffer.BlockCopy(_header, 0, _buffer, 0, 12); //Copy RTP header from to the buffer Buffer.BlockCopy(buffer, offset, _buffer, 12, count); _next.WriteHeader(_nextSeq, _nextTimestamp, false); await _next.WriteAsync(_buffer, 0, count + 12).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); } } }