Improve iOS voice stability and user audio controls
This commit is contained in:
@@ -48,16 +48,26 @@ internal sealed class ClientMediaTransport : IAsyncDisposable
|
||||
{
|
||||
while (!stop.IsCancellationRequested)
|
||||
{
|
||||
if (Environment.TickCount64 >= nextKeepalive)
|
||||
try
|
||||
{
|
||||
if (!bound.Task.IsCompleted) socket.Send(binding, SocketFlags.None);
|
||||
socket.Send(keepalive, SocketFlags.None);
|
||||
nextKeepalive = Environment.TickCount64 + (bound.Task.IsCompleted ? 5000 : 250);
|
||||
if (Environment.TickCount64 >= nextKeepalive)
|
||||
{
|
||||
if (!bound.Task.IsCompleted) socket.Send(binding, SocketFlags.None);
|
||||
socket.Send(keepalive, SocketFlags.None);
|
||||
nextKeepalive = Environment.TickCount64 + (bound.Task.IsCompleted ? 5000 : 250);
|
||||
}
|
||||
while (packets.TryRead(plain, out VoiceFrameHeader header, out int length))
|
||||
{
|
||||
int size = crypto.Encryptor.Encrypt(header, plain.AsSpan(0, length), packet);
|
||||
socket.Send(packet.AsSpan(0, size), SocketFlags.None);
|
||||
}
|
||||
}
|
||||
while (packets.TryRead(plain, out VoiceFrameHeader header, out int length))
|
||||
catch (SocketException exception) when (IsTransientNetworkError(exception))
|
||||
{
|
||||
int size = crypto.Encryptor.Encrypt(header, plain.AsSpan(0, length), packet);
|
||||
socket.Send(packet.AsSpan(0, size), SocketFlags.None);
|
||||
// iOS can briefly lose its UDP route while Wi-Fi and cellular switch.
|
||||
// Keep the sender and socket alive so the next route can carry media.
|
||||
nextKeepalive = 0;
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
@@ -77,6 +87,8 @@ internal sealed class ClientMediaTransport : IAsyncDisposable
|
||||
int length;
|
||||
try { length = await socket.ReceiveAsync(packet, SocketFlags.None, stop.Token).ConfigureAwait(false); }
|
||||
catch (SocketException exception) when (exception.SocketErrorCode is SocketError.ConnectionReset or SocketError.MessageSize) { continue; }
|
||||
catch (SocketException exception) when (IsTransientNetworkError(exception))
|
||||
{ await Task.Delay(100, stop.Token).ConfigureAwait(false); continue; }
|
||||
if (!VoiceFrameHeader.TryRead(packet.AsSpan(0, length), out var candidate)) continue;
|
||||
if (candidate.Type == MediaFrameType.Keepalive && length == VoiceFrameHeader.Size) { bound.TrySetResult(); continue; }
|
||||
if (candidate.Type != MediaFrameType.Voice || candidate.Codec != 0 ||
|
||||
@@ -89,6 +101,10 @@ internal sealed class ClientMediaTransport : IAsyncDisposable
|
||||
finally { bound.TrySetCanceled(); stop.Cancel(); }
|
||||
}
|
||||
|
||||
private static bool IsTransientNetworkError(SocketException exception) => exception.SocketErrorCode is
|
||||
SocketError.NetworkDown or SocketError.NetworkUnreachable or SocketError.HostUnreachable or
|
||||
SocketError.AddressNotAvailable or SocketError.NotConnected or SocketError.ConnectionReset;
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
stop.Cancel(); socket.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user