Skip to content

Commit

Permalink
Expose Opus data to VoiceNext receive events. This can be potentially…
Browse files Browse the repository at this point in the history
… cleaned up.
  • Loading branch information
Emzi0767 committed Feb 8, 2019
1 parent c7761ee commit 2cbb9ff
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 18 deletions.
2 changes: 1 addition & 1 deletion DSharpPlus.Test/TestBotVoiceCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 30,7 @@ private async Task OnVoiceReceived(VoiceReceiveEventArgs e)
var fs = this._ssrcFilemap[e.SSRC];

//e.Client.DebugLogger.LogMessage(LogLevel.Debug, "VNEXT RX", $"{e.User?.Username ?? "Unknown user"} sent voice data.", DateTime.Now);
var buff = e.Voice.ToArray();
var buff = e.PcmData.ToArray();
await fs.WriteAsync(buff, 0, buff.Length).ConfigureAwait(false);
//await fs.FlushAsync().ConfigureAwait(false);
}
Expand Down
9 changes: 7 additions & 2 deletions DSharpPlus.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 20,14 @@ public class VoiceReceiveEventArgs : DiscordEventArgs
public DiscordUser User { get; internal set; }

/// <summary>
/// Gets the received data.
/// Gets the received voice data, decoded to PCM format.
/// </summary>
public ReadOnlyMemory<byte> Voice { get; internal set; }
public ReadOnlyMemory<byte> PcmData { get; internal set; }

/// <summary>
/// Gets the received voice data, in Opus format. Note that for packets that were lost and/or compensated for, this will be empty.
/// </summary>
public ReadOnlyMemory<byte> OpusData { get; internal set; }

/// <summary>
/// Gets the format of the received PCM data.
Expand Down
33 changes: 18 additions & 15 deletions DSharpPlus.VoiceNext/VoiceNextConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 416,7 @@ private async Task VoiceSenderTask()
}

#if !NETSTANDARD1_1
private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> pcm, IList<ReadOnlyMemory<byte>> pcmPackets, out AudioSender voiceSender, out AudioFormat outputFormat)
private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> opus, ref Memory<byte> pcm, IList<ReadOnlyMemory<byte>> pcmPackets, out AudioSender voiceSender, out AudioFormat outputFormat)
{
voiceSender = null;
outputFormat = default;
Expand All @@ -439,24 439,24 @@ private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> pcm, IList<
this.Rtp.GetDataFromPacket(data, out var encryptedOpus, this.SelectedEncryptionMode);

var opusSize = Sodium.CalculateSourceSize(encryptedOpus);
var opusArray = ArrayPool<byte>.Shared.Rent(opusSize);
var opus = opusArray.AsSpan(0, opusSize);
opus = opus.Slice(0, opusSize);
var opusSpan = opus.Span;
try
{
this.Sodium.Decrypt(encryptedOpus, opus, nonce);
this.Sodium.Decrypt(encryptedOpus, opusSpan, nonce);

// Strip extensions, if any
if (hasExtension)
{
// RFC 5285, 4.2 One-Byte header
// http://www.rfcreader.com/#rfc5285_line186
if (opus[0] == 0xBE && opus[1] == 0xDE)
if (opusSpan[0] == 0xBE && opusSpan[1] == 0xDE)
{
var headerLen = opus[2] << 8 | opus[3];
var headerLen = opusSpan[2] << 8 | opusSpan[3];
var i = 4;
for (; i < headerLen 4; i )
{
var @byte = opus[i];
var @byte = opusSpan[i];

// ID is currently unused since we skip it anyway
//var id = (byte)(@byte >> 4);
Expand All @@ -466,10 466,10 @@ private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> pcm, IList<
}

// Strip extension padding too
while (opus[i] == 0)
while (opusSpan[i] == 0)
i ;

opus = opus.Slice(i);
opusSpan = opusSpan.Slice(i);
}

// TODO: consider implementing RFC 5285, 4.3. Two-Byte Header
Expand All @@ -480,7 480,7 @@ private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> pcm, IList<
var lastSampleCount = this.Opus.GetLastPacketSampleCount(vtx.Decoder);
var fecpcm = new byte[this.AudioFormat.SampleCountToSampleSize(lastSampleCount)];
var fecpcmMem = fecpcm.AsSpan();
this.Opus.Decode(vtx.Decoder, opus, ref fecpcmMem, true, out _);
this.Opus.Decode(vtx.Decoder, opusSpan, ref fecpcmMem, true, out _);
pcmPackets.Add(fecpcm.AsMemory(0, fecpcmMem.Length));
}
else if (gap > 1)
Expand All @@ -496,13 496,12 @@ private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> pcm, IList<
}

var pcmSpan = pcm.Span;
this.Opus.Decode(vtx.Decoder, opus, ref pcmSpan, false, out outputFormat);
this.Opus.Decode(vtx.Decoder, opusSpan, ref pcmSpan, false, out outputFormat);
pcm = pcm.Slice(0, pcmSpan.Length);
}
finally
{
vtx.LastSequence = sequence;
ArrayPool<byte>.Shared.Return(opusArray);
}

return true;
Expand All @@ -517,16 516,19 @@ private async Task ProcessVoicePacket(byte[] data)
{
var pcm = new byte[this.AudioFormat.CalculateMaximumFrameSize()];
var pcmMem = pcm.AsMemory();
var opus = new byte[pcm.Length];
var opusMem = opus.AsMemory();
var pcmFillers = new List<ReadOnlyMemory<byte>>();
if (!this.ProcessPacket(data, ref pcmMem, pcmFillers, out var vtx, out var audioFormat))
if (!this.ProcessPacket(data, ref opusMem, ref pcmMem, pcmFillers, out var vtx, out var audioFormat))
return;

foreach (var pcmFiller in pcmFillers)
await this._voiceReceived.InvokeAsync(new VoiceReceiveEventArgs(this.Discord)
{
SSRC = vtx.SSRC,
User = vtx.User,
Voice = pcmFiller,
PcmData = pcmFiller,
OpusData = new byte[0].AsMemory(),
AudioFormat = audioFormat,
AudioDuration = this.AudioFormat.CalculateSampleDuration(pcmFiller.Length)
}).ConfigureAwait(false);
Expand All @@ -535,7 537,8 @@ await this._voiceReceived.InvokeAsync(new VoiceReceiveEventArgs(this.Discord)
{
SSRC = vtx.SSRC,
User = vtx.User,
Voice = pcmMem,
PcmData = pcmMem,
OpusData = opusMem,
AudioFormat = audioFormat,
AudioDuration = this.AudioFormat.CalculateSampleDuration(pcmMem.Length)
}).ConfigureAwait(false);
Expand Down

0 comments on commit 2cbb9ff

Please sign in to comment.