Refactoring and fixed a few stylecop errors

This commit is contained in:
RogueException
2015-12-09 00:56:09 -04:00
parent a44e08bba1
commit 82746e9207
25 changed files with 574 additions and 505 deletions

View File

@@ -145,27 +145,27 @@ namespace Discord.Audio
return Task.FromResult(_defaultClient);
}
var client = _voiceClients.GetOrAdd(server.Id, _ =>
var client = _voiceClients.GetOrAdd(server.Id, (Func<long, DiscordAudioClient>)(_ =>
{
int id = unchecked(++_nextClientId);
var logger = Client.Log().CreateLogger($"Voice #{id}");
GatewayWebSocket gatewaySocket = null;
Net.WebSockets.GatewaySocket gatewaySocket = null;
var voiceSocket = new VoiceWebSocket(Client.Config, _config, logger);
var voiceClient = new DiscordAudioClient(this, id, logger, gatewaySocket, voiceSocket);
var voiceClient = new DiscordAudioClient((AudioService)(this), (int)id, (Logger)logger, (Net.WebSockets.GatewaySocket)gatewaySocket, (VoiceWebSocket)voiceSocket);
voiceClient.SetServerId(server.Id);
voiceSocket.OnPacket += (s, e) =>
{
RaiseOnPacket(e);
RaiseOnPacket(e);
};
voiceSocket.IsSpeaking += (s, e) =>
{
var user = Client.GetUser(server, e.UserId);
RaiseUserIsSpeakingUpdated(user, e.IsSpeaking);
RaiseUserIsSpeakingUpdated(user, e.IsSpeaking);
};
return voiceClient;
});
}));
//await client.Connect(gatewaySocket.Host, _client.Token).ConfigureAwait(false);
return Task.FromResult(client);
}

View File

@@ -37,8 +37,12 @@ namespace Discord.Audio
public int? Bitrate { get { return _bitrate; } set { SetValue(ref _bitrate, value); } }
private int? _bitrate = null;
//Lock
protected bool _isLocked;
/// <summary> Gets or sets the number of channels (1 or 2) used for outgoing audio. </summary>
public int Channels { get { return _channels; } set { SetValue(ref _channels, value); } }
private int _channels = 1;
//Lock
protected bool _isLocked;
internal void Lock() { _isLocked = true; }
protected void SetValue<T>(ref T storage, T value)
{

View File

@@ -10,14 +10,14 @@ namespace Discord.Audio
public int Id => _id;
private readonly AudioService _service;
private readonly GatewayWebSocket _gatewaySocket;
private readonly GatewaySocket _gatewaySocket;
private readonly VoiceWebSocket _voiceSocket;
private readonly Logger _logger;
public long? ServerId => _voiceSocket.ServerId;
public long? ChannelId => _voiceSocket.ChannelId;
public DiscordAudioClient(AudioService service, int id, Logger logger, GatewayWebSocket gatewaySocket, VoiceWebSocket voiceSocket)
public DiscordAudioClient(AudioService service, int id, Logger logger, GatewaySocket gatewaySocket, VoiceWebSocket voiceSocket)
{
_service = service;
_id = id;

View File

@@ -1,5 +1,7 @@
using Discord.API;
using Discord.Audio;
using Discord.Audio.Opus;
using Discord.Audio.Sodium;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@@ -54,7 +56,7 @@ namespace Discord.Net.WebSockets
_targetAudioBufferLength = _audioConfig.BufferLength / 20; //20 ms frames
_encodingBuffer = new byte[MaxOpusSize];
_ssrcMapping = new ConcurrentDictionary<uint, long>();
_encoder = new OpusEncoder(48000, 1, 20, _audioConfig.Bitrate, Opus.Application.Audio);
_encoder = new OpusEncoder(48000, _audioConfig.Channels, 20, _audioConfig.Bitrate, OpusApplication.Audio);
_sendBuffer = new VoiceBuffer((int)Math.Ceiling(_audioConfig.BufferLength / (double)_encoder.FrameLength), _encoder.FrameSize);
}
@@ -223,7 +225,7 @@ namespace Discord.Net.WebSockets
return;
Buffer.BlockCopy(packet, 0, nonce, 0, 12);
int ret = Sodium.Decrypt(packet, 12, packetLength - 12, decodingBuffer, nonce, _secretKey);
int ret = SecretBox.Decrypt(packet, 12, packetLength - 12, decodingBuffer, nonce, _secretKey);
if (ret != 0)
continue;
result = decodingBuffer;
@@ -294,7 +296,7 @@ namespace Discord.Net.WebSockets
if (_isEncrypted)
{
Buffer.BlockCopy(pingPacket, 0, nonce, 0, 8);
int ret = Sodium.Encrypt(pingPacket, 8, encodedFrame, 0, nonce, _secretKey);
int ret = SecretBox.Encrypt(pingPacket, 8, encodedFrame, 0, nonce, _secretKey);
if (ret != 0)
throw new InvalidOperationException("Failed to encrypt ping packet");
pingPacket = new byte[pingPacket.Length + 16];
@@ -333,7 +335,7 @@ namespace Discord.Net.WebSockets
if (_isEncrypted)
{
Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); //Update nonce
int ret = Sodium.Encrypt(encodedFrame, encodedLength, voicePacket, 12, nonce, _secretKey);
int ret = SecretBox.Encrypt(encodedFrame, encodedLength, voicePacket, 12, nonce, _secretKey);
if (ret != 0)
continue;
rtpPacketLength = encodedLength + 12 + 16;

View File

@@ -1,71 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Discord.Audio
{
internal unsafe static class Opus
{
[DllImport("opus", EntryPoint = "opus_encoder_create", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateEncoder(int Fs, int channels, int application, out Error error);
[DllImport("opus", EntryPoint = "opus_encoder_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroyEncoder(IntPtr encoder);
[DllImport("opus", EntryPoint = "opus_encode", CallingConvention = CallingConvention.Cdecl)]
public static extern int Encode(IntPtr st, byte* pcm, int frame_size, byte[] data, int max_data_bytes);
[DllImport("opus", EntryPoint = "opus_decoder_create", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateDecoder(int Fs, int channels, out Error error);
[DllImport("opus", EntryPoint = "opus_decoder_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroyDecoder(IntPtr decoder);
[DllImport("opus", EntryPoint = "opus_decode", CallingConvention = CallingConvention.Cdecl)]
public static extern int Decode(IntPtr st, byte* data, int len, byte[] pcm, int frame_size, int decode_fec);
[DllImport("opus", EntryPoint = "opus_encoder_ctl", CallingConvention = CallingConvention.Cdecl)]
public static extern int EncoderCtl(IntPtr st, Ctl request, int value);
public enum Ctl : int
{
SetBitrateRequest = 4002,
GetBitrateRequest = 4003,
SetInbandFECRequest = 4012,
GetInbandFECRequest = 4013
}
/// <summary>Supported coding modes.</summary>
public enum Application : int
{
/// <summary>
/// Gives best quality at a given bitrate for voice signals. It enhances the input signal by high-pass filtering and emphasizing formants and harmonics.
/// Optionally it includes in-band forward error correction to protect against packet loss. Use this mode for typical VoIP applications.
/// Because of the enhancement, even at high bitrates the output may sound different from the input.
/// </summary>
Voip = 2048,
/// <summary>
/// Gives best quality at a given bitrate for most non-voice signals like music.
/// Use this mode for music and mixed (music/voice) content, broadcast, and applications requiring less than 15 ms of coding delay.
/// </summary>
Audio = 2049,
/// <summary> Low-delay mode that disables the speech-optimized mode in exchange for slightly reduced delay. </summary>
Restricted_LowLatency = 2051
}
public enum Error : int
{
/// <summary> No error. </summary>
OK = 0,
/// <summary> One or more invalid/out of range arguments. </summary>
BadArg = -1,
/// <summary> The mode struct passed is invalid. </summary>
BufferToSmall = -2,
/// <summary> An internal error was detected. </summary>
InternalError = -3,
/// <summary> The compressed data passed is corrupted. </summary>
InvalidPacket = -4,
/// <summary> Invalid/unsupported request number. </summary>
Unimplemented = -5,
/// <summary> An encoder or decoder structure is invalid or already freed. </summary>
InvalidState = -6,
/// <summary> Memory allocation has failed. </summary>
AllocFail = -7
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace Discord.Audio.Opus
{
internal enum OpusCtl : int
{
SetBitrateRequest = 4002,
GetBitrateRequest = 4003,
SetInbandFECRequest = 4012,
GetInbandFECRequest = 4013
}
/// <summary>Supported coding modes.</summary>
internal enum OpusApplication : int
{
/// <summary>
/// Gives best quality at a given bitrate for voice signals. It enhances the input signal by high-pass filtering and emphasizing formants and harmonics.
/// Optionally it includes in-band forward error correction to protect against packet loss. Use this mode for typical VoIP applications.
/// Because of the enhancement, even at high bitrates the output may sound different from the input.
/// </summary>
Voip = 2048,
/// <summary>
/// Gives best quality at a given bitrate for most non-voice signals like music.
/// Use this mode for music and mixed (music/voice) content, broadcast, and applications requiring less than 15 ms of coding delay.
/// </summary>
Audio = 2049,
/// <summary> Low-delay mode that disables the speech-optimized mode in exchange for slightly reduced delay. </summary>
Restricted_LowLatency = 2051
}
internal enum OpusError : int
{
/// <summary> No error. </summary>
OK = 0,
/// <summary> One or more invalid/out of range arguments. </summary>
BadArg = -1,
/// <summary> The mode struct passed is invalid. </summary>
BufferToSmall = -2,
/// <summary> An internal error was detected. </summary>
InternalError = -3,
/// <summary> The compressed data passed is corrupted. </summary>
InvalidPacket = -4,
/// <summary> Invalid/unsupported request number. </summary>
Unimplemented = -5,
/// <summary> An encoder or decoder structure is invalid or already freed. </summary>
InvalidState = -6,
/// <summary> Memory allocation has failed. </summary>
AllocFail = -7
}
}

View File

@@ -1,11 +1,26 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace Discord.Audio
namespace Discord.Audio.Opus
{
/// <summary> Opus codec wrapper. </summary>
internal class OpusDecoder : IDisposable
{
private readonly IntPtr _ptr;
{
#if NET45
[SuppressUnmanagedCodeSecurity]
#endif
private unsafe static class UnsafeNativeMethods
{
[DllImport("opus", EntryPoint = "opus_decoder_create", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateDecoder(int Fs, int channels, out OpusError error);
[DllImport("opus", EntryPoint = "opus_decoder_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroyDecoder(IntPtr decoder);
[DllImport("opus", EntryPoint = "opus_decode", CallingConvention = CallingConvention.Cdecl)]
public static extern int Decode(IntPtr st, byte* data, int len, byte[] pcm, int frame_size, int decode_fec);
}
private readonly IntPtr _ptr;
/// <summary> Gets the bit rate of the encoder. </summary>
public const int BitRate = 16;
@@ -22,7 +37,7 @@ namespace Discord.Audio
/// <summary> Gets the bytes per frame. </summary>
public int FrameSize { get; private set; }
/// <summary> Creates a new Opus encoder. </summary>
/// <summary> Creates a new Opus decoder. </summary>
/// <param name="samplingRate">Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000.</param>
/// <param name="channels">Number of channels (1 or 2) in input signal.</param>
/// <param name="frameLength">Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60</param>
@@ -44,45 +59,32 @@ namespace Discord.Audio
SamplesPerFrame = samplingRate / 1000 * FrameLength;
FrameSize = SamplesPerFrame * SampleSize;
Opus.Error error;
_ptr = Opus.CreateDecoder(samplingRate, channels, out error);
if (error != Opus.Error.OK)
OpusError error;
_ptr = UnsafeNativeMethods.CreateDecoder(samplingRate, channels, out error);
if (error != OpusError.OK)
throw new InvalidOperationException($"Error occured while creating decoder: {error}");
SetForwardErrorCorrection(true);
}
/// <summary> Produces Opus encoded audio from PCM samples. </summary>
/// <param name="input">PCM samples to encode.</param>
/// <param name="inputOffset">Offset of the frame in pcmSamples.</param>
/// <param name="output">Buffer to store the encoded frame.</param>
/// <returns>Length of the frame contained in outputBuffer.</returns>
public unsafe int DecodeFrame(byte[] input, int inputOffset, byte[] output)
/// <summary> Produces PCM samples from Opus-encoded audio. </summary>
/// <param name="input">PCM samples to decode.</param>
/// <param name="inputOffset">Offset of the frame in input.</param>
/// <param name="output">Buffer to store the decoded frame.</param>
/// <returns>Length of the frame contained in output.</returns>
public unsafe int DecodeFrame(byte[] input, int inputOffset, byte[] output)
{
if (disposed)
throw new ObjectDisposedException(nameof(OpusDecoder));
int result = 0;
fixed (byte* inPtr = input)
result = Opus.Encode(_ptr, inPtr + inputOffset, SamplesPerFrame, output, output.Length);
result = UnsafeNativeMethods.Decode(_ptr, inPtr + inputOffset, SamplesPerFrame, output, output.Length, 0);
if (result < 0)
throw new Exception("Decoding failed: " + ((Opus.Error)result).ToString());
throw new Exception("Decoding failed: " + ((OpusError)result).ToString());
return result;
}
/// <summary> Gets or sets whether Forward Error Correction is enabled. </summary>
public void SetForwardErrorCorrection(bool value)
{
if (disposed)
throw new ObjectDisposedException(nameof(OpusDecoder));
var result = Opus.EncoderCtl(_ptr, Opus.Ctl.SetInbandFECRequest, value ? 1 : 0);
if (result < 0)
throw new Exception("Decoder error: " + ((Opus.Error)result).ToString());
}
#region IDisposable
#region IDisposable
private bool disposed;
public void Dispose()
{
@@ -92,7 +94,7 @@ namespace Discord.Audio
GC.SuppressFinalize(this);
if (_ptr != IntPtr.Zero)
Opus.DestroyEncoder(_ptr);
UnsafeNativeMethods.DestroyDecoder(_ptr);
disposed = true;
}
@@ -100,6 +102,6 @@ namespace Discord.Audio
{
Dispose();
}
#endregion
#endregion
}
}

View File

@@ -1,11 +1,28 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace Discord.Audio
namespace Discord.Audio.Opus
{
/// <summary> Opus codec wrapper. </summary>
internal class OpusEncoder : IDisposable
{
private readonly IntPtr _ptr;
{
#if NET45
[SuppressUnmanagedCodeSecurity]
#endif
private unsafe static class UnsafeNativeMethods
{
[DllImport("opus", EntryPoint = "opus_encoder_create", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateEncoder(int Fs, int channels, int application, out OpusError error);
[DllImport("opus", EntryPoint = "opus_encoder_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroyEncoder(IntPtr encoder);
[DllImport("opus", EntryPoint = "opus_encode", CallingConvention = CallingConvention.Cdecl)]
public static extern int Encode(IntPtr st, byte* pcm, int frame_size, byte[] data, int max_data_bytes);
[DllImport("opus", EntryPoint = "opus_encoder_ctl", CallingConvention = CallingConvention.Cdecl)]
public static extern int EncoderCtl(IntPtr st, OpusCtl request, int value);
}
private readonly IntPtr _ptr;
/// <summary> Gets the bit rate of the encoder. </summary>
public const int BitsPerSample = 16;
@@ -24,7 +41,7 @@ namespace Discord.Audio
/// <summary> Gets the bit rate in kbit/s. </summary>
public int? BitRate { get; private set; }
/// <summary> Gets the coding mode of the encoder. </summary>
public Opus.Application Application { get; private set; }
public OpusApplication Application { get; private set; }
/// <summary> Creates a new Opus encoder. </summary>
/// <param name="samplingRate">Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000.</param>
@@ -33,7 +50,7 @@ namespace Discord.Audio
/// <param name="bitrate">Bitrate (kbit/s) used for this encoder. Supported Values: 1-512. Null will use the recommended bitrate. </param>
/// <param name="application">Coding mode.</param>
/// <returns>A new <c>OpusEncoder</c></returns>
public OpusEncoder(int samplingRate, int channels, int frameLength, int? bitrate, Opus.Application application)
public OpusEncoder(int samplingRate, int channels, int frameLength, int? bitrate, OpusApplication application)
{
if (samplingRate != 8000 && samplingRate != 12000 &&
samplingRate != 16000 && samplingRate != 24000 &&
@@ -53,9 +70,9 @@ namespace Discord.Audio
FrameSize = SamplesPerFrame * SampleSize;
BitRate = bitrate;
Opus.Error error;
_ptr = Opus.CreateEncoder(samplingRate, channels, (int)application, out error);
if (error != Opus.Error.OK)
OpusError error;
_ptr = UnsafeNativeMethods.CreateEncoder(samplingRate, channels, (int)application, out error);
if (error != OpusError.OK)
throw new InvalidOperationException($"Error occured while creating encoder: {error}");
SetForwardErrorCorrection(true);
@@ -75,10 +92,10 @@ namespace Discord.Audio
int result = 0;
fixed (byte* inPtr = input)
result = Opus.Encode(_ptr, inPtr + inputOffset, SamplesPerFrame, output, output.Length);
result = UnsafeNativeMethods.Encode(_ptr, inPtr + inputOffset, SamplesPerFrame, output, output.Length);
if (result < 0)
throw new Exception("Encoding failed: " + ((Opus.Error)result).ToString());
throw new Exception("Encoding failed: " + ((OpusError)result).ToString());
return result;
}
@@ -88,9 +105,9 @@ namespace Discord.Audio
if (disposed)
throw new ObjectDisposedException(nameof(OpusEncoder));
var result = Opus.EncoderCtl(_ptr, Opus.Ctl.SetInbandFECRequest, value ? 1 : 0);
var result = UnsafeNativeMethods.EncoderCtl(_ptr, OpusCtl.SetInbandFECRequest, value ? 1 : 0);
if (result < 0)
throw new Exception("Encoder error: " + ((Opus.Error)result).ToString());
throw new Exception("Encoder error: " + ((OpusError)result).ToString());
}
/// <summary> Gets or sets whether Forward Error Correction is enabled. </summary>
@@ -99,9 +116,9 @@ namespace Discord.Audio
if (disposed)
throw new ObjectDisposedException(nameof(OpusEncoder));
var result = Opus.EncoderCtl(_ptr, Opus.Ctl.SetBitrateRequest, value * 1000);
var result = UnsafeNativeMethods.EncoderCtl(_ptr, OpusCtl.SetBitrateRequest, value * 1000);
if (result < 0)
throw new Exception("Encoder error: " + ((Opus.Error)result).ToString());
throw new Exception("Encoder error: " + ((OpusError)result).ToString());
}
#region IDisposable
@@ -114,7 +131,7 @@ namespace Discord.Audio
GC.SuppressFinalize(this);
if (_ptr != IntPtr.Zero)
Opus.DestroyEncoder(_ptr);
UnsafeNativeMethods.DestroyEncoder(_ptr);
disposed = true;
}

View File

@@ -2,25 +2,4 @@
namespace Discord.Audio
{
internal unsafe static class Sodium
{
[DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)]
private static extern int SecretBoxEasy(byte* output, byte[] input, long inputLength, byte[] nonce, byte[] secret);
public static int Encrypt(byte[] input, long inputLength, byte[] output, int outputOffset, byte[] nonce, byte[] secret)
{
fixed (byte* outPtr = output)
return SecretBoxEasy(outPtr + outputOffset, input, inputLength, nonce, secret);
}
[DllImport("libsodium", EntryPoint = "crypto_secretbox_open_easy", CallingConvention = CallingConvention.Cdecl)]
private static extern int SecretBoxOpenEasy(byte[] output, byte* input, long inputLength, byte[] nonce, byte[] secret);
public static int Decrypt(byte[] input, int inputOffset, long inputLength, byte[] output, byte[] nonce, byte[] secret)
{
fixed (byte* inPtr = input)
return SecretBoxOpenEasy(output, inPtr + inputLength, inputLength, nonce, secret);
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Runtime.InteropServices;
using System.Security;
namespace Discord.Audio.Sodium
{
public unsafe static class SecretBox
{
#if NET45
[SuppressUnmanagedCodeSecurity]
#endif
private static class SafeNativeMethods
{
[DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)]
public static extern int SecretBoxEasy(byte* output, byte[] input, long inputLength, byte[] nonce, byte[] secret);
[DllImport("libsodium", EntryPoint = "crypto_secretbox_open_easy", CallingConvention = CallingConvention.Cdecl)]
public static extern int SecretBoxOpenEasy(byte[] output, byte* input, long inputLength, byte[] nonce, byte[] secret);
}
public static int Encrypt(byte[] input, long inputLength, byte[] output, int outputOffset, byte[] nonce, byte[] secret)
{
fixed (byte* outPtr = output)
return SafeNativeMethods.SecretBoxEasy(outPtr + outputOffset, input, inputLength, nonce, secret);
}
public static int Decrypt(byte[] input, int inputOffset, long inputLength, byte[] output, byte[] nonce, byte[] secret)
{
fixed (byte* inPtr = input)
return SafeNativeMethods.SecretBoxOpenEasy(output, inPtr + inputLength, inputLength, nonce, secret);
}
}
}