Added buffer length to outgoing audio
This commit is contained in:
@@ -133,7 +133,7 @@ namespace Discord
|
||||
#if !DNXCORE50
|
||||
if (_config.EnableVoice)
|
||||
{
|
||||
_voiceWebSocket = new DiscordVoiceSocket(this, _config.VoiceConnectionTimeout, _config.WebSocketInterval, config.EnableDebug);
|
||||
_voiceWebSocket = new DiscordVoiceSocket(this, _config.VoiceConnectionTimeout, _config.WebSocketInterval, _config.VoiceBufferLength, _config.EnableDebug);
|
||||
_voiceWebSocket.Connected += (s, e) => RaiseVoiceConnected();
|
||||
_voiceWebSocket.Disconnected += async (s, e) =>
|
||||
{
|
||||
@@ -643,17 +643,18 @@ namespace Discord
|
||||
}
|
||||
|
||||
/// <summary> Sends a PCM frame to the voice server. </summary>
|
||||
/// <param name="data">PCM frame to send.</param>
|
||||
/// <param name="data">PCM frame to send. This must be a 48Kz 20ms </param>
|
||||
/// <param name="count">Number of bytes in this frame. </param>
|
||||
public Task SendVoicePCM(byte[] data, int count)
|
||||
/// <remarks>Will block until</remarks>
|
||||
public void SendVoicePCM(byte[] data, int count)
|
||||
{
|
||||
CheckReady();
|
||||
if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client.");
|
||||
if (count == 0) return TaskHelper.CompletedTask;
|
||||
if (count == 0) return;
|
||||
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.VoiceOutput, $"Queued {count} bytes for voice output.");
|
||||
return _voiceWebSocket.SendPCMFrame(data, count);
|
||||
_voiceWebSocket.SendPCMFrame(data, count);
|
||||
}
|
||||
|
||||
/// <summary> Clears the PCM buffer. </summary>
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
public bool UseMessageQueue { get; set; } = false;
|
||||
/// <summary> Gets or sets the time (in milliseconds) to wait when the message queue is empty before checking again. </summary>
|
||||
public int MessageQueueInterval { get; set; } = 100;
|
||||
/// <summary> Gets or sets the max buffer length (in milliseconds) for outgoing voice packets. </summary>
|
||||
/// <remarks> This value is the target maximum but is not guaranteed. The buffer will often go a bit above this value. </remarks>
|
||||
public int VoiceBufferLength { get; set; } = 1000;
|
||||
|
||||
public DiscordClientConfig() { }
|
||||
}
|
||||
|
||||
@@ -19,23 +19,14 @@ namespace Discord
|
||||
{
|
||||
internal sealed partial class DiscordVoiceSocket : DiscordWebSocket
|
||||
{
|
||||
private struct Packet
|
||||
{
|
||||
public byte[] Data;
|
||||
public int Count;
|
||||
public Packet(byte[] data, int count)
|
||||
{
|
||||
Data = data;
|
||||
Count = count;
|
||||
}
|
||||
}
|
||||
|
||||
private ManualResetEventSlim _connectWaitOnLogin;
|
||||
private readonly int _targetAudioBufferLength;
|
||||
private ManualResetEventSlim _connectWaitOnLogin;
|
||||
private uint _ssrc;
|
||||
private readonly Random _rand = new Random();
|
||||
private readonly Random _rand = new Random();
|
||||
|
||||
private OpusEncoder _encoder;
|
||||
private ConcurrentQueue<Packet> _sendQueue;
|
||||
private ConcurrentQueue<byte[]> _sendQueue;
|
||||
private ManualResetEventSlim _sendQueueWait;
|
||||
private UdpClient _udp;
|
||||
private IPEndPoint _endpoint;
|
||||
private bool _isReady, _isClearing;
|
||||
@@ -43,17 +34,21 @@ namespace Discord
|
||||
private string _myIp;
|
||||
private ushort _sequence;
|
||||
private string _mode;
|
||||
private byte[] _encodingBuffer;
|
||||
#if USE_THREAD
|
||||
private Thread _sendThread;
|
||||
#endif
|
||||
|
||||
public DiscordVoiceSocket(DiscordClient client, int timeout, int interval, bool isDebug)
|
||||
public DiscordVoiceSocket(DiscordClient client, int timeout, int interval, int audioBufferLength, bool isDebug)
|
||||
: base(client, timeout, interval, isDebug)
|
||||
{
|
||||
_connectWaitOnLogin = new ManualResetEventSlim(false);
|
||||
_sendQueue = new ConcurrentQueue<Packet>();
|
||||
_sendQueue = new ConcurrentQueue<byte[]>();
|
||||
_sendQueueWait = new ManualResetEventSlim(true);
|
||||
_encoder = new OpusEncoder(48000, 1, 20, Application.Audio);
|
||||
}
|
||||
_encodingBuffer = new byte[4000];
|
||||
_targetAudioBufferLength = audioBufferLength / 20;
|
||||
}
|
||||
|
||||
protected override void OnConnect()
|
||||
{
|
||||
@@ -81,9 +76,9 @@ namespace Discord
|
||||
#endif
|
||||
return new Task[]
|
||||
{
|
||||
ReceiveAsync(),
|
||||
ReceiveVoiceAsync(),
|
||||
#if !USE_THREAD
|
||||
SendAsync(),
|
||||
SendVoiceAsync(),
|
||||
#endif
|
||||
WatcherAsync()
|
||||
}.Concat(base.CreateTasks()).ToArray();
|
||||
@@ -118,7 +113,7 @@ namespace Discord
|
||||
SetConnected();
|
||||
}
|
||||
|
||||
private async Task ReceiveAsync()
|
||||
private async Task ReceiveVoiceAsync()
|
||||
{
|
||||
var cancelSource = _disconnectToken;
|
||||
var cancelToken = cancelSource.Token;
|
||||
@@ -138,17 +133,17 @@ namespace Discord
|
||||
}
|
||||
|
||||
#if USE_THREAD
|
||||
private void SendAsync(CancellationTokenSource cancelSource)
|
||||
private void SendVoiceAsync(CancellationTokenSource cancelSource)
|
||||
{
|
||||
var cancelToken = cancelSource.Token;
|
||||
#else
|
||||
private async Task SendAsync()
|
||||
private async Task SendVoiceAsync()
|
||||
{
|
||||
var cancelSource = _disconnectToken;
|
||||
var cancelToken = cancelSource.Token;
|
||||
await Task.Yield();
|
||||
|
||||
Packet packet;
|
||||
byte[] packet;
|
||||
try
|
||||
{
|
||||
while (!cancelToken.IsCancellationRequested && !_isReady)
|
||||
@@ -165,7 +160,7 @@ namespace Discord
|
||||
uint samplesPerFrame = (uint)_encoder.SamplesPerFrame;
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
byte[] rtpPacket = new byte[4012];
|
||||
byte[] rtpPacket = new byte[_encodingBuffer.Length + 12];
|
||||
rtpPacket[0] = 0x80; //Flags;
|
||||
rtpPacket[1] = 0x78; //Payload Type
|
||||
rtpPacket[8] = (byte)((_ssrc >> 24) & 0xFF);
|
||||
@@ -175,6 +170,10 @@ namespace Discord
|
||||
|
||||
while (!cancelToken.IsCancellationRequested)
|
||||
{
|
||||
//If we have less than our target data buffered, request more
|
||||
if (_sendQueue.Count < _targetAudioBufferLength)
|
||||
_sendQueueWait.Set();
|
||||
|
||||
double ticksToNextFrame = nextTicks - sw.ElapsedTicks;
|
||||
if (ticksToNextFrame <= 0.0)
|
||||
{
|
||||
@@ -191,11 +190,11 @@ namespace Discord
|
||||
rtpPacket[5] = (byte)((timestamp >> 16) & 0xFF);
|
||||
rtpPacket[6] = (byte)((timestamp >> 8) & 0xFF);
|
||||
rtpPacket[7] = (byte)((timestamp >> 0) & 0xFF);
|
||||
Buffer.BlockCopy(packet.Data, 0, rtpPacket, 12, packet.Count);
|
||||
Buffer.BlockCopy(packet, 0, rtpPacket, 12, packet.Length);
|
||||
#if USE_THREAD
|
||||
_udp.Send(rtpPacket, packet.Count + 12);
|
||||
#else
|
||||
await _udp.SendAsync(rtpPacket, packet.Count + 12);
|
||||
await _udp.SendAsync(rtpPacket, packet.Length + 12);
|
||||
#endif
|
||||
}
|
||||
timestamp = unchecked(timestamp + samplesPerFrame);
|
||||
@@ -369,31 +368,34 @@ namespace Discord
|
||||
}
|
||||
}
|
||||
|
||||
public Task SendPCMFrame(byte[] data, int count)
|
||||
public void SendPCMFrame(byte[] data, int count)
|
||||
{
|
||||
if (count != _encoder.FrameSize)
|
||||
throw new InvalidOperationException($"Invalid frame size. Got {count}, expected {_encoder.FrameSize}.");
|
||||
|
||||
byte[] payload;
|
||||
int encodedLength;
|
||||
lock (_encoder)
|
||||
{
|
||||
payload = new byte[4000];
|
||||
encodedLength = _encoder.EncodeFrame(data, payload);
|
||||
int encodedLength = _encoder.EncodeFrame(data, _encodingBuffer);
|
||||
|
||||
if (_mode == "xsalsa20_poly1305")
|
||||
{
|
||||
//TODO: Encode
|
||||
}
|
||||
}
|
||||
_sendQueue.Enqueue(new Packet(payload, encodedLength));
|
||||
|
||||
return Task.Delay(0);
|
||||
payload = new byte[encodedLength];
|
||||
Buffer.BlockCopy(_encodingBuffer, 0, payload, 0, encodedLength);
|
||||
}
|
||||
|
||||
_sendQueueWait.Wait(_disconnectToken.Token);
|
||||
_sendQueue.Enqueue(payload);
|
||||
if (_sendQueue.Count >= _targetAudioBufferLength)
|
||||
_sendQueueWait.Reset();
|
||||
}
|
||||
public void ClearPCMFrames()
|
||||
{
|
||||
_isClearing = true;
|
||||
Packet ignored;
|
||||
byte[] ignored;
|
||||
while (_sendQueue.TryDequeue(out ignored)) { }
|
||||
_isClearing = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user