private Task GetVideoCodecs() { var task = Task.Run(() => { #if NETFX_CORE var videoCodecList = WebRTC.GetVideoCodecs().OrderBy(CodecInfo => { switch (CodecInfo.Name) { case "VP8": return(1); case "VP9": return(2); case "H264": return(3); default: return(99); } }); lock (_videoLock) { foreach (var videoCodec in videoCodecList) { VideoCodecs.Add(videoCodec); } } #endif }); return(task); }
public async Task ConfigureRtcAsync() { var settings = ApplicationData.Current.LocalSettings; var videoDeviceId = string.Empty; if (settings.Values.ContainsKey(MediaSettingsIds.VideoDeviceSettings)) { videoDeviceId = (string)settings.Values[MediaSettingsIds.VideoDeviceSettings]; } var videoDevices = Media.GetVideoCaptureDevices(); var selectedVideoDevice = videoDevices.FirstOrDefault(d => d.Id.Equals(videoDeviceId)); selectedVideoDevice = selectedVideoDevice ?? videoDevices.FirstOrDefault(); if (selectedVideoDevice != null) { Media.SelectVideoDevice(selectedVideoDevice); } if (settings.Values.ContainsKey(MediaSettingsIds.AudioDeviceSettings)) { var audioDeviceId = (string)settings.Values[MediaSettingsIds.AudioDeviceSettings]; var audioDevices = Media.GetAudioCaptureDevices(); var selectedAudioDevice = audioDevices.FirstOrDefault(d => d.Id.Equals(audioDeviceId)); if (selectedAudioDevice == null) { settings.Values.Remove(MediaSettingsIds.AudioDeviceSettings); } Media.SelectAudioCaptureDevice(selectedAudioDevice); } else { Media.SelectAudioCaptureDevice(null); } if (settings.Values.ContainsKey(MediaSettingsIds.AudioPlayoutDeviceSettings)) { var audioPlayoutDeviceId = (string)settings.Values[MediaSettingsIds.AudioPlayoutDeviceSettings]; var audioPlayoutDevices = Media.GetAudioPlayoutDevices(); var selectedAudioPlayoutDevice = audioPlayoutDevices.FirstOrDefault(d => d.Id.Equals(audioPlayoutDeviceId)); if (selectedAudioPlayoutDevice == null) { settings.Values.Remove(MediaSettingsIds.AudioPlayoutDeviceSettings); } Media.SelectAudioPlayoutDevice(selectedAudioPlayoutDevice); } else { Media.SelectAudioPlayoutDevice(null); } var videoCodecId = int.MinValue; if (settings.Values.ContainsKey(MediaSettingsIds.VideoCodecSettings)) { videoCodecId = (int)settings.Values[MediaSettingsIds.VideoCodecSettings]; } var videoCodecs = WebRTC.GetVideoCodecs(); var selectedVideoCodec = videoCodecs.FirstOrDefault(c => c.Id.Equals(videoCodecId)); await Hub.Instance.MediaSettingsChannel.SetVideoCodecAsync( (selectedVideoCodec ?? videoCodecs.FirstOrDefault()).ToDto()); var audioCodecId = int.MinValue; if (settings.Values.ContainsKey(MediaSettingsIds.AudioCodecSettings)) { audioCodecId = (int)settings.Values[MediaSettingsIds.AudioCodecSettings]; } var audioCodecs = WebRTC.GetAudioCodecs(); var selectedAudioCodec = audioCodecs.FirstOrDefault(c => c.Id.Equals(audioCodecId)); await Hub.Instance.MediaSettingsChannel.SetAudioCodecAsync( (selectedAudioCodec ?? audioCodecs.FirstOrDefault()).ToDto()); if (settings.Values.ContainsKey(MediaSettingsIds.PreferredVideoCaptureWidth) && settings.Values.ContainsKey(MediaSettingsIds.PreferredVideoCaptureHeight) && settings.Values.ContainsKey(MediaSettingsIds.PreferredVideoCaptureFrameRate)) { WebRTC.SetPreferredVideoCaptureFormat( (int)settings.Values[MediaSettingsIds.PreferredVideoCaptureWidth], (int)settings.Values[MediaSettingsIds.PreferredVideoCaptureHeight], (int)settings.Values[MediaSettingsIds.PreferredVideoCaptureFrameRate]); } }
public void Initialize() { WebRTC.Initialize(_uiDispatcher); Conductor.Instance.ETWStatsEnabled = false; Cameras = new ObservableCollection <MediaDevice>(); Microphones = new ObservableCollection <MediaDevice>(); AudioPlayoutDevices = new ObservableCollection <MediaDevice>(); // WebRTCUWP M58 library does not support audio capture/playout devices //foreach (MediaDevice audioCaptureDevice in Conductor.Instance.Media.GetAudioCaptureDevices()) //{ // Microphones.Add(audioCaptureDevice); //} //foreach (MediaDevice audioPlayoutDevice in Conductor.Instance.Media.GetAudioPlayoutDevices()) //{ // AudioPlayoutDevices.Add(audioPlayoutDevice); //} // HACK Remove Automatic Device Assignment if (SelectedCamera == null && Cameras.Count > 0) { SelectedCamera = Cameras.First(); } if (SelectedMicrophone == null && Microphones.Count > 0) { SelectedMicrophone = Microphones.First(); } Debug.WriteLine("Device Status: SelectedCamera: {0} - SelectedMic: {1}", SelectedCamera == null ? "NULL" : "OK", SelectedMicrophone == null ? "NULL" : "OK"); if (SelectedAudioPlayoutDevice == null && AudioPlayoutDevices.Count > 0) { SelectedAudioPlayoutDevice = AudioPlayoutDevices.First(); } Conductor.Instance.Media.OnMediaDevicesChanged += OnMediaDevicesChanged; Conductor.Instance.Signaller.OnPeerConnected += (peerId, peerName) => { RunOnUiThread(() => { if (Peers == null) { Peers = new ObservableCollection <Peer>(); Conductor.Instance.Peers = Peers; } Peers.Add(new Peer { Id = peerId, Name = peerName }); }); }; Conductor.Instance.Signaller.OnPeerDisconnected += peerId => { RunOnUiThread(() => { var peerToRemove = Peers?.FirstOrDefault(p => p.Id == peerId); if (peerToRemove != null) { Peers.Remove(peerToRemove); } }); }; Conductor.Instance.Signaller.OnSignedIn += () => { RunOnUiThread(() => { IsConnected = true; IsMicrophoneEnabled = false; IsCameraEnabled = false; IsConnecting = false; OnStatusMessageUpdate?.Invoke("Signed-In"); }); }; Conductor.Instance.Signaller.OnServerConnectionFailure += (Exception ex) => { RunOnUiThread(() => { IsConnecting = false; OnStatusMessageUpdate?.Invoke("Server Connection Failure: " + ex.Message + "\n" + ex.StackTrace); }); }; Conductor.Instance.Signaller.OnDisconnected += () => { RunOnUiThread(() => { IsConnected = false; IsMicrophoneEnabled = false; IsCameraEnabled = false; IsDisconnecting = false; Peers?.Clear(); OnStatusMessageUpdate?.Invoke("Disconnected"); }); }; Conductor.Instance.Signaller.OnMessageFromPeer += (id, message) => { RunOnUiThread(() => { // TODO: Handles All Peer Messages (Signal Channel) }); }; Conductor.Instance.Signaller.OnPeerConnected += (id, name) => { RunOnUiThread(() => { SelectedPeer = Peers.First(x => x.Id == id); OnStatusMessageUpdate?.Invoke(string.Format("Connected Peer: {0}-{1}", SelectedPeer.Id, SelectedPeer.Name)); }); }; // TODO: Restore Event Handler in Utility Wrapper // Implemented in Unity Consumer due to Event Handling Issue // Conductor.Instance.OnAddRemoteStream += Conductor_OnAddRemoteStream does not propagate Conductor.Instance.OnRemoveRemoteStream += Conductor_OnRemoveRemoteStream; Conductor.Instance.OnAddLocalStream += Conductor_OnAddLocalStream; Conductor.Instance.OnConnectionHealthStats += Conductor_OnPeerConnectionHealthStats; Conductor.Instance.OnPeerConnectionCreated += () => { RunOnUiThread(() => { IsReadyToConnect = false; IsConnectedToPeer = true; IsReadyToDisconnect = false; IsMicrophoneEnabled = false; OnStatusMessageUpdate?.Invoke("Peer Connection Created"); }); }; Conductor.Instance.OnPeerConnectionClosed += () => { RunOnUiThread(() => { IsConnectedToPeer = false; _peerVideoTrack = null; _selfVideoTrack = null; IsMicrophoneEnabled = false; IsCameraEnabled = false; // TODO: Clean-up References //GC.Collect(); // Ensure all references are truly dropped. OnStatusMessageUpdate?.Invoke("Peer Connection Closed"); }); }; Conductor.Instance.OnPeerMessageDataReceived += (peerId, message) => { OnPeerMessageDataReceived?.Invoke(peerId, message); }; // DATA Channel Setup Conductor.Instance.OnPeerMessageDataReceived += (i, s) => { }; Conductor.Instance.OnReadyToConnect += () => { RunOnUiThread(() => { IsReadyToConnect = true; }); }; IceServers = new ObservableCollection <IceServer>(); NewIceServer = new IceServer(); AudioCodecs = new ObservableCollection <CodecInfo>(); var audioCodecList = WebRTC.GetAudioCodecs(); string[] incompatibleAudioCodecs = new string[] { "CN32000", "CN16000", "CN8000", "red8000", "telephone-event8000" }; VideoCodecs = new ObservableCollection <CodecInfo>(); // TODO: REMOVE DISPLAY LIST SUPPORT var videoCodecList = WebRTC.GetVideoCodecs().OrderBy(codec => { switch (codec.Name) { case "VP8": return(1); case "VP9": return(2); case "H264": return(3); default: return(99); } }); RunOnUiThread(() => { foreach (var audioCodec in audioCodecList) { if (!incompatibleAudioCodecs.Contains(audioCodec.Name + audioCodec.ClockRate)) { AudioCodecs.Add(audioCodec); } } if (AudioCodecs.Count > 0) { SelectedAudioCodec = AudioCodecs.FirstOrDefault(x => x.Name.Contains("PCMU")); } foreach (var videoCodec in videoCodecList) { VideoCodecs.Add(videoCodec); } if (VideoCodecs.Count > 0) { SelectedVideoCodec = VideoCodecs.FirstOrDefault(x => x.Name.Contains("H264")); } }); RunOnUiThread(() => { OnInitialized?.Invoke(); }); }
public DtoCodecInfos GetVideoCodecs() { return(DtoExtensions.ToDto(WebRTC.GetVideoCodecs().ToArray())); }
/// <summary> /// On Win10 in a background task, WebRTC initialization has to be done /// when we have access to the resources. That's inside an active /// voip call. /// This function must be called after VoipCoordinator.StartVoipTask() /// </summary> /// <returns></returns> public void InitializeRTC() { if (Media == null) { WebRTC.Initialize(_dispatcher); Media = WebRTCMedia.CreateMedia(); WebRTCMedia.SetDisplayOrientation(_displayOrientation); // Uncomment the following line to enable WebRTC logging. // Logs are: // - Saved to local storage. Log folder location can be obtained using WebRTC.LogFolder() // - Sent over network if client is connected to TCP port 47003 //WebRTC.EnableLogging(LogLevel.LOGLVL_INFO); } if (DisplayOrientations.None != _displayOrientation) { WebRTCMedia.SetDisplayOrientation(_displayOrientation); } string videoDeviceId = string.Empty; if (_localSettings.Values.ContainsKey(MediaSettingsIds.VideoDeviceSettings)) { videoDeviceId = (string)_localSettings.Values[MediaSettingsIds.VideoDeviceSettings]; } var videoDevices = Media.GetVideoCaptureDevices(); var selectedVideoDevice = videoDevices.FirstOrDefault(d => d.Id.Equals(videoDeviceId)); selectedVideoDevice = selectedVideoDevice ?? videoDevices.FirstOrDefault(); if (selectedVideoDevice != null) { Media.SelectVideoDevice(selectedVideoDevice); } string audioDeviceId = string.Empty; if (_localSettings.Values.ContainsKey(MediaSettingsIds.AudioDeviceSettings)) { audioDeviceId = (string)_localSettings.Values[MediaSettingsIds.AudioDeviceSettings]; } var audioDevices = Media.GetAudioCaptureDevices(); var selectedAudioDevice = audioDevices.FirstOrDefault(d => d.Id.Equals(audioDeviceId)); selectedAudioDevice = selectedAudioDevice ?? audioDevices.FirstOrDefault(); if (selectedAudioDevice != null) { Media.SelectAudioDevice(selectedAudioDevice); } string audioPlayoutDeviceId = string.Empty; if (_localSettings.Values.ContainsKey(MediaSettingsIds.AudioPlayoutDeviceSettings)) { audioPlayoutDeviceId = (string)_localSettings.Values[MediaSettingsIds.AudioPlayoutDeviceSettings]; } var audioPlayoutDevices = Media.GetAudioPlayoutDevices(); var selectedAudioPlayoutDevice = audioPlayoutDevices.FirstOrDefault(d => d.Id.Equals(audioPlayoutDeviceId)); selectedAudioPlayoutDevice = selectedAudioPlayoutDevice ?? audioPlayoutDevices.FirstOrDefault(); if (selectedAudioPlayoutDevice != null) { Media.SelectAudioPlayoutDevice(selectedAudioPlayoutDevice); } int videoCodecId = int.MinValue; if (_localSettings.Values.ContainsKey(MediaSettingsIds.VideoCodecSettings)) { videoCodecId = (int)_localSettings.Values[MediaSettingsIds.VideoCodecSettings]; } var videoCodecs = WebRTC.GetVideoCodecs(); var selectedVideoCodec = videoCodecs.FirstOrDefault(c => c.Id.Equals(videoCodecId)); SetVideoCodec(DtoExtensions.ToDto(selectedVideoCodec ?? videoCodecs.FirstOrDefault())); int audioCodecId = int.MinValue; if (_localSettings.Values.ContainsKey(MediaSettingsIds.AudioCodecSettings)) { audioCodecId = (int)_localSettings.Values[MediaSettingsIds.AudioCodecSettings]; } var audioCodecs = WebRTC.GetAudioCodecs(); var selectedAudioCodec = audioCodecs.FirstOrDefault(c => c.Id.Equals(audioCodecId)); SetAudioCodec(DtoExtensions.ToDto(selectedAudioCodec ?? audioCodecs.FirstOrDefault())); if (_localSettings.Values.ContainsKey(MediaSettingsIds.PreferredVideoCaptureWidth) && _localSettings.Values.ContainsKey(MediaSettingsIds.PreferredVideoCaptureHeight) && _localSettings.Values.ContainsKey(MediaSettingsIds.PreferredVideoCaptureFrameRate)) { WebRTC.SetPreferredVideoCaptureFormat((int)_localSettings.Values[MediaSettingsIds.PreferredVideoCaptureWidth], (int)_localSettings.Values[MediaSettingsIds.PreferredVideoCaptureHeight], (int)_localSettings.Values[MediaSettingsIds.PreferredVideoCaptureFrameRate]); } ResolutionHelper.ResolutionChanged += (id, width, height) => { if (id == LocalMediaStreamId) { LocalVideoRenderer.ResolutionChanged(width, height); } else if (id == PeerMediaStreamId) { RemoteVideoRenderer.ResolutionChanged(width, height); } }; FrameCounterHelper.FramesPerSecondChanged += (id, frameRate) => { if (id == LocalMediaStreamId) { LocalVideo_FrameRateUpdate(frameRate); } else if (id == PeerMediaStreamId) { RemoteVideo_FrameRateUpdate(frameRate); } }; }
/// <summary> /// The initializer for MainViewModel. /// </summary> /// <param name="uiDispatcher">The UI dispatcher.</param> public void Initialize(CoreDispatcher uiDispatcher) { WebRTC.Initialize(uiDispatcher); // For the HoloLens if (_isHoloLens) { WebRTC.SetPreferredVideoCaptureFormat(896, 504, 30); } // Pick the codec var videoCodecs = WebRTC.GetVideoCodecs(); foreach (var codec in videoCodecs) { if (codec.Name == "H264") { Conductor.Instance.VideoCodec = codec; break; } } // Pick the bitrate Conductor.Instance.VideoBitrate = 512; var settings = ApplicationData.Current.LocalSettings; // A Peer is connected to the server event handler Conductor.Instance.Signaller.OnPeerConnected += (peerId, peerName) => { RunOnUiThread(() => { if (Peers == null) { Peers = new ObservableCollection <Peer>(); Conductor.Instance.Peers = Peers; } Peers.Add(new Peer { Id = peerId, Name = peerName }); }); }; // A Peer is disconnected from the server event handler Conductor.Instance.Signaller.OnPeerDisconnected += peerId => { RunOnUiThread(() => { var peerToRemove = Peers?.FirstOrDefault(p => p.Id == peerId); if (peerToRemove != null) { Peers.Remove(peerToRemove); } }); }; // The user is Signed in to the server event handler Conductor.Instance.Signaller.OnSignedIn += () => { RunOnUiThread(() => { IsConnected = true; IsMicrophoneEnabled = true; IsCameraEnabled = true; IsConnecting = false; }); }; // Failed to connect to the server event handler Conductor.Instance.Signaller.OnServerConnectionFailure += () => { RunOnUiThread(async() => { IsConnecting = false; MessageDialog msgDialog = new MessageDialog("Failed to connect to server!"); await msgDialog.ShowAsync(); }); }; // The current user is disconnected from the server event handler Conductor.Instance.Signaller.OnDisconnected += () => { RunOnUiThread(() => { IsConnected = false; IsMicrophoneEnabled = false; IsCameraEnabled = false; IsDisconnecting = false; Peers?.Clear(); }); }; LoadSettings(); Connect(); // Event handlers for managing the media streams Conductor.Instance.OnAddRemoteStream += Conductor_OnAddRemoteStream; Conductor.Instance.OnRemoveRemoteStream += Conductor_OnRemoveRemoteStream; Conductor.Instance.OnAddLocalStream += Conductor_OnAddLocalStream; /** * // Connected to a peer event handler * Conductor.Instance.OnPeerConnectionCreated += () => * { * RunOnUiThread(() => * { * IsReadyToConnect = false; * IsConnectedToPeer = true; * if (SettingsButtonChecked) * { * // close settings screen if open * SettingsButtonChecked = false; * ScrollBarVisibilityType = ScrollBarVisibility.Disabled; * } * IsReadyToDisconnect = false; * if (SettingsButtonChecked) * { * // close settings screen if open * SettingsButtonChecked = false; * ScrollBarVisibilityType = ScrollBarVisibility.Disabled; * } * * // Make sure the screen is always active while on call * if (!_keepOnScreenRequested) * { * _keepScreenOnRequest.RequestActive(); * _keepOnScreenRequested = true; * } * * UpdateScrollBarVisibilityTypeHelper(); * }); * }; * * // Connection between the current user and a peer is closed event handler * Conductor.Instance.OnPeerConnectionClosed += () => * { * RunOnUiThread(() => * { * IsConnectedToPeer = false; * Conductor.Instance.Media.RemoveVideoTrackMediaElementPair(_peerVideoTrack); * //PeerVideo.Source = null; * * Conductor.Instance.Media.RemoveVideoTrackMediaElementPair(_selfVideoTrack); * //SelfVideo.Stop(); * //SelfVideo.ClearValue(MediaElement.SourceProperty); * //SelfVideo.Source = null; * * _peerVideoTrack = null; * _selfVideoTrack = null; * GC.Collect(); // Ensure all references are truly dropped. * IsMicrophoneEnabled = true; * IsCameraEnabled = true; * SelfVideoFps = PeerVideoFps = ""; * * // Make sure to allow the screen to be locked after the call * if (_keepOnScreenRequested) * { * _keepScreenOnRequest.RequestRelease(); * _keepOnScreenRequested = false; * } * UpdateScrollBarVisibilityTypeHelper(); * }); * }; * * // Ready to connect to the server event handler * Conductor.Instance.OnReadyToConnect += () => { RunOnUiThread(() => { IsReadyToConnect = true; }); }; * * // Initialize the Ice servers list * IceServers = new ObservableCollection<IceServer>(); * NewIceServer = new IceServer(); * * // Prepare to list supported audio codecs * AudioCodecs = new ObservableCollection<CodecInfo>(); * var audioCodecList = WebRTC.GetAudioCodecs(); * * // These are features added to existing codecs, they can't decode/encode real audio data so ignore them * string[] incompatibleAudioCodecs = new string[] { "CN32000", "CN16000", "CN8000", "red8000", "telephone-event8000" }; * * // Prepare to list supported video codecs * VideoCodecs = new ObservableCollection<CodecInfo>(); * * // Order the video codecs so that the stable VP8 is in front. * var videoCodecList = WebRTC.GetVideoCodecs().OrderBy(codec => * { * switch (codec.Name) * { * case "VP8": return 1; * case "VP9": return 2; * case "H264": return 3; * default: return 99; * } * }); * * // Load the supported audio/video information into the Settings controls * RunOnUiThread(() => * { * foreach (var audioCodec in audioCodecList) * { * if (!incompatibleAudioCodecs.Contains(audioCodec.Name + audioCodec.ClockRate)) * { * AudioCodecs.Add(audioCodec); * } * } * * if (AudioCodecs.Count > 0) * { * if (settings.Values["SelectedAudioCodecId"] != null) * { * * int id = Convert.ToInt32(settings.Values["SelectedAudioCodecId"]); * * foreach (var audioCodec in AudioCodecs) * { * * int audioCodecId = audioCodec.Id; * if (audioCodecId == id) * { * SelectedAudioCodec = audioCodec; * break; * } * } * } * if (SelectedAudioCodec == null) * { * SelectedAudioCodec = AudioCodecs.First(); * } * } * * foreach (var videoCodec in videoCodecList) * { * VideoCodecs.Add(videoCodec); * } * * if (VideoCodecs.Count > 0) * { * if (settings.Values["SelectedVideoCodecId"] != null) * { * * int id = Convert.ToInt32(settings.Values["SelectedVideoCodecId"]); * foreach (var videoCodec in VideoCodecs) * { * int videoCodecId = videoCodec.Id; * if (videoCodecId == id) * { * SelectedVideoCodec = videoCodec; * break; * } * } * } * if (SelectedVideoCodec == null) * { * SelectedVideoCodec = VideoCodecs.First(); * } * } * }); * LoadSettings(); * RunOnUiThread(() => * { * OnInitialized?.Invoke(); * });*/ }
public void SetWindow(CoreWindow window) { ApplicationView.GetForCurrentView().TryEnterFullScreenMode(); // Initializes DirectX. _appCallbacks.SetWindow(window); // Initializes webrtc. WebRTC.Initialize(CoreApplication.MainView.CoreWindow.Dispatcher); Conductor.Instance.ETWStatsEnabled = false; Conductor.Instance.Signaller.OnPeerConnected += (peerId, peerName) => { Conductor.Instance.Peers.Add( _selectedPeer = new Peer { Id = peerId, Name = peerName }); }; Conductor.Instance.Signaller.OnPeerDisconnected += peerId => { var peerToRemove = Conductor.Instance.Peers?.FirstOrDefault(p => p.Id == peerId); if (peerToRemove != null) { Conductor.Instance.Peers.Remove(peerToRemove); } }; Conductor.Instance.OnAddRemoteStream += Conductor_OnAddRemoteStream; Conductor.Instance.OnRemoveRemoteStream += Conductor_OnRemoveRemoteStream; Conductor.Instance.OnAddLocalStream += Conductor_OnAddLocalStream; if (Conductor.Instance.Peers == null) { Conductor.Instance.Peers = new ObservableCollection <Peer>(); } Task.Run(() => { var videoCodecList = WebRTC.GetVideoCodecs().OrderBy(codec => { switch (codec.Name) { case "VP8": return(1); case "VP9": return(2); case "H264": return(3); default: return(99); } }); //Conductor.Instance.VideoCodec = videoCodecList.FirstOrDefault(x => x.Name.Contains("VP8")); Conductor.Instance.VideoCodec = videoCodecList.FirstOrDefault(x => x.Name.Contains("H264")); var audioCodecList = WebRTC.GetAudioCodecs(); string[] incompatibleAudioCodecs = new string[] { "CN32000", "CN16000", "CN8000", "red8000", "telephone-event8000" }; var audioCodecs = new List <CodecInfo>(); foreach (var audioCodec in audioCodecList) { if (!incompatibleAudioCodecs.Contains(audioCodec.Name + audioCodec.ClockRate)) { audioCodecs.Add(audioCodec); } } if (audioCodecs.Count > 0) { Conductor.Instance.AudioCodec = audioCodecs.FirstOrDefault(x => x.Name.Contains("PCMU")); } Conductor.Instance.DisableLocalVideoStream(); Conductor.Instance.MuteMicrophone(); }); }
public IAsyncOperation <DtoCodecInfos> GetVideoCodecsAsync() { RtcManager.Instance.EnsureRtcIsInitialized(); return(Task.FromResult(WebRTC.GetVideoCodecs().ToArray().ToDto()).AsAsyncOperation()); }