Stabilize iOS reconnect and screen audio sessions
This commit is contained in:
@@ -51,6 +51,9 @@ internal sealed class AppModel
|
||||
internal bool IsConnecting { get; private set; }
|
||||
internal bool VoiceJoined => microphoneStream != 0;
|
||||
internal bool ScreenSharing => broadcast?.IsActive == true;
|
||||
#if DEBUG
|
||||
internal bool ScreenRingMapped => broadcast?.IsMappingOpen == true;
|
||||
#endif
|
||||
internal bool IsBackgrounded => backgrounded;
|
||||
internal float MicrophoneLevel { get; private set; }
|
||||
internal string Status { get; private set; } = "Not connected";
|
||||
@@ -162,7 +165,7 @@ internal sealed class AppModel
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Connection failed: {exception}");
|
||||
Console.Error.WriteLine($"VoiceCat connection setup failed: {exception}");
|
||||
IsConnecting = false; Status = exception.Message;
|
||||
if (ReferenceEquals(client, next)) { client = null; await StopSessionResourcesAsync(releaseAudio: !restoring); }
|
||||
await next.DisposeAsync();
|
||||
@@ -270,15 +273,35 @@ internal sealed class AppModel
|
||||
if (microphoneStream != 0)
|
||||
{
|
||||
IosAudioEngine.Shared.StopMicrophone(); active.StopStream(microphoneStream); microphoneStream = 0;
|
||||
await active.SubscribeVoiceAsync(false); MicrophoneLevel = 0; feedback.Play(SoundEvent.VoiceOff); Notify(); return;
|
||||
if (!ScreenSharing) await active.SubscribeVoiceAsync(false);
|
||||
MicrophoneLevel = 0; feedback.Play(SoundEvent.VoiceOff); Notify(); return;
|
||||
}
|
||||
if (AVFoundation.AVCaptureDevice.GetAuthorizationStatus(AVFoundation.AVAuthorizationMediaType.Audio) == AVFoundation.AVAuthorizationStatus.NotDetermined)
|
||||
await AVFoundation.AVCaptureDevice.RequestAccessForMediaTypeAsync(AVFoundation.AVAuthorizationMediaType.Audio);
|
||||
AVFoundation.AVAuthorizationStatus authorization = AVFoundation.AVCaptureDevice.GetAuthorizationStatus(AVFoundation.AVAuthorizationMediaType.Audio);
|
||||
if (authorization == AVFoundation.AVAuthorizationStatus.NotDetermined)
|
||||
authorization = await AVFoundation.AVCaptureDevice.RequestAccessForMediaTypeAsync(AVFoundation.AVAuthorizationMediaType.Audio)
|
||||
? AVFoundation.AVAuthorizationStatus.Authorized : AVFoundation.AVAuthorizationStatus.Denied;
|
||||
if (authorization != AVFoundation.AVAuthorizationStatus.Authorized)
|
||||
throw new InvalidOperationException("Microphone access is required to join voice. Allow it in iOS Settings.");
|
||||
VoiceSubscriptionResult subscribed = await active.SubscribeVoiceAsync();
|
||||
if (!subscribed.Ok) throw new InvalidOperationException(subscribed.Error);
|
||||
int channels = IosAudioRouter.Shared.CaptureChannels;
|
||||
StreamInfo stream = await active.StartStreamAsync(StreamKind.StreamMic, "Microphone", channels);
|
||||
microphoneStream = stream.StreamId; IosAudioEngine.Shared.StartMicrophone(stream.StreamId, channels); feedback.Play(SoundEvent.VoiceOn); Notify();
|
||||
uint startedStream = 0;
|
||||
try
|
||||
{
|
||||
int channels = IosAudioRouter.Shared.CaptureChannels;
|
||||
StreamInfo stream = await active.StartStreamAsync(StreamKind.StreamMic, "Microphone", channels);
|
||||
startedStream = stream.StreamId;
|
||||
IosAudioEngine.Shared.StartMicrophone(stream.StreamId, channels);
|
||||
microphoneStream = stream.StreamId; feedback.Play(SoundEvent.VoiceOn); Notify();
|
||||
}
|
||||
catch
|
||||
{
|
||||
IosAudioEngine.Shared.StopMicrophone();
|
||||
if (startedStream != 0)
|
||||
try { active.StopStream(startedStream); } catch (Exception exception) when (exception is IOException or InvalidOperationException or ObjectDisposedException) { }
|
||||
if (!ScreenSharing)
|
||||
try { await active.SubscribeVoiceAsync(false); } catch (Exception exception) when (exception is IOException or InvalidOperationException or ObjectDisposedException) { }
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal void ToggleScreenAudio()
|
||||
@@ -390,10 +413,9 @@ internal sealed class AppModel
|
||||
{
|
||||
UIApplication.SharedApplication.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
// Presenting either system picker can replace or interrupt the app's audio session.
|
||||
// Rebuild after the producer becomes active so playback and an existing mic tap are
|
||||
// attached to the session that will remain in use for the broadcast.
|
||||
if (ScreenSharing) IosAudioRouter.Shared.Recover("screen sharing started");
|
||||
// Presenting either picker can change the audio route. Keep a healthy graph; if the
|
||||
// picker stopped it, Recover resumes the same graph or rebuilds for changed hardware.
|
||||
IosAudioRouter.Shared.Recover(ScreenSharing ? "screen sharing started" : "screen sharing stopped", force: false);
|
||||
Notify();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user