Skip to content

Commit dd49057

Browse files
authored
Refactor voice receiving (#283)
* Refactor voice receiving * Add DoNotParallelize attribute to idle timeout tests * Fix a test * Update BufferedVoiceReceiveHandlerTests to support async operations * Improve tests and the buffered handler * feat: Enhance voice handling with loss correction capabilities - Updated VoiceReceiveEventArgs to include a canCorrectLoss flag for better loss management. - Modified OpusDecoder methods to accept a decodeFec parameter for handling forward error correction. - Adjusted OpusDecodeStream to utilize the new OpusDecoder methods. - Enhanced VoiceClient to invoke events with loss correction information. - Refactored VoiceReceiveData to use flags for packet status, including canCorrectLoss. - Updated BufferedVoiceReceiveHandlerTests to validate new loss correction behavior. - Added tests for canCorrectLoss scenarios to ensure proper handling of lost packets. - Adjusted VoiceCommands to utilize updated decoding logic for handling voice frames. * Correct (I believe) logic for handling lost packets in BufferedVoiceReceiveHandler * Refactor * Update buffered voice receive handler * Improve voice receive handler * Add FEC support * Add FEC handling to BufferedVoiceReceiveHandlerTests * Improve EvictLostFrames and rewrite and improve tests * Refactor VoiceReceiveEventArgs and LostVoiceReceiveEventArgs for clarity and improved FEC handling; update VoiceCommands to streamline lost frame handling * Refactor test voice commands * Replace delegate with Action for VoiceReceive event in VoiceReceiveHandler * Remove commented-out code and unnecessary console output in VoiceClient and BufferedVoiceReceiveHandler * Remove unused using directive from BufferedVoiceReceiveHandler * Optimize buffered voice receive handler state allocations * Refactor window and buffer range checks in BufferedVoiceReceiveHandler for clarity and correctness * Rename stop timer to timeout timer and improve datagram length validation in VoiceClient * Refactor BufferedVoiceReceiveHandler to use VoiceReceiveData for improved clarity and data handling * Remove BufferedVoiceReceiveHandler * Cleanup * Simplify voice receive event args * Abandon VoiceReceiveHandler * Fix voice guide snippet * Remove allow unsafe blocks from NetCord.Test.csproj * Make voice receiving exception safe against malformed payloads (hopefully) * Skip decryption when there is no VoiceReceive event handler and remove a useless extension range check * Fix voice receive logging and refactor * Add a new line * Add a comment to ReceiveVoice in guide snippet * Make IP Discovery mandatory * Optimize logging * Improve a guides snippet comment and refactor VoiceClient slightly
1 parent df3ec70 commit dd49057

11 files changed

Lines changed: 177 additions & 201 deletions

File tree

Documentation/guides/basic-concepts/Voice/VoiceModule.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ public async Task<string> EchoAsync()
129129
voiceState.ChannelId.GetValueOrDefault(),
130130
new VoiceClientConfiguration
131131
{
132-
ReceiveHandler = new VoiceReceiveHandler(), // Required to receive voice
133132
Logger = new ConsoleLogger(),
134133
});
135134

@@ -141,14 +140,9 @@ public async Task<string> EchoAsync()
141140

142141
voiceClient.VoiceReceive += args =>
143142
{
144-
// If the timestamp is null, the packet was lost.
145-
// We skip it, which mirrors the packet loss to the echo recipients.
146-
if (args.Timestamp is not { } timestamp)
147-
return default;
148-
149-
// Pass current user voice directly to SendAsync to create echo
143+
// Send the received voice back if the received voice is from the user that invoked the command
150144
if (voiceClient.Cache.SsrcUsers.TryGetValue(args.Ssrc, out var voiceUserId) && voiceUserId == userId)
151-
voiceClient.SendVoice(args.SequenceNumber, timestamp, args.Frame);
145+
voiceClient.SendVoice(args.SequenceNumber, args.Timestamp, args.Frame);
152146

153147
return default;
154148
};

Documentation/guides/basic-concepts/voice.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ Follow the [installation guide](installing-native-dependencies.md) to install th
1313
[!code-cs[VoiceModule.cs](Voice/VoiceModule.cs#L13-L110)]
1414

1515
### Receiving Voice
16-
[!code-cs[VoiceModule.cs](Voice/VoiceModule.cs#L112-L158)]
16+
[!code-cs[VoiceModule.cs](Voice/VoiceModule.cs#L112-L152)]
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
1+
using System.Runtime.InteropServices;
2+
13
namespace NetCord.Gateway.Voice;
24

3-
public readonly ref struct VoiceReceiveEventArgs(byte[]? buffer, int frameIndex, int frameLength, uint ssrc, uint? timestamp, ushort sequenceNumber)
5+
[StructLayout(LayoutKind.Auto)]
6+
public readonly ref struct VoiceReceiveEventArgs
47
{
5-
internal readonly byte[]? _buffer = buffer;
8+
public VoiceReceiveEventArgs(ReadOnlySpan<byte> frame, uint ssrc, uint timestamp, ushort sequenceNumber)
9+
{
10+
Frame = frame;
11+
Ssrc = ssrc;
12+
Timestamp = timestamp;
13+
SequenceNumber = sequenceNumber;
14+
}
615

716
/// <summary>
817
/// The voice frame data.
918
/// </summary>
10-
public ReadOnlySpan<byte> Frame => new(_buffer, frameIndex, frameLength);
19+
public readonly ReadOnlySpan<byte> Frame { get; }
1120

1221
/// <summary>
1322
/// The synchronization source (SSRC) of the sender of the voice frame.
1423
/// </summary>
15-
public uint Ssrc => ssrc;
24+
public readonly uint Ssrc { get; }
1625

1726
/// <summary>
18-
/// The timestamp of the voice frame. <see langword="null"/> when the frame was lost.
27+
/// The timestamp of the voice frame.
1928
/// </summary>
20-
public uint? Timestamp => timestamp;
29+
public readonly uint Timestamp { get; }
2130

2231
/// <summary>
2332
/// The sequence number of the voice frame.
2433
/// </summary>
25-
public ushort SequenceNumber => sequenceNumber;
34+
public readonly ushort SequenceNumber { get; }
2635
}

NetCord/Gateway/Voice/IVoiceReceiveHandler.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

NetCord/Gateway/Voice/NullVoiceReceiveHandler.cs

Lines changed: 0 additions & 17 deletions
This file was deleted.

NetCord/Gateway/Voice/OpusDecoder.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ public OpusDecoder(VoiceChannels channels)
3333
/// <param name="data">Input payload. Use <see langword="null"/> to indicate packet loss.</param>
3434
/// <param name="pcm">Output signal.</param>
3535
/// <param name="frameSize">Number of samples per channel in the output signal.</param>
36+
/// <param name="decodeFec">Whether to decode using forward error correction data, if available.</param>
3637
/// <returns>The number of decoded samples per channel.</returns>
37-
public int Decode(ReadOnlySpan<byte> data, Span<byte> pcm, int frameSize)
38+
public int Decode(ReadOnlySpan<byte> data, Span<byte> pcm, int frameSize, bool decodeFec)
3839
{
3940
ValidatePcm(pcm.Length, frameSize, PcmFormat.Short, _channels, nameof(pcm));
4041

41-
int result = OpusDecode(_decoder, data, data.Length, pcm, frameSize, 0);
42+
int result = OpusDecode(_decoder, data, data.Length, pcm, frameSize, decodeFec ? 1 : 0);
4243

4344
ValidateResult(result);
4445

@@ -51,10 +52,11 @@ public int Decode(ReadOnlySpan<byte> data, Span<byte> pcm, int frameSize)
5152
/// <param name="data">Input payload. Use <see langword="null"/> to indicate packet loss.</param>
5253
/// <param name="pcm">Output signal.</param>
5354
/// <param name="frameSize">Number of samples per channel in the output signal.</param>
55+
/// <param name="decodeFec">Whether to decode using forward error correction data, if available.</param>
5456
/// <returns>The number of decoded samples per channel.</returns>
55-
public int Decode(ReadOnlySpan<byte> data, Span<short> pcm, int frameSize)
57+
public int Decode(ReadOnlySpan<byte> data, Span<short> pcm, int frameSize, bool decodeFec)
5658
{
57-
return Decode(data, MemoryMarshal.AsBytes(pcm), frameSize);
59+
return Decode(data, MemoryMarshal.AsBytes(pcm), frameSize, decodeFec);
5860
}
5961

6062
/// <summary>
@@ -63,12 +65,13 @@ public int Decode(ReadOnlySpan<byte> data, Span<short> pcm, int frameSize)
6365
/// <param name="data">Input payload. Use <see langword="null"/> to indicate packet loss.</param>
6466
/// <param name="pcm">Output signal.</param>
6567
/// <param name="frameSize">Number of samples per channel in the output signal.</param>
68+
/// <param name="decodeFec">Whether to decode using forward error correction data, if available.</param>
6669
/// <returns>The number of decoded samples per channel.</returns>
67-
public int DecodeFloat(ReadOnlySpan<byte> data, Span<byte> pcm, int frameSize)
70+
public int DecodeFloat(ReadOnlySpan<byte> data, Span<byte> pcm, int frameSize, bool decodeFec)
6871
{
6972
ValidatePcm(pcm.Length, frameSize, PcmFormat.Float, _channels, nameof(pcm));
7073

71-
int result = OpusDecodeFloat(_decoder, data, data.Length, pcm, frameSize, 0);
74+
int result = OpusDecodeFloat(_decoder, data, data.Length, pcm, frameSize, decodeFec ? 1 : 0);
7275

7376
ValidateResult(result);
7477

@@ -81,10 +84,11 @@ public int DecodeFloat(ReadOnlySpan<byte> data, Span<byte> pcm, int frameSize)
8184
/// <param name="data">Input payload. Use <see langword="null"/> to indicate packet loss.</param>
8285
/// <param name="pcm">Output signal.</param>
8386
/// <param name="frameSize">Number of samples per channel in the output signal.</param>
87+
/// <param name="decodeFec">Whether to decode using forward error correction data, if available.</param>
8488
/// <returns>The number of decoded samples per channel.</returns>
85-
public int DecodeFloat(ReadOnlySpan<byte> data, Span<float> pcm, int frameSize)
89+
public int DecodeFloat(ReadOnlySpan<byte> data, Span<float> pcm, int frameSize, bool decodeFec)
8690
{
87-
return DecodeFloat(data, MemoryMarshal.AsBytes(pcm), frameSize);
91+
return DecodeFloat(data, MemoryMarshal.AsBytes(pcm), frameSize, decodeFec);
8892
}
8993

9094
public void Dispose()

NetCord/Gateway/Voice/Streams/OpusDecodeStream.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ public OpusDecodeStream(Stream next, PcmFormat format, VoiceChannels channels, b
3636

3737
private int Decode(ReadOnlySpan<byte> data, Span<byte> pcm)
3838
{
39-
return _decoder.Decode(data, pcm, _frameSize);
39+
return _decoder.Decode(data, pcm, _frameSize, false);
4040
}
4141

4242
private int DecodeFloat(ReadOnlySpan<byte> data, Span<byte> pcm)
4343
{
44-
return _decoder.DecodeFloat(data, pcm, _frameSize);
44+
return _decoder.DecodeFloat(data, pcm, _frameSize, false);
4545
}
4646

4747
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)

0 commit comments

Comments
 (0)