/// <summary> /// Initialize the underlying WebRTC peer connection. /// </summary> /// <remarks> /// This method is asynchronous and completes its task when the initializing completed. /// On successful completion, it also trigger the <see cref="OnInitialized"/> event. /// Note however that this completion is free-threaded and complete immediately when the /// underlying peer connection is initialized, whereas any <see cref="OnInitialized"/> /// event handler is invoked when control returns to the main Unity app thread. The former /// is faster, but does not allow accessing the underlying peer connection because it /// returns before <see cref="OnPostInitialize"/> executed. Therefore it is generally /// recommended to listen to the <see cref="OnInitialized"/> event, and ignore the returned /// <see xref="System.Threading.Tasks.Task"/> object. /// </remarks> private async Task <WebRTC.PeerConnection> InitializePluginAsync(CancellationToken token) { // Ensure Android binding is initialized before accessing the native implementation Android.Initialize(); #if UNITY_WSA && !UNITY_EDITOR if (Library.UsedAudioDeviceModule == AudioDeviceModule.LegacyModule) { // Preventing access to audio crashes the ADM1 at startup and the entire application. bool permissionGranted = await UwpUtils.RequestAccessAsync(StreamingCaptureMode.Audio); if (!permissionGranted) { return(null); } } #endif // Create the peer connection managed wrapper and its native implementation var nativePeer = new WebRTC.PeerConnection(); nativePeer.AudioTrackAdded += (RemoteAudioTrack track) => { // Tracks will be output by AudioReceivers, so avoid outputting them twice. track.OutputToDevice(false); }; Debug.Log("Initializing WebRTC Peer Connection..."); var config = new PeerConnectionConfiguration(); foreach (var server in IceServers) { config.IceServers.Add(new IceServer { Urls = { server.ToString() }, TurnUserName = IceUsername, TurnPassword = IceCredential }); } try { await nativePeer.InitializeAsync(config, token); return(nativePeer); } catch (OperationCanceledException canceled) { throw canceled; } catch (Exception ex) { nativePeer.Dispose(); token.ThrowIfCancellationRequested(); EnsureIsMainAppThread(); var errorMessage = new StringBuilder(); errorMessage.Append("WebRTC plugin initializing failed. See full log for exception details.\n"); errorMessage.Append($"Exception: {ex.Message}"); OnError.Invoke(errorMessage.ToString()); throw ex; } }
private Task <bool> RequestAccessAsync(CancellationToken token) { #if !UNITY_EDITOR && UNITY_ANDROID // Ensure Android binding is initialized before accessing the native implementation Android.Initialize(); // Check for permission to access the camera if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) { // Display dialog requesting user permission. This will return immediately, // and unfortunately there's no good way to tell when this completes. Permission.RequestUserPermission(Permission.Microphone); // As a rule of thumb, application should lose focus, so check when focus resumes should // be sufficient without having to poll every frame. // Monitor the OnApplicationFocus(true) event during the next 5 minutes, // and check for permission again each time var tcs = new TaskCompletionSource <bool>(); lock (_androidPermissionRequestLock) { Debug.Assert(_androidPermissionRequestTcs == null); _androidPermissionRequestTcs = tcs; } Task.Delay(TimeSpan.FromMinutes(5)).ContinueWith(_ => { lock (_androidPermissionRequestLock) { // Check if the component is still waiting on the same permission request. // If it has been disabled and then re-enabled, _androidPermissionRequestTcs will be different. if (_androidPermissionRequestTcs == tcs) { Debug.LogError("User denied RecordAudio (microphone) permission; cannot use MicrophoneSource."); _androidPermissionRequestTcs.SetResult(false); _androidPermissionRequestTcs = null; } } }); // If the initialization is canceled, end the task and reset the TCS. token.Register(() => { lock (_androidPermissionRequestLock) { // Check if the component is still waiting on the same permission request. // If the request has completed or timed out, _androidPermissionRequestTcs will be null. if (_androidPermissionRequestTcs != null) { Debug.Assert(_androidPermissionRequestTcs == tcs); _androidPermissionRequestTcs.SetCanceled(); _androidPermissionRequestTcs = null; } } }); return(tcs.Task); } // Already has permission. return(Task.FromResult(true)); #elif UNITY_WSA && !UNITY_EDITOR return(UwpUtils.RequestAccessAsync(StreamingCaptureMode.Audio)); #else return(Task.FromResult(true)); #endif }