private async Task SendVoiceAsync(CancellationToken cancelToken)
{
try
{
while (!cancelToken.IsCancellationRequested && State != ConnectionState.Connected)
await Task.Delay(1).ConfigureAwait(false);
if (cancelToken.IsCancellationRequested)
return;
byte[] frame = new byte[_encoder.FrameSize];
byte[] encodedFrame = new byte[MaxOpusSize];
byte[] voicePacket, pingPacket, nonce = null;
uint timestamp = 0;
double nextTicks = 0.0, nextPingTicks = 0.0;
long ticksPerSeconds = Stopwatch.Frequency;
double ticksPerMillisecond = Stopwatch.Frequency / 1000.0;
double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength;
double spinLockThreshold = 3 * ticksPerMillisecond;
uint samplesPerFrame = (uint)_encoder.SamplesPerFrame;
Stopwatch sw = Stopwatch.StartNew();
if (_isEncrypted)
{
nonce = new byte[24];
voicePacket = new byte[MaxOpusSize + 12 + 16];
}
else
voicePacket = new byte[MaxOpusSize + 12];
pingPacket = new byte[8];
int rtpPacketLength = 0;
voicePacket[0] = 0x80; //Flags;
voicePacket[1] = 0x78; //Payload Type
voicePacket[8] = (byte)(_ssrc >> 24);
voicePacket[9] = (byte)(_ssrc >> 16);
voicePacket[10] = (byte)(_ssrc >> 8);
voicePacket[11] = (byte)(_ssrc >> 0);
if (_isEncrypted)
Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12);
bool hasFrame = false;
while (!cancelToken.IsCancellationRequested)
{
if (!hasFrame && _sendBuffer.Pop(frame))
{
ushort sequence = unchecked(_sequence++);
voicePacket[2] = (byte)(sequence >> 8);
voicePacket[3] = (byte)(sequence >> 0);
voicePacket[4] = (byte)(timestamp >> 24);
voicePacket[5] = (byte)(timestamp >> 16);
voicePacket[6] = (byte)(timestamp >> 8);
voicePacket[7] = (byte)(timestamp >> 0);
//Encode
int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame);
//Encrypt
if (_isEncrypted)
{
Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); //Update nonce
int ret = SecretBox.Encrypt(encodedFrame, encodedLength, voicePacket, 12, nonce, _secretKey);
if (ret != 0)
continue;
rtpPacketLength = encodedLength + 12 + 16;
}
else
{
Buffer.BlockCopy(encodedFrame, 0, voicePacket, 12, encodedLength);
rtpPacketLength = encodedLength + 12;
}
timestamp = unchecked(timestamp + samplesPerFrame);
hasFrame = true;
}
long currentTicks = sw.ElapsedTicks;
double ticksToNextFrame = nextTicks - currentTicks;
if (ticksToNextFrame <= 0.0)
{
if (hasFrame)
{
try
{
await _udp.SendAsync(voicePacket, rtpPacketLength, _endpoint).ConfigureAwait(false);
}
catch (SocketException ex)
{
Logger.Error("Failed to send UDP packet.", ex);
}
hasFrame = false;
}
nextTicks += ticksPerFrame;
//Is it time to send out another ping?
if (currentTicks > nextPingTicks)
{
//Increment in LE
for (int i = 0; i < 8; i++)
{
var b = pingPacket[i];
if (b == byte.MaxValue)
pingPacket[i] = 0;
else
{
pingPacket[i] = (byte)(b + 1);
break;
}
}
await _udp.SendAsync(pingPacket, pingPacket.Length, _endpoint).ConfigureAwait(false);
nextPingTicks = currentTicks + 5 * ticksPerSeconds;
}
}
else
{
if (hasFrame)
{
int time = (int)Math.Floor(ticksToNextFrame / ticksPerMillisecond);
if (time > 0)
await Task.Delay(time).ConfigureAwait(false);
}
else
await Task.Delay(1).ConfigureAwait(false); //Give as much time to the encrypter as possible
}
}
}
catch (OperationCanceledException) { }
catch (InvalidOperationException) { } //Includes ObjectDisposedException
}
#if !NETSTANDARD1_3