mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
Namespace changes to make them make sense
This commit is contained in:
207
src/NadekoBot.Voice/VoiceClient.cs
Normal file
207
src/NadekoBot.Voice/VoiceClient.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace NadekoBot.Voice
|
||||
{
|
||||
public sealed class VoiceClient : IDisposable
|
||||
{
|
||||
delegate int EncodeDelegate(Span<byte> input, byte[] output);
|
||||
|
||||
private readonly int sampleRate;
|
||||
private readonly int bitRate;
|
||||
private readonly int channels;
|
||||
private readonly int frameDelay;
|
||||
private readonly int bitDepth;
|
||||
|
||||
public LibOpusEncoder Encoder { get; }
|
||||
private readonly ArrayPool<byte> _arrayPool;
|
||||
|
||||
public int BitDepth => bitDepth * 8;
|
||||
public int Delay => frameDelay;
|
||||
|
||||
private int FrameSizePerChannel => Encoder.FrameSizePerChannel;
|
||||
public int InputLength => FrameSizePerChannel * channels * bitDepth;
|
||||
|
||||
EncodeDelegate Encode;
|
||||
|
||||
// https://github.com/xiph/opus/issues/42 w
|
||||
public VoiceClient(SampleRate sampleRate = SampleRate._48k,
|
||||
Bitrate bitRate = Bitrate._192k,
|
||||
Channels channels = Channels.Two,
|
||||
FrameDelay frameDelay = FrameDelay.Delay20,
|
||||
BitDepthEnum bitDepthEnum = BitDepthEnum.Float32)
|
||||
{
|
||||
this.frameDelay = (int) frameDelay;
|
||||
this.sampleRate = (int) sampleRate;
|
||||
this.bitRate = (int) bitRate;
|
||||
this.channels = (int) channels;
|
||||
this.bitDepth = (int) bitDepthEnum;
|
||||
|
||||
this.Encoder = new(this.sampleRate, this.channels, this.bitRate, this.frameDelay);
|
||||
|
||||
Encode = bitDepthEnum switch
|
||||
{
|
||||
BitDepthEnum.Float32 => Encoder.EncodeFloat,
|
||||
BitDepthEnum.UInt16 => Encoder.Encode,
|
||||
_ => throw new NotSupportedException(nameof(BitDepth))
|
||||
};
|
||||
|
||||
if (bitDepthEnum == BitDepthEnum.Float32)
|
||||
{
|
||||
Encode = Encoder.EncodeFloat;
|
||||
}
|
||||
else
|
||||
{
|
||||
Encode = Encoder.Encode;
|
||||
}
|
||||
|
||||
_arrayPool = ArrayPool<byte>.Shared;
|
||||
}
|
||||
|
||||
public int SendPcmFrame(VoiceGateway gw, Span<byte> data, int offset, int count)
|
||||
{
|
||||
var secretKey = gw.SecretKey;
|
||||
if (secretKey.Length == 0)
|
||||
{
|
||||
return (int) SendPcmError.SecretKeyUnavailable;
|
||||
}
|
||||
|
||||
// encode using opus
|
||||
var encodeOutput = _arrayPool.Rent(LibOpusEncoder.MaxData);
|
||||
try
|
||||
{
|
||||
var encodeOutputLength = Encode(data, encodeOutput);
|
||||
return SendOpusFrame(gw, encodeOutput, 0, encodeOutputLength);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_arrayPool.Return(encodeOutput);
|
||||
}
|
||||
}
|
||||
|
||||
public int SendOpusFrame(VoiceGateway gw, byte[] data, int offset, int count)
|
||||
{
|
||||
var secretKey = gw.SecretKey;
|
||||
if (secretKey is null)
|
||||
{
|
||||
return (int) SendPcmError.SecretKeyUnavailable;
|
||||
}
|
||||
|
||||
// form RTP header
|
||||
var headerLength = 1 // version + flags
|
||||
+ 1 // payload type
|
||||
+ 2 // sequence
|
||||
+ 4 // timestamp
|
||||
+ 4; // ssrc
|
||||
|
||||
var header = new byte[headerLength];
|
||||
|
||||
header[0] = 0x80; // version + flags
|
||||
header[1] = 0x78; // payload type
|
||||
|
||||
// get byte values for header data
|
||||
var seqBytes = BitConverter.GetBytes(gw.Sequence); // 2
|
||||
var nonceBytes = BitConverter.GetBytes(gw.NonceSequence); // 2
|
||||
var timestampBytes = BitConverter.GetBytes(gw.Timestamp); // 4
|
||||
var ssrcBytes = BitConverter.GetBytes(gw.Ssrc); // 4
|
||||
|
||||
gw.Timestamp += (uint) FrameSizePerChannel;
|
||||
gw.Sequence++;
|
||||
gw.NonceSequence++;
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(seqBytes);
|
||||
Array.Reverse(nonceBytes);
|
||||
Array.Reverse(timestampBytes);
|
||||
Array.Reverse(ssrcBytes);
|
||||
}
|
||||
|
||||
// copy headers
|
||||
Buffer.BlockCopy(seqBytes, 0, header, 2, 2);
|
||||
Buffer.BlockCopy(timestampBytes, 0, header, 4, 4);
|
||||
Buffer.BlockCopy(ssrcBytes, 0, header, 8, 4);
|
||||
|
||||
//// encryption part
|
||||
//// create a byte array where to store the encrypted data
|
||||
//// it has to be inputLength + crypto_secretbox_MACBYTES (constant with value 16)
|
||||
var encryptedBytes = new byte[count + 16];
|
||||
|
||||
//// form nonce with header + 12 empty bytes
|
||||
//var nonce = new byte[24];
|
||||
//Buffer.BlockCopy(rtpHeader, 0, nonce, 0, rtpHeader.Length);
|
||||
|
||||
var nonce = new byte[4];
|
||||
Buffer.BlockCopy(seqBytes, 0, nonce, 2, 2);
|
||||
|
||||
Sodium.Encrypt(data, 0, count, encryptedBytes, 0, nonce, secretKey);
|
||||
|
||||
var rtpDataLength = headerLength + encryptedBytes.Length + nonce.Length;
|
||||
var rtpData = _arrayPool.Rent(rtpDataLength);
|
||||
try
|
||||
{
|
||||
//copy headers
|
||||
Buffer.BlockCopy(header, 0, rtpData, 0, header.Length);
|
||||
//copy audio data
|
||||
Buffer.BlockCopy(encryptedBytes, 0, rtpData, header.Length, encryptedBytes.Length);
|
||||
Buffer.BlockCopy(nonce, 0, rtpData, rtpDataLength - 4, 4);
|
||||
|
||||
gw.SendRtpData(rtpData, rtpDataLength);
|
||||
// FUTURE When there's a break in the sent data,
|
||||
// the packet transmission shouldn't simply stop.
|
||||
// Instead, send five frames of silence (0xF8, 0xFF, 0xFE)
|
||||
// before stopping to avoid unintended Opus interpolation
|
||||
// with subsequent transmissions.
|
||||
|
||||
return rtpDataLength;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_arrayPool.Return(rtpData);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> Encoder.Dispose();
|
||||
}
|
||||
|
||||
public enum SendPcmError
|
||||
{
|
||||
SecretKeyUnavailable = -1,
|
||||
}
|
||||
|
||||
|
||||
public enum FrameDelay
|
||||
{
|
||||
Delay5 = 5,
|
||||
Delay10 = 10,
|
||||
Delay20 = 20,
|
||||
Delay40 = 40,
|
||||
Delay60 = 60,
|
||||
}
|
||||
|
||||
public enum BitDepthEnum
|
||||
{
|
||||
UInt16 = sizeof(UInt16),
|
||||
Float32 = sizeof(float),
|
||||
}
|
||||
|
||||
public enum SampleRate
|
||||
{
|
||||
_48k = 48_000,
|
||||
}
|
||||
|
||||
public enum Bitrate
|
||||
{
|
||||
_64k = 64 * 1024,
|
||||
_96k = 96 * 1024,
|
||||
_128k = 128 * 1024,
|
||||
_192k = 192 * 1024,
|
||||
}
|
||||
|
||||
public enum Channels
|
||||
{
|
||||
One = 1,
|
||||
Two = 2,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user