Voice performance update, and reenabled pings
This commit is contained in:
@@ -9,7 +9,7 @@ namespace Discord.Audio
|
|||||||
private readonly byte[] _buffer;
|
private readonly byte[] _buffer;
|
||||||
private readonly byte[] _blankFrame;
|
private readonly byte[] _blankFrame;
|
||||||
private ushort _readCursor, _writeCursor;
|
private ushort _readCursor, _writeCursor;
|
||||||
private ManualResetEventSlim _underflowEvent, _notOverflowEvent;
|
private ManualResetEventSlim _notOverflowEvent;
|
||||||
private bool _isClearing;
|
private bool _isClearing;
|
||||||
|
|
||||||
public int FrameSize => _frameSize;
|
public int FrameSize => _frameSize;
|
||||||
@@ -26,7 +26,6 @@ namespace Discord.Audio
|
|||||||
_writeCursor = 0;
|
_writeCursor = 0;
|
||||||
_buffer = new byte[_bufferSize];
|
_buffer = new byte[_bufferSize];
|
||||||
_blankFrame = new byte[_frameSize];
|
_blankFrame = new byte[_frameSize];
|
||||||
_underflowEvent = new ManualResetEventSlim(); //Notifies when an underflow has occurred
|
|
||||||
_notOverflowEvent = new ManualResetEventSlim(); //Notifies when an overflow is solved
|
_notOverflowEvent = new ManualResetEventSlim(); //Notifies when an overflow is solved
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +78,6 @@ namespace Discord.Audio
|
|||||||
|
|
||||||
//Advance the write cursor to the next position
|
//Advance the write cursor to the next position
|
||||||
AdvanceCursorPos(ref _writeCursor);
|
AdvanceCursorPos(ref _writeCursor);
|
||||||
_underflowEvent.Set();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +86,6 @@ namespace Discord.Audio
|
|||||||
{
|
{
|
||||||
if (_writeCursor == _readCursor)
|
if (_writeCursor == _readCursor)
|
||||||
{
|
{
|
||||||
_underflowEvent.Set();
|
|
||||||
_notOverflowEvent.Set();
|
_notOverflowEvent.Set();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -112,8 +109,8 @@ namespace Discord.Audio
|
|||||||
Buffer.BlockCopy(_blankFrame, 0, _buffer, i * _frameCount, i++);
|
Buffer.BlockCopy(_blankFrame, 0, _buffer, i * _frameCount, i++);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_underflowEvent.Wait(cancelToken);
|
Wait(cancelToken);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) { }
|
catch (OperationCanceledException) { }
|
||||||
_writeCursor = 0;
|
_writeCursor = 0;
|
||||||
_readCursor = 0;
|
_readCursor = 0;
|
||||||
@@ -123,7 +120,12 @@ namespace Discord.Audio
|
|||||||
|
|
||||||
public void Wait(CancellationToken cancelToken)
|
public void Wait(CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
_underflowEvent.Wait(cancelToken);
|
while (true)
|
||||||
|
{
|
||||||
|
_notOverflowEvent.Wait(cancelToken);
|
||||||
|
if (_writeCursor == _readCursor)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AdvanceCursorPos(ref ushort pos)
|
private void AdvanceCursorPos(ref ushort pos)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Discord.Net.WebSockets
|
|||||||
private const string EncryptedMode = "xsalsa20_poly1305";
|
private const string EncryptedMode = "xsalsa20_poly1305";
|
||||||
private const string UnencryptedMode = "plain";
|
private const string UnencryptedMode = "plain";
|
||||||
|
|
||||||
private readonly Random _rand;
|
//private readonly Random _rand;
|
||||||
private readonly int _targetAudioBufferLength;
|
private readonly int _targetAudioBufferLength;
|
||||||
private OpusEncoder _encoder;
|
private OpusEncoder _encoder;
|
||||||
private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders;
|
private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders;
|
||||||
@@ -48,7 +48,7 @@ namespace Discord.Net.WebSockets
|
|||||||
public VoiceWebSocket(DiscordWSClient client)
|
public VoiceWebSocket(DiscordWSClient client)
|
||||||
: base(client)
|
: base(client)
|
||||||
{
|
{
|
||||||
_rand = new Random();
|
//_rand = new Random();
|
||||||
_decoders = new ConcurrentDictionary<uint, OpusDecoder>();
|
_decoders = new ConcurrentDictionary<uint, OpusDecoder>();
|
||||||
_targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames
|
_targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames
|
||||||
_encodingBuffer = new byte[MaxOpusSize];
|
_encodingBuffer = new byte[MaxOpusSize];
|
||||||
@@ -301,7 +301,8 @@ namespace Discord.Net.WebSockets
|
|||||||
byte[] voicePacket, pingPacket, nonce = null;
|
byte[] voicePacket, pingPacket, nonce = null;
|
||||||
uint timestamp = 0;
|
uint timestamp = 0;
|
||||||
double nextTicks = 0.0, nextPingTicks = 0.0;
|
double nextTicks = 0.0, nextPingTicks = 0.0;
|
||||||
double ticksPerMillisecond = Stopwatch.Frequency / 1000.0;
|
long ticksPerSeconds = Stopwatch.Frequency;
|
||||||
|
double ticksPerMillisecond = Stopwatch.Frequency / 1000.0;
|
||||||
double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength;
|
double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength;
|
||||||
double spinLockThreshold = 3 * ticksPerMillisecond;
|
double spinLockThreshold = 3 * ticksPerMillisecond;
|
||||||
uint samplesPerFrame = (uint)_encoder.SamplesPerFrame;
|
uint samplesPerFrame = (uint)_encoder.SamplesPerFrame;
|
||||||
@@ -346,57 +347,69 @@ namespace Discord.Net.WebSockets
|
|||||||
if (_isEncrypted)
|
if (_isEncrypted)
|
||||||
Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12);
|
Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12);
|
||||||
|
|
||||||
|
bool hasFrame = false;
|
||||||
while (!cancelToken.IsCancellationRequested)
|
while (!cancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
double ticksToNextFrame = nextTicks - sw.ElapsedTicks;
|
if (!hasFrame && _sendBuffer.Pop(frame))
|
||||||
|
{
|
||||||
|
ushort sequence = unchecked(_sequence++);
|
||||||
|
voicePacket[2] = (byte)((sequence >> 8) & 0xFF);
|
||||||
|
voicePacket[3] = (byte)((sequence >> 0) & 0xFF);
|
||||||
|
voicePacket[4] = (byte)((timestamp >> 24) & 0xFF);
|
||||||
|
voicePacket[5] = (byte)((timestamp >> 16) & 0xFF);
|
||||||
|
voicePacket[6] = (byte)((timestamp >> 8) & 0xFF);
|
||||||
|
voicePacket[7] = (byte)((timestamp >> 0) & 0xFF);
|
||||||
|
|
||||||
|
//Encode
|
||||||
|
int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame);
|
||||||
|
|
||||||
|
//Encrypt
|
||||||
|
if (_isEncrypted)
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); //Update nonce
|
||||||
|
int ret = Sodium.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 (ticksToNextFrame <= 0.0)
|
||||||
{
|
{
|
||||||
long currentTicks = sw.ElapsedTicks;
|
if (hasFrame)
|
||||||
while (currentTicks > nextTicks)
|
|
||||||
{
|
{
|
||||||
if (_sendBuffer.Pop(frame))
|
_udp.Send(voicePacket, rtpPacketLength);
|
||||||
{
|
hasFrame = false;
|
||||||
ushort sequence = unchecked(_sequence++);
|
|
||||||
voicePacket[2] = (byte)((sequence >> 8) & 0xFF);
|
|
||||||
voicePacket[3] = (byte)((sequence >> 0) & 0xFF);
|
|
||||||
voicePacket[4] = (byte)((timestamp >> 24) & 0xFF);
|
|
||||||
voicePacket[5] = (byte)((timestamp >> 16) & 0xFF);
|
|
||||||
voicePacket[6] = (byte)((timestamp >> 8) & 0xFF);
|
|
||||||
voicePacket[7] = (byte)((timestamp >> 0) & 0xFF);
|
|
||||||
|
|
||||||
//Encode
|
|
||||||
int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame);
|
|
||||||
|
|
||||||
//Encrypt
|
|
||||||
if (_isEncrypted)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); //Update nonce
|
|
||||||
int ret = Sodium.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;
|
|
||||||
}
|
|
||||||
_udp.Send(voicePacket, rtpPacketLength);
|
|
||||||
}
|
|
||||||
timestamp = unchecked(timestamp + samplesPerFrame);
|
|
||||||
nextTicks += ticksPerFrame;
|
|
||||||
}
|
}
|
||||||
|
nextTicks += ticksPerFrame;
|
||||||
|
|
||||||
|
//Is it time to send out another ping?
|
||||||
if (currentTicks > nextPingTicks)
|
if (currentTicks > nextPingTicks)
|
||||||
{
|
{
|
||||||
//_udp.Send(pingPacket, pingPacket.Length);
|
_udp.Send(pingPacket, pingPacket.Length);
|
||||||
nextPingTicks = currentTicks + 5 * Stopwatch.Frequency;
|
nextPingTicks = currentTicks + 5 * ticksPerSeconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Dont sleep if we need to output audio in the next spinLockThreshold
|
else
|
||||||
else if (ticksToNextFrame > spinLockThreshold)
|
|
||||||
{
|
{
|
||||||
int time = (int)Math.Ceiling((ticksToNextFrame - spinLockThreshold) / ticksPerMillisecond);
|
if (hasFrame)
|
||||||
Thread.Sleep(1);
|
{
|
||||||
|
int time = (int)Math.Floor(ticksToNextFrame / ticksPerMillisecond);
|
||||||
|
if (time > 0)
|
||||||
|
Thread.Sleep(time);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Thread.Sleep(1); //Give as much time to the encrypter as possible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,7 +458,7 @@ namespace Discord.Net.WebSockets
|
|||||||
}
|
}
|
||||||
_udp.Connect(_endpoint);
|
_udp.Connect(_endpoint);
|
||||||
|
|
||||||
_sequence = (ushort)_rand.Next(0, ushort.MaxValue);
|
_sequence = 0;// (ushort)_rand.Next(0, ushort.MaxValue);
|
||||||
//No thread issue here because SendAsync doesn't start until _isReady is true
|
//No thread issue here because SendAsync doesn't start until _isReady is true
|
||||||
byte[] packet = new byte[70];
|
byte[] packet = new byte[70];
|
||||||
packet[0] = (byte)((_ssrc >> 24) & 0xFF);
|
packet[0] = (byte)((_ssrc >> 24) & 0xFF);
|
||||||
|
|||||||
Reference in New Issue
Block a user