From eb9da939eb92bf8b678430a1835d0e1fdeabdbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:03:45 +0100 Subject: [PATCH 01/31] temp --- .../Core/DeviceManagers/AudioDeviceManager.cs | 20 ++++- .../Core/DeviceManagers/CameraDeviceInfo.cs | 17 +++++ .../DeviceManagers/CameraDeviceInfo.cs.meta | 3 + .../Core/DeviceManagers/DeviceManagerBase.cs | 25 +++++-- .../DeviceManagers/MicrophoneDeviceInfo.cs | 15 ++++ .../MicrophoneDeviceInfo.cs.meta | 3 + .../Core/DeviceManagers/VideoDeviceManager.cs | 74 ++++++++++++++++++- .../Runtime/Core/LowLevelClient/RtcSession.cs | 1 + 8 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs create mode 100644 Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs.meta create mode 100644 Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs create mode 100644 Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs.meta diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index 2fe52333..acde165c 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -1,14 +1,30 @@ -using StreamVideo.Core.LowLevelClient; +using System.Collections.Generic; +using System.Threading.Tasks; +using StreamVideo.Core.LowLevelClient; +using UnityEngine; namespace StreamVideo.Core.DeviceManagers { - internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager + internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager { internal AudioDeviceManager(RtcSession rtcSession) : base(rtcSession) { } + public override IEnumerable EnumerateDevices() + { + foreach (var device in Microphone.devices) + { + yield return new MicrophoneDeviceInfo(device); + } + } + + public override Task IsDeviceStreamingAsync(MicrophoneDeviceInfo device) + { + throw new System.NotImplementedException(); + } + protected override void OnSetEnabled(bool isEnabled) => RtcSession.TrySetAudioTrackEnabled(isEnabled); //StreamTodo: wrap all operations on the Microphone devices + monitor for devices list changes diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs new file mode 100644 index 00000000..b77ee18b --- /dev/null +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs @@ -0,0 +1,17 @@ +namespace StreamVideo.Core.DeviceManagers +{ + /// + /// Represents a Physical Camera Device that can potentially be activated to capture a video stream + /// + public readonly struct CameraDeviceInfo + { + public string Name { get; } + public bool IsFrontFacing { get; } + + public CameraDeviceInfo(string name, bool isFrontFacing) + { + Name = name; + IsFrontFacing = isFrontFacing; + } + } +} \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs.meta b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs.meta new file mode 100644 index 00000000..c637c0f2 --- /dev/null +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 685a39514ebf4db9983b43f86b44f6c2 +timeCreated: 1710241888 \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index a99240be..4c661d87 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -1,19 +1,14 @@ using System; +using System.Collections.Generic; +using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; namespace StreamVideo.Core.DeviceManagers { - internal abstract class DeviceManagerBase : IDeviceManager + internal abstract class DeviceManagerBase : IDeviceManager { public bool IsEnabled { get; private set; } = true; - internal DeviceManagerBase(RtcSession rtcSession) - { - RtcSession = rtcSession ?? throw new ArgumentNullException(nameof(rtcSession)); - - //StreamTodo: react to when video & audio streams become available and disable them if IsEnabled was set to false before the call - } - public void Enable() => SetEnabled(true); public void Disable() => SetEnabled(false); @@ -24,6 +19,20 @@ public void SetEnabled(bool isEnabled) OnSetEnabled(isEnabled); } + /// + /// Enumerate all available devices. This list contains all devices exposed by the underlying OS. + /// + public abstract IEnumerable EnumerateDevices(); + + public abstract Task IsDeviceStreamingAsync(TDeviceInfo device); + + internal DeviceManagerBase(RtcSession rtcSession) + { + RtcSession = rtcSession ?? throw new ArgumentNullException(nameof(rtcSession)); + + //StreamTodo: react to when video & audio streams become available and disable them if IsEnabled was set to false before the call + } + protected RtcSession RtcSession { get; } protected abstract void OnSetEnabled(bool isEnabled); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs new file mode 100644 index 00000000..eaf19455 --- /dev/null +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs @@ -0,0 +1,15 @@ +namespace StreamVideo.Core.DeviceManagers +{ + /// + /// Represents a Microphone Device that can potentially be activated to capture an audio stream + /// + public readonly struct MicrophoneDeviceInfo + { + public string Name { get; } + + public MicrophoneDeviceInfo(string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs.meta b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs.meta new file mode 100644 index 00000000..2a0fab2d --- /dev/null +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5e87adf655884bc9884d3a9f7047dd47 +timeCreated: 1710241888 \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 1b48a250..bcdb0463 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -1,14 +1,75 @@ -using StreamVideo.Core.LowLevelClient; +using System.Collections.Generic; +using System.Threading.Tasks; +using StreamVideo.Core.LowLevelClient; +using UnityEngine; namespace StreamVideo.Core.DeviceManagers { - internal class VideoDeviceManager : DeviceManagerBase, IVideoDeviceManager + internal class VideoDeviceManager : DeviceManagerBase, IVideoDeviceManager { internal VideoDeviceManager(RtcSession rtcSession) : base(rtcSession) { } + public override IEnumerable EnumerateDevices() + { + foreach (var device in WebCamTexture.devices) + { + yield return new CameraDeviceInfo(device.name, device.isFrontFacing); + } + } + + /// + /// + /// + public override async Task IsDeviceStreamingAsync(CameraDeviceInfo device) + { + var camTexture = new WebCamTexture(device.Name); + camTexture.Play(); + await Task.Delay(200); + + // Simple check for valid texture size + var isStreaming = camTexture.width > 16 && camTexture.height > 16; + + camTexture.Stop(); + Object.Destroy(camTexture); + return isStreaming; + } + + public void SelectDevice(CameraDeviceInfo device, int fps = 30) + => SelectDevice(device, VideoResolution.Res_720p, fps); + + public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, int fps = 30) + { + var deviceChanged = _activeCamera == null || _activeCamera.name != device.Name; + var newInstanceNeeded = IsNewInstanceNeeded(device, resolution); + + if (_activeCamera != null && _activeCamera.isPlaying) + { + _activeCamera.Stop(); + } + + if (newInstanceNeeded) + { + _activeCamera = new WebCamTexture(device.Name, (int)resolution.Width, (int)resolution.Height, fps); + } + else + { + if (deviceChanged) + { + _activeCamera.deviceName = device.Name; + } + } + + if (IsEnabled) + { + _activeCamera.Play(); + } + + //StreamTodo: set as a track input source + } + protected override void OnSetEnabled(bool isEnabled) => RtcSession.TrySetVideoTrackEnabled(isEnabled); //StreamTodo: wrap all Unity webcam texture operations here. Enabling/Disabling tracks should manage the WebCamTexture so that users only @@ -16,5 +77,14 @@ internal VideoDeviceManager(RtcSession rtcSession) //StreamTodo: add AutoDetectActiveDevice() method -> will sample each device and pick the first that delivers data //We could also favor front camera on mobile devices + + private WebCamTexture _activeCamera; + + private bool IsNewInstanceNeeded(CameraDeviceInfo device, VideoResolution resolution, int fps = 30) + { + return _activeCamera == null || _activeCamera.requestedWidth != resolution.Width || + _activeCamera.requestedHeight != resolution.Height || + Mathf.Abs(_activeCamera.requestedFPS - fps) < 0.01f; + } } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs index dd758608..5c288c48 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs @@ -254,6 +254,7 @@ public void TrySetAudioTrackEnabled(bool isEnabled) { if (_publisher?.PublisherAudioTrack == null) { + //StreamTodo: we probably want to cache this here and use once the track is available return; } From a0ae96c8cb8e31a7c44077b0235a3fa6f2286b61 Mon Sep 17 00:00:00 2001 From: Daniel Sierpinski Date: Wed, 13 Mar 2024 19:08:43 +0100 Subject: [PATCH 02/31] Add audio device recording & testing + add camera testing + properly dispose resources allocated by the video & audio device managers + set audio & camera sources when camera & microphone are initiated --- .../Core/DeviceManagers/AudioDeviceManager.cs | 127 ++++++++++++++++-- .../Core/DeviceManagers/DeviceManagerBase.cs | 35 ++++- .../Core/DeviceManagers/IDeviceManager.cs | 6 +- .../DeviceManagers/MicrophoneDeviceInfo.cs | 2 + .../Core/DeviceManagers/VideoDeviceManager.cs | 65 +++++---- .../Runtime/Core/LowLevelClient/RtcSession.cs | 10 ++ .../LowLevelClient/StreamPeerConnection.cs | 2 +- .../Runtime/Core/StreamVideoClient.cs | 4 +- 8 files changed, 212 insertions(+), 39 deletions(-) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index acde165c..0de1e83c 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -1,16 +1,15 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; using UnityEngine; +using Object = UnityEngine.Object; namespace StreamVideo.Core.DeviceManagers { internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager { - internal AudioDeviceManager(RtcSession rtcSession) - : base(rtcSession) - { - } + public override MicrophoneDeviceInfo SelectedDevice { get; protected set; } public override IEnumerable EnumerateDevices() { @@ -20,14 +19,126 @@ public override IEnumerable EnumerateDevices() } } - public override Task IsDeviceStreamingAsync(MicrophoneDeviceInfo device) + protected override async Task OnTestDeviceAsync(MicrophoneDeviceInfo device, int msDuration) { - throw new System.NotImplementedException(); + const int sampleRate = 44100; + var maxRecordingTime = (int)Math.Ceiling(msDuration / 1000f); + var clip = Microphone.Start(device.Name, true, maxRecordingTime, sampleRate); + if (clip == null) + { + return false; + } + + await Task.Delay(msDuration); + + //StreamTodo: should we check Microphone.IsRecording? Also some sources add this after Mic.Start() while (!(Microphone.GetPosition(null) > 0)) { } + + var data = new float[clip.samples * clip.channels]; + clip.GetData(data, 0); + bool hasData = false; + foreach (var sample in data) + { + if (sample != 0f) + { + hasData = true; + break; + } + } + + return hasData; } - protected override void OnSetEnabled(bool isEnabled) => RtcSession.TrySetAudioTrackEnabled(isEnabled); + public void SelectDevice(MicrophoneDeviceInfo device) + { + if (!device.IsValid) + { + throw new ArgumentException($"{nameof(device)} argument is not valid. The device name is empty."); + } + + TryStopRecording(); + + SelectedDevice = device; + + var targetAudioSource = GetOrCreateTargetAudioSource(); + + targetAudioSource.clip + = Microphone.Start(SelectedDevice.Name, true, 3, AudioSettings.outputSampleRate); + targetAudioSource.loop = true; + + //StreamTodo: in some cases starting the mic recording before the call was causing the recorded audio being played in speakers + //I think the reason was that AudioSource was being captured by an AudioListener but once I've joined the call, this disappeared + //Check if we can have this AudioSource to be ignored by AudioListener's or otherwise mute it when there is not active call session + + if (IsEnabled) + { + targetAudioSource.Play(); + } + } + + //StreamTodo: https://docs.unity3d.com/ScriptReference/AudioSource-ignoreListenerPause.html perhaps this should be enabled so that AudioListener doesn't affect recorded audio + internal AudioDeviceManager(RtcSession rtcSession, IStreamVideoClient client) + : base(rtcSession, client) + { + } + + protected override void OnSetEnabled(bool isEnabled) + { + if (isEnabled && SelectedDevice.IsValid && !GetOrCreateTargetAudioSource().isPlaying) + { + GetOrCreateTargetAudioSource().Play(); + } + + RtcSession.TrySetAudioTrackEnabled(isEnabled); + } + + protected override void OnDisposing() + { + TryStopRecording(); + + if (_targetAudioSourceContainer != null) + { + Object.Destroy(_targetAudioSourceContainer); + } + + base.OnDisposing(); + } + //StreamTodo: wrap all operations on the Microphone devices + monitor for devices list changes //We could also allow to smart pick device -> sample each device and check which of them are actually gathering any input + + private AudioSource _targetAudioSource; + private GameObject _targetAudioSourceContainer; + + private AudioSource GetOrCreateTargetAudioSource() + { + if (_targetAudioSource != null) + { + return _targetAudioSource; + } + + _targetAudioSourceContainer = new GameObject() + { + name = $"{nameof(AudioDeviceManager)} - Microphone Buffer", +#if !STREAM_DEBUG_ENABLED + hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSave +#endif + }; + + _targetAudioSource = _targetAudioSourceContainer.AddComponent(); + Client.SetAudioInputSource(_targetAudioSource); + return _targetAudioSource; + } + + private void TryStopRecording() + { + if (SelectedDevice.IsValid) + { + if (Microphone.IsRecording(SelectedDevice.Name)) + { + Microphone.End(SelectedDevice.Name); + } + } + } } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 4c661d87..f88e717a 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -8,6 +8,9 @@ namespace StreamVideo.Core.DeviceManagers internal abstract class DeviceManagerBase : IDeviceManager { public bool IsEnabled { get; private set; } = true; + + //StreamTodo: check this warning about nullable type + public abstract TDeviceInfo? SelectedDevice { get; protected set; } public void Enable() => SetEnabled(true); @@ -24,17 +27,45 @@ public void SetEnabled(bool isEnabled) /// public abstract IEnumerable EnumerateDevices(); - public abstract Task IsDeviceStreamingAsync(TDeviceInfo device); + /// + /// Check if the device is capturing data. + /// This can be useful when there are multiple devices available and we want to filter out the ones that actually work. + /// For example, on Windows/Mac/Linux there can be many virtual cameras provided by various installed software that are not capturing any data. + /// You usually want to present all available devices to users but it may be a good idea to show working devices first or try to enable a first working device. + /// + /// Device obtained from + /// + /// True if device is providing captured data + public Task TestDeviceAsync(TDeviceInfo device, float duration = 0.2f) + { + if (duration >= 0f || duration > 20f) + { + throw new ArgumentOutOfRangeException($"'{nameof(duration)}' argument must be between 0 and 20 seconds, given: {duration}"); + } + + return OnTestDeviceAsync(device, (int)(duration * 1000)); + } + + public void Dispose() => OnDisposing(); - internal DeviceManagerBase(RtcSession rtcSession) + internal DeviceManagerBase(RtcSession rtcSession, IStreamVideoClient client) { RtcSession = rtcSession ?? throw new ArgumentNullException(nameof(rtcSession)); + Client = client ?? throw new ArgumentNullException(nameof(client)); //StreamTodo: react to when video & audio streams become available and disable them if IsEnabled was set to false before the call } protected RtcSession RtcSession { get; } + protected IStreamVideoClient Client { get; } protected abstract void OnSetEnabled(bool isEnabled); + + protected abstract Task OnTestDeviceAsync(TDeviceInfo device, int msDuration); + + protected virtual void OnDisposing() + { + + } } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index ec5083c3..d206db15 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -1,6 +1,8 @@ -namespace StreamVideo.Core.DeviceManagers +using System; + +namespace StreamVideo.Core.DeviceManagers { - public interface IDeviceManager + public interface IDeviceManager : IDisposable { /// /// Is device enabled. Enabled device will stream output during the call. diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs index eaf19455..1c38eb02 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs @@ -5,6 +5,8 @@ /// public readonly struct MicrophoneDeviceInfo { + public bool IsValid => !string.IsNullOrEmpty(Name); + public string Name { get; } public MicrophoneDeviceInfo(string name) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index bcdb0463..19a20c34 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -1,16 +1,15 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; using UnityEngine; +using Object = UnityEngine.Object; namespace StreamVideo.Core.DeviceManagers { internal class VideoDeviceManager : DeviceManagerBase, IVideoDeviceManager { - internal VideoDeviceManager(RtcSession rtcSession) - : base(rtcSession) - { - } + public override CameraDeviceInfo SelectedDevice { get; protected set; } public override IEnumerable EnumerateDevices() { @@ -20,23 +19,6 @@ public override IEnumerable EnumerateDevices() } } - /// - /// - /// - public override async Task IsDeviceStreamingAsync(CameraDeviceInfo device) - { - var camTexture = new WebCamTexture(device.Name); - camTexture.Play(); - await Task.Delay(200); - - // Simple check for valid texture size - var isStreaming = camTexture.width > 16 && camTexture.height > 16; - - camTexture.Stop(); - Object.Destroy(camTexture); - return isStreaming; - } - public void SelectDevice(CameraDeviceInfo device, int fps = 30) => SelectDevice(device, VideoResolution.Res_720p, fps); @@ -53,6 +35,9 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, in if (newInstanceNeeded) { _activeCamera = new WebCamTexture(device.Name, (int)resolution.Width, (int)resolution.Height, fps); + + // we probably need to make this internal so we don't end up out of sync if they select a device + set cam input source + Client.SetCameraInputSource(_activeCamera); } else { @@ -66,12 +51,44 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, in { _activeCamera.Play(); } - - //StreamTodo: set as a track input source + } + + internal VideoDeviceManager(RtcSession rtcSession, IStreamVideoClient client) + : base(rtcSession, client) + { } protected override void OnSetEnabled(bool isEnabled) => RtcSession.TrySetVideoTrackEnabled(isEnabled); + protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, int msDuration) + { + var camTexture = new WebCamTexture(device.Name); + camTexture.Play(); + await Task.Delay(msDuration); + + // Simple check for valid texture size + var isStreaming = camTexture.width > 16 && camTexture.height > 16; + + camTexture.Stop(); + Object.Destroy(camTexture); + return isStreaming; + } + + protected override void OnDisposing() + { + if (_activeCamera != null) + { + if (_activeCamera.isPlaying) + { + _activeCamera.Stop(); + } + + Object.Destroy(_activeCamera); + } + + base.OnDisposing(); + } + //StreamTodo: wrap all Unity webcam texture operations here. Enabling/Disabling tracks should manage the WebCamTexture so that users only //Also take into account that user may want to provide his instance of WebCamTexture + monitor for devices list changes diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs index 5c288c48..4d3e4ee2 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs @@ -82,6 +82,11 @@ public AudioSource AudioInput get => _audioInput; set { + if (value == null) + { + throw new ArgumentNullException(); + } + var prev = _audioInput; _audioInput = value; @@ -97,6 +102,11 @@ public WebCamTexture VideoInput get => _videoInput; set { + if (value == null) + { + throw new ArgumentNullException(); + } + var prev = _videoInput; _videoInput = value; diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs index 236d73c8..48f5701b 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs @@ -509,7 +509,7 @@ private VideoResolution GetPublisherResolution() return new VideoResolution(maxResolution.Width, maxResolution.Height); } - return VideoResolution.Res_1080p; + return VideoResolution.Res_720p; } private VideoStreamTrack CreatePublisherVideoTrack() diff --git a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs index 3adbc83a..1ac44c52 100644 --- a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs @@ -387,8 +387,8 @@ private StreamVideoClient(IWebsocketClient coordinatorWebSocket, IWebsocketClien _cache = new Cache(this, serializer, _logs); InternalLowLevelClient.RtcSession.SetCache(_cache); - VideoDeviceManager = new VideoDeviceManager(InternalLowLevelClient.RtcSession); - AudioDeviceManager = new AudioDeviceManager(InternalLowLevelClient.RtcSession); + VideoDeviceManager = new VideoDeviceManager(InternalLowLevelClient.RtcSession, this); + AudioDeviceManager = new AudioDeviceManager(InternalLowLevelClient.RtcSession, this); SubscribeTo(InternalLowLevelClient); } From 03e946ff702c845b49404e0c6c6cae0ea7cd202c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Fri, 22 Mar 2024 11:14:51 +0100 Subject: [PATCH 03/31] temp --- .../Core/DeviceManagers/CameraDeviceInfo.cs | 11 +++++-- .../Core/DeviceManagers/DeviceManagerBase.cs | 17 ++--------- .../DeviceManagers/IAudioDeviceManager.cs | 2 +- .../Core/DeviceManagers/IDeviceManager.cs | 29 +++++++++++++++++-- .../DeviceManagers/IVideoDeviceManager.cs | 8 +++-- .../DeviceManagers/MicrophoneDeviceInfo.cs | 4 +-- .../Core/DeviceManagers/VideoDeviceManager.cs | 15 ++++++++-- 7 files changed, 58 insertions(+), 28 deletions(-) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs index b77ee18b..c20b40ea 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs @@ -1,4 +1,6 @@ -namespace StreamVideo.Core.DeviceManagers +using System.Threading.Tasks; + +namespace StreamVideo.Core.DeviceManagers { /// /// Represents a Physical Camera Device that can potentially be activated to capture a video stream @@ -8,10 +10,15 @@ public readonly struct CameraDeviceInfo public string Name { get; } public bool IsFrontFacing { get; } - public CameraDeviceInfo(string name, bool isFrontFacing) + public CameraDeviceInfo(string name, bool isFrontFacing, IVideoDeviceManager videoDeviceManager) { + _videoDeviceManager = videoDeviceManager; Name = name; IsFrontFacing = isFrontFacing; } + + public Task TestDeviceAsync() => _videoDeviceManager.TestDeviceAsync(this); + + private readonly IVideoDeviceManager _videoDeviceManager; } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index f88e717a..97761c08 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -5,12 +5,11 @@ namespace StreamVideo.Core.DeviceManagers { - internal abstract class DeviceManagerBase : IDeviceManager + internal abstract class DeviceManagerBase : IDeviceManager { public bool IsEnabled { get; private set; } = true; - //StreamTodo: check this warning about nullable type - public abstract TDeviceInfo? SelectedDevice { get; protected set; } + public abstract TDeviceInfo SelectedDevice { get; protected set; } public void Enable() => SetEnabled(true); @@ -22,20 +21,8 @@ public void SetEnabled(bool isEnabled) OnSetEnabled(isEnabled); } - /// - /// Enumerate all available devices. This list contains all devices exposed by the underlying OS. - /// public abstract IEnumerable EnumerateDevices(); - /// - /// Check if the device is capturing data. - /// This can be useful when there are multiple devices available and we want to filter out the ones that actually work. - /// For example, on Windows/Mac/Linux there can be many virtual cameras provided by various installed software that are not capturing any data. - /// You usually want to present all available devices to users but it may be a good idea to show working devices first or try to enable a first working device. - /// - /// Device obtained from - /// - /// True if device is providing captured data public Task TestDeviceAsync(TDeviceInfo device, float duration = 0.2f) { if (duration >= 0f || duration > 20f) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs index 0d3e8d89..0f9d5e79 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs @@ -3,7 +3,7 @@ /// /// Manages interactions with audio recording devices (Microphones). /// - public interface IAudioDeviceManager : IDeviceManager + public interface IAudioDeviceManager : IDeviceManager { } diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index d206db15..7709fdf0 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; +using System.Threading.Tasks; namespace StreamVideo.Core.DeviceManagers { - public interface IDeviceManager : IDisposable + public interface IDeviceManager : IDisposable { /// /// Is device enabled. Enabled device will stream output during the call. @@ -10,12 +12,17 @@ public interface IDeviceManager : IDisposable bool IsEnabled { get; } /// - /// Enable this device. Enabled device will stream output during the call. + /// Currently selected device. This device will be used to capture data. + /// + public TDeviceInfo SelectedDevice { get; } + + /// + /// START capturing data from the . Before calling this method you must first use /// void Enable(); /// - /// Disable this device. This works like "mute" and stops streaming output during the call until you enable this again. + /// STOP capturing data from the /// void Disable(); @@ -23,5 +30,21 @@ public interface IDeviceManager : IDisposable /// Set enabled state for this device. /// void SetEnabled(bool isEnabled); + + /// + /// Enumerate all available devices. This list contains all devices exposed by the underlying OS. + /// + IEnumerable EnumerateDevices(); + + /// + /// Check if the device is capturing data. + /// This can be useful when there are multiple devices available and we want to filter out the ones that actually work. + /// For example, on Windows/Mac/Linux there can be many virtual cameras provided by various installed software that are not capturing any data. + /// You usually want to present all available devices to users but it may be a good idea to show working devices first or try to enable a first working device. + /// + /// Device obtained from + /// + /// True if device is providing captured data + Task TestDeviceAsync(TDeviceInfo device, float duration = 0.2f); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs index 61fa70c3..e25e4cdb 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs @@ -1,10 +1,14 @@ namespace StreamVideo.Core.DeviceManagers { /// - /// Manages interactions with video recording devices (Cameras). + /// Manages interactions with video recording devices - Cameras. /// - public interface IVideoDeviceManager : IDeviceManager + public interface IVideoDeviceManager : IDeviceManager { + //StreamTodo: probably move all members from IDeviceManager here so we can have all comments specifically about video or audio + + void SelectDevice(CameraDeviceInfo device, int fps = 30); + void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, int fps = 30); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs index 1c38eb02..1e463d04 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs @@ -5,13 +5,13 @@ /// public readonly struct MicrophoneDeviceInfo { - public bool IsValid => !string.IsNullOrEmpty(Name); - public string Name { get; } public MicrophoneDeviceInfo(string name) { Name = name; } + + internal bool IsValid => !string.IsNullOrEmpty(Name); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 19a20c34..20c43692 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; using UnityEngine; @@ -15,7 +14,7 @@ public override IEnumerable EnumerateDevices() { foreach (var device in WebCamTexture.devices) { - yield return new CameraDeviceInfo(device.name, device.isFrontFacing); + yield return new CameraDeviceInfo(device.name, device.isFrontFacing, this); } } @@ -52,6 +51,16 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, in _activeCamera.Play(); } } + + /// + /// Inject your own instance of to be used as an active camera. + /// Use this only if you need to control the instance of . Otherwise, simply use the + /// + /// + private void SetRawWebCamTexture(WebCamTexture webCamTexture) + { + //StreamTodo: implement and expose + } internal VideoDeviceManager(RtcSession rtcSession, IStreamVideoClient client) : base(rtcSession, client) From 38e625a539027d37d6d48e9aa167e27ef89ca87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:34:35 +0200 Subject: [PATCH 04/31] temp --- .../Scripts/UI/ParticipantView.cs | 22 ++++----- .../Scripts/UI/UIManager.cs | 6 +-- .../01-basics/QuickStartCodeSamples.cs | 6 +-- .../03-guides/CameraAndMicrophone.cs | 6 +-- .../Core/DeviceManagers/AudioDeviceManager.cs | 14 +++--- .../Core/DeviceManagers/CameraDeviceInfo.cs | 17 ++++++- .../Core/DeviceManagers/DeviceManagerBase.cs | 47 ++++++++++++++----- .../Core/DeviceManagers/IDeviceManager.cs | 12 ++--- .../DeviceManagers/IVideoDeviceManager.cs | 8 +++- .../DeviceManagers/MicrophoneDeviceInfo.cs | 18 +++++-- .../Core/DeviceManagers/VideoDeviceManager.cs | 39 +++++++++++---- .../Core/IInternalStreamVideoClient.cs | 5 ++ .../Runtime/Core/IStreamVideoClient.cs | 17 +------ .../Runtime/Core/StreamVideoClient.cs | 12 ++--- .../Runtime/Core/VideoResolution.cs | 2 +- 15 files changed, 147 insertions(+), 84 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs index 8b9d2a12..6574c300 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs @@ -45,12 +45,12 @@ public void SetLocalCameraSource(WebCamTexture localWebCamTexture) { _localWebCamTexture = localWebCamTexture; - if (_localParticipantRenderTexture != null) - { - // Dispose previous texture - _localParticipantRenderTexture.Release(); - _localParticipantRenderTexture = null; - } + // if (_localParticipantRenderTexture != null) + // { + // // Dispose previous texture + // _localParticipantRenderTexture.Release(); + // _localParticipantRenderTexture = null; + // } if (localWebCamTexture == null) { @@ -58,11 +58,11 @@ public void SetLocalCameraSource(WebCamTexture localWebCamTexture) return; } - _localParticipantRenderTexture = new RenderTexture(localWebCamTexture.width, localWebCamTexture.height, 0, RenderTextureFormat.Default); - _localParticipantRenderTexture.Create(); + // _localParticipantRenderTexture = new RenderTexture(localWebCamTexture.width, localWebCamTexture.height, 0, RenderTextureFormat.Default); + // _localParticipantRenderTexture.Create(); // we set RenderTexture a a RawImage.texture because the RenderTexture will receive video stream from the local camera - _video.texture = _localParticipantRenderTexture; + _video.texture = localWebCamTexture; } // Called by Unity Engine @@ -76,7 +76,7 @@ protected void Update() { if (_localWebCamTexture != null) { - Graphics.Blit(_localWebCamTexture, _localParticipantRenderTexture); + //Graphics.Blit(_localWebCamTexture, _localParticipantRenderTexture); } var rect = _videoRectTransform.rect; @@ -115,7 +115,7 @@ protected void OnDestroy() private Color32 _defaultSpeakerFrameColor; private AudioSource _audioSource; - private RenderTexture _localParticipantRenderTexture; + //private RenderTexture _localParticipantRenderTexture; private WebCamTexture _localWebCamTexture; private RectTransform _videoRectTransform; private Vector2 _lastVideoRenderedSize; diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index 1c19469a..d7cc2cd5 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -53,7 +53,7 @@ public void ChangeCamera(string deviceName, bool isActive) Debug.Log($"Changed active CAMERA from `{prevDevice}` to `{deviceName}`"); - _videoManager.Client?.SetCameraInputSource(ActiveCamera); + //_videoManager.Client?.SetCameraInputSource(ActiveCamera); ActiveCameraChanged?.Invoke(ActiveCamera); } @@ -113,8 +113,8 @@ protected void Awake() protected void Start() { - _videoManager.Client.SetAudioInputSource(_inputAudioSource); - _videoManager.Client.SetCameraInputSource(ActiveCamera); + //_videoManager.Client.SetAudioInputSource(_inputAudioSource); + //_videoManager.Client.SetCameraInputSource(ActiveCamera); ShowMainScreen(); } diff --git a/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs b/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs index 74b9413a..73688654 100644 --- a/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs +++ b/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs @@ -126,7 +126,7 @@ public void SetAudioInput() { // Obtain reference to an AudioSource that will be used a source of audio var audioSource = GetComponent(); - _client.SetAudioInputSource(audioSource); + //_client.SetAudioInputSource(audioSource); } public void BindMicrophoneToAudioSource() @@ -153,7 +153,7 @@ public void SetVideoInput() { // Obtain reference to a WebCamTexture that will be used a source of video var webCamTexture = GetComponent(); - _client.SetCameraInputSource(webCamTexture); + //_client.SetCameraInputSource(webCamTexture); } public void BindCameraToWebCamTexture() @@ -172,7 +172,7 @@ public void BindCameraToWebCamTexture() activeCamera.Play(); // Set WebCamTexture in Stream's Client - this WebCamTexture will be the video source in video calls - _client.SetCameraInputSource(activeCamera); + //_client.SetCameraInputSource(activeCamera); } private IStreamVideoClient _client; diff --git a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs index d1665a64..1b35e086 100644 --- a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs +++ b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs @@ -20,7 +20,7 @@ public void SetupMicrophoneInput() inputAudioSource.loop = true; inputAudioSource.Play(); - _client.SetAudioInputSource(inputAudioSource); + //_client.SetAudioInputSource(inputAudioSource); } public void ChangeMicrophoneDevice() @@ -48,7 +48,7 @@ public void SetupCameraInput() activeCamera.Play(); // Set WebCamTexture in Stream's Client - this WebCamTexture will be the video source in video calls - _client.SetCameraInputSource(activeCamera); + //_client.SetCameraInputSource(activeCamera); } public void ChangeVideoDevice() @@ -72,7 +72,7 @@ public void UpdateCameraInputSource() // Call Play() in order to start capturing the video activeCamera.Play(); - _client.SetCameraInputSource(activeCamera); + //_client.SetCameraInputSource(activeCamera); } private IStreamVideoClient _client; diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index 0de1e83c..5e3c34b2 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -9,8 +9,6 @@ namespace StreamVideo.Core.DeviceManagers { internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager { - public override MicrophoneDeviceInfo SelectedDevice { get; protected set; } - public override IEnumerable EnumerateDevices() { foreach (var device in Microphone.devices) @@ -19,23 +17,25 @@ public override IEnumerable EnumerateDevices() } } - protected override async Task OnTestDeviceAsync(MicrophoneDeviceInfo device, int msDuration) + protected override async Task OnTestDeviceAsync(MicrophoneDeviceInfo device, int msTimeout) { const int sampleRate = 44100; - var maxRecordingTime = (int)Math.Ceiling(msDuration / 1000f); + var maxRecordingTime = (int)Math.Ceiling(msTimeout / 1000f); + var clip = Microphone.Start(device.Name, true, maxRecordingTime, sampleRate); if (clip == null) { return false; } - await Task.Delay(msDuration); + //StreamTodo: check in loop and exit early if device is working already + await Task.Delay(msTimeout); //StreamTodo: should we check Microphone.IsRecording? Also some sources add this after Mic.Start() while (!(Microphone.GetPosition(null) > 0)) { } var data = new float[clip.samples * clip.channels]; clip.GetData(data, 0); - bool hasData = false; + var hasData = false; foreach (var sample in data) { if (sample != 0f) @@ -77,7 +77,7 @@ public void SelectDevice(MicrophoneDeviceInfo device) //StreamTodo: https://docs.unity3d.com/ScriptReference/AudioSource-ignoreListenerPause.html perhaps this should be enabled so that AudioListener doesn't affect recorded audio - internal AudioDeviceManager(RtcSession rtcSession, IStreamVideoClient client) + internal AudioDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client) : base(rtcSession, client) { } diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs index c20b40ea..9443c8fd 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs @@ -1,11 +1,12 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace StreamVideo.Core.DeviceManagers { /// /// Represents a Physical Camera Device that can potentially be activated to capture a video stream /// - public readonly struct CameraDeviceInfo + public readonly struct CameraDeviceInfo : IEquatable { public string Name { get; } public bool IsFrontFacing { get; } @@ -17,7 +18,19 @@ public CameraDeviceInfo(string name, bool isFrontFacing, IVideoDeviceManager vid IsFrontFacing = isFrontFacing; } + public bool Equals(CameraDeviceInfo other) => Name == other.Name; + + public override bool Equals(object obj) => obj is CameraDeviceInfo other && Equals(other); + + public override int GetHashCode() => (Name != null ? Name.GetHashCode() : 0); + + public static bool operator ==(CameraDeviceInfo left, CameraDeviceInfo right) => left.Equals(right); + + public static bool operator !=(CameraDeviceInfo left, CameraDeviceInfo right) => !left.Equals(right); + public Task TestDeviceAsync() => _videoDeviceManager.TestDeviceAsync(this); + + internal bool IsValid => !string.IsNullOrEmpty(Name); private readonly IVideoDeviceManager _videoDeviceManager; } diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 97761c08..23973121 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -5,11 +5,30 @@ namespace StreamVideo.Core.DeviceManagers { + public delegate void + SelectedDeviceChangeHandler(TDeviceInfo previousDevice, TDeviceInfo currentDevice); + internal abstract class DeviceManagerBase : IDeviceManager { + public event SelectedDeviceChangeHandler SelectedDeviceChanged; + public bool IsEnabled { get; private set; } = true; - - public abstract TDeviceInfo SelectedDevice { get; protected set; } + + public TDeviceInfo SelectedDevice + { + get => _selectedDevice; + protected set + { + if (EqualityComparer.Default.Equals(value, _selectedDevice)) + { + return; + } + + var prev = _selectedDevice; + _selectedDevice = value; + SelectedDeviceChanged?.Invoke(prev, value); + } + } public void Enable() => SetEnabled(true); @@ -23,36 +42,38 @@ public void SetEnabled(bool isEnabled) public abstract IEnumerable EnumerateDevices(); - public Task TestDeviceAsync(TDeviceInfo device, float duration = 0.2f) + public Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f) { - if (duration >= 0f || duration > 20f) + if (timeout >= 0f || timeout > 20f) { - throw new ArgumentOutOfRangeException($"'{nameof(duration)}' argument must be between 0 and 20 seconds, given: {duration}"); + throw new ArgumentOutOfRangeException( + $"'{nameof(timeout)}' argument must be between 0 and 20 seconds, given: {timeout}"); } - return OnTestDeviceAsync(device, (int)(duration * 1000)); + return OnTestDeviceAsync(device, (int)(timeout * 1000)); } - + public void Dispose() => OnDisposing(); - internal DeviceManagerBase(RtcSession rtcSession, IStreamVideoClient client) + internal DeviceManagerBase(RtcSession rtcSession, IInternalStreamVideoClient client) { RtcSession = rtcSession ?? throw new ArgumentNullException(nameof(rtcSession)); Client = client ?? throw new ArgumentNullException(nameof(client)); - + //StreamTodo: react to when video & audio streams become available and disable them if IsEnabled was set to false before the call } protected RtcSession RtcSession { get; } - protected IStreamVideoClient Client { get; } + protected IInternalStreamVideoClient Client { get; } protected abstract void OnSetEnabled(bool isEnabled); - - protected abstract Task OnTestDeviceAsync(TDeviceInfo device, int msDuration); + + protected abstract Task OnTestDeviceAsync(TDeviceInfo device, int msTimeout); protected virtual void OnDisposing() { - } + + private TDeviceInfo _selectedDevice; } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 7709fdf0..5fe5f575 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -38,13 +38,13 @@ public interface IDeviceManager : IDisposable /// /// Check if the device is capturing data. - /// This can be useful when there are multiple devices available and we want to filter out the ones that actually work. - /// For example, on Windows/Mac/Linux there can be many virtual cameras provided by various installed software that are not capturing any data. - /// You usually want to present all available devices to users but it may be a good idea to show working devices first or try to enable a first working device. + /// This can be useful when there are multiple devices available and you want to filter out the ones that actually work. + /// For example, on Windows/Mac/Linux there can be many virtual cameras that are not capturing any data. + /// You usually want to show all available devices to users but it may be a good idea to show working devices first or enable a first working device by default. /// - /// Device obtained from - /// + /// Device to test. You can obtain them from + /// How long the test will wait for camera input. /// True if device is providing captured data - Task TestDeviceAsync(TDeviceInfo device, float duration = 0.2f); + Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs index e25e4cdb..6574e5a6 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs @@ -9,6 +9,12 @@ public interface IVideoDeviceManager : IDeviceManager void SelectDevice(CameraDeviceInfo device, int fps = 30); - void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, int fps = 30); + /// + /// + /// + /// + /// Requested video resolution for the captured video. If the requested resolution is not supported by the camera, the closest available one will be selected. + /// Requested frame rate for the captured video. If the requested FPS is not supported by the camera, the closets available one will be selected + void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, int requestedFPS = 30); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs index 1e463d04..e6177fd8 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs @@ -1,9 +1,11 @@ -namespace StreamVideo.Core.DeviceManagers +using System; + +namespace StreamVideo.Core.DeviceManagers { /// /// Represents a Microphone Device that can potentially be activated to capture an audio stream /// - public readonly struct MicrophoneDeviceInfo + public readonly struct MicrophoneDeviceInfo : IEquatable { public string Name { get; } @@ -11,7 +13,17 @@ public MicrophoneDeviceInfo(string name) { Name = name; } - + + public bool Equals(MicrophoneDeviceInfo other) => Name == other.Name; + + public override bool Equals(object obj) => obj is MicrophoneDeviceInfo other && Equals(other); + + public override int GetHashCode() => (Name != null ? Name.GetHashCode() : 0); + + public static bool operator ==(MicrophoneDeviceInfo left, MicrophoneDeviceInfo right) => left.Equals(right); + + public static bool operator !=(MicrophoneDeviceInfo left, MicrophoneDeviceInfo right) => !left.Equals(right); + internal bool IsValid => !string.IsNullOrEmpty(Name); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 20c43692..4509cf90 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; using UnityEngine; @@ -6,9 +7,18 @@ namespace StreamVideo.Core.DeviceManagers { + // StreamTodo: write tests: + /* - change in video res & FPS needs to be reflected in sent video +- If you disable track before call it should stay disabled during the call +- disabling camera should disable the video track (same with mic) +- enabling the camera should enable the video track again (same with mic) +- changing a disabled camera should not enable it +- test that monitoring for video devices works and deviceAdded, deviceRemoved events are fired accordingly +- test that enabling device triggers capturing and disabling stops capturing +*/ internal class VideoDeviceManager : DeviceManagerBase, IVideoDeviceManager { - public override CameraDeviceInfo SelectedDevice { get; protected set; } + public bool IsCapturing => _activeCamera != null && _activeCamera.isPlaying; public override IEnumerable EnumerateDevices() { @@ -21,10 +31,15 @@ public override IEnumerable EnumerateDevices() public void SelectDevice(CameraDeviceInfo device, int fps = 30) => SelectDevice(device, VideoResolution.Res_720p, fps); - public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, int fps = 30) + public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, int requestedFPS = 30) { - var deviceChanged = _activeCamera == null || _activeCamera.name != device.Name; - var newInstanceNeeded = IsNewInstanceNeeded(device, resolution); + if (!device.IsValid) + { + throw new ArgumentException($"{nameof(device)} argument is not valid. The device name is empty."); + } + + var deviceChanged = SelectedDevice != device; + var newInstanceNeeded = IsNewInstanceNeeded(device, requestedResolution); if (_activeCamera != null && _activeCamera.isPlaying) { @@ -33,7 +48,8 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, in if (newInstanceNeeded) { - _activeCamera = new WebCamTexture(device.Name, (int)resolution.Width, (int)resolution.Height, fps); + _activeCamera = new WebCamTexture(device.Name, (int)requestedResolution.Width, (int)requestedResolution.Height, requestedFPS); + SelectedDevice = device; // we probably need to make this internal so we don't end up out of sync if they select a device + set cam input source Client.SetCameraInputSource(_activeCamera); @@ -43,6 +59,7 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, in if (deviceChanged) { _activeCamera.deviceName = device.Name; + SelectedDevice = device; } } @@ -59,21 +76,23 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution resolution, in /// private void SetRawWebCamTexture(WebCamTexture webCamTexture) { - //StreamTodo: implement and expose + //StreamTodo: implement and make public } - internal VideoDeviceManager(RtcSession rtcSession, IStreamVideoClient client) + internal VideoDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client) : base(rtcSession, client) { } protected override void OnSetEnabled(bool isEnabled) => RtcSession.TrySetVideoTrackEnabled(isEnabled); - protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, int msDuration) + protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, int msTimeout) { var camTexture = new WebCamTexture(device.Name); camTexture.Play(); - await Task.Delay(msDuration); + + //StreamTodo: check in loop and exit early if device is working already + await Task.Delay(msTimeout); // Simple check for valid texture size var isStreaming = camTexture.width > 16 && camTexture.height > 16; diff --git a/Packages/StreamVideo/Runtime/Core/IInternalStreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/IInternalStreamVideoClient.cs index b77e4f03..0d6a94ca 100644 --- a/Packages/StreamVideo/Runtime/Core/IInternalStreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/IInternalStreamVideoClient.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; using StreamVideo.Core.StatefulModels; +using UnityEngine; namespace StreamVideo.Core { @@ -40,5 +41,9 @@ Task UpdateUserPermissions(IStreamCall call, string userId, List grantPe Task SetParticipantCustomDataAsync(IStreamVideoCallParticipant participant, Dictionary internalDictionary); + + void SetAudioInputSource(AudioSource audioSource); + + void SetCameraInputSource(WebCamTexture webCamTexture); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs index 67bb78d0..29afd017 100644 --- a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs @@ -72,24 +72,11 @@ public interface IStreamVideoClient : IStreamVideoClientEventsListener, IDisposa Task JoinCallAsync(StreamCallType callType, string callId, bool create, bool ring, bool notify); - /// - /// Set the source for sending AUDIO. Check out the docs to learn on how to capture audio from a Microphone device https://getstream.io/video/docs/unity/guides/camera-and-microphone/ - /// - /// - void SetAudioInputSource(AudioSource audioSource); - - /// - /// Set the source for sending VIDEO from a Camera device. - /// Video resolution and FPS (frames per second) defined in the passed will be used to define the max resolution and FPS of the published video - /// Check out the docs to learn how to setup capturing video from a camera device https://getstream.io/video/docs/unity/guides/camera-and-microphone/ - /// - /// - void SetCameraInputSource(WebCamTexture webCamTexture); - + //StreamTodo: revise setting 3D Scene Camera as a video source /// /// Set the source for sending VIDEO or rendered Scene Camera. You can pass any scene camera and the video will be sent to other participants. /// - void SetCameraInputSource(Camera sceneCamera); + //void SetCameraInputSource(Camera sceneCamera); /// /// Will return null if the call doesn't exist diff --git a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs index e3384c79..91742679 100644 --- a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs @@ -183,7 +183,7 @@ public async Task ConnectUserAsync(AuthCredentials credentials public Task DisconnectAsync() => InternalLowLevelClient.DisconnectAsync(); - public void SetAudioInputSource(AudioSource audioSource) + void IInternalStreamVideoClient.SetAudioInputSource(AudioSource audioSource) { if (audioSource == null) { @@ -196,7 +196,7 @@ public void SetAudioInputSource(AudioSource audioSource) //StreamTodo: add IsActive flag to SetCameraInputSource SetAudioInputSource SetCameraInputSource //StreamTodo: later we should accept just Texture or RenderTexture or TextureProvider - public void SetCameraInputSource(WebCamTexture webCamTexture) + void IInternalStreamVideoClient.SetCameraInputSource(WebCamTexture webCamTexture) { if (webCamTexture == null) { @@ -206,10 +206,10 @@ public void SetCameraInputSource(WebCamTexture webCamTexture) InternalLowLevelClient.RtcSession.VideoInput = webCamTexture; } - public void SetCameraInputSource(Camera sceneCamera) - { - InternalLowLevelClient.RtcSession.VideoSceneInput = sceneCamera; - } + // public void SetCameraInputSource(Camera sceneCamera) + // { + // InternalLowLevelClient.RtcSession.VideoSceneInput = sceneCamera; + // } public async Task QueryCallsAsync(IEnumerable filters = null, CallSort sort = null, int limit = 25, string prev = null, string next = null, bool watch = false) diff --git a/Packages/StreamVideo/Runtime/Core/VideoResolution.cs b/Packages/StreamVideo/Runtime/Core/VideoResolution.cs index 06788547..0a0c9c06 100644 --- a/Packages/StreamVideo/Runtime/Core/VideoResolution.cs +++ b/Packages/StreamVideo/Runtime/Core/VideoResolution.cs @@ -6,7 +6,7 @@ namespace StreamVideo.Core /// /// Video resolution /// - /// Create a custom resolution or use one of the predefined: + /// You can create a custom resolution or use one of the predefined: /// - FullHD -> 1920x1080 /// - HD -> 1280x720 /// - SD -> 640x480 From 93378ab244cf975bad1ac3e5c47b5a89640d10dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:53:49 +0200 Subject: [PATCH 05/31] temp --- .../Core/DeviceManagers/AudioDeviceManager.cs | 27 +++- .../Core/DeviceManagers/CameraDeviceInfo.cs | 4 +- .../Core/DeviceManagers/DeviceManagerBase.cs | 2 +- .../DeviceManagers/MicrophoneDeviceInfo.cs | 2 + .../Core/DeviceManagers/VideoDeviceManager.cs | 135 ++++++++++++++++-- .../Runtime/Core/IStreamVideoClient.cs | 6 + .../Core/StatefulModels/IStreamCall.cs | 7 +- .../IStreamVideoCallParticipant.cs | 1 + .../Runtime/Core/StreamCallType.cs | 5 +- .../StreamVideo/Tests/Runtime/CallsTests.cs | 27 ++-- 10 files changed, 181 insertions(+), 35 deletions(-) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index 5e3c34b2..e7e4d9ab 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -9,6 +9,8 @@ namespace StreamVideo.Core.DeviceManagers { internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager { + public bool IsCapturing => SelectedDevice.IsValid && Microphone.IsRecording(SelectedDevice.Name); + public override IEnumerable EnumerateDevices() { foreach (var device in Microphone.devices) @@ -48,6 +50,13 @@ protected override async Task OnTestDeviceAsync(MicrophoneDeviceInfo devic return hasData; } + /// + /// Select microphone device to capture audio input. Available microphone devices are listed in . + /// You can check the currently selected audio device with , and + /// get notified when the selected device changes by subscribing to . + /// + /// + /// Thrown when the provided device has an invalid name public void SelectDevice(MicrophoneDeviceInfo device) { if (!device.IsValid) @@ -119,8 +128,10 @@ private AudioSource GetOrCreateTargetAudioSource() _targetAudioSourceContainer = new GameObject() { - name = $"{nameof(AudioDeviceManager)} - Microphone Buffer", -#if !STREAM_DEBUG_ENABLED + name = $"[Stream][{nameof(AudioDeviceManager)}] Microphone Buffer", +#if STREAM_DEBUG_ENABLED + hideFlags = HideFlags.DontSave +#else hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSave #endif }; @@ -132,12 +143,14 @@ private AudioSource GetOrCreateTargetAudioSource() private void TryStopRecording() { - if (SelectedDevice.IsValid) + if (!SelectedDevice.IsValid) { - if (Microphone.IsRecording(SelectedDevice.Name)) - { - Microphone.End(SelectedDevice.Name); - } + return; + } + + if (Microphone.IsRecording(SelectedDevice.Name)) + { + Microphone.End(SelectedDevice.Name); } } } diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs index 9443c8fd..86cf8859 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs @@ -29,7 +29,9 @@ public CameraDeviceInfo(string name, bool isFrontFacing, IVideoDeviceManager vid public static bool operator !=(CameraDeviceInfo left, CameraDeviceInfo right) => !left.Equals(right); public Task TestDeviceAsync() => _videoDeviceManager.TestDeviceAsync(this); - + + public override string ToString() => $"Camera Device - {Name}"; + internal bool IsValid => !string.IsNullOrEmpty(Name); private readonly IVideoDeviceManager _videoDeviceManager; diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 23973121..ce7d6a50 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -44,7 +44,7 @@ public void SetEnabled(bool isEnabled) public Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f) { - if (timeout >= 0f || timeout > 20f) + if (timeout <= 0f || timeout > 20f) { throw new ArgumentOutOfRangeException( $"'{nameof(timeout)}' argument must be between 0 and 20 seconds, given: {timeout}"); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs index e6177fd8..6599700c 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs @@ -23,6 +23,8 @@ public MicrophoneDeviceInfo(string name) public static bool operator ==(MicrophoneDeviceInfo left, MicrophoneDeviceInfo right) => left.Equals(right); public static bool operator !=(MicrophoneDeviceInfo left, MicrophoneDeviceInfo right) => !left.Equals(right); + + public override string ToString() => $"Microphone Device - {Name}"; internal bool IsValid => !string.IsNullOrEmpty(Name); } diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 4509cf90..976a5cb9 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; using UnityEngine; +using Debug = UnityEngine.Debug; using Object = UnityEngine.Object; namespace StreamVideo.Core.DeviceManagers @@ -51,7 +53,6 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol _activeCamera = new WebCamTexture(device.Name, (int)requestedResolution.Width, (int)requestedResolution.Height, requestedFPS); SelectedDevice = device; - // we probably need to make this internal so we don't end up out of sync if they select a device + set cam input source Client.SetCameraInputSource(_activeCamera); } else @@ -69,6 +70,13 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol } } + /// + /// Get the instance of for the selected device. This is useful if you want to + /// + /// This can change whenever a selected device is changed. Subscribe to to get notified when the selected device changes. + /// + public WebCamTexture GetSelectedDeviceWebCamTexture() => _activeCamera; + /// /// Inject your own instance of to be used as an active camera. /// Use this only if you need to control the instance of . Otherwise, simply use the @@ -88,18 +96,84 @@ internal VideoDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient cl protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, int msTimeout) { - var camTexture = new WebCamTexture(device.Name); - camTexture.Play(); - - //StreamTodo: check in loop and exit early if device is working already - await Task.Delay(msTimeout); - - // Simple check for valid texture size - var isStreaming = camTexture.width > 16 && camTexture.height > 16; - - camTexture.Stop(); - Object.Destroy(camTexture); - return isStreaming; + WebCamTexture camTexture = null; + try + { + camTexture = new WebCamTexture(device.Name); + + // This can fail and the only result will be Unity logging "Could not start graph" and "Could not pause pControl" - these are logs and not exceptions. + camTexture.Play(); + + if (_stopwatch == null) + { + _stopwatch = new Stopwatch(); + } + + _stopwatch.Stop(); + _stopwatch.Reset(); + _stopwatch.Start(); + + var isCapturing = false; + + //Investigate https://forum.unity.com/threads/get-webcamtexture-pixel-data-without-using-getpixels32.1315821/ + + Color[] frame1 = null, frame2 = null; + + while (_stopwatch.ElapsedMilliseconds < msTimeout) + { + //StreamTodo: this does not guarantee that camera is capturing data + if (camTexture.didUpdateThisFrame) + { + var frame = camTexture.GetPixels(); + + if (frame1 == null) + { + if (!IsFrameBlack(frame)) + { + frame1 = frame; + Debug.Log( + $"TEST DEVICE - {device.Name} - Frame 1 SET after {_stopwatch.ElapsedMilliseconds}"); + + continue; + } + } + else + { + if (!IsFrameBlack(frame)) + { + frame2 = frame; + Debug.Log( + $"TEST DEVICE - {device.Name} - Frame 2 SET after {_stopwatch.ElapsedMilliseconds}"); + } + } + } + + if (frame1 != null && frame2 != null && !AreFramesEqual(frame1, frame2)) + { + Debug.Log( + $"TEST DEVICE - {device.Name} - Frame ARE DIFFERENT after {_stopwatch.ElapsedMilliseconds}"); + isCapturing = true; + break; + } + + await Task.Delay(1); + } + + return isCapturing; + } + catch (Exception e) + { + Debug.LogError("AAAAAAAAAAAAAAAAAAAAAAAA " + e.Message); + return false; + } + finally + { + if (camTexture != null) + { + camTexture.Stop(); + Object.Destroy(camTexture); + } + } } protected override void OnDisposing() @@ -124,12 +198,45 @@ protected override void OnDisposing() //We could also favor front camera on mobile devices private WebCamTexture _activeCamera; - + private Stopwatch _stopwatch; + private bool IsNewInstanceNeeded(CameraDeviceInfo device, VideoResolution resolution, int fps = 30) { return _activeCamera == null || _activeCamera.requestedWidth != resolution.Width || _activeCamera.requestedHeight != resolution.Height || Mathf.Abs(_activeCamera.requestedFPS - fps) < 0.01f; } + + private static bool AreFramesEqual(IReadOnlyList frame1, IReadOnlyList frame2) + { + if (frame1.Count != frame2.Count) + { + return false; + } + + for (var i = 0; i < frame1.Count; i++) + { + if (frame1[i] != frame2[i]) + { + return false; + } + } + + return true; + } + + private static bool IsFrameBlack(IReadOnlyList frame1) + { + for (var i = 0; i < frame1.Count; i++) + { + //StreamTodo: perhaps check if the whole frame is same color. Some virtual camera can be e.g. orange + if (frame1[i] != Color.black) + { + return true; + } + } + + return false; + } } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs index 29afd017..c459a5f8 100644 --- a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs @@ -83,6 +83,12 @@ Task JoinCallAsync(StreamCallType callType, string callId, bool cre /// Task GetCallAsync(StreamCallType callType, string callId); + /// + /// Get a call with a specified Type and ID. If such a call doesn't exist, it will be created. + /// + /// Call type - this defines the permissions and other settings for the call. Read more in the Call Types Docs + /// Call ID + /// Call object of type: Task GetOrCreateCallAsync(StreamCallType callType, string callId); /// diff --git a/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamCall.cs b/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamCall.cs index ccab98b9..d959ed7f 100644 --- a/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamCall.cs +++ b/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamCall.cs @@ -9,6 +9,9 @@ namespace StreamVideo.Core.StatefulModels { + /// + /// Represents a call session where participants can share audio and video streams. + /// public interface IStreamCall : IStreamStatefulModel, IHasCustomData { /// @@ -284,14 +287,14 @@ Task QueryMembersAsync(IEnumerable filters Task SendCustomEventAsync(Dictionary eventData); /// - /// Pin this participant locally. This will take effect on this client only. + /// Pin this participant locally. This will take effect on this device only. /// You can get all pinned participants with /// /// Participant to pin void PinLocally(IStreamVideoCallParticipant participant); /// - /// Unpin this participant locally. This will take effect on this client only. + /// Unpin this participant locally. This will take effect on this device only. /// You can get all pinned participants with /// /// Participant to unpin diff --git a/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamVideoCallParticipant.cs b/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamVideoCallParticipant.cs index 2e2b954d..688c7fda 100644 --- a/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamVideoCallParticipant.cs +++ b/Packages/StreamVideo/Runtime/Core/StatefulModels/IStreamVideoCallParticipant.cs @@ -41,6 +41,7 @@ public interface IStreamVideoCallParticipant : IStreamStatefulModel, IHasCustomD /// Is this participant currently the most actively speaking participant. /// bool IsDominantSpeaker { get; } + string UserId { get; } /// diff --git a/Packages/StreamVideo/Runtime/Core/StreamCallType.cs b/Packages/StreamVideo/Runtime/Core/StreamCallType.cs index b9552453..f448aa8e 100644 --- a/Packages/StreamVideo/Runtime/Core/StreamCallType.cs +++ b/Packages/StreamVideo/Runtime/Core/StreamCallType.cs @@ -3,7 +3,8 @@ namespace StreamVideo.Core { /// - /// Call type defines permission settings. You can set permissions for each type in + /// Call type defines permission settings. You can set permissions for each type in Stream Dashboard. + /// Read more about the call types in the Call Types Docs /// public readonly struct StreamCallType { @@ -25,7 +26,7 @@ public readonly struct StreamCallType public static StreamCallType Livestream => new StreamCallType("livestream"); /// - /// ** Use for development only! ** everything enabled, permissions open + /// ** Use for development only! ** should only be used for testing, permissions are open and everything is enabled (use carefully) /// public static StreamCallType Development => new StreamCallType("development"); diff --git a/Packages/StreamVideo/Tests/Runtime/CallsTests.cs b/Packages/StreamVideo/Tests/Runtime/CallsTests.cs index d25eb2a9..550cf4ad 100644 --- a/Packages/StreamVideo/Tests/Runtime/CallsTests.cs +++ b/Packages/StreamVideo/Tests/Runtime/CallsTests.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Threading.Tasks; using NUnit.Framework; +using StreamVideo.Core; +using StreamVideo.Core.DeviceManagers; using StreamVideo.Core.StatefulModels.Tracks; using StreamVideo.Tests.Shared; using UnityEngine; @@ -36,10 +38,8 @@ private async Task When_client_joins_call_with_video_expect_receiving_video_trac { var streamCall = await clientA.JoinRandomCallAsync(); - var webCamTexture = DisposableAssetsProvider.WebCamTextureFactory.Create(WebCamTexture.devices.First().name, 1920, 1080, 20); - webCamTexture.Play(); - - clientA.Client.SetCameraInputSource(webCamTexture); + var cameraDevice = await TryGetFirstWorkingCameraDeviceAsync(clientA.Client); + clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice); var call = await clientB.Client.JoinCallAsync(streamCall.Type, streamCall.Id, create: false, ring: false, notify: false); @@ -64,6 +64,19 @@ private async Task When_client_joins_call_with_video_expect_receiving_video_trac Assert.IsNotNull(streamTrack); } + + //StreamTodo: put this in VideoDeviceManager + private static async Task TryGetFirstWorkingCameraDeviceAsync(IStreamVideoClient client) + { + var cameraManager = client.VideoDeviceManager; + foreach (var cameraDevice in cameraManager.EnumerateDevices()) + { + var isWorking = await cameraManager.TestDeviceAsync(cameraDevice, 0.5f); + Debug.Log($"TEST DEVICE {cameraDevice} - {isWorking}"); + } + + return cameraManager.EnumerateDevices().First(d => d.Name.Contains("Logi") && d.Name.Contains("Capture")); + } [UnityTest] public IEnumerator When_client_enables_video_during_call_expect_other_client_receiving_video_track() @@ -90,10 +103,8 @@ private async Task When_client_enables_video_during_call_expect_other_client_rec }; // First participant - enable video track - var webCamTexture = DisposableAssetsProvider.WebCamTextureFactory.Create(WebCamTexture.devices.First().name, 1920, 1080, 20); - webCamTexture.Play(); - - clientA.Client.SetCameraInputSource(webCamTexture); + var cameraDevice = await TryGetFirstWorkingCameraDeviceAsync(clientA.Client); + clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice); // Wait for event await WaitForConditionAsync(() => streamTrack != null); From 10a6c8b69eb2724c996ee5891c68e86cb4ad181c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:27:46 +0200 Subject: [PATCH 06/31] Fix tests & testing for a video device that is actually capturing any data --- .../Core/DeviceManagers/AudioDeviceManager.cs | 16 +++-- .../Core/DeviceManagers/DeviceManagerBase.cs | 6 +- .../Core/DeviceManagers/IDeviceManager.cs | 2 +- .../DeviceManagers/IVideoDeviceManager.cs | 11 +++- .../Core/DeviceManagers/VideoDeviceManager.cs | 62 ++++++++----------- .../Runtime/Core/LowLevelClient/RtcSession.cs | 11 ++++ .../Runtime/Core/StreamVideoClient.cs | 4 +- .../StreamVideo/Tests/Runtime/CallsTests.cs | 47 +++++--------- .../StreamVideo/Tests/Shared/TestClient.cs | 2 +- .../StreamVideo/Tests/Shared/TestUtils.cs | 32 +++++++++- .../StreamVideo/Tests/Shared/TestsBase.cs | 23 ++++--- 11 files changed, 124 insertions(+), 92 deletions(-) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index e7e4d9ab..b9ff0482 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; +using StreamVideo.Libs.Logs; using UnityEngine; using Object = UnityEngine.Object; @@ -9,8 +10,6 @@ namespace StreamVideo.Core.DeviceManagers { internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager { - public bool IsCapturing => SelectedDevice.IsValid && Microphone.IsRecording(SelectedDevice.Name); - public override IEnumerable EnumerateDevices() { foreach (var device in Microphone.devices) @@ -80,14 +79,14 @@ public void SelectDevice(MicrophoneDeviceInfo device) if (IsEnabled) { - targetAudioSource.Play(); + Enable(); } } //StreamTodo: https://docs.unity3d.com/ScriptReference/AudioSource-ignoreListenerPause.html perhaps this should be enabled so that AudioListener doesn't affect recorded audio - internal AudioDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client) - : base(rtcSession, client) + internal AudioDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client, ILogs logs) + : base(rtcSession, client, logs) { } @@ -97,6 +96,11 @@ protected override void OnSetEnabled(bool isEnabled) { GetOrCreateTargetAudioSource().Play(); } + + if (!isEnabled) + { + TryStopRecording(); + } RtcSession.TrySetAudioTrackEnabled(isEnabled); } @@ -126,7 +130,7 @@ private AudioSource GetOrCreateTargetAudioSource() return _targetAudioSource; } - _targetAudioSourceContainer = new GameObject() + _targetAudioSourceContainer = new GameObject { name = $"[Stream][{nameof(AudioDeviceManager)}] Microphone Buffer", #if STREAM_DEBUG_ENABLED diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index ce7d6a50..9ce0e39b 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; +using StreamVideo.Libs.Logs; namespace StreamVideo.Core.DeviceManagers { @@ -55,16 +56,19 @@ public Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f) public void Dispose() => OnDisposing(); - internal DeviceManagerBase(RtcSession rtcSession, IInternalStreamVideoClient client) + internal DeviceManagerBase(RtcSession rtcSession, IInternalStreamVideoClient client, ILogs logs) { RtcSession = rtcSession ?? throw new ArgumentNullException(nameof(rtcSession)); Client = client ?? throw new ArgumentNullException(nameof(client)); + Logs = logs ?? throw new ArgumentNullException(nameof(logs)); //StreamTodo: react to when video & audio streams become available and disable them if IsEnabled was set to false before the call } protected RtcSession RtcSession { get; } protected IInternalStreamVideoClient Client { get; } + protected ILogs Logs { get; } + protected abstract void OnSetEnabled(bool isEnabled); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 5fe5f575..9221affe 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -14,7 +14,7 @@ public interface IDeviceManager : IDisposable /// /// Currently selected device. This device will be used to capture data. /// - public TDeviceInfo SelectedDevice { get; } + TDeviceInfo SelectedDevice { get; } /// /// START capturing data from the . Before calling this method you must first use diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs index 6574e5a6..b706de9d 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs @@ -1,4 +1,6 @@ -namespace StreamVideo.Core.DeviceManagers +using UnityEngine; + +namespace StreamVideo.Core.DeviceManagers { /// /// Manages interactions with video recording devices - Cameras. @@ -16,5 +18,12 @@ public interface IVideoDeviceManager : IDeviceManager /// Requested video resolution for the captured video. If the requested resolution is not supported by the camera, the closest available one will be selected. /// Requested frame rate for the captured video. If the requested FPS is not supported by the camera, the closets available one will be selected void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, int requestedFPS = 30); + + /// + /// Get the instance of for the selected device. This is useful if you want to + /// + /// This can change whenever a selected device is changed. Subscribe to to get notified when the selected device changes. + /// + WebCamTexture GetSelectedDeviceWebCamTexture(); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 976a5cb9..da8d80bf 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -3,8 +3,8 @@ using System.Diagnostics; using System.Threading.Tasks; using StreamVideo.Core.LowLevelClient; +using StreamVideo.Libs.Logs; using UnityEngine; -using Debug = UnityEngine.Debug; using Object = UnityEngine.Object; namespace StreamVideo.Core.DeviceManagers @@ -20,8 +20,6 @@ namespace StreamVideo.Core.DeviceManagers */ internal class VideoDeviceManager : DeviceManagerBase, IVideoDeviceManager { - public bool IsCapturing => _activeCamera != null && _activeCamera.isPlaying; - public override IEnumerable EnumerateDevices() { foreach (var device in WebCamTexture.devices) @@ -52,8 +50,6 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol { _activeCamera = new WebCamTexture(device.Name, (int)requestedResolution.Width, (int)requestedResolution.Height, requestedFPS); SelectedDevice = device; - - Client.SetCameraInputSource(_activeCamera); } else { @@ -66,10 +62,11 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol if (IsEnabled) { - _activeCamera.Play(); + Enable(); } } + //StreamTodo: better to not expose this and make fake tracks for local user. This way every participant is processed exactly the same /// /// Get the instance of for the selected device. This is useful if you want to /// @@ -77,23 +74,27 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol /// public WebCamTexture GetSelectedDeviceWebCamTexture() => _activeCamera; - /// - /// Inject your own instance of to be used as an active camera. - /// Use this only if you need to control the instance of . Otherwise, simply use the - /// - /// - private void SetRawWebCamTexture(WebCamTexture webCamTexture) + internal VideoDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client, ILogs logs) + : base(rtcSession, client, logs) { - //StreamTodo: implement and make public } - - internal VideoDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client) - : base(rtcSession, client) + + protected override void OnSetEnabled(bool isEnabled) { + if (isEnabled && _activeCamera != null && !_activeCamera.isPlaying) + { + _activeCamera.Play(); + Client.SetCameraInputSource(_activeCamera); + } + + if (!isEnabled && _activeCamera != null) + { + _activeCamera.Stop(); + } + + RtcSession.TrySetVideoTrackEnabled(isEnabled); } - protected override void OnSetEnabled(bool isEnabled) => RtcSession.TrySetVideoTrackEnabled(isEnabled); - protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, int msTimeout) { WebCamTexture camTexture = null; @@ -115,13 +116,13 @@ protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, i var isCapturing = false; - //Investigate https://forum.unity.com/threads/get-webcamtexture-pixel-data-without-using-getpixels32.1315821/ + //StreamTodo: Investigate https://forum.unity.com/threads/get-webcamtexture-pixel-data-without-using-getpixels32.1315821/ Color[] frame1 = null, frame2 = null; while (_stopwatch.ElapsedMilliseconds < msTimeout) { - //StreamTodo: this does not guarantee that camera is capturing data + //WebCamTexture.didUpdateThisFrame does not guarantee that camera is capturing data. We need to compare frames if (camTexture.didUpdateThisFrame) { var frame = camTexture.GetPixels(); @@ -131,9 +132,6 @@ protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, i if (!IsFrameBlack(frame)) { frame1 = frame; - Debug.Log( - $"TEST DEVICE - {device.Name} - Frame 1 SET after {_stopwatch.ElapsedMilliseconds}"); - continue; } } @@ -142,16 +140,12 @@ protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, i if (!IsFrameBlack(frame)) { frame2 = frame; - Debug.Log( - $"TEST DEVICE - {device.Name} - Frame 2 SET after {_stopwatch.ElapsedMilliseconds}"); } } } if (frame1 != null && frame2 != null && !AreFramesEqual(frame1, frame2)) { - Debug.Log( - $"TEST DEVICE - {device.Name} - Frame ARE DIFFERENT after {_stopwatch.ElapsedMilliseconds}"); isCapturing = true; break; } @@ -163,7 +157,7 @@ protected override async Task OnTestDeviceAsync(CameraDeviceInfo device, i } catch (Exception e) { - Debug.LogError("AAAAAAAAAAAAAAAAAAAAAAAA " + e.Message); + Logs.Error(e.Message); return false; } finally @@ -191,12 +185,6 @@ protected override void OnDisposing() base.OnDisposing(); } - //StreamTodo: wrap all Unity webcam texture operations here. Enabling/Disabling tracks should manage the WebCamTexture so that users only - //Also take into account that user may want to provide his instance of WebCamTexture + monitor for devices list changes - - //StreamTodo: add AutoDetectActiveDevice() method -> will sample each device and pick the first that delivers data - //We could also favor front camera on mobile devices - private WebCamTexture _activeCamera; private Stopwatch _stopwatch; @@ -229,14 +217,14 @@ private static bool IsFrameBlack(IReadOnlyList frame1) { for (var i = 0; i < frame1.Count; i++) { - //StreamTodo: perhaps check if the whole frame is same color. Some virtual camera can be e.g. orange + //StreamTodo: perhaps check if the whole frame is same color. In one case a virtual camera was solid orange if (frame1[i] != Color.black) { - return true; + return false; } } - return false; + return true; } } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs index 86f8743c..1a212e19 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs @@ -91,6 +91,11 @@ public AudioSource AudioInput { throw new ArgumentNullException(); } + + if (value == _audioInput) + { + return; + } var prev = _audioInput; _audioInput = value; @@ -111,6 +116,11 @@ public WebCamTexture VideoInput { throw new ArgumentNullException(); } + + if (value == _videoInput) + { + return; + } var prev = _videoInput; _videoInput = value; @@ -301,6 +311,7 @@ public void TrySetVideoTrackEnabled(bool isEnabled) { if (Publisher?.PublisherVideoTrack == null) { + //StreamTodo: we probably want to cache this here and use once the track is available return; } diff --git a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs index 91742679..74fcf418 100644 --- a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs @@ -385,8 +385,8 @@ private StreamVideoClient(IWebsocketClient coordinatorWebSocket, IWebsocketClien _cache = new Cache(this, serializer, _logs); InternalLowLevelClient.RtcSession.SetCache(_cache); - VideoDeviceManager = new VideoDeviceManager(InternalLowLevelClient.RtcSession, this); - AudioDeviceManager = new AudioDeviceManager(InternalLowLevelClient.RtcSession, this); + VideoDeviceManager = new VideoDeviceManager(InternalLowLevelClient.RtcSession, this, _logs); + AudioDeviceManager = new AudioDeviceManager(InternalLowLevelClient.RtcSession, this, _logs); SubscribeTo(InternalLowLevelClient); } diff --git a/Packages/StreamVideo/Tests/Runtime/CallsTests.cs b/Packages/StreamVideo/Tests/Runtime/CallsTests.cs index 550cf4ad..89102b75 100644 --- a/Packages/StreamVideo/Tests/Runtime/CallsTests.cs +++ b/Packages/StreamVideo/Tests/Runtime/CallsTests.cs @@ -3,8 +3,6 @@ using System.Linq; using System.Threading.Tasks; using NUnit.Framework; -using StreamVideo.Core; -using StreamVideo.Core.DeviceManagers; using StreamVideo.Core.StatefulModels.Tracks; using StreamVideo.Tests.Shared; using UnityEngine; @@ -31,17 +29,20 @@ private async Task When_two_clients_join_same_call_expect_no_errors_Async(ITestC [UnityTest] public IEnumerator When_client_joins_call_with_video_expect_receiving_video_track() - => ConnectAndExecute(When_client_joins_call_with_video_expect_receiving_video_track_Async); + => ConnectAndExecute(When_client_joins_call_with_video_expect_receiving_video_track_Async, + ignoreFailingMessages: true); private async Task When_client_joins_call_with_video_expect_receiving_video_track_Async( ITestClient clientA, ITestClient clientB) { var streamCall = await clientA.JoinRandomCallAsync(); - var cameraDevice = await TryGetFirstWorkingCameraDeviceAsync(clientA.Client); + var cameraDevice = await TestUtils.TryGetFirstWorkingCameraDeviceAsync(clientA.Client); + Debug.Log("Selected camera device: " + cameraDevice); clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice); - var call = await clientB.Client.JoinCallAsync(streamCall.Type, streamCall.Id, create: false, ring: false, + var call = await clientB.Client.JoinCallAsync(streamCall.Type, streamCall.Id, create: false, + ring: false, notify: false); var otherParticipant = call.Participants.First(p => !p.IsLocalParticipant); @@ -54,10 +55,7 @@ private async Task When_client_joins_call_with_video_expect_receiving_video_trac } else { - otherParticipant.TrackAdded += (_, track) => - { - streamTrack = (StreamVideoTrack)track; - }; + otherParticipant.TrackAdded += (_, track) => { streamTrack = (StreamVideoTrack)track; }; await WaitForConditionAsync(() => streamTrack != null); } @@ -65,23 +63,11 @@ private async Task When_client_joins_call_with_video_expect_receiving_video_trac Assert.IsNotNull(streamTrack); } - //StreamTodo: put this in VideoDeviceManager - private static async Task TryGetFirstWorkingCameraDeviceAsync(IStreamVideoClient client) - { - var cameraManager = client.VideoDeviceManager; - foreach (var cameraDevice in cameraManager.EnumerateDevices()) - { - var isWorking = await cameraManager.TestDeviceAsync(cameraDevice, 0.5f); - Debug.Log($"TEST DEVICE {cameraDevice} - {isWorking}"); - } - - return cameraManager.EnumerateDevices().First(d => d.Name.Contains("Logi") && d.Name.Contains("Capture")); - } - [UnityTest] public IEnumerator When_client_enables_video_during_call_expect_other_client_receiving_video_track() - => ConnectAndExecute(When_client_enables_video_during_call_expect_other_client_receiving_video_track_Async); - + => ConnectAndExecute(When_client_enables_video_during_call_expect_other_client_receiving_video_track_Async, + ignoreFailingMessages: true); + private async Task When_client_enables_video_during_call_expect_other_client_receiving_video_track_Async( ITestClient clientA, ITestClient clientB) { @@ -97,21 +83,18 @@ private async Task When_client_enables_video_during_call_expect_other_client_rec // Watch other participant video track StreamVideoTrack streamTrack = null; - otherParticipant.TrackAdded += (_, track) => - { - streamTrack = (StreamVideoTrack)track; - }; - + otherParticipant.TrackAdded += (_, track) => { streamTrack = (StreamVideoTrack)track; }; + // First participant - enable video track - var cameraDevice = await TryGetFirstWorkingCameraDeviceAsync(clientA.Client); + var cameraDevice = await TestUtils.TryGetFirstWorkingCameraDeviceAsync(clientA.Client); clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice); - + // Wait for event await WaitForConditionAsync(() => streamTrack != null); Assert.IsNotNull(streamTrack); } - + //StreamTodo: test EndedAt field. (1) is it set when /video/call/{type}/{id}/mark_ended is called, (2) what happens if participants just leave the call // (3) if we re-join a previously ended call, is the endedAt null again? } diff --git a/Packages/StreamVideo/Tests/Shared/TestClient.cs b/Packages/StreamVideo/Tests/Shared/TestClient.cs index 01437166..2bb37c5e 100644 --- a/Packages/StreamVideo/Tests/Shared/TestClient.cs +++ b/Packages/StreamVideo/Tests/Shared/TestClient.cs @@ -83,7 +83,7 @@ var credentials Debug.Log($"Client connected in {timer.Elapsed.TotalSeconds:F2} seconds"); } - + private class DemoCredentialsApiResponse { public string UserId; diff --git a/Packages/StreamVideo/Tests/Shared/TestUtils.cs b/Packages/StreamVideo/Tests/Shared/TestUtils.cs index 685a4145..e4f1d53c 100644 --- a/Packages/StreamVideo/Tests/Shared/TestUtils.cs +++ b/Packages/StreamVideo/Tests/Shared/TestUtils.cs @@ -1,20 +1,34 @@ #if STREAM_TESTS_ENABLED using System; using System.Collections; +using System.Linq; using System.Threading.Tasks; +using StreamVideo.Core; +using StreamVideo.Core.DeviceManagers; +using UnityEngine.TestTools; namespace StreamVideo.Tests.Shared { public static class TestUtils { public static IEnumerator RunAsIEnumerator(this Task task, - Action onSuccess = null) + Action onSuccess = null, bool ignoreFailingMessages = false) { + if (ignoreFailingMessages) + { + LogAssert.ignoreFailingMessages = true; + } + while (!task.IsCompleted) { yield return null; } + if (ignoreFailingMessages) + { + LogAssert.ignoreFailingMessages = false; + } + if (task.IsFaulted) { throw task.Exception; @@ -22,6 +36,22 @@ public static IEnumerator RunAsIEnumerator(this Task task, onSuccess?.Invoke(); } + + //StreamTodo: put this in VideoDeviceManager? + public static async Task TryGetFirstWorkingCameraDeviceAsync(IStreamVideoClient client) + { + var cameraManager = client.VideoDeviceManager; + foreach (var cameraDevice in cameraManager.EnumerateDevices()) + { + var isWorking = await cameraManager.TestDeviceAsync(cameraDevice, 0.5f); + if (isWorking) + { + return cameraDevice; + } + } + + return cameraManager.EnumerateDevices().First(); + } } } #endif \ No newline at end of file diff --git a/Packages/StreamVideo/Tests/Shared/TestsBase.cs b/Packages/StreamVideo/Tests/Shared/TestsBase.cs index f97141e6..0c0ed17b 100644 --- a/Packages/StreamVideo/Tests/Shared/TestsBase.cs +++ b/Packages/StreamVideo/Tests/Shared/TestsBase.cs @@ -13,8 +13,9 @@ namespace StreamVideo.Tests.Shared { public delegate Task SingleClientTestHandler(ITestClient client); + public delegate Task TwoClientsTestHandler(ITestClient client1, ITestClient client2); - + public class TestsBase { [OneTimeSetUp] @@ -47,7 +48,7 @@ public async void TearDown() { return (true, TimeSpan.Zero); } - + var stopwatch = new Stopwatch(); stopwatch.Start(); while (stopwatch.ElapsedMilliseconds < timeoutMs) @@ -59,25 +60,26 @@ public async void TearDown() return (true, stopwatch.Elapsed); } } - + return (false, stopwatch.Elapsed); } - + protected static IEnumerator ConnectAndExecute(Func test) { yield return ConnectAndExecuteAsync(_ => test()).RunAsIEnumerator(); } - + protected static IEnumerator ConnectAndExecute(SingleClientTestHandler test) { yield return ConnectAndExecuteAsync(clients => test(clients[0]), clientsToSpawn: 1).RunAsIEnumerator(); } - - protected static IEnumerator ConnectAndExecute(TwoClientsTestHandler test) + + protected static IEnumerator ConnectAndExecute(TwoClientsTestHandler test, bool ignoreFailingMessages = false) { - yield return ConnectAndExecuteAsync(clients => test(clients[0], clients[1]), clientsToSpawn: 2).RunAsIEnumerator(); + yield return ConnectAndExecuteAsync(clients => test(clients[0], clients[1]), clientsToSpawn: 2) + .RunAsIEnumerator(ignoreFailingMessages: ignoreFailingMessages); } - + private static async Task ConnectAndExecuteAsync(Func test, int clientsToSpawn = 1) { var clients = await StreamTestClientProvider.Instance.GetConnectedTestClientsAsync(clientsToSpawn); @@ -110,7 +112,8 @@ private static async Task ConnectAndExecuteAsync(Func test, if (!completed) { - throw new AggregateException($"Failed all attempts. Last Exception: {exceptions.Last().Message} ", exceptions); + throw new AggregateException($"Failed all attempts. Last Exception: {exceptions.Last().Message} ", + exceptions); } } } From add11928d2708bc48bc74054e3c053e804b9a839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Tue, 16 Apr 2024 17:09:07 +0200 Subject: [PATCH 07/31] Update example project to use new device managers --- .../UI/Devices/CameraMediaDevicePanel.cs | 9 +- .../UI/Devices/MediaDevicePanelBase.cs | 65 +++++----- .../UI/Devices/MicrophoneMediaDevicePanel.cs | 8 +- .../Scripts/UI/ParticipantView.cs | 2 + .../Scripts/UI/Screens/CallScreenView.cs | 7 +- .../Scripts/UI/Screens/MainScreenView.cs | 56 +-------- .../Scripts/UI/UIManager.cs | 111 +++--------------- .../Core/DeviceManagers/AudioDeviceManager.cs | 1 + .../Core/DeviceManagers/DeviceManagerBase.cs | 2 +- .../DeviceManagers/IAudioDeviceManager.cs | 2 +- .../Core/DeviceManagers/IDeviceManager.cs | 2 +- .../Core/DeviceManagers/VideoDeviceManager.cs | 1 + 12 files changed, 83 insertions(+), 183 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs index 98c0d4f2..46e9330c 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; -using System.Linq; -using UnityEngine; +using StreamVideo.Core.DeviceManagers; namespace StreamVideo.ExampleProject.UI.Devices { - public class CameraMediaDevicePanel : MediaDevicePanelBase + public class CameraMediaDevicePanel : MediaDevicePanelBase { - protected override IEnumerable GetDevicesNames() => WebCamTexture.devices.Select(d => d.name); + protected override IEnumerable GetDevices() => Client.VideoDeviceManager.EnumerateDevices(); + + protected override string GetDeviceName(CameraDeviceInfo device) => device.Name; } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs index 534884eb..16c84e0a 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -1,16 +1,13 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using StreamVideo.Core; using TMPro; using UnityEngine; namespace StreamVideo.ExampleProject.UI.Devices { - /// - /// Event handler for device changed event - /// - public delegate void DeviceChangeHandler(string deviceName, bool isActive); - /// /// Event handler for device toggled event /// @@ -19,27 +16,39 @@ namespace StreamVideo.ExampleProject.UI.Devices /// /// Panel that displays media device (microphone or camera) dropdown to pick the active device and a button to toggle on/off state /// - public abstract class MediaDevicePanelBase : MonoBehaviour + public abstract class MediaDevicePanelBase : MonoBehaviour { + /// + /// Event handler for device changed event + /// + public delegate void DeviceChangeHandler(TDevice deviceName, bool isActive); + public event DeviceChangeHandler DeviceChanged; public event DeviceToggleHandler DeviceToggled; - public string SelectedDeviceName { get; private set; } + public TDevice SelectedDevice { get; private set; } //StreamTodo: android has DeviceStatus: Enabled, Disabled, NotSelected public bool IsDeviceActive { get; private set; } = true; - public void SelectDeviceWithoutNotify(string deviceName) + public void Init(IStreamVideoClient client) { - var index = _deviceNames.IndexOf(deviceName); + Client = client ?? throw new ArgumentNullException(nameof(client)); + } + + public void SelectDeviceWithoutNotify(TDevice device) + { + var index = _devices.IndexOf(device); if (index == -1) { - Debug.LogError($"Failed to find index for device: {deviceName}"); + Debug.LogError($"Failed to find index for device: {device}"); return; } _dropdown.SetValueWithoutNotify(index); } + + protected IStreamVideoClient Client { get; private set; } // Called by Unity protected void Awake() @@ -49,7 +58,7 @@ protected void Awake() _deviceButton.Init(_buttonOnSprite, _buttonOffSprite); _deviceButton.Clicked += OnDeviceButtonClicked; - UpdateDevicesDropdown(GetDevicesNames().ToList()); + UpdateDevicesDropdown(GetDevices()); _refreshDeviceInterval = new WaitForSeconds(0.5f); _refreshCoroutine = StartCoroutine(RefreshDevicesList()); @@ -70,7 +79,9 @@ protected void OnDestroy() } } - protected abstract IEnumerable GetDevicesNames(); + protected abstract IEnumerable GetDevices(); + + protected abstract string GetDeviceName(TDevice device); [SerializeField] private Sprite _buttonOnSprite; @@ -86,21 +97,21 @@ protected void OnDestroy() private Coroutine _refreshCoroutine; private YieldInstruction _refreshDeviceInterval; - private readonly List _deviceNames = new List(); + private readonly List _devices = new List(); private void OnDropdownValueChanged(int optionIndex) { - var deviceName = _deviceNames.ElementAt(optionIndex); + var deviceName = _devices.ElementAt(optionIndex); if (deviceName == null) { Debug.LogError($"Failed to select device with index: {optionIndex}. Available devices: " + - string.Join(", ", _deviceNames)); + string.Join(", ", _devices)); return; } - SelectedDeviceName = deviceName; + SelectedDevice = deviceName; - DeviceChanged?.Invoke(SelectedDeviceName, IsDeviceActive); + DeviceChanged?.Invoke(SelectedDevice, IsDeviceActive); } private void OnDeviceButtonClicked() @@ -115,11 +126,11 @@ private IEnumerator RefreshDevicesList() { while (true) { - var availableDevices = GetDevicesNames().ToList(); - var devicesChanged = !_deviceNames.SequenceEqual(availableDevices); + var availableDevices = GetDevices().ToList(); + var devicesChanged = !_devices.SequenceEqual(availableDevices); if (devicesChanged) { - var prevDevicesLog = string.Join(", ", _deviceNames); + var prevDevicesLog = string.Join(", ", _devices); var newDevicesLog = string.Join(", ", availableDevices); Debug.Log($"Device list changed. Previous: {prevDevicesLog}, Current: {newDevicesLog}"); @@ -130,17 +141,17 @@ private IEnumerator RefreshDevicesList() } } - private void UpdateDevicesDropdown(List devices) + private void UpdateDevicesDropdown(IEnumerable devices) { - _deviceNames.Clear(); - _deviceNames.AddRange(devices); + _devices.Clear(); + _devices.AddRange(devices); _dropdown.ClearOptions(); - _dropdown.AddOptions(devices); + _dropdown.AddOptions(devices.Select(GetDeviceName).ToList()); - if (!string.IsNullOrEmpty(SelectedDeviceName) && !devices.Contains(SelectedDeviceName)) + if (SelectedDevice != null && !devices.Contains(SelectedDevice)) { - Debug.LogError($"Previously active device was unplugged: {SelectedDeviceName}"); + Debug.LogError($"Previously active device was unplugged: {SelectedDevice}"); //StreamTodo: handle case when user unplugged active device } } diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs index 876f9e62..13fc772d 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; -using UnityEngine; +using StreamVideo.Core.DeviceManagers; namespace StreamVideo.ExampleProject.UI.Devices { - public class MicrophoneMediaDevicePanel : MediaDevicePanelBase + public class MicrophoneMediaDevicePanel : MediaDevicePanelBase { - protected override IEnumerable GetDevicesNames() => Microphone.devices; + protected override IEnumerable GetDevices() => Client.AudioDeviceManager.EnumerateDevices(); + + protected override string GetDeviceName(MicrophoneDeviceInfo device) => device.Name; } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs index 6574c300..5d122984 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs @@ -85,6 +85,8 @@ protected void Update() { _lastVideoRenderedSize = videoRenderedSize; var videoResolution = new VideoResolution((int)videoRenderedSize.x, (int)videoRenderedSize.y); + + // To optimize bandwidth we always request the video resolution that matches what we're actually rendering Participant.UpdateRequestedVideoResolution(videoResolution); Debug.Log($"Rendered resolution changed for participant `{Participant.UserId}`. Requested video resolution update to: {videoResolution}"); } diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs index f0b5de93..c761fc90 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs @@ -62,6 +62,9 @@ protected override void OnInit() _leaveBtn.onClick.AddListener(VideoManager.LeaveActiveCall); _endBtn.onClick.AddListener(VideoManager.EndActiveCall); + _cameraPanel.Init(VideoManager.Client); + _microphonePanel.Init(VideoManager.Client); + _cameraPanel.DeviceChanged += UIManager.ChangeCamera; _cameraPanel.DeviceToggled += UIManager.SetCameraActive; @@ -156,7 +159,9 @@ private void AddParticipant(IStreamVideoCallParticipant participant, bool sortPa if (participant.IsLocalParticipant) { // Set input camera as a video source for local participant - we won't receive OnTrack event for local participant - view.SetLocalCameraSource(UIManager.ActiveCamera); + var webCamTexture = VideoManager.Client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); + view.SetLocalCameraSource(webCamTexture); + //StreamTodo: this will invalidate each time WebCamTexture is internally replaced so we need a better way to expose this } if (sortParticipantViews) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs index 5d398789..4f84e65b 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using StreamVideo.ExampleProject.UI.Devices; using TMPro; using UnityEngine; @@ -29,14 +28,14 @@ protected override void OnInit() _audioRedToggle.onValueChanged.AddListener(VideoManager.SetAudioREDundancyEncoding); _audioDtxToggle.onValueChanged.AddListener(VideoManager.SetAudioDtx); + _cameraPanel.Init(VideoManager.Client); + _microphonePanel.Init(VideoManager.Client); + _cameraPanel.DeviceChanged += UIManager.ChangeCamera; _cameraPanel.DeviceToggled += UIManager.SetCameraActive; _microphonePanel.DeviceChanged += UIManager.ChangeMicrophone; _microphonePanel.DeviceToggled += UIManager.SetMicrophoneActive; - - SmartPickDefaultCamera(); - SmartPickDefaultMicrophone(); } protected override void OnShow(CallScreenView.ShowArgs showArgs) @@ -112,55 +111,6 @@ private void OnActiveCameraChanged(WebCamTexture activeCamera) _localCameraImage.texture = activeCamera; } - private void SmartPickDefaultCamera() - { - var devices = WebCamTexture.devices; - -#if UNITY_STANDALONE_WIN - //StreamTodo: remove this, "Capture" is our debug camera - _defaultCamera = devices.FirstOrDefault(d => d.name.Contains("Capture")); - -#elif UNITY_ANDROID || UNITY_IOS - _defaultCamera = devices.FirstOrDefault(d => d.isFrontFacing); -#endif - - if (string.IsNullOrEmpty(_defaultCamera.name)) - { - _defaultCamera = devices.FirstOrDefault(); - } - - if (string.IsNullOrEmpty(_defaultCamera.name)) - { - Debug.LogError("Failed to pick default camera device"); - return; - } - - _cameraPanel.SelectDeviceWithoutNotify(_defaultCamera.name); - UIManager.ChangeCamera(_defaultCamera.name, _cameraPanel.IsDeviceActive); - } - - //StreamTodo: remove - private void SmartPickDefaultMicrophone() - { - var preferredMicDevices = new[] { "bose", "airpods" }; - _defaultMicrophoneDeviceName = Microphone.devices.FirstOrDefault(d - => preferredMicDevices.Any(m => d.IndexOf(m, StringComparison.OrdinalIgnoreCase) != -1)); - - if (string.IsNullOrEmpty(_defaultMicrophoneDeviceName)) - { - _defaultMicrophoneDeviceName = Microphone.devices.FirstOrDefault(); - } - - if (string.IsNullOrEmpty(_defaultMicrophoneDeviceName)) - { - Debug.LogError("Failed to pick default microphone device"); - return; - } - - _microphonePanel.SelectDeviceWithoutNotify(_defaultMicrophoneDeviceName); - UIManager.ChangeMicrophone(_defaultMicrophoneDeviceName, _microphonePanel.IsDeviceActive); - } - private static string CreateRandomCallId() => Guid.NewGuid().ToString().Replace("-", ""); } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index d7cc2cd5..1a91bdbd 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -1,4 +1,5 @@ using System; +using StreamVideo.Core.DeviceManagers; using StreamVideo.Core.StatefulModels; using StreamVideo.ExampleProject.UI.Screens; using UnityEngine; @@ -9,53 +10,31 @@ public class UIManager : MonoBehaviour { public event Action ActiveCameraChanged; - public WebCamTexture ActiveCamera { get; private set; } - public AudioSource InputAudioSource => _inputAudioSource; + //public WebCamTexture ActiveCamera { get; private set; } + //public AudioSource InputAudioSource => _inputAudioSource; public Camera InputSceneSource => _inputSceneCamera; - public void ChangeMicrophone(string deviceName, bool isActive) + public void ChangeMicrophone(MicrophoneDeviceInfo device, bool isActive) { - if (!string.IsNullOrEmpty(_selectedMicrophoneDeviceName)) - { - StopAudioRecording(); - } - - var prevDevice = _selectedMicrophoneDeviceName ?? "None"; - _selectedMicrophoneDeviceName = deviceName; - - if (isActive) - { - StartAudioRecording(); - } + var prevDevice = _videoManager.Client.AudioDeviceManager.SelectedDevice.Name ?? "None"; + _selectedMicrophoneDeviceName = device.Name; + _videoManager.Client.AudioDeviceManager.SelectDevice(device); + Debug.Log( $"Changed selected MICROPHONE from `{prevDevice}` to `{_selectedMicrophoneDeviceName}`. Recording: {isActive}"); } - public void ChangeCamera(string deviceName, bool isActive) + public void ChangeCamera(CameraDeviceInfo device, bool isActive) { - var prevDevice = ActiveCamera != null ? ActiveCamera.deviceName : "None"; - - if (ActiveCamera == null) - { - ActiveCamera = new WebCamTexture(deviceName, _senderVideoWidth, _senderVideoHeight, _senderVideoFps); - } - - // Camera needs to be stopped before changing the deviceName - ActiveCamera.Stop(); - ActiveCamera.deviceName = deviceName; - - if (isActive) - { - ActiveCamera.Play(); - //StreamTodo: handle in coroutine and check if the camera started - } - - Debug.Log($"Changed active CAMERA from `{prevDevice}` to `{deviceName}`"); - - //_videoManager.Client?.SetCameraInputSource(ActiveCamera); - - ActiveCameraChanged?.Invoke(ActiveCamera); + var cameraManager = _videoManager.Client.VideoDeviceManager; + var prevDevice = cameraManager.SelectedDevice.Name ?? "None"; + + cameraManager.SelectDevice(device); + + Debug.Log($"Changed active CAMERA from `{prevDevice}` to `{device}`"); + + ActiveCameraChanged?.Invoke(cameraManager.GetSelectedDeviceWebCamTexture()); } /// @@ -64,14 +43,6 @@ public void ChangeCamera(string deviceName, bool isActive) public void SetMicrophoneActive(bool isActive) { _videoManager.Client.AudioDeviceManager.SetEnabled(isActive); - - if (isActive) - { - StartAudioRecording(); - return; - } - - StopAudioRecording(); } /// @@ -83,13 +54,11 @@ public void SetCameraActive(bool isActive) if (isActive) { - ActiveCamera.Play(); - Debug.Log($"Camera recording started for `{ActiveCamera.deviceName}`"); + Debug.Log($"Camera recording started for `{_videoManager.Client.VideoDeviceManager.SelectedDevice.Name}`"); return; } - ActiveCamera.Stop(); - Debug.Log($"Camera recording stopped for `{ActiveCamera.deviceName}`"); + Debug.Log($"Camera recording stopped for `{_videoManager.Client.VideoDeviceManager.SelectedDevice.Name}`"); } public void Log(string message, LogType type) @@ -113,9 +82,6 @@ protected void Awake() protected void Start() { - //_videoManager.Client.SetAudioInputSource(_inputAudioSource); - //_videoManager.Client.SetCameraInputSource(ActiveCamera); - ShowMainScreen(); } @@ -137,9 +103,6 @@ protected void OnDestroy() [SerializeField] private int _senderVideoFps = 30; - [SerializeField] - private AudioSource _inputAudioSource; - [SerializeField] private Camera _inputSceneCamera; @@ -155,42 +118,6 @@ protected void OnDestroy() private void OnCallEnded() => ShowMainScreen(); - private void StartAudioRecording() - { - if (_inputAudioSource == null) - { - Debug.LogError("Audio recording failed. Input Audio Source is null"); - return; - } - - if (string.IsNullOrEmpty(_selectedMicrophoneDeviceName)) - { - Debug.LogError("Audio recording failed. No microphone device selected."); - return; - } - - //StreamTodo: should the volume be 0 so we never hear input from our own microphone? - _inputAudioSource.clip - = Microphone.Start(_selectedMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); - _inputAudioSource.loop = true; - _inputAudioSource.Play(); - - Debug.Log($"Audio recording started for `{_selectedMicrophoneDeviceName}`"); - } - - private void StopAudioRecording() - { - var isRecording = !string.IsNullOrEmpty(_selectedMicrophoneDeviceName) && - Microphone.IsRecording(_selectedMicrophoneDeviceName); - if (!isRecording) - { - return; - } - - Microphone.End(_selectedMicrophoneDeviceName); - Debug.Log($"Audio recording stopped for `{_selectedMicrophoneDeviceName}`"); - } - private void ShowMainScreen() { _callScreen.Hide(); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index b9ff0482..ac0e122d 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -10,6 +10,7 @@ namespace StreamVideo.Core.DeviceManagers { internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager { + //StreamTodo: user can add/remove devices, we might want to expose DeviceAdded, DeviceRemoved events public override IEnumerable EnumerateDevices() { foreach (var device in Microphone.devices) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 9ce0e39b..0cc71041 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -9,7 +9,7 @@ namespace StreamVideo.Core.DeviceManagers public delegate void SelectedDeviceChangeHandler(TDeviceInfo previousDevice, TDeviceInfo currentDevice); - internal abstract class DeviceManagerBase : IDeviceManager + internal abstract class DeviceManagerBase : IDeviceManager where TDeviceInfo : struct { public event SelectedDeviceChangeHandler SelectedDeviceChanged; diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs index 0f9d5e79..0fdbbc23 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs @@ -5,6 +5,6 @@ /// public interface IAudioDeviceManager : IDeviceManager { - + void SelectDevice(MicrophoneDeviceInfo device); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 9221affe..de02f58b 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -4,7 +4,7 @@ namespace StreamVideo.Core.DeviceManagers { - public interface IDeviceManager : IDisposable + public interface IDeviceManager : IDisposable where TDeviceInfo : struct { /// /// Is device enabled. Enabled device will stream output during the call. diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index da8d80bf..4a29655b 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -20,6 +20,7 @@ namespace StreamVideo.Core.DeviceManagers */ internal class VideoDeviceManager : DeviceManagerBase, IVideoDeviceManager { + //StreamTodo: user can add/remove devices, we might want to expose DeviceAdded, DeviceRemoved events public override IEnumerable EnumerateDevices() { foreach (var device in WebCamTexture.devices) From dce7f1e0f0cd97dc4fc733335ef2000ce6bb85ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:19:26 +0200 Subject: [PATCH 08/31] Fix initialization order --- .../Scripts/StreamVideoManager.cs | 24 +++++++++---------- .../UI/Devices/MediaDevicePanelBase.cs | 15 ++++++++---- .../Scripts/UI/UIManager.cs | 2 ++ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/StreamVideoManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/StreamVideoManager.cs index 3e29a317..1d5f8f6d 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/StreamVideoManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/StreamVideoManager.cs @@ -17,6 +17,18 @@ public class StreamVideoManager : MonoBehaviour public event Action CallEnded; public IStreamVideoClient Client { get; private set; } + + public void Init() + { + _clientConfig = new StreamClientConfig + { + LogLevel = StreamLogLevel.Debug, + }; + + Client = StreamVideoClient.CreateDefaultClient(_clientConfig); + Client.CallStarted += OnCallStarted; + Client.CallEnded += OnCallEnded; + } /// /// Join the Call with a given ID. We can either create it or try to join only. @@ -65,18 +77,6 @@ public void LeaveActiveCall() /// public void SetAudioREDundancyEncoding(bool value) => _clientConfig.Audio.EnableRed = value; - protected void Awake() - { - _clientConfig = new StreamClientConfig - { - LogLevel = StreamLogLevel.Debug, - }; - - Client = StreamVideoClient.CreateDefaultClient(_clientConfig); - Client.CallStarted += OnCallStarted; - Client.CallEnded += OnCallEnded; - } - protected async void Start() { var credentials = new AuthCredentials(_apiKey, _userId, _userToken); diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs index 16c84e0a..a5c1acaa 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -16,7 +16,7 @@ namespace StreamVideo.ExampleProject.UI.Devices /// /// Panel that displays media device (microphone or camera) dropdown to pick the active device and a button to toggle on/off state /// - public abstract class MediaDevicePanelBase : MonoBehaviour + public abstract class MediaDevicePanelBase : MonoBehaviour where TDevice : struct { /// /// Event handler for device changed event @@ -34,6 +34,8 @@ public abstract class MediaDevicePanelBase : MonoBehaviour public void Init(IStreamVideoClient client) { Client = client ?? throw new ArgumentNullException(nameof(client)); + + UpdateDevicesDropdown(GetDevices()); } public void SelectDeviceWithoutNotify(TDevice device) @@ -57,8 +59,6 @@ protected void Awake() _deviceButton.Init(_buttonOnSprite, _buttonOffSprite); _deviceButton.Clicked += OnDeviceButtonClicked; - - UpdateDevicesDropdown(GetDevices()); _refreshDeviceInterval = new WaitForSeconds(0.5f); _refreshCoroutine = StartCoroutine(RefreshDevicesList()); @@ -102,7 +102,7 @@ protected void OnDestroy() private void OnDropdownValueChanged(int optionIndex) { var deviceName = _devices.ElementAt(optionIndex); - if (deviceName == null) + if (deviceName.Equals(default)) { Debug.LogError($"Failed to select device with index: {optionIndex}. Available devices: " + string.Join(", ", _devices)); @@ -126,6 +126,11 @@ private IEnumerator RefreshDevicesList() { while (true) { + while (Client == null) + { + yield return _refreshDeviceInterval; + } + var availableDevices = GetDevices().ToList(); var devicesChanged = !_devices.SequenceEqual(availableDevices); if (devicesChanged) @@ -149,7 +154,7 @@ private void UpdateDevicesDropdown(IEnumerable devices) _dropdown.ClearOptions(); _dropdown.AddOptions(devices.Select(GetDeviceName).ToList()); - if (SelectedDevice != null && !devices.Contains(SelectedDevice)) + if (!EqualityComparer.Default.Equals(SelectedDevice, default) && !devices.Contains(SelectedDevice)) { Debug.LogError($"Previously active device was unplugged: {SelectedDevice}"); //StreamTodo: handle case when user unplugged active device diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index 1a91bdbd..d93f5f08 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -73,6 +73,8 @@ public void Log(string message, LogType type) protected void Awake() { + _videoManager.Init(); + _videoManager.CallStarted += OnCallStarted; _videoManager.CallEnded += OnCallEnded; From 3a1dbce613fdc2d3723e0322b0f27c7211020985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:38:59 +0200 Subject: [PATCH 09/31] Select first working camera by default and the first microphone device --- .../UI/Devices/CameraMediaDevicePanel.cs | 23 +++++++++++++++ .../UI/Devices/MediaDevicePanelBase.cs | 29 +++++++++++++++---- .../UI/Devices/MicrophoneMediaDevicePanel.cs | 19 ++++++++++++ .../Core/DeviceManagers/DeviceManagerBase.cs | 14 +++++++++ .../Core/DeviceManagers/IDeviceManager.cs | 11 +++++-- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs index 46e9330c..5691ed50 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; +using System.Threading.Tasks; using StreamVideo.Core.DeviceManagers; +using StreamVideo.Libs.Utils; +using UnityEngine; namespace StreamVideo.ExampleProject.UI.Devices { @@ -8,5 +11,25 @@ public class CameraMediaDevicePanel : MediaDevicePanelBase protected override IEnumerable GetDevices() => Client.VideoDeviceManager.EnumerateDevices(); protected override string GetDeviceName(CameraDeviceInfo device) => device.Name; + + protected override void OnInit() + { + base.OnInit(); + + TrySelectFirstWorkingDeviceAsync().LogIfFailed(); + } + + private async Task TrySelectFirstWorkingDeviceAsync() + { + var workingDevice = await Client.VideoDeviceManager.TryFindFirstWorkingDeviceAsync(); + if (!workingDevice.HasValue) + { + Debug.LogError("No working camera found"); + return; + } + + SelectDeviceWithoutNotify(workingDevice.Value); + SelectedDevice = workingDevice.Value; + } } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs index a5c1acaa..93973999 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -26,8 +26,20 @@ public abstract class MediaDevicePanelBase : MonoBehaviour where TDevic public event DeviceChangeHandler DeviceChanged; public event DeviceToggleHandler DeviceToggled; - public TDevice SelectedDevice { get; private set; } - + public TDevice SelectedDevice + { + get => _selectedDevice; + protected set + { + if (EqualityComparer.Default.Equals(_selectedDevice, value)) + { + return; + } + _selectedDevice = value; + DeviceChanged?.Invoke(_selectedDevice, IsDeviceActive); + } + } + //StreamTodo: android has DeviceStatus: Enabled, Disabled, NotSelected public bool IsDeviceActive { get; private set; } = true; @@ -36,6 +48,8 @@ public void Init(IStreamVideoClient client) Client = client ?? throw new ArgumentNullException(nameof(client)); UpdateDevicesDropdown(GetDevices()); + + OnInit(); } public void SelectDeviceWithoutNotify(TDevice device) @@ -78,11 +92,18 @@ protected void OnDestroy() StopCoroutine(_refreshCoroutine); } } + + protected virtual void OnInit() + { + + } protected abstract IEnumerable GetDevices(); protected abstract string GetDeviceName(TDevice device); + private readonly List _devices = new List(); + [SerializeField] private Sprite _buttonOnSprite; @@ -97,7 +118,7 @@ protected void OnDestroy() private Coroutine _refreshCoroutine; private YieldInstruction _refreshDeviceInterval; - private readonly List _devices = new List(); + private TDevice _selectedDevice; private void OnDropdownValueChanged(int optionIndex) { @@ -110,8 +131,6 @@ private void OnDropdownValueChanged(int optionIndex) } SelectedDevice = deviceName; - - DeviceChanged?.Invoke(SelectedDevice, IsDeviceActive); } private void OnDeviceButtonClicked() diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs index 13fc772d..d3000a4c 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using System.Linq; using StreamVideo.Core.DeviceManagers; +using UnityEngine; namespace StreamVideo.ExampleProject.UI.Devices { @@ -8,5 +10,22 @@ public class MicrophoneMediaDevicePanel : MediaDevicePanelBase GetDevices() => Client.AudioDeviceManager.EnumerateDevices(); protected override string GetDeviceName(MicrophoneDeviceInfo device) => device.Name; + + protected override void OnInit() + { + base.OnInit(); + + // Select first microphone by default + var microphoneDevice = Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); + if (microphoneDevice == default) + { + Debug.LogError("No microphone found"); + return; + } + + SelectDeviceWithoutNotify(microphoneDevice); + SelectedDevice = microphoneDevice; + + } } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 0cc71041..5a545d3d 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -53,6 +53,20 @@ public Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f) return OnTestDeviceAsync(device, (int)(timeout * 1000)); } + + public async Task TryFindFirstWorkingDeviceAsync(float testTimeoutPerDevice = 0.2f) + { + foreach (var device in EnumerateDevices()) + { + var isWorking = await TestDeviceAsync(device, testTimeoutPerDevice); + if (isWorking) + { + return device; + } + } + + return null; + } public void Dispose() => OnDisposing(); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index de02f58b..6361ef57 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -39,12 +39,19 @@ public interface IDeviceManager : IDisposable where TDeviceInfo : s /// /// Check if the device is capturing data. /// This can be useful when there are multiple devices available and you want to filter out the ones that actually work. - /// For example, on Windows/Mac/Linux there can be many virtual cameras that are not capturing any data. - /// You usually want to show all available devices to users but it may be a good idea to show working devices first or enable a first working device by default. + /// For example, on Windows/Mac/Linux there can be many virtual cameras/microphones that are not capturing any data. + /// You typically want to present all available devices to users but you may want to show working devices first or enable the first working device by default. /// /// Device to test. You can obtain them from /// How long the test will wait for camera input. /// True if device is providing captured data Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f); + + /// + /// Iterates over all available devices and performs on each until the first working device is found + /// + /// + /// First found working device of NULL if none of the devices worked + Task TryFindFirstWorkingDeviceAsync(float testTimeoutPerDevice = 0.2f); } } \ No newline at end of file From c04e68a29683d68fd34c11d1ad55fe4dc941feaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:05:12 +0200 Subject: [PATCH 10/31] Refactor device panels to not keep any state and only rely on the device manager state + sync UI with any changes to device managers state --- .../UI/Devices/CameraMediaDevicePanel.cs | 31 ++--- .../UI/Devices/MediaDevicePanelBase.cs | 53 ++------ .../UI/Devices/MicrophoneMediaDevicePanel.cs | 29 +++-- .../Scripts/UI/Screens/BaseScreenView.cs | 2 - .../Scripts/UI/Screens/CallScreenView.cs | 14 +- .../Scripts/UI/Screens/MainScreenView.cs | 14 +- .../Scripts/UI/UIManager.cs | 122 ++++++++---------- .../Core/DeviceManagers/CameraDeviceInfo.cs | 2 +- .../Core/DeviceManagers/IDeviceManager.cs | 5 + .../DeviceManagers/MicrophoneDeviceInfo.cs | 2 +- 10 files changed, 114 insertions(+), 160 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs index 5691ed50..e0f0cedc 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -1,35 +1,32 @@ using System.Collections.Generic; -using System.Threading.Tasks; using StreamVideo.Core.DeviceManagers; -using StreamVideo.Libs.Utils; -using UnityEngine; namespace StreamVideo.ExampleProject.UI.Devices { public class CameraMediaDevicePanel : MediaDevicePanelBase { + protected override CameraDeviceInfo SelectedDevice => Client.VideoDeviceManager.SelectedDevice; + + protected override bool IsDeviceEnabled + { + get => Client.VideoDeviceManager.IsEnabled; + set => Client.VideoDeviceManager.SetEnabled(value); + } + protected override IEnumerable GetDevices() => Client.VideoDeviceManager.EnumerateDevices(); protected override string GetDeviceName(CameraDeviceInfo device) => device.Name; + protected override void ChangeDevice(CameraDeviceInfo device) => Client.VideoDeviceManager.SelectDevice(device); + protected override void OnInit() { base.OnInit(); - - TrySelectFirstWorkingDeviceAsync().LogIfFailed(); + + Client.VideoDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; } - private async Task TrySelectFirstWorkingDeviceAsync() - { - var workingDevice = await Client.VideoDeviceManager.TryFindFirstWorkingDeviceAsync(); - if (!workingDevice.HasValue) - { - Debug.LogError("No working camera found"); - return; - } - - SelectDeviceWithoutNotify(workingDevice.Value); - SelectedDevice = workingDevice.Value; - } + private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) + => SelectDeviceWithoutNotify(currentDevice); } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs index 93973999..d108560d 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -8,47 +8,18 @@ namespace StreamVideo.ExampleProject.UI.Devices { - /// - /// Event handler for device toggled event - /// - public delegate void DeviceToggleHandler(bool isActive); - /// /// Panel that displays media device (microphone or camera) dropdown to pick the active device and a button to toggle on/off state /// - public abstract class MediaDevicePanelBase : MonoBehaviour where TDevice : struct + public abstract class MediaDevicePanelBase : MonoBehaviour + where TDevice : struct { - /// - /// Event handler for device changed event - /// - public delegate void DeviceChangeHandler(TDevice deviceName, bool isActive); - - public event DeviceChangeHandler DeviceChanged; - public event DeviceToggleHandler DeviceToggled; - - public TDevice SelectedDevice - { - get => _selectedDevice; - protected set - { - if (EqualityComparer.Default.Equals(_selectedDevice, value)) - { - return; - } - _selectedDevice = value; - DeviceChanged?.Invoke(_selectedDevice, IsDeviceActive); - } - } - - //StreamTodo: android has DeviceStatus: Enabled, Disabled, NotSelected - public bool IsDeviceActive { get; private set; } = true; - public void Init(IStreamVideoClient client) { Client = client ?? throw new ArgumentNullException(nameof(client)); UpdateDevicesDropdown(GetDevices()); - + OnInit(); } @@ -81,7 +52,7 @@ protected void Awake() // Called by Unity protected void Start() { - _deviceButton.UpdateSprite(IsDeviceActive); + _deviceButton.UpdateSprite(IsDeviceEnabled); } // Called by Unity @@ -99,9 +70,13 @@ protected virtual void OnInit() } protected abstract IEnumerable GetDevices(); + protected abstract TDevice SelectedDevice { get; } + protected abstract bool IsDeviceEnabled { get; set; } protected abstract string GetDeviceName(TDevice device); + protected abstract void ChangeDevice(TDevice device); + private readonly List _devices = new List(); [SerializeField] @@ -118,26 +93,24 @@ protected virtual void OnInit() private Coroutine _refreshCoroutine; private YieldInstruction _refreshDeviceInterval; - private TDevice _selectedDevice; private void OnDropdownValueChanged(int optionIndex) { - var deviceName = _devices.ElementAt(optionIndex); - if (deviceName.Equals(default)) + var device = _devices.ElementAt(optionIndex); + if (device.Equals(default)) { Debug.LogError($"Failed to select device with index: {optionIndex}. Available devices: " + string.Join(", ", _devices)); return; } - SelectedDevice = deviceName; + ChangeDevice(device); } private void OnDeviceButtonClicked() { - IsDeviceActive = !IsDeviceActive; - _deviceButton.UpdateSprite(IsDeviceActive); - DeviceToggled?.Invoke(IsDeviceActive); + IsDeviceEnabled = !IsDeviceEnabled; + _deviceButton.UpdateSprite(IsDeviceEnabled); } // User can add/remove devices any time so we must constantly monitor the devices list diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs index d3000a4c..72964e6d 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs @@ -1,31 +1,32 @@ using System.Collections.Generic; -using System.Linq; using StreamVideo.Core.DeviceManagers; -using UnityEngine; namespace StreamVideo.ExampleProject.UI.Devices { public class MicrophoneMediaDevicePanel : MediaDevicePanelBase { + protected override MicrophoneDeviceInfo SelectedDevice => Client.AudioDeviceManager.SelectedDevice; + + protected override bool IsDeviceEnabled + { + get => Client.AudioDeviceManager.IsEnabled; + set => Client.AudioDeviceManager.SetEnabled(value); + } + protected override IEnumerable GetDevices() => Client.AudioDeviceManager.EnumerateDevices(); protected override string GetDeviceName(MicrophoneDeviceInfo device) => device.Name; - + + protected override void ChangeDevice(MicrophoneDeviceInfo device) => Client.AudioDeviceManager.SelectDevice(device); + protected override void OnInit() { base.OnInit(); - - // Select first microphone by default - var microphoneDevice = Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); - if (microphoneDevice == default) - { - Debug.LogError("No microphone found"); - return; - } - - SelectDeviceWithoutNotify(microphoneDevice); - SelectedDevice = microphoneDevice; + Client.AudioDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; } + + private void OnSelectedDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) + => SelectDeviceWithoutNotify(currentDevice); } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/BaseScreenView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/BaseScreenView.cs index 83d2ac0e..28a317bc 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/BaseScreenView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/BaseScreenView.cs @@ -51,8 +51,6 @@ public void Hide() protected abstract void OnHide(); - protected void Log(string message, LogType type) => UIManager.Log(message, type); - private GameObject _gameObject; } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs index c761fc90..8778af0f 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs @@ -61,15 +61,9 @@ protected override void OnInit() { _leaveBtn.onClick.AddListener(VideoManager.LeaveActiveCall); _endBtn.onClick.AddListener(VideoManager.EndActiveCall); - + _cameraPanel.Init(VideoManager.Client); _microphonePanel.Init(VideoManager.Client); - - _cameraPanel.DeviceChanged += UIManager.ChangeCamera; - _cameraPanel.DeviceToggled += UIManager.SetCameraActive; - - _microphonePanel.DeviceChanged += UIManager.ChangeMicrophone; - _microphonePanel.DeviceToggled += UIManager.SetMicrophoneActive; } protected override void OnShow(ShowArgs showArgs) @@ -96,7 +90,7 @@ protected override void OnShow(ShowArgs showArgs) _activeCall.SortedParticipantsUpdated += SortParticipantViews; - UIManager.ActiveCameraChanged += OnActiveCameraChanged; + UIManager.LocalCameraChanged += OnLocalCameraChanged; // Show active call ID so user can copy it and send others to join _joinCallIdInput.text = _activeCall.Id; @@ -115,7 +109,7 @@ protected override void OnHide() RemoveAllParticipants(); - UIManager.ActiveCameraChanged -= OnActiveCameraChanged; + UIManager.LocalCameraChanged -= OnLocalCameraChanged; } private void OnDominantSpeakerChanged(IStreamVideoCallParticipant currentDominantSpeaker, @@ -229,7 +223,7 @@ private void RemoveAllParticipants() _participantSessionIdToView.Clear(); } - private void OnActiveCameraChanged(WebCamTexture activeCamera) + private void OnLocalCameraChanged(WebCamTexture activeCamera) { // Input Camera changed so let's update the preview for local participant var localParticipant diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs index 4f84e65b..5a7d5dff 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs @@ -30,22 +30,16 @@ protected override void OnInit() _cameraPanel.Init(VideoManager.Client); _microphonePanel.Init(VideoManager.Client); - - _cameraPanel.DeviceChanged += UIManager.ChangeCamera; - _cameraPanel.DeviceToggled += UIManager.SetCameraActive; - - _microphonePanel.DeviceChanged += UIManager.ChangeMicrophone; - _microphonePanel.DeviceToggled += UIManager.SetMicrophoneActive; } protected override void OnShow(CallScreenView.ShowArgs showArgs) { - UIManager.ActiveCameraChanged += OnActiveCameraChanged; + UIManager.LocalCameraChanged += OnLocalCameraChanged; } protected override void OnHide() { - UIManager.ActiveCameraChanged -= OnActiveCameraChanged; + UIManager.LocalCameraChanged -= OnLocalCameraChanged; } [SerializeField] @@ -81,7 +75,7 @@ private async void OnJoinCallButtonClicked() { if (string.IsNullOrEmpty(_joinCallIdInput.text)) { - Log("`Call ID` is required when trying to join a call", LogType.Error); + Debug.LogError("`Call ID` is required when trying to join a call"); return; } @@ -106,7 +100,7 @@ private async void OnCreateAndJoinCallButtonClicked() } } - private void OnActiveCameraChanged(WebCamTexture activeCamera) + private void OnLocalCameraChanged(WebCamTexture activeCamera) { _localCameraImage.texture = activeCamera; } diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index d93f5f08..a30865a9 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -1,96 +1,49 @@ using System; +using System.Linq; +using System.Threading.Tasks; using StreamVideo.Core.DeviceManagers; using StreamVideo.Core.StatefulModels; using StreamVideo.ExampleProject.UI.Screens; +using StreamVideo.Libs.Utils; using UnityEngine; namespace StreamVideo.ExampleProject.UI { public class UIManager : MonoBehaviour { - public event Action ActiveCameraChanged; + public event Action LocalCameraChanged; - //public WebCamTexture ActiveCamera { get; private set; } - //public AudioSource InputAudioSource => _inputAudioSource; public Camera InputSceneSource => _inputSceneCamera; - public void ChangeMicrophone(MicrophoneDeviceInfo device, bool isActive) - { - var prevDevice = _videoManager.Client.AudioDeviceManager.SelectedDevice.Name ?? "None"; - _selectedMicrophoneDeviceName = device.Name; - - _videoManager.Client.AudioDeviceManager.SelectDevice(device); - - Debug.Log( - $"Changed selected MICROPHONE from `{prevDevice}` to `{_selectedMicrophoneDeviceName}`. Recording: {isActive}"); - } - - public void ChangeCamera(CameraDeviceInfo device, bool isActive) - { - var cameraManager = _videoManager.Client.VideoDeviceManager; - var prevDevice = cameraManager.SelectedDevice.Name ?? "None"; - - cameraManager.SelectDevice(device); - - Debug.Log($"Changed active CAMERA from `{prevDevice}` to `{device}`"); - - ActiveCameraChanged?.Invoke(cameraManager.GetSelectedDeviceWebCamTexture()); - } - - /// - /// Start/stop microphone recording - /// - public void SetMicrophoneActive(bool isActive) - { - _videoManager.Client.AudioDeviceManager.SetEnabled(isActive); - } - - /// - /// Start/stop camera recording - /// - public void SetCameraActive(bool isActive) - { - _videoManager.Client.VideoDeviceManager.SetEnabled(isActive); - - if (isActive) - { - Debug.Log($"Camera recording started for `{_videoManager.Client.VideoDeviceManager.SelectedDevice.Name}`"); - return; - } - - Debug.Log($"Camera recording stopped for `{_videoManager.Client.VideoDeviceManager.SelectedDevice.Name}`"); - } - - public void Log(string message, LogType type) - { - if (type == LogType.Exception) - { - throw new NotSupportedException("To log exceptions use " + nameof(Debug.LogException)); - } - - Debug.LogFormat(type, LogOption.None, context: null, format: message); - } - protected void Awake() { _videoManager.Init(); - + _videoManager.CallStarted += OnCallStarted; _videoManager.CallEnded += OnCallEnded; + _videoManager.Client.VideoDeviceManager.SelectedDeviceChanged += OnCameraDeviceChanged; + _videoManager.Client.AudioDeviceManager.SelectedDeviceChanged += OnMicrophoneDeviceChanged; + _mainScreen.Init(_videoManager, uiManager: this); _callScreen.Init(_videoManager, uiManager: this); + + TrySelectFirstWorkingCameraAsync().LogIfFailed(); + TrySelectFirstMicrophoneAsync().LogIfFailed(); } - protected void Start() - { - ShowMainScreen(); - } + protected void Start() => ShowMainScreen(); protected void OnDestroy() { _videoManager.CallStarted -= OnCallStarted; _videoManager.CallEnded -= OnCallEnded; + + if (_videoManager.Client != null) + { + _videoManager.Client.VideoDeviceManager.SelectedDeviceChanged -= OnCameraDeviceChanged; + _videoManager.Client.AudioDeviceManager.SelectedDeviceChanged -= OnMicrophoneDeviceChanged; + } } [SerializeField] @@ -131,5 +84,44 @@ private void ShowCallScreen(IStreamCall call) _mainScreen.Hide(); _callScreen.Show(new CallScreenView.ShowArgs(call)); } + + private void OnMicrophoneDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) + { + Debug.Log($"Changed selected MICROPHONE from `{previousDevice}` to `{currentDevice}`"); + } + + private void OnCameraDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) + { + Debug.Log($"Changed active CAMERA from `{previousDevice}` to `{currentDevice}`"); + + var webCamTexture = _videoManager.Client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); + LocalCameraChanged?.Invoke(webCamTexture); + } + + private async Task TrySelectFirstWorkingCameraAsync() + { + var workingDevice = await _videoManager.Client.VideoDeviceManager.TryFindFirstWorkingDeviceAsync(); + if (!workingDevice.HasValue) + { + Debug.LogError("No working camera found"); + return; + } + + _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value); + } + + private Task TrySelectFirstMicrophoneAsync() + { + // Select first microphone by default + var microphoneDevice = _videoManager.Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); + if (microphoneDevice == default) + { + Debug.LogError("No microphone found"); + return Task.CompletedTask; + } + + _videoManager.Client.AudioDeviceManager.SelectDevice(microphoneDevice); + return Task.CompletedTask; + } } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs index 86cf8859..a824a6ab 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs @@ -30,7 +30,7 @@ public CameraDeviceInfo(string name, bool isFrontFacing, IVideoDeviceManager vid public Task TestDeviceAsync() => _videoDeviceManager.TestDeviceAsync(this); - public override string ToString() => $"Camera Device - {Name}"; + public override string ToString() => string.IsNullOrEmpty(Name) ? "None" : Name; internal bool IsValid => !string.IsNullOrEmpty(Name); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 6361ef57..9533b9eb 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -6,6 +6,11 @@ namespace StreamVideo.Core.DeviceManagers { public interface IDeviceManager : IDisposable where TDeviceInfo : struct { + /// + /// Event triggered when the changes. + /// + event SelectedDeviceChangeHandler SelectedDeviceChanged; + /// /// Is device enabled. Enabled device will stream output during the call. /// diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs index 6599700c..993c0dd5 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/MicrophoneDeviceInfo.cs @@ -24,7 +24,7 @@ public MicrophoneDeviceInfo(string name) public static bool operator !=(MicrophoneDeviceInfo left, MicrophoneDeviceInfo right) => !left.Equals(right); - public override string ToString() => $"Microphone Device - {Name}"; + public override string ToString() => string.IsNullOrEmpty(Name) ? "None" : Name; internal bool IsValid => !string.IsNullOrEmpty(Name); } From 8ef592c6a4fc66fea4171f92968c15dab7054bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:03:24 +0200 Subject: [PATCH 11/31] Set resolution + remove obsolete microphone input game object in scene --- .../MainScene.unity | 172 ++++-------------- .../UI/Devices/CameraMediaDevicePanel.cs | 10 +- .../UI/Devices/MediaDevicePanelBase.cs | 6 +- .../Scripts/UI/Screens/CallScreenView.cs | 4 +- .../Scripts/UI/Screens/MainScreenView.cs | 4 +- .../Scripts/UI/UIManager.cs | 4 + .../Core/DeviceManagers/AudioDeviceManager.cs | 4 + 7 files changed, 56 insertions(+), 148 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/MainScene.unity b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/MainScene.unity index 565081b0..d1399989 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/MainScene.unity +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/MainScene.unity @@ -104,7 +104,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -117,7 +117,7 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: @@ -128,6 +128,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 996985032} m_Modifications: - target: {fileID: 149840264038047480, guid: 117eea22f833ae64f95b5be7435be32d, type: 3} @@ -587,6 +588,9 @@ PrefabInstance: value: -40 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 117eea22f833ae64f95b5be7435be32d, type: 3} --- !u!224 &123332879 stripped RectTransform: @@ -635,7 +639,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 996985032} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -740,13 +743,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 575041316} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &705507993 GameObject: @@ -834,13 +837,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 705507993} + serializedVersion: 2 m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} --- !u!1 &963194225 GameObject: @@ -882,9 +885,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -918,13 +929,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 963194225} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 1, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &996985028 GameObject: @@ -1003,7 +1014,9 @@ Canvas: m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 25 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -1023,7 +1036,6 @@ RectTransform: - {fileID: 1214904789} - {fileID: 123332879} m_Father: {fileID: 0} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1046,7 +1058,6 @@ MonoBehaviour: _senderVideoWidth: 1280 _senderVideoHeight: 720 _senderVideoFps: 30 - _inputAudioSource: {fileID: 1825738054} _inputSceneCamera: {fileID: 963194227} _callScreen: {fileID: 123332880} _mainScreen: {fileID: 355221056687574256} @@ -1096,147 +1107,20 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1354881823} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1825738053 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1825738055} - - component: {fileID: 1825738054} - m_Layer: 0 - m_Name: MicrophoneInput - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!82 &1825738054 -AudioSource: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1825738053} - m_Enabled: 1 - serializedVersion: 4 - OutputAudioMixerGroup: {fileID: 0} - m_audioClip: {fileID: 0} - m_PlayOnAwake: 1 - m_Volume: 1 - m_Pitch: 1 - Loop: 0 - Mute: 0 - Spatialize: 0 - SpatializePostEffects: 0 - Priority: 128 - DopplerLevel: 1 - MinDistance: 1 - MaxDistance: 500 - Pan2D: 0 - rolloffMode: 0 - BypassEffects: 0 - BypassListenerEffects: 0 - BypassReverbZones: 0 - rolloffCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 1 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - panLevelCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - spreadCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - reverbZoneMixCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 ---- !u!4 &1825738055 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1825738053} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &355221056687574255 PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 996985032} m_Modifications: - target: {fileID: 355221055551435707, guid: 0718803385b9d8840b6d4f562c3d956a, type: 3} @@ -1812,6 +1696,9 @@ PrefabInstance: value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 0718803385b9d8840b6d4f562c3d956a, type: 3} --- !u!114 &355221056687574256 stripped MonoBehaviour: @@ -1824,3 +1711,12 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: ecb25fba984048238b2cf9ae786dab82, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1354881825} + - {fileID: 996985032} + - {fileID: 705507995} + - {fileID: 575041319} + - {fileID: 963194228} diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs index e0f0cedc..8d3897db 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -6,7 +6,7 @@ namespace StreamVideo.ExampleProject.UI.Devices public class CameraMediaDevicePanel : MediaDevicePanelBase { protected override CameraDeviceInfo SelectedDevice => Client.VideoDeviceManager.SelectedDevice; - + protected override bool IsDeviceEnabled { get => Client.VideoDeviceManager.IsEnabled; @@ -17,16 +17,18 @@ protected override bool IsDeviceEnabled protected override string GetDeviceName(CameraDeviceInfo device) => device.Name; - protected override void ChangeDevice(CameraDeviceInfo device) => Client.VideoDeviceManager.SelectDevice(device); + protected override void ChangeDevice(CameraDeviceInfo device) + => Client.VideoDeviceManager.SelectDevice(device, UIManager.SenderVideoResolution, + UIManager.SenderVideoFps); protected override void OnInit() { base.OnInit(); - + Client.VideoDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; } - private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) + private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) => SelectDeviceWithoutNotify(currentDevice); } } \ No newline at end of file diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs index d108560d..fe9174f6 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -14,10 +14,11 @@ namespace StreamVideo.ExampleProject.UI.Devices public abstract class MediaDevicePanelBase : MonoBehaviour where TDevice : struct { - public void Init(IStreamVideoClient client) + public void Init(IStreamVideoClient client, UIManager uiManager) { Client = client ?? throw new ArgumentNullException(nameof(client)); - + UIManager = uiManager ? uiManager : throw new ArgumentNullException(nameof(uiManager)); + UpdateDevicesDropdown(GetDevices()); OnInit(); @@ -93,6 +94,7 @@ protected virtual void OnInit() private Coroutine _refreshCoroutine; private YieldInstruction _refreshDeviceInterval; + protected UIManager UIManager { get; private set; } private void OnDropdownValueChanged(int optionIndex) { diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs index 8778af0f..ae136c51 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/CallScreenView.cs @@ -62,8 +62,8 @@ protected override void OnInit() _leaveBtn.onClick.AddListener(VideoManager.LeaveActiveCall); _endBtn.onClick.AddListener(VideoManager.EndActiveCall); - _cameraPanel.Init(VideoManager.Client); - _microphonePanel.Init(VideoManager.Client); + _cameraPanel.Init(VideoManager.Client, UIManager); + _microphonePanel.Init(VideoManager.Client, UIManager); } protected override void OnShow(ShowArgs showArgs) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs index 5a7d5dff..a365270a 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Screens/MainScreenView.cs @@ -28,8 +28,8 @@ protected override void OnInit() _audioRedToggle.onValueChanged.AddListener(VideoManager.SetAudioREDundancyEncoding); _audioDtxToggle.onValueChanged.AddListener(VideoManager.SetAudioDtx); - _cameraPanel.Init(VideoManager.Client); - _microphonePanel.Init(VideoManager.Client); + _cameraPanel.Init(VideoManager.Client, UIManager); + _microphonePanel.Init(VideoManager.Client, UIManager); } protected override void OnShow(CallScreenView.ShowArgs showArgs) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index a30865a9..395efcb9 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using StreamVideo.Core; using StreamVideo.Core.DeviceManagers; using StreamVideo.Core.StatefulModels; using StreamVideo.ExampleProject.UI.Screens; @@ -14,6 +15,9 @@ public class UIManager : MonoBehaviour public event Action LocalCameraChanged; public Camera InputSceneSource => _inputSceneCamera; + + public VideoResolution SenderVideoResolution => new VideoResolution(_senderVideoWidth, _senderVideoHeight); + public int SenderVideoFps => _senderVideoFps; protected void Awake() { diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index ac0e122d..a552fa69 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -74,6 +74,10 @@ public void SelectDevice(MicrophoneDeviceInfo device) = Microphone.Start(SelectedDevice.Name, true, 3, AudioSettings.outputSampleRate); targetAudioSource.loop = true; +#if STREAM_DEBUG_ENABLED + Logs.Info($"Changed microphone device to: {SelectedDevice}"); +#endif + //StreamTodo: in some cases starting the mic recording before the call was causing the recorded audio being played in speakers //I think the reason was that AudioSource was being captured by an AudioListener but once I've joined the call, this disappeared //Check if we can have this AudioSource to be ignored by AudioListener's or otherwise mute it when there is not active call session From faa7af89ba0df4200a12f86ed6b0264f91d516c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:10:54 +0200 Subject: [PATCH 12/31] Remove obsolete code --- .../Scripts/UI/UIManager.cs | 7 ------- .../Runtime/Core/DeviceManagers/DeviceManagerBase.cs | 4 ++-- .../Runtime/Core/DeviceManagers/IDeviceManager.cs | 10 +++++----- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index 395efcb9..455eb5a9 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -14,8 +14,6 @@ public class UIManager : MonoBehaviour { public event Action LocalCameraChanged; - public Camera InputSceneSource => _inputSceneCamera; - public VideoResolution SenderVideoResolution => new VideoResolution(_senderVideoWidth, _senderVideoHeight); public int SenderVideoFps => _senderVideoFps; @@ -62,17 +60,12 @@ protected void OnDestroy() [SerializeField] private int _senderVideoFps = 30; - [SerializeField] - private Camera _inputSceneCamera; - [SerializeField] private CallScreenView _callScreen; [SerializeField] private MainScreenView _mainScreen; - private string _selectedMicrophoneDeviceName; - private void OnCallStarted(IStreamCall call) => ShowCallScreen(call); private void OnCallEnded() => ShowMainScreen(); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 5a545d3d..1e377755 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -43,7 +43,7 @@ public void SetEnabled(bool isEnabled) public abstract IEnumerable EnumerateDevices(); - public Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f) + public Task TestDeviceAsync(TDeviceInfo device, float timeout = 1f) { if (timeout <= 0f || timeout > 20f) { @@ -54,7 +54,7 @@ public Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f) return OnTestDeviceAsync(device, (int)(timeout * 1000)); } - public async Task TryFindFirstWorkingDeviceAsync(float testTimeoutPerDevice = 0.2f) + public async Task TryFindFirstWorkingDeviceAsync(float testTimeoutPerDevice = 1f) { foreach (var device in EnumerateDevices()) { diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 9533b9eb..20d513a4 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -42,21 +42,21 @@ public interface IDeviceManager : IDisposable where TDeviceInfo : s IEnumerable EnumerateDevices(); /// - /// Check if the device is capturing data. + /// Check if the device is capturing any data. /// This can be useful when there are multiple devices available and you want to filter out the ones that actually work. - /// For example, on Windows/Mac/Linux there can be many virtual cameras/microphones that are not capturing any data. + /// For example, on Windows/Mac/Linux there can be many virtual cameras/microphones available that are not capturing any data. /// You typically want to present all available devices to users but you may want to show working devices first or enable the first working device by default. /// /// Device to test. You can obtain them from - /// How long the test will wait for camera input. + /// How long the test will wait for camera input. Please not that depending on OS and the device there can be delay in starting the device. Timeout below 0.5 seconds can not be enough for some device. /// True if device is providing captured data - Task TestDeviceAsync(TDeviceInfo device, float timeout = 0.2f); + Task TestDeviceAsync(TDeviceInfo device, float timeout = 1f); /// /// Iterates over all available devices and performs on each until the first working device is found /// /// /// First found working device of NULL if none of the devices worked - Task TryFindFirstWorkingDeviceAsync(float testTimeoutPerDevice = 0.2f); + Task TryFindFirstWorkingDeviceAsync(float testTimeoutPerDevice = 1f); } } \ No newline at end of file From a1d4f896710674f20181982aa92fac6cd95a912c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:31:34 +0200 Subject: [PATCH 13/31] Disable device by default & add device enable argument to SelectDevice methods --- .../UI/Devices/CameraMediaDevicePanel.cs | 2 +- .../UI/Devices/MicrophoneMediaDevicePanel.cs | 2 +- .../Scripts/UI/UIManager.cs | 4 ++-- .../Core/DeviceManagers/AudioDeviceManager.cs | 7 ++----- .../Core/DeviceManagers/DeviceManagerBase.cs | 11 ++++++++--- .../Core/DeviceManagers/IAudioDeviceManager.cs | 7 ++++++- .../Core/DeviceManagers/IDeviceManager.cs | 2 +- .../Core/DeviceManagers/IVideoDeviceManager.cs | 17 ++++++++++++----- .../Core/DeviceManagers/VideoDeviceManager.cs | 11 ++++------- .../StreamVideo/Tests/Runtime/CallsTests.cs | 4 ++-- 10 files changed, 39 insertions(+), 28 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs index 8d3897db..a0807c15 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -18,7 +18,7 @@ protected override bool IsDeviceEnabled protected override string GetDeviceName(CameraDeviceInfo device) => device.Name; protected override void ChangeDevice(CameraDeviceInfo device) - => Client.VideoDeviceManager.SelectDevice(device, UIManager.SenderVideoResolution, + => Client.VideoDeviceManager.SelectDevice(device, UIManager.SenderVideoResolution, IsDeviceEnabled, UIManager.SenderVideoFps); protected override void OnInit() diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs index 72964e6d..44aa18b8 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs @@ -17,7 +17,7 @@ protected override bool IsDeviceEnabled protected override string GetDeviceName(MicrophoneDeviceInfo device) => device.Name; - protected override void ChangeDevice(MicrophoneDeviceInfo device) => Client.AudioDeviceManager.SelectDevice(device); + protected override void ChangeDevice(MicrophoneDeviceInfo device) => Client.AudioDeviceManager.SelectDevice(device, IsDeviceEnabled); protected override void OnInit() { diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index 455eb5a9..35b3821f 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -104,7 +104,7 @@ private async Task TrySelectFirstWorkingCameraAsync() return; } - _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value); + _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value, enable: false); } private Task TrySelectFirstMicrophoneAsync() @@ -117,7 +117,7 @@ private Task TrySelectFirstMicrophoneAsync() return Task.CompletedTask; } - _videoManager.Client.AudioDeviceManager.SelectDevice(microphoneDevice); + _videoManager.Client.AudioDeviceManager.SelectDevice(microphoneDevice, enable: false); return Task.CompletedTask; } } diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs index a552fa69..e3269081 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs @@ -57,7 +57,7 @@ protected override async Task OnTestDeviceAsync(MicrophoneDeviceInfo devic /// /// /// Thrown when the provided device has an invalid name - public void SelectDevice(MicrophoneDeviceInfo device) + public void SelectDevice(MicrophoneDeviceInfo device, bool enable) { if (!device.IsValid) { @@ -82,10 +82,7 @@ public void SelectDevice(MicrophoneDeviceInfo device) //I think the reason was that AudioSource was being captured by an AudioListener but once I've joined the call, this disappeared //Check if we can have this AudioSource to be ignored by AudioListener's or otherwise mute it when there is not active call session - if (IsEnabled) - { - Enable(); - } + SetEnabled(enable); } //StreamTodo: https://docs.unity3d.com/ScriptReference/AudioSource-ignoreListenerPause.html perhaps this should be enabled so that AudioListener doesn't affect recorded audio diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 1e377755..24a91a36 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -13,7 +13,7 @@ internal abstract class DeviceManagerBase : IDeviceManager SelectedDeviceChanged; - public bool IsEnabled { get; private set; } = true; + public bool IsEnabled { get; private set; } public TDeviceInfo SelectedDevice { @@ -37,6 +37,11 @@ protected set public void SetEnabled(bool isEnabled) { + if (IsEnabled == isEnabled) + { + return; + } + IsEnabled = isEnabled; OnSetEnabled(isEnabled); } @@ -53,7 +58,7 @@ public Task TestDeviceAsync(TDeviceInfo device, float timeout = 1f) return OnTestDeviceAsync(device, (int)(timeout * 1000)); } - + public async Task TryFindFirstWorkingDeviceAsync(float testTimeoutPerDevice = 1f) { foreach (var device in EnumerateDevices()) @@ -91,7 +96,7 @@ internal DeviceManagerBase(RtcSession rtcSession, IInternalStreamVideoClient cli protected virtual void OnDisposing() { } - + private TDeviceInfo _selectedDevice; } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs index 0fdbbc23..96d91cdc 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs @@ -5,6 +5,11 @@ /// public interface IAudioDeviceManager : IDeviceManager { - void SelectDevice(MicrophoneDeviceInfo device); + /// + /// Select a microphone device for audio capturing. + /// + /// Device to select + /// Enable this device (Start Capturing Audio) + void SelectDevice(MicrophoneDeviceInfo device, bool enable); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 20d513a4..8444ab15 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -22,7 +22,7 @@ public interface IDeviceManager : IDisposable where TDeviceInfo : s TDeviceInfo SelectedDevice { get; } /// - /// START capturing data from the . Before calling this method you must first use + /// START capturing data from the . /// void Enable(); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs index b706de9d..d4511dd3 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs @@ -9,15 +9,22 @@ public interface IVideoDeviceManager : IDeviceManager { //StreamTodo: probably move all members from IDeviceManager here so we can have all comments specifically about video or audio - void SelectDevice(CameraDeviceInfo device, int fps = 30); + /// + /// Select a camera device for video capturing. + /// + /// Camera device + /// Enable this device (Start Capturing Video) + /// Requested frame rate for the captured video. If the requested FPS is not supported by the camera, the closets available value will be selected + void SelectDevice(CameraDeviceInfo device, bool enable, int fps = 30); /// - /// + /// Select a camera device for video capturing. /// - /// + /// Camera device /// Requested video resolution for the captured video. If the requested resolution is not supported by the camera, the closest available one will be selected. - /// Requested frame rate for the captured video. If the requested FPS is not supported by the camera, the closets available one will be selected - void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, int requestedFPS = 30); + /// Enable this device (Start Capturing Video) + /// Requested frame rate for the captured video. If the requested FPS is not supported by the camera, the closets available value will be selected + void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, bool enable, int requestedFPS = 30); /// /// Get the instance of for the selected device. This is useful if you want to diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 4a29655b..3a6889dc 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -29,10 +29,10 @@ public override IEnumerable EnumerateDevices() } } - public void SelectDevice(CameraDeviceInfo device, int fps = 30) - => SelectDevice(device, VideoResolution.Res_720p, fps); + public void SelectDevice(CameraDeviceInfo device, bool enable, int fps = 30) + => SelectDevice(device, VideoResolution.Res_720p, enable, fps); - public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, int requestedFPS = 30) + public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, bool enable, int requestedFPS = 30) { if (!device.IsValid) { @@ -61,10 +61,7 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol } } - if (IsEnabled) - { - Enable(); - } + SetEnabled(enable); } //StreamTodo: better to not expose this and make fake tracks for local user. This way every participant is processed exactly the same diff --git a/Packages/StreamVideo/Tests/Runtime/CallsTests.cs b/Packages/StreamVideo/Tests/Runtime/CallsTests.cs index 89102b75..46a28b40 100644 --- a/Packages/StreamVideo/Tests/Runtime/CallsTests.cs +++ b/Packages/StreamVideo/Tests/Runtime/CallsTests.cs @@ -39,7 +39,7 @@ private async Task When_client_joins_call_with_video_expect_receiving_video_trac var cameraDevice = await TestUtils.TryGetFirstWorkingCameraDeviceAsync(clientA.Client); Debug.Log("Selected camera device: " + cameraDevice); - clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice); + clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice, enable: true); var call = await clientB.Client.JoinCallAsync(streamCall.Type, streamCall.Id, create: false, ring: false, @@ -87,7 +87,7 @@ private async Task When_client_enables_video_during_call_expect_other_client_rec // First participant - enable video track var cameraDevice = await TestUtils.TryGetFirstWorkingCameraDeviceAsync(clientA.Client); - clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice); + clientA.Client.VideoDeviceManager.SelectDevice(cameraDevice, enable: true); // Wait for event await WaitForConditionAsync(() => streamTrack != null); From 551b5ea759944ca89e5ebd9f423badd61068d366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:34:20 +0200 Subject: [PATCH 14/31] Remove obsolete code --- .../Scripts/UI/ParticipantView.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs index 5d122984..5e99ea23 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs @@ -45,22 +45,12 @@ public void SetLocalCameraSource(WebCamTexture localWebCamTexture) { _localWebCamTexture = localWebCamTexture; - // if (_localParticipantRenderTexture != null) - // { - // // Dispose previous texture - // _localParticipantRenderTexture.Release(); - // _localParticipantRenderTexture = null; - // } - if (localWebCamTexture == null) { _video.texture = null; return; } - // _localParticipantRenderTexture = new RenderTexture(localWebCamTexture.width, localWebCamTexture.height, 0, RenderTextureFormat.Default); - // _localParticipantRenderTexture.Create(); - // we set RenderTexture a a RawImage.texture because the RenderTexture will receive video stream from the local camera _video.texture = localWebCamTexture; } @@ -74,11 +64,6 @@ protected void Awake() // Called by Unity Engine protected void Update() { - if (_localWebCamTexture != null) - { - //Graphics.Blit(_localWebCamTexture, _localParticipantRenderTexture); - } - var rect = _videoRectTransform.rect; var videoRenderedSize = new Vector2(rect.width, rect.height); if (videoRenderedSize != _lastVideoRenderedSize) From 75475b1c6ecb6982408a79c2c5845b7c678e0951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:34:43 +0200 Subject: [PATCH 15/31] sync imported & packages example projects --- .../Samples~/VideoChat/MainScene.unity | 172 +++------------ .../VideoChat/Scripts/StreamVideoManager.cs | 24 +-- .../UI/Devices/CameraMediaDevicePanel.cs | 31 ++- .../UI/Devices/MediaDevicePanelBase.cs | 98 +++++---- .../UI/Devices/MicrophoneMediaDevicePanel.cs | 28 ++- .../VideoChat/Scripts/UI/ParticipantView.cs | 21 +- .../Scripts/UI/Screens/BaseScreenView.cs | 2 - .../Scripts/UI/Screens/CallScreenView.cs | 17 +- .../Scripts/UI/Screens/MainScreenView.cs | 68 +----- .../VideoChat/Scripts/UI/UIManager.cs | 200 ++++++------------ 10 files changed, 229 insertions(+), 432 deletions(-) diff --git a/Packages/StreamVideo/Samples~/VideoChat/MainScene.unity b/Packages/StreamVideo/Samples~/VideoChat/MainScene.unity index 565081b0..d1399989 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/MainScene.unity +++ b/Packages/StreamVideo/Samples~/VideoChat/MainScene.unity @@ -104,7 +104,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -117,7 +117,7 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: @@ -128,6 +128,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 996985032} m_Modifications: - target: {fileID: 149840264038047480, guid: 117eea22f833ae64f95b5be7435be32d, type: 3} @@ -587,6 +588,9 @@ PrefabInstance: value: -40 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 117eea22f833ae64f95b5be7435be32d, type: 3} --- !u!224 &123332879 stripped RectTransform: @@ -635,7 +639,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 996985032} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -740,13 +743,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 575041316} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &705507993 GameObject: @@ -834,13 +837,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 705507993} + serializedVersion: 2 m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} --- !u!1 &963194225 GameObject: @@ -882,9 +885,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -918,13 +929,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 963194225} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 1, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &996985028 GameObject: @@ -1003,7 +1014,9 @@ Canvas: m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 25 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -1023,7 +1036,6 @@ RectTransform: - {fileID: 1214904789} - {fileID: 123332879} m_Father: {fileID: 0} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1046,7 +1058,6 @@ MonoBehaviour: _senderVideoWidth: 1280 _senderVideoHeight: 720 _senderVideoFps: 30 - _inputAudioSource: {fileID: 1825738054} _inputSceneCamera: {fileID: 963194227} _callScreen: {fileID: 123332880} _mainScreen: {fileID: 355221056687574256} @@ -1096,147 +1107,20 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1354881823} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1825738053 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1825738055} - - component: {fileID: 1825738054} - m_Layer: 0 - m_Name: MicrophoneInput - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!82 &1825738054 -AudioSource: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1825738053} - m_Enabled: 1 - serializedVersion: 4 - OutputAudioMixerGroup: {fileID: 0} - m_audioClip: {fileID: 0} - m_PlayOnAwake: 1 - m_Volume: 1 - m_Pitch: 1 - Loop: 0 - Mute: 0 - Spatialize: 0 - SpatializePostEffects: 0 - Priority: 128 - DopplerLevel: 1 - MinDistance: 1 - MaxDistance: 500 - Pan2D: 0 - rolloffMode: 0 - BypassEffects: 0 - BypassListenerEffects: 0 - BypassReverbZones: 0 - rolloffCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 1 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - panLevelCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - spreadCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - reverbZoneMixCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 ---- !u!4 &1825738055 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1825738053} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &355221056687574255 PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 996985032} m_Modifications: - target: {fileID: 355221055551435707, guid: 0718803385b9d8840b6d4f562c3d956a, type: 3} @@ -1812,6 +1696,9 @@ PrefabInstance: value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 0718803385b9d8840b6d4f562c3d956a, type: 3} --- !u!114 &355221056687574256 stripped MonoBehaviour: @@ -1824,3 +1711,12 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: ecb25fba984048238b2cf9ae786dab82, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1354881825} + - {fileID: 996985032} + - {fileID: 705507995} + - {fileID: 575041319} + - {fileID: 963194228} diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/StreamVideoManager.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/StreamVideoManager.cs index 3e29a317..1d5f8f6d 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/StreamVideoManager.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/StreamVideoManager.cs @@ -17,6 +17,18 @@ public class StreamVideoManager : MonoBehaviour public event Action CallEnded; public IStreamVideoClient Client { get; private set; } + + public void Init() + { + _clientConfig = new StreamClientConfig + { + LogLevel = StreamLogLevel.Debug, + }; + + Client = StreamVideoClient.CreateDefaultClient(_clientConfig); + Client.CallStarted += OnCallStarted; + Client.CallEnded += OnCallEnded; + } /// /// Join the Call with a given ID. We can either create it or try to join only. @@ -65,18 +77,6 @@ public void LeaveActiveCall() /// public void SetAudioREDundancyEncoding(bool value) => _clientConfig.Audio.EnableRed = value; - protected void Awake() - { - _clientConfig = new StreamClientConfig - { - LogLevel = StreamLogLevel.Debug, - }; - - Client = StreamVideoClient.CreateDefaultClient(_clientConfig); - Client.CallStarted += OnCallStarted; - Client.CallEnded += OnCallEnded; - } - protected async void Start() { var credentials = new AuthCredentials(_apiKey, _userId, _userToken); diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs index 98c0d4f2..a0807c15 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -1,11 +1,34 @@ using System.Collections.Generic; -using System.Linq; -using UnityEngine; +using StreamVideo.Core.DeviceManagers; namespace StreamVideo.ExampleProject.UI.Devices { - public class CameraMediaDevicePanel : MediaDevicePanelBase + public class CameraMediaDevicePanel : MediaDevicePanelBase { - protected override IEnumerable GetDevicesNames() => WebCamTexture.devices.Select(d => d.name); + protected override CameraDeviceInfo SelectedDevice => Client.VideoDeviceManager.SelectedDevice; + + protected override bool IsDeviceEnabled + { + get => Client.VideoDeviceManager.IsEnabled; + set => Client.VideoDeviceManager.SetEnabled(value); + } + + protected override IEnumerable GetDevices() => Client.VideoDeviceManager.EnumerateDevices(); + + protected override string GetDeviceName(CameraDeviceInfo device) => device.Name; + + protected override void ChangeDevice(CameraDeviceInfo device) + => Client.VideoDeviceManager.SelectDevice(device, UIManager.SenderVideoResolution, IsDeviceEnabled, + UIManager.SenderVideoFps); + + protected override void OnInit() + { + base.OnInit(); + + Client.VideoDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; + } + + private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) + => SelectDeviceWithoutNotify(currentDevice); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs index 534884eb..fe9174f6 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -1,45 +1,42 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using StreamVideo.Core; using TMPro; using UnityEngine; namespace StreamVideo.ExampleProject.UI.Devices { - /// - /// Event handler for device changed event - /// - public delegate void DeviceChangeHandler(string deviceName, bool isActive); - - /// - /// Event handler for device toggled event - /// - public delegate void DeviceToggleHandler(bool isActive); - /// /// Panel that displays media device (microphone or camera) dropdown to pick the active device and a button to toggle on/off state /// - public abstract class MediaDevicePanelBase : MonoBehaviour + public abstract class MediaDevicePanelBase : MonoBehaviour + where TDevice : struct { - public event DeviceChangeHandler DeviceChanged; - public event DeviceToggleHandler DeviceToggled; + public void Init(IStreamVideoClient client, UIManager uiManager) + { + Client = client ?? throw new ArgumentNullException(nameof(client)); + UIManager = uiManager ? uiManager : throw new ArgumentNullException(nameof(uiManager)); - public string SelectedDeviceName { get; private set; } - - //StreamTodo: android has DeviceStatus: Enabled, Disabled, NotSelected - public bool IsDeviceActive { get; private set; } = true; + UpdateDevicesDropdown(GetDevices()); + + OnInit(); + } - public void SelectDeviceWithoutNotify(string deviceName) + public void SelectDeviceWithoutNotify(TDevice device) { - var index = _deviceNames.IndexOf(deviceName); + var index = _devices.IndexOf(device); if (index == -1) { - Debug.LogError($"Failed to find index for device: {deviceName}"); + Debug.LogError($"Failed to find index for device: {device}"); return; } _dropdown.SetValueWithoutNotify(index); } + + protected IStreamVideoClient Client { get; private set; } // Called by Unity protected void Awake() @@ -48,8 +45,6 @@ protected void Awake() _deviceButton.Init(_buttonOnSprite, _buttonOffSprite); _deviceButton.Clicked += OnDeviceButtonClicked; - - UpdateDevicesDropdown(GetDevicesNames().ToList()); _refreshDeviceInterval = new WaitForSeconds(0.5f); _refreshCoroutine = StartCoroutine(RefreshDevicesList()); @@ -58,7 +53,7 @@ protected void Awake() // Called by Unity protected void Start() { - _deviceButton.UpdateSprite(IsDeviceActive); + _deviceButton.UpdateSprite(IsDeviceEnabled); } // Called by Unity @@ -69,9 +64,22 @@ protected void OnDestroy() StopCoroutine(_refreshCoroutine); } } + + protected virtual void OnInit() + { + + } + + protected abstract IEnumerable GetDevices(); + protected abstract TDevice SelectedDevice { get; } + protected abstract bool IsDeviceEnabled { get; set; } - protected abstract IEnumerable GetDevicesNames(); + protected abstract string GetDeviceName(TDevice device); + protected abstract void ChangeDevice(TDevice device); + + private readonly List _devices = new List(); + [SerializeField] private Sprite _buttonOnSprite; @@ -86,28 +94,25 @@ protected void OnDestroy() private Coroutine _refreshCoroutine; private YieldInstruction _refreshDeviceInterval; - private readonly List _deviceNames = new List(); + protected UIManager UIManager { get; private set; } private void OnDropdownValueChanged(int optionIndex) { - var deviceName = _deviceNames.ElementAt(optionIndex); - if (deviceName == null) + var device = _devices.ElementAt(optionIndex); + if (device.Equals(default)) { Debug.LogError($"Failed to select device with index: {optionIndex}. Available devices: " + - string.Join(", ", _deviceNames)); + string.Join(", ", _devices)); return; } - SelectedDeviceName = deviceName; - - DeviceChanged?.Invoke(SelectedDeviceName, IsDeviceActive); + ChangeDevice(device); } private void OnDeviceButtonClicked() { - IsDeviceActive = !IsDeviceActive; - _deviceButton.UpdateSprite(IsDeviceActive); - DeviceToggled?.Invoke(IsDeviceActive); + IsDeviceEnabled = !IsDeviceEnabled; + _deviceButton.UpdateSprite(IsDeviceEnabled); } // User can add/remove devices any time so we must constantly monitor the devices list @@ -115,11 +120,16 @@ private IEnumerator RefreshDevicesList() { while (true) { - var availableDevices = GetDevicesNames().ToList(); - var devicesChanged = !_deviceNames.SequenceEqual(availableDevices); + while (Client == null) + { + yield return _refreshDeviceInterval; + } + + var availableDevices = GetDevices().ToList(); + var devicesChanged = !_devices.SequenceEqual(availableDevices); if (devicesChanged) { - var prevDevicesLog = string.Join(", ", _deviceNames); + var prevDevicesLog = string.Join(", ", _devices); var newDevicesLog = string.Join(", ", availableDevices); Debug.Log($"Device list changed. Previous: {prevDevicesLog}, Current: {newDevicesLog}"); @@ -130,17 +140,17 @@ private IEnumerator RefreshDevicesList() } } - private void UpdateDevicesDropdown(List devices) + private void UpdateDevicesDropdown(IEnumerable devices) { - _deviceNames.Clear(); - _deviceNames.AddRange(devices); + _devices.Clear(); + _devices.AddRange(devices); _dropdown.ClearOptions(); - _dropdown.AddOptions(devices); + _dropdown.AddOptions(devices.Select(GetDeviceName).ToList()); - if (!string.IsNullOrEmpty(SelectedDeviceName) && !devices.Contains(SelectedDeviceName)) + if (!EqualityComparer.Default.Equals(SelectedDevice, default) && !devices.Contains(SelectedDevice)) { - Debug.LogError($"Previously active device was unplugged: {SelectedDeviceName}"); + Debug.LogError($"Previously active device was unplugged: {SelectedDevice}"); //StreamTodo: handle case when user unplugged active device } } diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs index 876f9e62..44aa18b8 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs @@ -1,10 +1,32 @@ using System.Collections.Generic; -using UnityEngine; +using StreamVideo.Core.DeviceManagers; namespace StreamVideo.ExampleProject.UI.Devices { - public class MicrophoneMediaDevicePanel : MediaDevicePanelBase + public class MicrophoneMediaDevicePanel : MediaDevicePanelBase { - protected override IEnumerable GetDevicesNames() => Microphone.devices; + protected override MicrophoneDeviceInfo SelectedDevice => Client.AudioDeviceManager.SelectedDevice; + + protected override bool IsDeviceEnabled + { + get => Client.AudioDeviceManager.IsEnabled; + set => Client.AudioDeviceManager.SetEnabled(value); + } + + protected override IEnumerable GetDevices() => Client.AudioDeviceManager.EnumerateDevices(); + + protected override string GetDeviceName(MicrophoneDeviceInfo device) => device.Name; + + protected override void ChangeDevice(MicrophoneDeviceInfo device) => Client.AudioDeviceManager.SelectDevice(device, IsDeviceEnabled); + + protected override void OnInit() + { + base.OnInit(); + + Client.AudioDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; + } + + private void OnSelectedDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) + => SelectDeviceWithoutNotify(currentDevice); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs index 8b9d2a12..5e99ea23 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs @@ -45,24 +45,14 @@ public void SetLocalCameraSource(WebCamTexture localWebCamTexture) { _localWebCamTexture = localWebCamTexture; - if (_localParticipantRenderTexture != null) - { - // Dispose previous texture - _localParticipantRenderTexture.Release(); - _localParticipantRenderTexture = null; - } - if (localWebCamTexture == null) { _video.texture = null; return; } - _localParticipantRenderTexture = new RenderTexture(localWebCamTexture.width, localWebCamTexture.height, 0, RenderTextureFormat.Default); - _localParticipantRenderTexture.Create(); - // we set RenderTexture a a RawImage.texture because the RenderTexture will receive video stream from the local camera - _video.texture = _localParticipantRenderTexture; + _video.texture = localWebCamTexture; } // Called by Unity Engine @@ -74,17 +64,14 @@ protected void Awake() // Called by Unity Engine protected void Update() { - if (_localWebCamTexture != null) - { - Graphics.Blit(_localWebCamTexture, _localParticipantRenderTexture); - } - var rect = _videoRectTransform.rect; var videoRenderedSize = new Vector2(rect.width, rect.height); if (videoRenderedSize != _lastVideoRenderedSize) { _lastVideoRenderedSize = videoRenderedSize; var videoResolution = new VideoResolution((int)videoRenderedSize.x, (int)videoRenderedSize.y); + + // To optimize bandwidth we always request the video resolution that matches what we're actually rendering Participant.UpdateRequestedVideoResolution(videoResolution); Debug.Log($"Rendered resolution changed for participant `{Participant.UserId}`. Requested video resolution update to: {videoResolution}"); } @@ -115,7 +102,7 @@ protected void OnDestroy() private Color32 _defaultSpeakerFrameColor; private AudioSource _audioSource; - private RenderTexture _localParticipantRenderTexture; + //private RenderTexture _localParticipantRenderTexture; private WebCamTexture _localWebCamTexture; private RectTransform _videoRectTransform; private Vector2 _lastVideoRenderedSize; diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/BaseScreenView.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/BaseScreenView.cs index 83d2ac0e..28a317bc 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/BaseScreenView.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/BaseScreenView.cs @@ -51,8 +51,6 @@ public void Hide() protected abstract void OnHide(); - protected void Log(string message, LogType type) => UIManager.Log(message, type); - private GameObject _gameObject; } } \ No newline at end of file diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/CallScreenView.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/CallScreenView.cs index f0b5de93..ae136c51 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/CallScreenView.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/CallScreenView.cs @@ -61,12 +61,9 @@ protected override void OnInit() { _leaveBtn.onClick.AddListener(VideoManager.LeaveActiveCall); _endBtn.onClick.AddListener(VideoManager.EndActiveCall); - - _cameraPanel.DeviceChanged += UIManager.ChangeCamera; - _cameraPanel.DeviceToggled += UIManager.SetCameraActive; - _microphonePanel.DeviceChanged += UIManager.ChangeMicrophone; - _microphonePanel.DeviceToggled += UIManager.SetMicrophoneActive; + _cameraPanel.Init(VideoManager.Client, UIManager); + _microphonePanel.Init(VideoManager.Client, UIManager); } protected override void OnShow(ShowArgs showArgs) @@ -93,7 +90,7 @@ protected override void OnShow(ShowArgs showArgs) _activeCall.SortedParticipantsUpdated += SortParticipantViews; - UIManager.ActiveCameraChanged += OnActiveCameraChanged; + UIManager.LocalCameraChanged += OnLocalCameraChanged; // Show active call ID so user can copy it and send others to join _joinCallIdInput.text = _activeCall.Id; @@ -112,7 +109,7 @@ protected override void OnHide() RemoveAllParticipants(); - UIManager.ActiveCameraChanged -= OnActiveCameraChanged; + UIManager.LocalCameraChanged -= OnLocalCameraChanged; } private void OnDominantSpeakerChanged(IStreamVideoCallParticipant currentDominantSpeaker, @@ -156,7 +153,9 @@ private void AddParticipant(IStreamVideoCallParticipant participant, bool sortPa if (participant.IsLocalParticipant) { // Set input camera as a video source for local participant - we won't receive OnTrack event for local participant - view.SetLocalCameraSource(UIManager.ActiveCamera); + var webCamTexture = VideoManager.Client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); + view.SetLocalCameraSource(webCamTexture); + //StreamTodo: this will invalidate each time WebCamTexture is internally replaced so we need a better way to expose this } if (sortParticipantViews) @@ -224,7 +223,7 @@ private void RemoveAllParticipants() _participantSessionIdToView.Clear(); } - private void OnActiveCameraChanged(WebCamTexture activeCamera) + private void OnLocalCameraChanged(WebCamTexture activeCamera) { // Input Camera changed so let's update the preview for local participant var localParticipant diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/MainScreenView.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/MainScreenView.cs index 5d398789..a365270a 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/MainScreenView.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Screens/MainScreenView.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using StreamVideo.ExampleProject.UI.Devices; using TMPro; using UnityEngine; @@ -29,24 +28,18 @@ protected override void OnInit() _audioRedToggle.onValueChanged.AddListener(VideoManager.SetAudioREDundancyEncoding); _audioDtxToggle.onValueChanged.AddListener(VideoManager.SetAudioDtx); - _cameraPanel.DeviceChanged += UIManager.ChangeCamera; - _cameraPanel.DeviceToggled += UIManager.SetCameraActive; - - _microphonePanel.DeviceChanged += UIManager.ChangeMicrophone; - _microphonePanel.DeviceToggled += UIManager.SetMicrophoneActive; - - SmartPickDefaultCamera(); - SmartPickDefaultMicrophone(); + _cameraPanel.Init(VideoManager.Client, UIManager); + _microphonePanel.Init(VideoManager.Client, UIManager); } protected override void OnShow(CallScreenView.ShowArgs showArgs) { - UIManager.ActiveCameraChanged += OnActiveCameraChanged; + UIManager.LocalCameraChanged += OnLocalCameraChanged; } protected override void OnHide() { - UIManager.ActiveCameraChanged -= OnActiveCameraChanged; + UIManager.LocalCameraChanged -= OnLocalCameraChanged; } [SerializeField] @@ -82,7 +75,7 @@ private async void OnJoinCallButtonClicked() { if (string.IsNullOrEmpty(_joinCallIdInput.text)) { - Log("`Call ID` is required when trying to join a call", LogType.Error); + Debug.LogError("`Call ID` is required when trying to join a call"); return; } @@ -107,60 +100,11 @@ private async void OnCreateAndJoinCallButtonClicked() } } - private void OnActiveCameraChanged(WebCamTexture activeCamera) + private void OnLocalCameraChanged(WebCamTexture activeCamera) { _localCameraImage.texture = activeCamera; } - private void SmartPickDefaultCamera() - { - var devices = WebCamTexture.devices; - -#if UNITY_STANDALONE_WIN - //StreamTodo: remove this, "Capture" is our debug camera - _defaultCamera = devices.FirstOrDefault(d => d.name.Contains("Capture")); - -#elif UNITY_ANDROID || UNITY_IOS - _defaultCamera = devices.FirstOrDefault(d => d.isFrontFacing); -#endif - - if (string.IsNullOrEmpty(_defaultCamera.name)) - { - _defaultCamera = devices.FirstOrDefault(); - } - - if (string.IsNullOrEmpty(_defaultCamera.name)) - { - Debug.LogError("Failed to pick default camera device"); - return; - } - - _cameraPanel.SelectDeviceWithoutNotify(_defaultCamera.name); - UIManager.ChangeCamera(_defaultCamera.name, _cameraPanel.IsDeviceActive); - } - - //StreamTodo: remove - private void SmartPickDefaultMicrophone() - { - var preferredMicDevices = new[] { "bose", "airpods" }; - _defaultMicrophoneDeviceName = Microphone.devices.FirstOrDefault(d - => preferredMicDevices.Any(m => d.IndexOf(m, StringComparison.OrdinalIgnoreCase) != -1)); - - if (string.IsNullOrEmpty(_defaultMicrophoneDeviceName)) - { - _defaultMicrophoneDeviceName = Microphone.devices.FirstOrDefault(); - } - - if (string.IsNullOrEmpty(_defaultMicrophoneDeviceName)) - { - Debug.LogError("Failed to pick default microphone device"); - return; - } - - _microphonePanel.SelectDeviceWithoutNotify(_defaultMicrophoneDeviceName); - UIManager.ChangeMicrophone(_defaultMicrophoneDeviceName, _microphonePanel.IsDeviceActive); - } - private static string CreateRandomCallId() => Guid.NewGuid().ToString().Replace("-", ""); } } \ No newline at end of file diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs index 1c19469a..35b3821f 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs @@ -1,128 +1,51 @@ using System; +using System.Linq; +using System.Threading.Tasks; +using StreamVideo.Core; +using StreamVideo.Core.DeviceManagers; using StreamVideo.Core.StatefulModels; using StreamVideo.ExampleProject.UI.Screens; +using StreamVideo.Libs.Utils; using UnityEngine; namespace StreamVideo.ExampleProject.UI { public class UIManager : MonoBehaviour { - public event Action ActiveCameraChanged; + public event Action LocalCameraChanged; - public WebCamTexture ActiveCamera { get; private set; } - public AudioSource InputAudioSource => _inputAudioSource; - public Camera InputSceneSource => _inputSceneCamera; - - public void ChangeMicrophone(string deviceName, bool isActive) - { - if (!string.IsNullOrEmpty(_selectedMicrophoneDeviceName)) - { - StopAudioRecording(); - } - - var prevDevice = _selectedMicrophoneDeviceName ?? "None"; - _selectedMicrophoneDeviceName = deviceName; - - if (isActive) - { - StartAudioRecording(); - } - - Debug.Log( - $"Changed selected MICROPHONE from `{prevDevice}` to `{_selectedMicrophoneDeviceName}`. Recording: {isActive}"); - } - - public void ChangeCamera(string deviceName, bool isActive) - { - var prevDevice = ActiveCamera != null ? ActiveCamera.deviceName : "None"; - - if (ActiveCamera == null) - { - ActiveCamera = new WebCamTexture(deviceName, _senderVideoWidth, _senderVideoHeight, _senderVideoFps); - } - - // Camera needs to be stopped before changing the deviceName - ActiveCamera.Stop(); - ActiveCamera.deviceName = deviceName; - - if (isActive) - { - ActiveCamera.Play(); - //StreamTodo: handle in coroutine and check if the camera started - } - - Debug.Log($"Changed active CAMERA from `{prevDevice}` to `{deviceName}`"); - - _videoManager.Client?.SetCameraInputSource(ActiveCamera); - - ActiveCameraChanged?.Invoke(ActiveCamera); - } - - /// - /// Start/stop microphone recording - /// - public void SetMicrophoneActive(bool isActive) - { - _videoManager.Client.AudioDeviceManager.SetEnabled(isActive); - - if (isActive) - { - StartAudioRecording(); - return; - } - - StopAudioRecording(); - } - - /// - /// Start/stop camera recording - /// - public void SetCameraActive(bool isActive) - { - _videoManager.Client.VideoDeviceManager.SetEnabled(isActive); - - if (isActive) - { - ActiveCamera.Play(); - Debug.Log($"Camera recording started for `{ActiveCamera.deviceName}`"); - return; - } - - ActiveCamera.Stop(); - Debug.Log($"Camera recording stopped for `{ActiveCamera.deviceName}`"); - } - - public void Log(string message, LogType type) - { - if (type == LogType.Exception) - { - throw new NotSupportedException("To log exceptions use " + nameof(Debug.LogException)); - } - - Debug.LogFormat(type, LogOption.None, context: null, format: message); - } + public VideoResolution SenderVideoResolution => new VideoResolution(_senderVideoWidth, _senderVideoHeight); + public int SenderVideoFps => _senderVideoFps; protected void Awake() { + _videoManager.Init(); + _videoManager.CallStarted += OnCallStarted; _videoManager.CallEnded += OnCallEnded; + _videoManager.Client.VideoDeviceManager.SelectedDeviceChanged += OnCameraDeviceChanged; + _videoManager.Client.AudioDeviceManager.SelectedDeviceChanged += OnMicrophoneDeviceChanged; + _mainScreen.Init(_videoManager, uiManager: this); _callScreen.Init(_videoManager, uiManager: this); - } - - protected void Start() - { - _videoManager.Client.SetAudioInputSource(_inputAudioSource); - _videoManager.Client.SetCameraInputSource(ActiveCamera); - ShowMainScreen(); + TrySelectFirstWorkingCameraAsync().LogIfFailed(); + TrySelectFirstMicrophoneAsync().LogIfFailed(); } + protected void Start() => ShowMainScreen(); + protected void OnDestroy() { _videoManager.CallStarted -= OnCallStarted; _videoManager.CallEnded -= OnCallEnded; + + if (_videoManager.Client != null) + { + _videoManager.Client.VideoDeviceManager.SelectedDeviceChanged -= OnCameraDeviceChanged; + _videoManager.Client.AudioDeviceManager.SelectedDeviceChanged -= OnMicrophoneDeviceChanged; + } } [SerializeField] @@ -137,70 +60,65 @@ protected void OnDestroy() [SerializeField] private int _senderVideoFps = 30; - [SerializeField] - private AudioSource _inputAudioSource; - - [SerializeField] - private Camera _inputSceneCamera; - [SerializeField] private CallScreenView _callScreen; [SerializeField] private MainScreenView _mainScreen; - private string _selectedMicrophoneDeviceName; - private void OnCallStarted(IStreamCall call) => ShowCallScreen(call); private void OnCallEnded() => ShowMainScreen(); - private void StartAudioRecording() + private void ShowMainScreen() { - if (_inputAudioSource == null) - { - Debug.LogError("Audio recording failed. Input Audio Source is null"); - return; - } + _callScreen.Hide(); + _mainScreen.Show(); + } - if (string.IsNullOrEmpty(_selectedMicrophoneDeviceName)) - { - Debug.LogError("Audio recording failed. No microphone device selected."); - return; - } + private void ShowCallScreen(IStreamCall call) + { + _mainScreen.Hide(); + _callScreen.Show(new CallScreenView.ShowArgs(call)); + } + + private void OnMicrophoneDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) + { + Debug.Log($"Changed selected MICROPHONE from `{previousDevice}` to `{currentDevice}`"); + } - //StreamTodo: should the volume be 0 so we never hear input from our own microphone? - _inputAudioSource.clip - = Microphone.Start(_selectedMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); - _inputAudioSource.loop = true; - _inputAudioSource.Play(); + private void OnCameraDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) + { + Debug.Log($"Changed active CAMERA from `{previousDevice}` to `{currentDevice}`"); - Debug.Log($"Audio recording started for `{_selectedMicrophoneDeviceName}`"); + var webCamTexture = _videoManager.Client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); + LocalCameraChanged?.Invoke(webCamTexture); } - - private void StopAudioRecording() + + private async Task TrySelectFirstWorkingCameraAsync() { - var isRecording = !string.IsNullOrEmpty(_selectedMicrophoneDeviceName) && - Microphone.IsRecording(_selectedMicrophoneDeviceName); - if (!isRecording) + var workingDevice = await _videoManager.Client.VideoDeviceManager.TryFindFirstWorkingDeviceAsync(); + if (!workingDevice.HasValue) { + Debug.LogError("No working camera found"); return; } - Microphone.End(_selectedMicrophoneDeviceName); - Debug.Log($"Audio recording stopped for `{_selectedMicrophoneDeviceName}`"); - } - - private void ShowMainScreen() - { - _callScreen.Hide(); - _mainScreen.Show(); + _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value, enable: false); } - private void ShowCallScreen(IStreamCall call) + private Task TrySelectFirstMicrophoneAsync() { - _mainScreen.Hide(); - _callScreen.Show(new CallScreenView.ShowArgs(call)); + // Select first microphone by default + var microphoneDevice = _videoManager.Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); + if (microphoneDevice == default) + { + Debug.LogError("No microphone found"); + return Task.CompletedTask; + } + + _videoManager.Client.AudioDeviceManager.SelectDevice(microphoneDevice, enable: false); + return Task.CompletedTask; } } } \ No newline at end of file From 1a533cb28d051bd1c9c15ca19a80b0409b4db427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:42:14 +0200 Subject: [PATCH 16/31] Add IsEnabledChanged event --- .../Core/DeviceManagers/DeviceManagerBase.cs | 20 ++++++++++++++++++- .../Core/DeviceManagers/IDeviceManager.cs | 5 +++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 24a91a36..38ee620c 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -6,14 +6,31 @@ namespace StreamVideo.Core.DeviceManagers { + public delegate void DeviceEnabledChangeHandler(bool isEnabled); + public delegate void SelectedDeviceChangeHandler(TDeviceInfo previousDevice, TDeviceInfo currentDevice); internal abstract class DeviceManagerBase : IDeviceManager where TDeviceInfo : struct { + public event DeviceEnabledChangeHandler IsEnabledChanged; + public event SelectedDeviceChangeHandler SelectedDeviceChanged; - public bool IsEnabled { get; private set; } + public bool IsEnabled + { + get => _isEnabled; + private set + { + if (value == _isEnabled) + { + return; + } + + _isEnabled = value; + IsEnabledChanged?.Invoke(IsEnabled); + } + } public TDeviceInfo SelectedDevice { @@ -98,5 +115,6 @@ protected virtual void OnDisposing() } private TDeviceInfo _selectedDevice; + private bool _isEnabled; } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 8444ab15..1c84cf8e 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -6,6 +6,11 @@ namespace StreamVideo.Core.DeviceManagers { public interface IDeviceManager : IDisposable where TDeviceInfo : struct { + /// + /// Event triggered when the changes. + /// + event DeviceEnabledChangeHandler IsEnabledChanged; + /// /// Event triggered when the changes. /// From fd2b4e88e43213742f9f5ad0df83edfb20b6baf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:44:19 +0200 Subject: [PATCH 17/31] Fix starting the camera again after changing the device --- .../Runtime/Core/DeviceManagers/VideoDeviceManager.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 3a6889dc..281f2fad 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -60,6 +60,13 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol SelectedDevice = device; } } + + if (IsEnabled && enable && _activeCamera != null && !_activeCamera.isPlaying) + { + //OnSetEnabled will not trigger because IsEnabled value didn't change + _activeCamera.Play(); + Client.SetCameraInputSource(_activeCamera); + } SetEnabled(enable); } From 4d81762918f0dbea996fc16ec4f790981bec227c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:44:47 +0200 Subject: [PATCH 18/31] Fix texture that we're passing to webRTC VideoStreamTrack --- .../Runtime/Core/LowLevelClient/StreamPeerConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs index 717556e1..d546d86b 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs @@ -533,7 +533,7 @@ private VideoStreamTrack CreatePublisherVideoTrack() $"CreatePublisherVideoTrack, isPlaying: {_mediaInputProvider.VideoInput.isPlaying}, readable: {_mediaInputProvider.VideoInput.isReadable}"); #endif - return new VideoStreamTrack(_mediaInputProvider.VideoInput); + return new VideoStreamTrack(_publisherVideoTrackTexture); } //StreamTodo: CreatePublisherVideoTrackFromSceneCamera() is not used in any path From 5fbea75f54c25a95989a39b8e9904ad1e2f69fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 24 Apr 2024 18:48:21 +0200 Subject: [PATCH 19/31] If working device not found -> fallback to first device --- .../Scripts/UI/UIManager.cs | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index 35b3821f..c1f35508 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -31,7 +31,7 @@ protected void Awake() _callScreen.Init(_videoManager, uiManager: this); TrySelectFirstWorkingCameraAsync().LogIfFailed(); - TrySelectFirstMicrophoneAsync().LogIfFailed(); + TrySelectFirstMicrophone(); } protected void Start() => ShowMainScreen(); @@ -97,28 +97,42 @@ private void OnCameraDeviceChanged(CameraDeviceInfo previousDevice, CameraDevice private async Task TrySelectFirstWorkingCameraAsync() { + if (!_videoManager.Client.VideoDeviceManager.EnumerateDevices().Any()) + { + Debug.LogError("No camera devices found! Video streaming will not work. Please ensure that a camera device is plugged in."); + return; + } + var workingDevice = await _videoManager.Client.VideoDeviceManager.TryFindFirstWorkingDeviceAsync(); - if (!workingDevice.HasValue) + if (workingDevice.HasValue) { - Debug.LogError("No working camera found"); + _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value, enable: false); return; } - - _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value, enable: false); + + Debug.LogWarning("No working camera found. Falling back to first device."); + + var firstDevice = _videoManager.Client.VideoDeviceManager.EnumerateDevices().FirstOrDefault(); + if (firstDevice == default) + { + Debug.LogError("No camera devices found! Video streaming will not work. Please ensure that a camera device is plugged in."); + return; + } + + _videoManager.Client.VideoDeviceManager.SelectDevice(firstDevice, enable: false); } - private Task TrySelectFirstMicrophoneAsync() + private void TrySelectFirstMicrophone() { // Select first microphone by default var microphoneDevice = _videoManager.Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); if (microphoneDevice == default) { - Debug.LogError("No microphone found"); - return Task.CompletedTask; + Debug.LogError("No microphone devices found! Audio streaming will not work. Please ensure that a microphone device is plugged in."); + return; } _videoManager.Client.AudioDeviceManager.SelectDevice(microphoneDevice, enable: false); - return Task.CompletedTask; } } } \ No newline at end of file From 2feeb43fe5fdb9afbbeecc5e2fcc8e3a45e869c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 24 Apr 2024 18:48:42 +0200 Subject: [PATCH 20/31] add logcat package --- Packages/manifest.json | 1 + Packages/packages-lock.json | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Packages/manifest.json b/Packages/manifest.json index f6abcf19..1b209937 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -6,6 +6,7 @@ "com.unity.ide.rider": "3.0.27", "com.unity.ide.visualstudio": "2.0.18", "com.unity.ide.vscode": "1.2.5", + "com.unity.mobile.android-logcat": "1.4.1", "com.unity.nuget.newtonsoft-json": "3.2.1", "com.unity.test-framework": "1.1.33", "com.unity.textmeshpro": "3.0.6", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index f103ed23..4fb65873 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -69,6 +69,13 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.mobile.android-logcat": { + "version": "1.4.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.nuget.newtonsoft-json": { "version": "3.2.1", "depth": 0, From b2c46f9ad800de869387702092318690186a35fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 24 Apr 2024 18:49:10 +0200 Subject: [PATCH 21/31] Add default settings for Android platform --- ProjectSettings/ProjectSettings.asset | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 10f66c3d..36e7a6ff 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -162,14 +162,14 @@ PlayerSettings: tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 22 + AndroidMinSdkVersion: 23 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 iPhoneScriptCallOptimization: 0 - ForceInternetPermission: 0 + ForceInternetPermission: 1 ForceSDCardPermission: 0 CreateWallpaper: 0 APKExpansionFiles: 0 @@ -245,7 +245,7 @@ PlayerSettings: useCustomBaseGradleTemplate: 0 useCustomGradlePropertiesTemplate: 0 useCustomProguardFile: 0 - AndroidTargetArchitectures: 1 + AndroidTargetArchitectures: 2 AndroidTargetDevices: 0 AndroidSplashScreenScale: 0 androidSplashScreen: {fileID: 0} From a83a75eca18e607e4a222fe0df6d68ecdc483224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 24 Apr 2024 18:49:37 +0200 Subject: [PATCH 22/31] sync sample projects --- .../VideoChat/Scripts/UI/UIManager.cs | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs index 35b3821f..c1f35508 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs @@ -31,7 +31,7 @@ protected void Awake() _callScreen.Init(_videoManager, uiManager: this); TrySelectFirstWorkingCameraAsync().LogIfFailed(); - TrySelectFirstMicrophoneAsync().LogIfFailed(); + TrySelectFirstMicrophone(); } protected void Start() => ShowMainScreen(); @@ -97,28 +97,42 @@ private void OnCameraDeviceChanged(CameraDeviceInfo previousDevice, CameraDevice private async Task TrySelectFirstWorkingCameraAsync() { + if (!_videoManager.Client.VideoDeviceManager.EnumerateDevices().Any()) + { + Debug.LogError("No camera devices found! Video streaming will not work. Please ensure that a camera device is plugged in."); + return; + } + var workingDevice = await _videoManager.Client.VideoDeviceManager.TryFindFirstWorkingDeviceAsync(); - if (!workingDevice.HasValue) + if (workingDevice.HasValue) { - Debug.LogError("No working camera found"); + _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value, enable: false); return; } - - _videoManager.Client.VideoDeviceManager.SelectDevice(workingDevice.Value, enable: false); + + Debug.LogWarning("No working camera found. Falling back to first device."); + + var firstDevice = _videoManager.Client.VideoDeviceManager.EnumerateDevices().FirstOrDefault(); + if (firstDevice == default) + { + Debug.LogError("No camera devices found! Video streaming will not work. Please ensure that a camera device is plugged in."); + return; + } + + _videoManager.Client.VideoDeviceManager.SelectDevice(firstDevice, enable: false); } - private Task TrySelectFirstMicrophoneAsync() + private void TrySelectFirstMicrophone() { // Select first microphone by default var microphoneDevice = _videoManager.Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); if (microphoneDevice == default) { - Debug.LogError("No microphone found"); - return Task.CompletedTask; + Debug.LogError("No microphone devices found! Audio streaming will not work. Please ensure that a microphone device is plugged in."); + return; } _videoManager.Client.AudioDeviceManager.SelectDevice(microphoneDevice, enable: false); - return Task.CompletedTask; } } } \ No newline at end of file From 1e3f9b163cfbec4c2e3f518b9847ca503e147c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Thu, 25 Apr 2024 13:56:19 +0200 Subject: [PATCH 23/31] review fixes --- .../Scripts/UI/Devices/CameraMediaDevicePanel.cs | 7 +++++++ .../Scripts/UI/Devices/MediaDevicePanelBase.cs | 9 ++++++++- .../Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs | 7 +++++++ .../Scripts/UI/ParticipantView.cs | 5 ----- .../Scripts/UI/UIManager.cs | 8 ++++---- .../Runtime/Core/DeviceManagers/DeviceManagerBase.cs | 7 +++++-- .../Runtime/Core/DeviceManagers/IDeviceManager.cs | 5 ++--- .../Runtime/Core/DeviceManagers/IVideoDeviceManager.cs | 1 + .../StreamVideo/Runtime/Core/IStreamVideoClient.cs | 10 +--------- .../Core/LowLevelClient/StreamPeerConnection.cs | 2 +- 10 files changed, 36 insertions(+), 25 deletions(-) diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs index a0807c15..d40b0b3f 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -27,6 +27,13 @@ protected override void OnInit() Client.VideoDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; } + + protected override void OnDestroying() + { + Client.VideoDeviceManager.SelectedDeviceChanged -= OnSelectedDeviceChanged; + + base.OnDestroying(); + } private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) => SelectDeviceWithoutNotify(currentDevice); diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs index fe9174f6..3d92b5cd 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -59,6 +59,8 @@ protected void Start() // Called by Unity protected void OnDestroy() { + OnDestroying(); + if (_refreshCoroutine != null) { StopCoroutine(_refreshCoroutine); @@ -70,9 +72,15 @@ protected virtual void OnInit() } + protected virtual void OnDestroying() + { + + } + protected abstract IEnumerable GetDevices(); protected abstract TDevice SelectedDevice { get; } protected abstract bool IsDeviceEnabled { get; set; } + protected UIManager UIManager { get; private set; } protected abstract string GetDeviceName(TDevice device); @@ -94,7 +102,6 @@ protected virtual void OnInit() private Coroutine _refreshCoroutine; private YieldInstruction _refreshDeviceInterval; - protected UIManager UIManager { get; private set; } private void OnDropdownValueChanged(int optionIndex) { diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs index 44aa18b8..704bbe8a 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs @@ -26,6 +26,13 @@ protected override void OnInit() Client.AudioDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; } + protected override void OnDestroying() + { + Client.AudioDeviceManager.SelectedDeviceChanged -= OnSelectedDeviceChanged; + + base.OnDestroying(); + } + private void OnSelectedDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) => SelectDeviceWithoutNotify(currentDevice); } diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs index 5e99ea23..ac22ea12 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/ParticipantView.cs @@ -43,15 +43,12 @@ public void UpdateIsDominantSpeaker(bool isDominantSpeaker) /// public void SetLocalCameraSource(WebCamTexture localWebCamTexture) { - _localWebCamTexture = localWebCamTexture; - if (localWebCamTexture == null) { _video.texture = null; return; } - // we set RenderTexture a a RawImage.texture because the RenderTexture will receive video stream from the local camera _video.texture = localWebCamTexture; } @@ -102,8 +99,6 @@ protected void OnDestroy() private Color32 _defaultSpeakerFrameColor; private AudioSource _audioSource; - //private RenderTexture _localParticipantRenderTexture; - private WebCamTexture _localWebCamTexture; private RectTransform _videoRectTransform; private Vector2 _lastVideoRenderedSize; diff --git a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs index c1f35508..21fb2721 100644 --- a/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs +++ b/Assets/Samples/Stream Video & Audio Chat SDK/0.7.0/Video & Audio Chat Example Project/Scripts/UI/UIManager.cs @@ -30,8 +30,8 @@ protected void Awake() _mainScreen.Init(_videoManager, uiManager: this); _callScreen.Init(_videoManager, uiManager: this); - TrySelectFirstWorkingCameraAsync().LogIfFailed(); - TrySelectFirstMicrophone(); + SelectFirstWorkingCameraOrDefaultAsync().LogIfFailed(); + SelectFirstMicrophone(); } protected void Start() => ShowMainScreen(); @@ -95,7 +95,7 @@ private void OnCameraDeviceChanged(CameraDeviceInfo previousDevice, CameraDevice LocalCameraChanged?.Invoke(webCamTexture); } - private async Task TrySelectFirstWorkingCameraAsync() + private async Task SelectFirstWorkingCameraOrDefaultAsync() { if (!_videoManager.Client.VideoDeviceManager.EnumerateDevices().Any()) { @@ -122,7 +122,7 @@ private async Task TrySelectFirstWorkingCameraAsync() _videoManager.Client.VideoDeviceManager.SelectDevice(firstDevice, enable: false); } - private void TrySelectFirstMicrophone() + private void SelectFirstMicrophone() { // Select first microphone by default var microphoneDevice = _videoManager.Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs index 38ee620c..3b38789a 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/DeviceManagerBase.cs @@ -67,10 +67,13 @@ public void SetEnabled(bool isEnabled) public Task TestDeviceAsync(TDeviceInfo device, float timeout = 1f) { - if (timeout <= 0f || timeout > 20f) + const float MinTimeout = 0f; + const float MaxTimeout = 20f; + + if (timeout <= MinTimeout || timeout > MaxTimeout) { throw new ArgumentOutOfRangeException( - $"'{nameof(timeout)}' argument must be between 0 and 20 seconds, given: {timeout}"); + $"'{nameof(timeout)}' argument must be between {MinTimeout} and {MaxTimeout} seconds, given: {timeout}"); } return OnTestDeviceAsync(device, (int)(timeout * 1000)); diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs index 1c84cf8e..649bec20 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IDeviceManager.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; namespace StreamVideo.Core.DeviceManagers { - public interface IDeviceManager : IDisposable where TDeviceInfo : struct + public interface IDeviceManager where TDeviceInfo : struct { /// /// Event triggered when the changes. diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs index d4511dd3..59ef6491 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs @@ -2,6 +2,7 @@ namespace StreamVideo.Core.DeviceManagers { + //StreamTodo: revise setting 3D Scene Camera as a video source /// /// Manages interactions with video recording devices - Cameras. /// diff --git a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs index c459a5f8..3fc43fc1 100644 --- a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs @@ -1,5 +1,4 @@ - -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using StreamVideo.Core.QueryBuilders.Sort.Calls; @@ -8,7 +7,6 @@ using StreamVideo.Core.StatefulModels; using StreamVideo.Libs.Auth; using StreamVideo.Libs.VideoClientInstanceRunner; -using UnityEngine; namespace StreamVideo.Core { @@ -72,12 +70,6 @@ public interface IStreamVideoClient : IStreamVideoClientEventsListener, IDisposa Task JoinCallAsync(StreamCallType callType, string callId, bool create, bool ring, bool notify); - //StreamTodo: revise setting 3D Scene Camera as a video source - /// - /// Set the source for sending VIDEO or rendered Scene Camera. You can pass any scene camera and the video will be sent to other participants. - /// - //void SetCameraInputSource(Camera sceneCamera); - /// /// Will return null if the call doesn't exist /// diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs index d546d86b..fb292718 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/StreamPeerConnection.cs @@ -511,7 +511,7 @@ private VideoResolution GetPublisherResolution() return new VideoResolution(maxResolution.Width, maxResolution.Height); } - return VideoResolution.Res_720p; + return VideoResolution.Res_1080p; } private VideoStreamTrack CreatePublisherVideoTrack() From 4119d890c2c10cc3ae71cb0fe506b3136e671c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:28:01 +0200 Subject: [PATCH 24/31] [Docs] Update quickstart guide to use device managers --- .../01-basics/QuickStartCodeSamples.cs | 141 +++++++++++----- .../docs/Unity/01-basics/03-quickstart.mdx | 153 ++++++++++++------ 2 files changed, 205 insertions(+), 89 deletions(-) diff --git a/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs b/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs index 73688654..a526399a 100644 --- a/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs +++ b/Packages/StreamVideo/DocsCodeSamples/01-basics/QuickStartCodeSamples.cs @@ -4,6 +4,7 @@ using StreamVideo.Core.StatefulModels; using StreamVideo.Core.StatefulModels.Tracks; using UnityEngine; +using UnityEngine.Android; using UnityEngine.UI; namespace StreamVideoDocsCodeSamples._01_basics @@ -33,7 +34,7 @@ public async void CreateCallAndJoin() var callType = StreamCallType.Default; // Call type affects default permissions var callId = "my-call-id"; -// Notice that we pass create argument as true - this will create the call if it doesn't already exist + // Notice that we pass create argument as true - this will create the call if it doesn't already exist var streamCall = await _client.JoinCallAsync(callType, callId, create: true, ring: true, notify: false); } @@ -42,7 +43,7 @@ public async void JoinOtherCall() var callType = StreamCallType.Default; // Call type affects default permissions var callId = "my-call-id"; -// Notice that we pass create argument as false - if the call doesn't exist the join attempt will fail + // Notice that we pass create argument as false - if the call doesn't exist the join attempt will fail var streamCall = await _client.JoinCallAsync(callType, callId, create: false, ring: true, notify: false); } @@ -50,13 +51,13 @@ public async void GetCallParticipants() { var callType = StreamCallType.Default; // Call type affects default permissions var callId = "my-call-id"; - + var streamCall = await _client.JoinCallAsync(callType, callId, create: false, ring: true, notify: false); - + // Subscribe to events to get notified that streamCall.Participants collection changed streamCall.ParticipantJoined += OnParticipantJoined; streamCall.ParticipantLeft += OnParticipantLeft; - + // Iterate through current participants foreach (var participant in streamCall.Participants) { @@ -106,73 +107,133 @@ private void OnParticipantTrackAdded(IStreamVideoCallParticipant participant, IS // This assumes that this gameObject contains the AudioSource component but it's not a requirement. You can obtain the AudioSource reference in your preferred way var audioSource = GetComponent(); - + // This AudioSource will receive audio from the participant streamAudioTrack.SetAudioSourceTarget(audioSource); break; case StreamVideoTrack streamVideoTrack: - + // This assumes that this gameObject contains the RawImage component but it's not a requirement. You can obtain the RawImage reference in your preferred way var rawImage = GetComponent(); - + // This RawImage will receive video from the participant streamVideoTrack.SetRenderTarget(rawImage); break; } } - public void SetAudioInput() + public void ListAvailableMicrophoneDevices() { - // Obtain reference to an AudioSource that will be used a source of audio - var audioSource = GetComponent(); - //_client.SetAudioInputSource(audioSource); + var microphones = _client.AudioDeviceManager.EnumerateDevices(); + + foreach (var mic in microphones) + { + Debug.Log(mic.Name); + } + } + + public void SelectAudioCapturingDevice() + { + // Enumerate available microphone devices + var microphones = _client.AudioDeviceManager.EnumerateDevices(); + + foreach (var mic in microphones) + { + Debug.Log(mic.Name); + } + + var firstMicrophone = microphones.First(); + + // Select microphone device to capture audio input. `enable` argument determines whether audio capturing should start + _client.AudioDeviceManager.SelectDevice(firstMicrophone, enable: true); + } + + public void StartStopAudioRecording() + { + // Start audio capturing + _client.AudioDeviceManager.Enable(); + + // Stop audio capturing + _client.AudioDeviceManager.Disable(); } - public void BindMicrophoneToAudioSource() + public void RequestMicrophonePermissionsIOSandWebGL() { - // Obtain reference to an AudioSource that will be used a source of audio - var inputAudioSource = GetComponent(); + // Request microphone permissions + Application.RequestUserAuthorization(UserAuthorization.Microphone); - // Get a valid microphone device name. - // You usually want to populate a dropdown list with Microphone.devices so that the user can pick which device should be used - _activeMicrophoneDeviceName = Microphone.devices.First(); + // Check if user granted microphone permission + if (!Application.HasUserAuthorization(UserAuthorization.Microphone)) + { + // Notify user that microphone permission was not granted and the microphone capturing will not work. + } + } - inputAudioSource.clip - = Microphone.Start(_activeMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); - inputAudioSource.loop = true; - inputAudioSource.Play(); + public void RequestMicrophonePermissionsAndroid() + { + // Request microphone permissions + Permission.RequestUserPermission(Permission.Microphone); + + // Check if user granted microphone permission + if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) + { + // Notify user that microphone permission was not granted and the microphone capturing will not work. + } } - public void StopAudioRecording() + public void ListAvailableCameraDevices() { - Microphone.End(_activeMicrophoneDeviceName); + var cameras = _client.VideoDeviceManager.EnumerateDevices(); + + foreach (var cam in cameras) + { + Debug.Log(cam.Name); + } } - public void SetVideoInput() + public void SelectVideoCapturingDevice() { -// Obtain reference to a WebCamTexture that will be used a source of video - var webCamTexture = GetComponent(); - //_client.SetCameraInputSource(webCamTexture); + // Enumerate available camera devices + var cameras = _client.VideoDeviceManager.EnumerateDevices(); + + var firstCamera = cameras.First(); + + // Select camera device to capture video input. `enable` argument determines whether video capturing should start + _client.VideoDeviceManager.SelectDevice(firstCamera, enable: true); } - public void BindCameraToWebCamTexture() + public void StartStopVideoCapturing() { -// Obtain a camera device - var cameraDevice = WebCamTexture.devices.First(); + // Start video capturing + _client.VideoDeviceManager.Enable(); - var width = 1920; - var height = 1080; - var fps = 30; + // Stop video capturing + _client.VideoDeviceManager.Disable(); + } -// Use device name to create a new WebCamTexture instance - var activeCamera = new WebCamTexture(cameraDevice.name, width, height, fps); + public void RequestCameraPermissionsIOSandWebGL() + { + // Request camera permissions + Application.RequestUserAuthorization(UserAuthorization.WebCam); + + // Check if user granted camera permission + if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) + { + // Notify user that camera permission was not granted and the camera capturing will not work. + } + } -// Call Play() in order to start capturing the video - activeCamera.Play(); + public void RequestCameraPermissionsAndroid() + { + // Request camera permissions + Permission.RequestUserPermission(Permission.Camera); -// Set WebCamTexture in Stream's Client - this WebCamTexture will be the video source in video calls - //_client.SetCameraInputSource(activeCamera); + // Check if user granted camera permission + if (!Permission.HasUserAuthorizedPermission(Permission.Camera)) + { + // Notify user that camera permission was not granted and the camera capturing will not work. + } } private IStreamVideoClient _client; diff --git a/docusaurus/docs/Unity/01-basics/03-quickstart.mdx b/docusaurus/docs/Unity/01-basics/03-quickstart.mdx index 3a52db54..ff47be4b 100644 --- a/docusaurus/docs/Unity/01-basics/03-quickstart.mdx +++ b/docusaurus/docs/Unity/01-basics/03-quickstart.mdx @@ -70,86 +70,141 @@ var callId = "my-call-id"; var streamCall = await _client.JoinCallAsync(callType, callId, create: false, ring: true, notify: false); ``` +## Capture Audio from a Microphone -## Setting Audio Input - -#### Bind microphone device to a `AudioSource` component +List available microphone devices: +```csharp +var microphones = _client.AudioDeviceManager.EnumerateDevices(); -This code will get the first microphone device from Unity's `Microphone.devices` list and stream it's input into a `AudioSource` component. +foreach (var mic in microphones) +{ + Debug.Log(mic.Name); +} +``` +Select active device: ```csharp -// Obtain reference to an AudioSource that will be used a source of audio -var inputAudioSource = GetComponent(); +var firstMicrophone = microphones.First(); -// Get a valid microphone device name. -// You usually want to populate a dropdown list with Microphone.devices so that the user can pick which device should be used -_activeMicrophoneDeviceName = Microphone.devices.First(); +// Select microphone device to capture audio input. `enable` argument determines whether audio capturing should start +_client.AudioDeviceManager.SelectDevice(firstMicrophone, enable: true); +``` +The `enable` argument determines whether audio capture should start for this device. -inputAudioSource.clip - = Microphone.Start(_activeMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); -inputAudioSource.loop = true; -inputAudioSource.Play(); +You can start/stop audio capturing with: +```csharp +// Start audio capturing +_client.AudioDeviceManager.Enable(); + +// Stop audio capturing +_client.AudioDeviceManager.Disable(); ``` -- For standalone platforms like Windows, macOS, and Linux, you usually want to provide the user with a dropdown menu populated from the `Microphone.devices` so that the user can select the active microphone. -- For mobile platforms like Android or IOS the underlying OS is controlling the devices and the `Microphone.devices` +### Permission to use the Microphone + +Users must grant permission to use the Microphone device for some platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable audio capturing. -Please also note that on mobile platforms you need to request appropriate [User Permissions](https://docs.unity3d.com/ScriptReference/Android.Permission.RequestUserPermission.html) in order to make use of the Microphone and the Camera. +### iOS and WebGL -#### Set `AudioSource` component as an Input Source for Audio. +You can request permission to use a microphone device on iOS and WebGL platforms by using Unity's [RequestUserAuthorization](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html): -You can provide any `AudioSource` component as an input for audio. All you need to do is call the `_client.SetAudioInputSource(audioSource)` method as in the following example: ```csharp -// Obtain reference to an AudioSource that will be used a source of audio -var audioSource = GetComponent(); -_client.SetAudioInputSource(audioSource); +// Request permission to use the Microphone +Application.RequestUserAuthorization(UserAuthorization.Microphone); + +// Check if user granted microphone permission +if (!Application.HasUserAuthorization(UserAuthorization.Microphone)) +{ + // Notify user that microphone permission was not granted and the microphone capturing will not work. +} ``` -Now the provided `AudioSource` will be used as an audio input for audio communication in calls. -Please note that the `AudioSrouce` does not necessarily need to be associated with a microphone device. This is indeed the most common use case but the `AudioSource` in fact serves as an audio buffer, so you can implement many other use cases with how the audio input is gathered. +### Android + +For the Android platform, Unity recommends using the [Permission.RequestUserPermission](https://docs.unity3d.com/ScriptReference/Android.Permission.RequestUserPermission.html): + +```csharp +// Request microphone permissions +Permission.RequestUserPermission(Permission.Microphone); + +// Check if user granted microphone permission +if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) +{ + // Notify user that microphone permission was not granted and the microphone capturing will not work. +} +``` #### Read more -Please refer to Unity's documentation for more information on how to use Microphone devices: -* [Microphone.devices](https://docs.unity3d.com/ScriptReference/Microphone-devices.html) -* [Microphone.Start](https://docs.unity3d.com/ScriptReference/Microphone.Start.html) -* [Microphone.End](https://docs.unity3d.com/ScriptReference/Microphone.End.html) +Read more about working with microphone in the [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) docs section. -## Setting Video Input +## Capture Video from a Web Camera -You can use Unity's [WebCamTexture](https://docs.unity3d.com/ScriptReference/WebCamTexture.html) to interact with the camera device. +List available camera devices: +```csharp +var cameras = _client.VideoDeviceManager.EnumerateDevices(); -Once you select the camera device you should create a new instance of `WebCamTexture` and call `Play()` on it. +foreach (var cam in cameras) +{ + Debug.Log(cam.Name); +} +``` + +Select active device: +```csharp +var firstCamera = cameras.First(); + +// Select camera device to capture video input. `enable` argument determines whether video capturing should start +_client.VideoDeviceManager.SelectDevice(firstCamera, enable: true); +``` +The `enable` argument determines whether video capture should start immediately for this device. -Now you can set the `WebCamTexture` as a video input with `_client.SetCameraInputSource(activeCamera);` as shown in the following example: +You can start/stop video capturing with: ```csharp -// Obtain a camera device -var cameraDevice = WebCamTexture.devices.First(); +// Start video capturing +_client.VideoDeviceManager.Enable(); + +// Stop video capturing +_client.VideoDeviceManager.Disable(); +``` + +### Permission to use the Microphone + +Users must grant permission to use the Microphone device for some platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable audio capturing. -var width = 1920; -var height = 1080; -var fps = 30; +### iOS and WebGL -// Use device name to create a new WebCamTexture instance -var activeCamera = new WebCamTexture(cameraDevice.name, width, height, fps); +You can request permission to use a microphone device on iOS and WebGL platforms by using Unity's [RequestUserAuthorization](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html): -// Call Play() in order to start capturing the video -activeCamera.Play(); +```csharp +// Request camera permissions +Application.RequestUserAuthorization(UserAuthorization.WebCam); -// Set WebCamTexture in Stream's Client - this WebCamTexture will be the video source in video calls -_client.SetCameraInputSource(activeCamera); +// Check if user granted camera permission +if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) +{ + // Notify user that camera permission was not granted and the camera capturing will not work. +} ``` -- For standalone platforms like Windows, macOS, and Linux, you usually want to provide the user with a dropdown menu populated from the `WebCamTexture.devices` so that the user can select the active camera. -- On mobile platforms like Android or IOS there will usually be two camera devices present in the `WebCamTexture.devices`: The front camera and the back camera. So you may either select the front camera automatically or give user an option to toggle between front and back cameras depending on your use case. +### Android + +For the Android platform, Unity recommends using the [Permission.RequestUserPermission](https://docs.unity3d.com/ScriptReference/Android.Permission.RequestUserPermission.html): + +```csharp +// Request camera permissions +Permission.RequestUserPermission(Permission.Camera); + +// Check if user granted camera permission +if (!Permission.HasUserAuthorizedPermission(Permission.Camera)) +{ + // Notify user that camera permission was not granted and the camera capturing will not work. +} +``` #### Read more -Please refer to Unity's documentation for more information on how to use Camera devices: -* [WebCamTexture](https://docs.unity3d.com/ScriptReference/WebCamTexture.html) -* [WebCamTexture.devices](https://docs.unity3d.com/ScriptReference/WebCamTexture-devices.html) -* [WebCamTexture.Play](https://docs.unity3d.com/ScriptReference/WebCamTexture.Play.html) -* [WebCamTexture.Stop](https://docs.unity3d.com/ScriptReference/WebCamTexture.Stop.html) +Read more about working with camera in the [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) docs section. ## Handling participants From 8ecc4fb46c1a1e4865eaa1a511eeb850afc52f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:43:13 +0200 Subject: [PATCH 25/31] [Docs] quickstart fixes --- docusaurus/docs/Unity/01-basics/03-quickstart.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docusaurus/docs/Unity/01-basics/03-quickstart.mdx b/docusaurus/docs/Unity/01-basics/03-quickstart.mdx index ff47be4b..d726b3cc 100644 --- a/docusaurus/docs/Unity/01-basics/03-quickstart.mdx +++ b/docusaurus/docs/Unity/01-basics/03-quickstart.mdx @@ -102,7 +102,7 @@ _client.AudioDeviceManager.Disable(); ### Permission to use the Microphone -Users must grant permission to use the Microphone device for some platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable audio capturing. +Users must grant permission to use the Microphone device for platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable audio capturing. ### iOS and WebGL @@ -170,7 +170,7 @@ _client.VideoDeviceManager.Disable(); ### Permission to use the Microphone -Users must grant permission to use the Microphone device for some platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable audio capturing. +Users must grant permission to use the Microphone device for platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable video capturing. ### iOS and WebGL From dbec2bc6288d228b9696af08be6ccd3c75cb552b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Fri, 26 Apr 2024 17:38:13 +0200 Subject: [PATCH 26/31] Rename argument --- .../Runtime/Core/DeviceManagers/IVideoDeviceManager.cs | 4 ++-- .../Runtime/Core/DeviceManagers/VideoDeviceManager.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs index 59ef6491..462d3235 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs @@ -15,8 +15,8 @@ public interface IVideoDeviceManager : IDeviceManager /// /// Camera device /// Enable this device (Start Capturing Video) - /// Requested frame rate for the captured video. If the requested FPS is not supported by the camera, the closets available value will be selected - void SelectDevice(CameraDeviceInfo device, bool enable, int fps = 30); + /// Requested frame rate for the captured video. If the requested FPS is not supported by the camera, the closets available value will be selected + void SelectDevice(CameraDeviceInfo device, bool enable, int requestedFPS = 30); /// /// Select a camera device for video capturing. diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs index 281f2fad..9be6a6b5 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs @@ -29,8 +29,8 @@ public override IEnumerable EnumerateDevices() } } - public void SelectDevice(CameraDeviceInfo device, bool enable, int fps = 30) - => SelectDevice(device, VideoResolution.Res_720p, enable, fps); + public void SelectDevice(CameraDeviceInfo device, bool enable, int requestedFPS = 30) + => SelectDevice(device, VideoResolution.Res_720p, enable, requestedFPS); public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, bool enable, int requestedFPS = 30) { From 25d28e3932ef5b02c7c066989d52ce3b253056ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Fri, 26 Apr 2024 17:39:12 +0200 Subject: [PATCH 27/31] [Docs] Update camera & microphone guide to use new device managers --- .../03-guides/CameraAndMicrophone.cs | 83 ----- .../03-guides/CameraAndMicrophone_Camera.cs | 158 ++++++++ .../CameraAndMicrophone_Camera.cs.meta | 3 + .../CameraAndMicrophone_Microphone.cs | 115 ++++++ ...=> CameraAndMicrophone_Microphone.cs.meta} | 0 .../docs/Unity/01-basics/03-quickstart.mdx | 80 +--- .../03-guides/04-camera-and-microphone.mdx | 341 ++++++++++++++---- 7 files changed, 552 insertions(+), 228 deletions(-) delete mode 100644 Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs create mode 100644 Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs create mode 100644 Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs.meta create mode 100644 Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Microphone.cs rename Packages/StreamVideo/DocsCodeSamples/03-guides/{CameraAndMicrophone.cs.meta => CameraAndMicrophone_Microphone.cs.meta} (100%) diff --git a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs deleted file mode 100644 index 1b35e086..00000000 --- a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Linq; -using StreamVideo.Core; -using UnityEngine; - -namespace StreamVideoDocsCodeSamples._03_guides -{ - internal class CameraAndMicrophone : MonoBehaviour - { - public void SetupMicrophoneInput() - { - // Obtain reference to an AudioSource that will be used a source of audio - var inputAudioSource = GetComponent(); - -// Get a valid microphone device name. -// You usually want to populate a dropdown list with Microphone.devices so that the user can pick which device should be used - _activeMicrophoneDeviceName = Microphone.devices.First(); - - inputAudioSource.clip - = Microphone.Start(_activeMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); - inputAudioSource.loop = true; - inputAudioSource.Play(); - - //_client.SetAudioInputSource(inputAudioSource); - } - - public void ChangeMicrophoneDevice() - { - var newMicrophoneDeviceName = "test"; - - // Stop previously active microphone - Microphone.End(_activeMicrophoneDeviceName); - - // Obtain reference to an AudioSource that was setup as an input source - var inputAudioSource = GetComponent(); - - inputAudioSource.clip = Microphone.Start(newMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); - } - - public void SetupCameraInput() - { - // Obtain a camera device - var cameraDevice = WebCamTexture.devices.First(); - - // Use device name to create a new WebCamTexture instance - var activeCamera = new WebCamTexture(cameraDevice.name, 1920, 1080, 24); - - // Call Play() in order to start capturing the video - activeCamera.Play(); - - // Set WebCamTexture in Stream's Client - this WebCamTexture will be the video source in video calls - //_client.SetCameraInputSource(activeCamera); - } - - public void ChangeVideoDevice() - { - // Item from WebCamTexture.devices - var newDeviceName = "deviceName"; - - _activeCamera.Stop(); - _activeCamera.deviceName = newDeviceName; - _activeCamera.Play(); - } - - public void UpdateCameraInputSource() - { - // Obtain a camera device - var cameraDevice = WebCamTexture.devices.First(); - - // Use device name to create a new WebCamTexture instance - var activeCamera = new WebCamTexture(cameraDevice.name); - - // Call Play() in order to start capturing the video - activeCamera.Play(); - - //_client.SetCameraInputSource(activeCamera); - } - - private IStreamVideoClient _client; - private string _activeMicrophoneDeviceName; - - private WebCamTexture _activeCamera; - } -} \ No newline at end of file diff --git a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs new file mode 100644 index 00000000..7ad6cf47 --- /dev/null +++ b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs @@ -0,0 +1,158 @@ +using System.Linq; +using StreamVideo.Core; +using StreamVideo.Core.DeviceManagers; +using UnityEngine; +using UnityEngine.Android; +using UnityEngine.UI; + +namespace StreamVideoDocsCodeSamples._03_guides +{ + internal class CameraAndMicrophone_Camera : MonoBehaviour + { + public void ListAvailableCameraDevices() + { +var cameras = _client.VideoDeviceManager.EnumerateDevices(); + +foreach (var camera in cameras) +{ + Debug.Log(camera.Name); // Get camera name +} + } + + public void SelectCamera() + { + // Get available camera devices. Returns IEnumerable + var cameras = _client.VideoDeviceManager.EnumerateDevices(); + + foreach (var cam in cameras) + { + Debug.Log(cam.Name); // Get the name of the camera + } + + var camera = cameras.First(); + +_client.VideoDeviceManager.SelectDevice(camera, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, enable: true, requestedFPS: 24); + + +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_720p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_720p, enable: true, requestedFPS: 30); + } + + public void VideoResolutionValues() + { + // Get available camera devices. Returns IEnumerable + var cameras = _client.VideoDeviceManager.EnumerateDevices(); + + + var camera = cameras.First(); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_144p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_240p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_360p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_480p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_720p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_1080p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, new VideoResolution(500, 500), enable: true); + } + + public void GetSelectedCamera() + { + var selectedCamera = _client.VideoDeviceManager.SelectedDevice; + } + + public void StartStopCamera() + { + // Enable device to start capturing camera input + _client.VideoDeviceManager.Enable(); + + // Disable device to stop capturing camera input + _client.VideoDeviceManager.Disable(); + + // Set the enabled state by passing a boolean argument + _client.VideoDeviceManager.SetEnabled(true); + } + + public void GetLocalParticipantVideoPreview() + { +var webCamTexture = _client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); + +// You can attach this texture to RawImage UI Component +GetComponent().texture = webCamTexture; + } + +public void GetLocalParticipantVideoPreviewFull() +{ + // Triggered when the selected devices changes + _client.VideoDeviceManager.SelectedDeviceChanged += UpdateLocalParticipantPreview; +} + +private void UpdateLocalParticipantPreview(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) +{ + var webCamTexture = _client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); + + // You can attach this texture to RawImage UI Component + GetComponent().texture = webCamTexture; +} + + public void CheckCameraStatus() + { + // Check if currently selected device is enabled + var isDeviceEnabled = _client.VideoDeviceManager.IsEnabled; + } + + public void VideoDeviceManagerEvents() + { + // Triggered when the selected devices changes + _client.VideoDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; + + // Triggered when the IsEnabled property changes + _client.VideoDeviceManager.IsEnabledChanged += OnIsEnabledChanged; + } + + private void OnIsEnabledChanged(bool isEnabled) + { + } + + private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) + { + } + + public void CameraTesting() + { + var cameras = _client.VideoDeviceManager.EnumerateDevices(); + var camera = cameras.First(); + + // Testing devices + + _client.VideoDeviceManager.TestDeviceAsync(camera); + + _client.VideoDeviceManager.TryFindFirstWorkingDeviceAsync(); + } + + public void CameraIOSPermissions() + { + // Request permission to use the Camera + Application.RequestUserAuthorization(UserAuthorization.WebCam); + + // Check if user granted camera permission + if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) + { + // Notify user that camera permission was not granted and the camera capturing will not work. + } + } + + public void CameraAndroidPermissions() + { + // Request camera permissions + Permission.RequestUserPermission(Permission.Camera); + + // Check if user granted camera permission + if (!Permission.HasUserAuthorizedPermission(Permission.Camera)) + { + // Notify user that camera permission was not granted and the camera capturing will not work. + } + } + + private IStreamVideoClient _client; + } +} \ No newline at end of file diff --git a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs.meta b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs.meta new file mode 100644 index 00000000..e7b9b8ec --- /dev/null +++ b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Camera.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 601ffac7ebe146cca1a7f818398f0174 +timeCreated: 1714136625 \ No newline at end of file diff --git a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Microphone.cs b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Microphone.cs new file mode 100644 index 00000000..b946e4a5 --- /dev/null +++ b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Microphone.cs @@ -0,0 +1,115 @@ +using System.Linq; +using StreamVideo.Core; +using StreamVideo.Core.DeviceManagers; +using UnityEngine; +using UnityEngine.Android; + +namespace StreamVideoDocsCodeSamples._03_guides +{ + internal class CameraAndMicrophone_Microphone + { + public void ListAvailableMicrophoneDevices() + { + var microphones = _client.AudioDeviceManager.EnumerateDevices(); + + foreach (var mic in microphones) + { + Debug.Log(mic.Name); // Get microphone name + } + } + + public void SelectMicrophone() + { + // Get available microphone devices. Returns IEnumerable + var microphones = _client.AudioDeviceManager.EnumerateDevices(); + + foreach (var mic in microphones) + { + Debug.Log(mic.Name); // Get the name of the microphone + } + + var microphone = microphones.First(); + + // Select device for audio capturing. Pass the `enable` argument to control if capturing should be enabled + _client.AudioDeviceManager.SelectDevice(microphone, enable: true); + } + + public void GetSelectedMicrophone() + { + var selectedMicrophone = _client.AudioDeviceManager.SelectedDevice; + } + + public void StartStopMicrophone() + { + // Enable device to start capturing microphone input + _client.AudioDeviceManager.Enable(); + + // Disable device to stop capturing microphone input + _client.AudioDeviceManager.Disable(); + + // Set the enabled state by passing a boolean argument + _client.AudioDeviceManager.SetEnabled(true); + } + + public void CheckMicrophoneStatus() + { + // Check if currently selected device is enabled + var isDeviceEnabled = _client.AudioDeviceManager.IsEnabled; + } + + public void AudioDeviceManagerEvents() + { + // Triggered when the selected devices changes + _client.AudioDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; + + // Triggered when the IsEnabled property changes + _client.AudioDeviceManager.IsEnabledChanged += OnIsEnabledChanged; + } + + private void OnIsEnabledChanged(bool isEnabled) + { + } + + private void OnSelectedDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) + { + } + + public void MicrophoneTesting() + { + var microphones = _client.AudioDeviceManager.EnumerateDevices(); + var microphone = microphones.First(); + + // Testing devices + + _client.AudioDeviceManager.TestDeviceAsync(microphone); + + _client.AudioDeviceManager.TryFindFirstWorkingDeviceAsync(); + } + + public void MicrophoneIOSPermissions() + { + // Request permission to use the Microphone + Application.RequestUserAuthorization(UserAuthorization.Microphone); + + // Check if user granted microphone permission + if (!Application.HasUserAuthorization(UserAuthorization.Microphone)) + { + // Notify user that microphone permission was not granted and the microphone capturing will not work. + } + } + + public void MicrophoneAndroidPermissions() + { + // Request microphone permissions + Permission.RequestUserPermission(Permission.Microphone); + + // Check if user granted microphone permission + if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) + { + // Notify user that microphone permission was not granted and the microphone capturing will not work. + } + } + + private IStreamVideoClient _client; + } +} \ No newline at end of file diff --git a/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs.meta b/Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Microphone.cs.meta similarity index 100% rename from Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone.cs.meta rename to Packages/StreamVideo/DocsCodeSamples/03-guides/CameraAndMicrophone_Microphone.cs.meta diff --git a/docusaurus/docs/Unity/01-basics/03-quickstart.mdx b/docusaurus/docs/Unity/01-basics/03-quickstart.mdx index d726b3cc..8743c159 100644 --- a/docusaurus/docs/Unity/01-basics/03-quickstart.mdx +++ b/docusaurus/docs/Unity/01-basics/03-quickstart.mdx @@ -72,6 +72,8 @@ var streamCall = await _client.JoinCallAsync(callType, callId, create: false, ri ## Capture Audio from a Microphone +The `AudioDeviceManager` manages all interactions with camera devices. Below are several fundamental operations; for a comprehensive list, please visit our [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) documentation section. + List available microphone devices: ```csharp var microphones = _client.AudioDeviceManager.EnumerateDevices(); @@ -100,46 +102,14 @@ _client.AudioDeviceManager.Enable(); _client.AudioDeviceManager.Disable(); ``` -### Permission to use the Microphone - -Users must grant permission to use the Microphone device for platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable audio capturing. - -### iOS and WebGL - -You can request permission to use a microphone device on iOS and WebGL platforms by using Unity's [RequestUserAuthorization](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html): - -```csharp -// Request permission to use the Microphone -Application.RequestUserAuthorization(UserAuthorization.Microphone); - -// Check if user granted microphone permission -if (!Application.HasUserAuthorization(UserAuthorization.Microphone)) -{ - // Notify user that microphone permission was not granted and the microphone capturing will not work. -} -``` - -### Android - -For the Android platform, Unity recommends using the [Permission.RequestUserPermission](https://docs.unity3d.com/ScriptReference/Android.Permission.RequestUserPermission.html): - -```csharp -// Request microphone permissions -Permission.RequestUserPermission(Permission.Microphone); - -// Check if user granted microphone permission -if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) -{ - // Notify user that microphone permission was not granted and the microphone capturing will not work. -} -``` - -#### Read more +#### Android & iOS platforms -Read more about working with microphone in the [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) docs section. +For platforms like Android and iOS, the user needs to grant permission to access the microphone devices. You can read more about requesting permissions in the [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) docs section. ## Capture Video from a Web Camera +The `VideoDeviceManager` manages all interactions with camera devices. Below are several fundamental operations; for a comprehensive list, please visit our [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) documentation section. + List available camera devices: ```csharp var cameras = _client.VideoDeviceManager.EnumerateDevices(); @@ -168,43 +138,9 @@ _client.VideoDeviceManager.Enable(); _client.VideoDeviceManager.Disable(); ``` -### Permission to use the Microphone - -Users must grant permission to use the Microphone device for platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable video capturing. - -### iOS and WebGL - -You can request permission to use a microphone device on iOS and WebGL platforms by using Unity's [RequestUserAuthorization](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html): - -```csharp -// Request camera permissions -Application.RequestUserAuthorization(UserAuthorization.WebCam); - -// Check if user granted camera permission -if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) -{ - // Notify user that camera permission was not granted and the camera capturing will not work. -} -``` - -### Android - -For the Android platform, Unity recommends using the [Permission.RequestUserPermission](https://docs.unity3d.com/ScriptReference/Android.Permission.RequestUserPermission.html): - -```csharp -// Request camera permissions -Permission.RequestUserPermission(Permission.Camera); - -// Check if user granted camera permission -if (!Permission.HasUserAuthorizedPermission(Permission.Camera)) -{ - // Notify user that camera permission was not granted and the camera capturing will not work. -} -``` - -#### Read more +#### Android & iOS platforms -Read more about working with camera in the [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) docs section. +For platforms like Android and iOS, a user needs to grant permission to access the camera devices. You can read more about requesting permissions in the [Camera & Microphone](https://getstream.io/video/docs/unity/guides/camera-and-microphone/) docs section. ## Handling participants diff --git a/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx b/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx index f70911fb..385501ad 100644 --- a/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx +++ b/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx @@ -3,134 +3,329 @@ title: Camera & Microphone description: Docs on sending & receiving --- -This page shortly describes how to send **Audio** and **Video** data to other participants. +This page details how to interact with **Microphone** and **Camera** devices to transmit audio and video streams to other call participants. Handling camera and microphone inputs in Unity can be challenging; however, Stream's Video SDK for Unity simplifies the process by managing the complexities internally, facilitating easy interaction with these devices. -Before you can set audio and video input sources you need to set up an instance of `StreamVideoClient`. Follow the [Client & Auth](../03-guides/01-client-auth.mdx) guide to learn how to do it. +## Interacting with Microphone -## Setup sending Audio +All interactions with microphone devices are handled by the `AudioDeviceManager` that can be accessed via `_client.AudioDeviceManager`. The `_client` field is an instance of `IStreamVideoClient`; if you haven't setup the video chat client yet, you can follow the [Client & Auth](../03-guides/01-client-auth.mdx) guide to learn how to do it. -In order to send audio data you need to set an instance of [AudioSource](https://docs.unity3d.com/ScriptReference/AudioSource.html) as an **input source** by calling the `SetAudioInputSource` method on the instance of `StreamVideoClient`. +#### List available microphone devices + +All available microphone devices can be accessed with `AudioDeviceManager.EnumerateDevices()`. This method returns the `IEnumerable`. +The `MicrophoneDeviceInfo` is a struct representing a single device. You can access the name of the device by the `Name` property. + +```csharp +var microphones = _client.AudioDeviceManager.EnumerateDevices(); + +foreach (var mic in microphones) +{ + Debug.Log(mic.Name); // Get microphone name +} +``` + +#### Select microphone + +**Declaration** +`public void SelectDevice(MicrophoneDeviceInfo device, bool enable)` + +| --- | --- | +| `device` | Microphone to select | +| `enable` | Enabled device is capturing audio input | + +```csharp +_client.AudioDeviceManager.SelectDevice(microphone, enable: true); +``` + +#### Get selected microphone + +You get the currently selected microphone device via `AudioDeviceManager.SelectedDevice`. Please note, that the returned `MicrophoneDeviceInfo` is a struct meaning + +```csharp +var selectedMicrophone = _client.AudioDeviceManager.SelectedDevice; +``` + +#### Start/Stop audio capturing + +Once a device is selected, you can start/stop the audio capturing with the `Enable()`, `Disable()`, or `SetEnabled(bool isEnabled)` methods. ```csharp -_client.SetAudioInputSource(audioSource); // audioSource is of type AudioSource +// Enable device to start capturing microphone input +_client.AudioDeviceManager.Enable(); + +// Disable device to stop capturing microphone input +_client.AudioDeviceManager.Disable(); + +// Set the enabled state by passing a boolean argument +_client.AudioDeviceManager.SetEnabled(true); ``` -### Handle microphone input in Unity +#### Check if microphone is enabled + +Enabled device is actively capturing audio input from a selected microphone. + +```csharp +var isDeviceEnabled = _client.AudioDeviceManager.IsEnabled; +``` + +#### Events + +The `SelectedDeviceChanged` and `IsEnabledChanged` events are triggered when a new device is selected or a device enabled state changes respectively. + +```csharp +public void AudioDeviceManagerEvents() +{ + // Triggered when the selected devices changes + _client.AudioDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; + + // Triggered when the IsEnabled property changes + _client.AudioDeviceManager.IsEnabledChanged += OnIsEnabledChanged; +} + +private void OnIsEnabledChanged(bool isEnabled) { } + +private void OnSelectedDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) { } +``` + +### Android & iOS + +Users must grant permission to use the Microphone device for platforms like Android and IOS. Otherwise, capturing audio will not work. Typical patterns are requesting permissions when the application starts or when a user attempts to enable audio capturing. -The way you start streaming audio from a microphone device is by calling Unity's `Microphone.Start` method and providing the microphone device name. -You obtain the microphone device name from Unity's `Microphone.devices` array. +#### iOS and WebGL + +You can request permission to use a microphone device on iOS and WebGL platforms by using Unity's [RequestUserAuthorization](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html): ```csharp -// Obtain reference to an AudioSource that will be used a source of audio -var inputAudioSource = GetComponent(); +// Request permission to use the Microphone +Application.RequestUserAuthorization(UserAuthorization.Microphone); + +// Check if user granted microphone permission +if (!Application.HasUserAuthorization(UserAuthorization.Microphone)) +{ + // Notify user that microphone permission was not granted and the microphone capturing will not work. +} +``` -// Get a valid microphone device name. -// You usually want to populate a dropdown list with Microphone.devices so that the user can pick which device should be used -_activeMicrophoneDeviceName = Microphone.devices.First(); +#### Android -inputAudioSource.clip - = Microphone.Start(_activeMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); -inputAudioSource.loop = true; -inputAudioSource.Play(); +For the Android platform, Unity recommends using the [Permission.RequestUserPermission](https://docs.unity3d.com/ScriptReference/Android.Permission.RequestUserPermission.html): -_client.SetAudioInputSource(inputAudioSource); +```csharp +// Request microphone permissions +Permission.RequestUserPermission(Permission.Microphone); + +// Check if user granted microphone permission +if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) +{ + // Notify user that microphone permission was not granted and the microphone capturing will not work. +} ``` -#### Change microphone device during the call +## Interacting with Web Camera + +All interactions with camera devices are handled by the `VideoDeviceManager` that can be accessed via `_client.VideoDeviceManager`. The `_client` field is an instance of `IStreamVideoClient`; if you haven't setup the video chat client yet, you can follow the [Client & Auth](../03-guides/01-client-auth.mdx) guide to learn how to do it. + -Here's an example of how to change the active microphone device: +#### List available camera devices + +All available microphone devices can be accessed with `AudioDeviceManager.EnumerateDevices()`. This method returns the `IEnumerable`. +The `MicrophoneDeviceInfo` is a struct representing a single device. You can access the name of the device by the `Name` property. ```csharp -// Stop previously active microphone -Microphone.End(_activeMicrophoneDeviceName); +var cameras = _client.VideoDeviceManager.EnumerateDevices(); + +foreach (var camera in cameras) +{ +Debug.Log(camera.Name); // Get camera name +} +``` + +#### Select camera -// Obtain reference to an AudioSource that was setup as an input source -var inputAudioSource = GetComponent(); +**Declaration** +`public void SelectDevice(CameraDeviceInfo device, bool enable, int fps = 30)` -inputAudioSource.clip = Microphone.Start(newMicrophoneDeviceName, true, 3, AudioSettings.outputSampleRate); +| --- | --- | +| `device` | Camera to select | +| `enable` | Enabled device is capturing video input | +| `fps` | (OPTIONAL) How many frames per second should the video be captured. The default value is `30` | + +```csharp +_client.VideoDeviceManager.SelectDevice(camera, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, enable: true, requestedFPS: 24); ``` -In case you need to use a different reference to `AudioSource`, you can simply call the `_client.SetAudioInputSource` again, and the audio track will be updated with the new audio input: +**Declaration** +`public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, bool enable, int requestedFPS = 30)` + +| --- | --- | +| `device` | Camera to select | +| `enable` | Enabled device is capturing video input | +| `requestedResolution` | At what resolution should the video be captured | +| `fps` | (OPTIONAL) How many frames per second should the video be captured. The default value is `30` | + ```csharp -_client.SetAudioInputSource(inputAudioSource); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_720p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_720p, enable: true, requestedFPS: 30); ``` -#### Additional Notes +The `VideoResolution` argument type contains multiple predefined resolutions: +| Value | Resolution | +| `VideoResolution.Res_144p` | 256x144 | +| `VideoResolution.Res_240p` | 320x240 | +| `VideoResolution.Res_360p` | 480x360 | +| `VideoResolution.Res_480p` | 640x480 | +| `VideoResolution.Res_720p` | 1280x720 | +| `VideoResolution.Res_1080p` | 1920x1080 | +| `new VideoResolution(int width, int height` | custom resolution | -- For standalone platforms like **Windows** or **macOS** you'd usually implement a dropdown menu populated with `Microphone.devices` so that the user can pick which microphone device should be used. The reason for this is that on standalone platforms there can be multiple microphone devices plugged in. -- For mobile platforms like **Android** or **iOS** microphone devices are handled by the OS, so you usually just pick the first device. -- You should handle the case where user does not have a microphone device at all and the `Microphone.devices` array is empty. -- For mobile platforms like **Android** or **iOS** it's best to request a permission to access the microphone and handle the case where user did not grant the permission to use it. Read more in [Unity's docs](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html) +```csharp +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_144p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_240p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_360p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_480p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_720p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_1080p, enable: true); +_client.VideoDeviceManager.SelectDevice(camera, new VideoResolution(500, 500), enable: true); +``` -Please refer to Unity's documentation for more information on how to use **Microphone** devices: -* [Microphone.devices](https://docs.unity3d.com/ScriptReference/Microphone-devices.html) -* [Microphone.Start](https://docs.unity3d.com/ScriptReference/Microphone.Start.html) -* [Microphone.End](https://docs.unity3d.com/ScriptReference/Microphone.End.html) +#### Requested resolution and FPS -## Setup sending Video +Please note that the video resolution and the FPS are the requested values passed to the camera device. Each device has it's own limitations and in case the passed values are not supported by the device the closest possible value will be selected. -In order to send video data you need to set an instance of [WebCamTexture](https://docs.unity3d.com/ScriptReference/WebCamTexture.html) as a **video source** by calling the `SetCameraInputSource` method on the instance of `StreamVideoClient`. +#### Get Selected Camera + +You can retrieve the currently selected camera device using `VideoDeviceManager.SelectedDevice`: ```csharp -_client.SetCameraInputSource(activeCamera); // activeCamera is of type WebCamTexture +public void GetSelectedCamera() +{ + var selectedCamera = _client.VideoDeviceManager.SelectedDevice; +} ``` -### Handle camera device input in Unity +#### Start/Stop Camera Capturing -They way you start streaming video from a camera device is by creating a `WebCamTexture` instance using the camera device name (obtained from `WebCamTexture.devices`) and calling `Play()` on the `WebCamTexture` instance. +Once a camera device is selected, you can start or stop the video capturing using the `Enable()`, `Disable()`, or `SetEnabled(bool isEnabled)` methods. ```csharp -// Obtain a camera device -var cameraDevice = WebCamTexture.devices.First(); +public void StartStopCamera() +{ + // Enable device to start capturing camera input + _client.VideoDeviceManager.Enable(); + + // Disable device to stop capturing camera input + _client.VideoDeviceManager.Disable(); -// Use device name to create a new WebCamTexture instance -var activeCamera = new WebCamTexture(cameraDevice.name, 1920, 1080, 24); + // Set the enabled state by passing a boolean argument + _client.VideoDeviceManager.SetEnabled(true); +} +``` + +#### Check if Camera is Enabled -// Call Play() in order to start capturing the video -activeCamera.Play(); +Check if the camera is enabled and actively capturing video input: -// Set WebCamTexture in Stream's Client - this WebCamTexture will be the video source in video calls -_client.SetCameraInputSource(activeCamera); +```csharp +public void CheckCameraStatus() +{ + // Check if currently selected device is enabled + var isDeviceEnabled = _client.VideoDeviceManager.IsEnabled; +} ``` -The video resolution and FPS parameters you set in your `WebCamTexture` instance will be used for the video publishing settings. In the above example, the video will aim to be streamed at 1080p (1920x1080) resolution and 24 frames per second. +#### Get Local Participant Camera Preview -:::note +For the local participant there will be no video and audio track defined in the `IStreamVideoCallParticipant` object because those streams are not being received from the remote servers like for all remote participants. If you'd wish to present the local participant video stream you get a reference to the instance of `WebCamTexture` associated with the selected device via `_client.VideoDeviceManager.GetSelectedDeviceWebCamTexture()`. -Stream service will dynamically adjust the video resolution and FPS parameters based on the network traffic. The ultimate goal is to ensure a smooth video experience without video stuttering. The settings you provide are the maximum aimed for if the network conditions allow it. +```csharp +var webCamTexture = _client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); -::: +// You can attach this texture to RawImage UI Component +GetComponent().texture = webCamTexture; +``` -#### Change camera device during the call +Please note that the instance of `WebCamTexture` may change every time a new device is selected therefore you should subscribe to the `SelectedDeviceChanged` event: +```csharp +public void GetLocalParticipantVideoPreviewFull() +{ + // Triggered when the selected devices changes + _client.VideoDeviceManager.SelectedDeviceChanged += UpdateLocalParticipantPreview; +} + +private void UpdateLocalParticipantPreview(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) +{ + var webCamTexture = _client.VideoDeviceManager.GetSelectedDeviceWebCamTexture(); + + // You can attach this texture to RawImage UI Component + GetComponent().texture = webCamTexture; +} +``` -The most efficient way to change the camera device is to update the `deviceName` property on the instance of `WebCamTexture` that was previously set as an input source via `_client.SetCameraInputSource`: +#### Check if camera is enabled ```csharp -_activeCamera.Stop(); -_activeCamera.deviceName = newDeviceName; -_activeCamera.Play(); +public void CheckCameraStatus() +{ + // Check if currently selected device is enabled + var isDeviceEnabled = _client.VideoDeviceManager.IsEnabled; +} ``` -In case you need to use a different reference to `WebCamTexture`, you can simply call the `_client.SetCameraInputSource` again, and the video track will be updated with the new camera input: +#### Events + +`SelectedDeviceChanged` and `IsEnabledChanged` events occur when a new device is selected or the device's enabled state changes. + ```csharp -_client.SetCameraInputSource(activeCamera); +public void VideoDeviceManagerEvents() +{ + // Triggered when the selected devices changes + _client.VideoDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; + + // Triggered when the IsEnabled property changes + _client.VideoDeviceManager.IsEnabledChanged += OnIsEnabledChanged; +} + +private void OnIsEnabledChanged(bool isEnabled) { } + +private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) { } ``` -#### Additional Notes +### Android & iOS + +Users must grant permission to use the Camera device on Android and iOS platforms. Permissions are typically requested when the application starts or when a user attempts to enable video capturing. -- For standalone platforms like **Windows** or **macOS** you'd usually implement a dropdown menu populated with `WebCamTexture.devices` so that the user can pick which camera device should be used. -- For mobile platforms like **Android** or **iOS** there are usually two cameras available: `Front` and `Back` cameras. Depending on your use case you may either want to automatically select the `Front` camera or allow user to toggle between the `Front` and the `Back` cameras. -- You should handle the case where user does not have a camera device at all and the `WebCamTexture.devices` array is empty. -- For mobile platforms like **Android** or **iOS** it's best to request a permission to access the camera and handle the case where user did not grant the permission to use it. Read more in [Unity's docs](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html) +#### iOS and WebGL -:::note +For iOS and WebGL platforms, you can request camera permission using Unity's `RequestUserAuthorization`: -For Android, if you're setting the **WebCamTexture** resolution, you need to set the resolution as a multiple of 16x16 as required by webRTC +```csharp +public void CameraIOSPermissions() +{ + // Request permission to use the Camera + Application.RequestUserAuthorization(UserAuthorization.WebCam); + + // Check if user granted camera permission + if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) + { + // Notify user that camera permission was not granted and the camera capturing will not work. + } +} +``` -::: +#### Android -Please refer to Unity's documentation for more information on how to use **Camera** devices: -* [WebCamTexture](https://docs.unity3d.com/ScriptReference/WebCamTexture.html) -* [WebCamTexture.devices](https://docs.unity3d.com/ScriptReference/WebCamTexture-devices.html) -* [WebCamTexture.Play](https://docs.unity3d.com/ScriptReference/WebCamTexture.Play.html) -* [WebCamTexture.Stop](https://docs.unity3d.com/ScriptReference/WebCamTexture.Stop.html) \ No newline at end of file +On Android, request camera permissions using `Permission.RequestUserPermission`: + +```csharp +public void CameraAndroidPermissions() +{ + // Request camera permissions + Permission.RequestUserPermission(Permission.Camera); + + // Check if user granted camera permission + if (!Permission.HasUserAuthorizedPermission(Permission.Camera)) + { + // Notify user that camera permission was not granted and the camera capturing will not work. + } +} +``` \ No newline at end of file From 9f29222599cd9b05d296e1bb048211ee4613764f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Fri, 26 Apr 2024 17:45:56 +0200 Subject: [PATCH 28/31] [Docs] fix tables --- docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx b/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx index 385501ad..14042db4 100644 --- a/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx +++ b/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx @@ -28,6 +28,7 @@ foreach (var mic in microphones) **Declaration** `public void SelectDevice(MicrophoneDeviceInfo device, bool enable)` +| Argument | Description | | --- | --- | | `device` | Microphone to select | | `enable` | Enabled device is capturing audio input | @@ -144,6 +145,7 @@ Debug.Log(camera.Name); // Get camera name **Declaration** `public void SelectDevice(CameraDeviceInfo device, bool enable, int fps = 30)` +| Argument | Description | | --- | --- | | `device` | Camera to select | | `enable` | Enabled device is capturing video input | @@ -157,6 +159,7 @@ _client.VideoDeviceManager.SelectDevice(camera, enable: true, requestedFPS: 24); **Declaration** `public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, bool enable, int requestedFPS = 30)` +| Argument | Description | | --- | --- | | `device` | Camera to select | | `enable` | Enabled device is capturing video input | @@ -169,7 +172,9 @@ _client.VideoDeviceManager.SelectDevice(camera, VideoResolution.Res_720p, enable ``` The `VideoResolution` argument type contains multiple predefined resolutions: + | Value | Resolution | +| --- | --- | | `VideoResolution.Res_144p` | 256x144 | | `VideoResolution.Res_240p` | 320x240 | | `VideoResolution.Res_360p` | 480x360 | From 9cdde1c1a4848c1a27fe7433cbd9e7df2fbbb78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Fri, 26 Apr 2024 17:50:52 +0200 Subject: [PATCH 29/31] [Docs] fix format --- docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx b/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx index 14042db4..65c4b42d 100644 --- a/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx +++ b/docusaurus/docs/Unity/03-guides/04-camera-and-microphone.mdx @@ -26,6 +26,7 @@ foreach (var mic in microphones) #### Select microphone **Declaration** + `public void SelectDevice(MicrophoneDeviceInfo device, bool enable)` | Argument | Description | @@ -143,6 +144,7 @@ Debug.Log(camera.Name); // Get camera name #### Select camera **Declaration** + `public void SelectDevice(CameraDeviceInfo device, bool enable, int fps = 30)` | Argument | Description | @@ -157,6 +159,7 @@ _client.VideoDeviceManager.SelectDevice(camera, enable: true, requestedFPS: 24); ``` **Declaration** + `public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResolution, bool enable, int requestedFPS = 30)` | Argument | Description | From a0d1d297697b3f79790b5f4b6a3f8cfe0cc5cbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:45:11 +0200 Subject: [PATCH 30/31] sync sample projects --- .../Scripts/UI/Devices/CameraMediaDevicePanel.cs | 7 +++++++ .../VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs | 9 ++++++++- .../Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs | 7 +++++++ .../Samples~/VideoChat/Scripts/UI/ParticipantView.cs | 5 ----- .../Samples~/VideoChat/Scripts/UI/UIManager.cs | 8 ++++---- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs index a0807c15..d40b0b3f 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/CameraMediaDevicePanel.cs @@ -27,6 +27,13 @@ protected override void OnInit() Client.VideoDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; } + + protected override void OnDestroying() + { + Client.VideoDeviceManager.SelectedDeviceChanged -= OnSelectedDeviceChanged; + + base.OnDestroying(); + } private void OnSelectedDeviceChanged(CameraDeviceInfo previousDevice, CameraDeviceInfo currentDevice) => SelectDeviceWithoutNotify(currentDevice); diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs index fe9174f6..3d92b5cd 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MediaDevicePanelBase.cs @@ -59,6 +59,8 @@ protected void Start() // Called by Unity protected void OnDestroy() { + OnDestroying(); + if (_refreshCoroutine != null) { StopCoroutine(_refreshCoroutine); @@ -70,9 +72,15 @@ protected virtual void OnInit() } + protected virtual void OnDestroying() + { + + } + protected abstract IEnumerable GetDevices(); protected abstract TDevice SelectedDevice { get; } protected abstract bool IsDeviceEnabled { get; set; } + protected UIManager UIManager { get; private set; } protected abstract string GetDeviceName(TDevice device); @@ -94,7 +102,6 @@ protected virtual void OnInit() private Coroutine _refreshCoroutine; private YieldInstruction _refreshDeviceInterval; - protected UIManager UIManager { get; private set; } private void OnDropdownValueChanged(int optionIndex) { diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs index 44aa18b8..704bbe8a 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/Devices/MicrophoneMediaDevicePanel.cs @@ -26,6 +26,13 @@ protected override void OnInit() Client.AudioDeviceManager.SelectedDeviceChanged += OnSelectedDeviceChanged; } + protected override void OnDestroying() + { + Client.AudioDeviceManager.SelectedDeviceChanged -= OnSelectedDeviceChanged; + + base.OnDestroying(); + } + private void OnSelectedDeviceChanged(MicrophoneDeviceInfo previousDevice, MicrophoneDeviceInfo currentDevice) => SelectDeviceWithoutNotify(currentDevice); } diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs index 5e99ea23..ac22ea12 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/ParticipantView.cs @@ -43,15 +43,12 @@ public void UpdateIsDominantSpeaker(bool isDominantSpeaker) /// public void SetLocalCameraSource(WebCamTexture localWebCamTexture) { - _localWebCamTexture = localWebCamTexture; - if (localWebCamTexture == null) { _video.texture = null; return; } - // we set RenderTexture a a RawImage.texture because the RenderTexture will receive video stream from the local camera _video.texture = localWebCamTexture; } @@ -102,8 +99,6 @@ protected void OnDestroy() private Color32 _defaultSpeakerFrameColor; private AudioSource _audioSource; - //private RenderTexture _localParticipantRenderTexture; - private WebCamTexture _localWebCamTexture; private RectTransform _videoRectTransform; private Vector2 _lastVideoRenderedSize; diff --git a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs index c1f35508..21fb2721 100644 --- a/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs +++ b/Packages/StreamVideo/Samples~/VideoChat/Scripts/UI/UIManager.cs @@ -30,8 +30,8 @@ protected void Awake() _mainScreen.Init(_videoManager, uiManager: this); _callScreen.Init(_videoManager, uiManager: this); - TrySelectFirstWorkingCameraAsync().LogIfFailed(); - TrySelectFirstMicrophone(); + SelectFirstWorkingCameraOrDefaultAsync().LogIfFailed(); + SelectFirstMicrophone(); } protected void Start() => ShowMainScreen(); @@ -95,7 +95,7 @@ private void OnCameraDeviceChanged(CameraDeviceInfo previousDevice, CameraDevice LocalCameraChanged?.Invoke(webCamTexture); } - private async Task TrySelectFirstWorkingCameraAsync() + private async Task SelectFirstWorkingCameraOrDefaultAsync() { if (!_videoManager.Client.VideoDeviceManager.EnumerateDevices().Any()) { @@ -122,7 +122,7 @@ private async Task TrySelectFirstWorkingCameraAsync() _videoManager.Client.VideoDeviceManager.SelectDevice(firstDevice, enable: false); } - private void TrySelectFirstMicrophone() + private void SelectFirstMicrophone() { // Select first microphone by default var microphoneDevice = _videoManager.Client.AudioDeviceManager.EnumerateDevices().FirstOrDefault(); From 7c7b72d7f8927ffe9ac625ebeba32bd3c5ebcba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sierpi=C5=84ski?= <33436839+sierpinskid@users.noreply.github.com> Date: Wed, 1 May 2024 16:40:19 +0200 Subject: [PATCH 31/31] Rename audio & video device managers interfaces to contain Stream prefix --- .../Runtime/Core/DeviceManagers/CameraDeviceInfo.cs | 4 ++-- ...AudioDeviceManager.cs => IStreamAudioDeviceManager.cs} | 2 +- ...eManager.cs.meta => IStreamAudioDeviceManager.cs.meta} | 0 ...VideoDeviceManager.cs => IStreamVideoDeviceManager.cs} | 2 +- ...eManager.cs.meta => IStreamVideoDeviceManager.cs.meta} | 0 ...{AudioDeviceManager.cs => StreamAudioDeviceManager.cs} | 6 +++--- ...ceManager.cs.meta => StreamAudioDeviceManager.cs.meta} | 0 ...{VideoDeviceManager.cs => StreamVideoDeviceManager.cs} | 4 ++-- ...ceManager.cs.meta => StreamVideoDeviceManager.cs.meta} | 0 Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs | 4 ++-- Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs | 8 ++++---- 11 files changed, 15 insertions(+), 15 deletions(-) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{IAudioDeviceManager.cs => IStreamAudioDeviceManager.cs} (84%) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{IAudioDeviceManager.cs.meta => IStreamAudioDeviceManager.cs.meta} (100%) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{IVideoDeviceManager.cs => IStreamVideoDeviceManager.cs} (96%) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{IVideoDeviceManager.cs.meta => IStreamVideoDeviceManager.cs.meta} (100%) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{AudioDeviceManager.cs => StreamAudioDeviceManager.cs} (94%) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{AudioDeviceManager.cs.meta => StreamAudioDeviceManager.cs.meta} (100%) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{VideoDeviceManager.cs => StreamVideoDeviceManager.cs} (97%) rename Packages/StreamVideo/Runtime/Core/DeviceManagers/{VideoDeviceManager.cs.meta => StreamVideoDeviceManager.cs.meta} (100%) diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs index a824a6ab..72f77933 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/CameraDeviceInfo.cs @@ -11,7 +11,7 @@ namespace StreamVideo.Core.DeviceManagers public string Name { get; } public bool IsFrontFacing { get; } - public CameraDeviceInfo(string name, bool isFrontFacing, IVideoDeviceManager videoDeviceManager) + public CameraDeviceInfo(string name, bool isFrontFacing, IStreamVideoDeviceManager videoDeviceManager) { _videoDeviceManager = videoDeviceManager; Name = name; @@ -34,6 +34,6 @@ public CameraDeviceInfo(string name, bool isFrontFacing, IVideoDeviceManager vid internal bool IsValid => !string.IsNullOrEmpty(Name); - private readonly IVideoDeviceManager _videoDeviceManager; + private readonly IStreamVideoDeviceManager _videoDeviceManager; } } \ No newline at end of file diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamAudioDeviceManager.cs similarity index 84% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamAudioDeviceManager.cs index 96d91cdc..42b8f5d7 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamAudioDeviceManager.cs @@ -3,7 +3,7 @@ /// /// Manages interactions with audio recording devices (Microphones). /// - public interface IAudioDeviceManager : IDeviceManager + public interface IStreamAudioDeviceManager : IDeviceManager { /// /// Select a microphone device for audio capturing. diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs.meta b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamAudioDeviceManager.cs.meta similarity index 100% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/IAudioDeviceManager.cs.meta rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamAudioDeviceManager.cs.meta diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamVideoDeviceManager.cs similarity index 96% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamVideoDeviceManager.cs index 462d3235..21901c9b 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamVideoDeviceManager.cs @@ -6,7 +6,7 @@ namespace StreamVideo.Core.DeviceManagers /// /// Manages interactions with video recording devices - Cameras. /// - public interface IVideoDeviceManager : IDeviceManager + public interface IStreamVideoDeviceManager : IDeviceManager { //StreamTodo: probably move all members from IDeviceManager here so we can have all comments specifically about video or audio diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs.meta b/Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamVideoDeviceManager.cs.meta similarity index 100% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/IVideoDeviceManager.cs.meta rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/IStreamVideoDeviceManager.cs.meta diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamAudioDeviceManager.cs similarity index 94% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamAudioDeviceManager.cs index e3269081..c579efbb 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamAudioDeviceManager.cs @@ -8,7 +8,7 @@ namespace StreamVideo.Core.DeviceManagers { - internal class AudioDeviceManager : DeviceManagerBase, IAudioDeviceManager + internal class StreamAudioDeviceManager : DeviceManagerBase, IStreamAudioDeviceManager { //StreamTodo: user can add/remove devices, we might want to expose DeviceAdded, DeviceRemoved events public override IEnumerable EnumerateDevices() @@ -87,7 +87,7 @@ public void SelectDevice(MicrophoneDeviceInfo device, bool enable) //StreamTodo: https://docs.unity3d.com/ScriptReference/AudioSource-ignoreListenerPause.html perhaps this should be enabled so that AudioListener doesn't affect recorded audio - internal AudioDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client, ILogs logs) + internal StreamAudioDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client, ILogs logs) : base(rtcSession, client, logs) { } @@ -134,7 +134,7 @@ private AudioSource GetOrCreateTargetAudioSource() _targetAudioSourceContainer = new GameObject { - name = $"[Stream][{nameof(AudioDeviceManager)}] Microphone Buffer", + name = $"[Stream][{nameof(StreamAudioDeviceManager)}] Microphone Buffer", #if STREAM_DEBUG_ENABLED hideFlags = HideFlags.DontSave #else diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs.meta b/Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamAudioDeviceManager.cs.meta similarity index 100% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/AudioDeviceManager.cs.meta rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamAudioDeviceManager.cs.meta diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs b/Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamVideoDeviceManager.cs similarity index 97% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamVideoDeviceManager.cs index 9be6a6b5..5982e585 100644 --- a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs +++ b/Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamVideoDeviceManager.cs @@ -18,7 +18,7 @@ namespace StreamVideo.Core.DeviceManagers - test that monitoring for video devices works and deviceAdded, deviceRemoved events are fired accordingly - test that enabling device triggers capturing and disabling stops capturing */ - internal class VideoDeviceManager : DeviceManagerBase, IVideoDeviceManager + internal class StreamVideoDeviceManager : DeviceManagerBase, IStreamVideoDeviceManager { //StreamTodo: user can add/remove devices, we might want to expose DeviceAdded, DeviceRemoved events public override IEnumerable EnumerateDevices() @@ -79,7 +79,7 @@ public void SelectDevice(CameraDeviceInfo device, VideoResolution requestedResol /// public WebCamTexture GetSelectedDeviceWebCamTexture() => _activeCamera; - internal VideoDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client, ILogs logs) + internal StreamVideoDeviceManager(RtcSession rtcSession, IInternalStreamVideoClient client, ILogs logs) : base(rtcSession, client, logs) { } diff --git a/Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs.meta b/Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamVideoDeviceManager.cs.meta similarity index 100% rename from Packages/StreamVideo/Runtime/Core/DeviceManagers/VideoDeviceManager.cs.meta rename to Packages/StreamVideo/Runtime/Core/DeviceManagers/StreamVideoDeviceManager.cs.meta diff --git a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs index 3fc43fc1..753fd9e2 100644 --- a/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/IStreamVideoClient.cs @@ -49,12 +49,12 @@ public interface IStreamVideoClient : IStreamVideoClientEventsListener, IDisposa /// /// Manager for video recording devices. Use it to interact with camera devices. /// - IVideoDeviceManager VideoDeviceManager { get; } + IStreamVideoDeviceManager VideoDeviceManager { get; } /// /// Manager for audio recording devices. Use it to interact with microphone devices. /// - IAudioDeviceManager AudioDeviceManager { get; } + IStreamAudioDeviceManager AudioDeviceManager { get; } /// /// Connect user to Stream server. Returns local user object of type diff --git a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs index 74fcf418..7f3de231 100644 --- a/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs +++ b/Packages/StreamVideo/Runtime/Core/StreamVideoClient.cs @@ -47,8 +47,8 @@ public class StreamVideoClient : IStreamVideoClient, IInternalStreamVideoClient public bool IsConnected => InternalLowLevelClient.ConnectionState == ConnectionState.Connected; - public IVideoDeviceManager VideoDeviceManager { get; } - public IAudioDeviceManager AudioDeviceManager { get; } + public IStreamVideoDeviceManager VideoDeviceManager { get; } + public IStreamAudioDeviceManager AudioDeviceManager { get; } /// /// Use this method to create the Video Client. You should have only one instance of this class @@ -385,8 +385,8 @@ private StreamVideoClient(IWebsocketClient coordinatorWebSocket, IWebsocketClien _cache = new Cache(this, serializer, _logs); InternalLowLevelClient.RtcSession.SetCache(_cache); - VideoDeviceManager = new VideoDeviceManager(InternalLowLevelClient.RtcSession, this, _logs); - AudioDeviceManager = new AudioDeviceManager(InternalLowLevelClient.RtcSession, this, _logs); + VideoDeviceManager = new StreamVideoDeviceManager(InternalLowLevelClient.RtcSession, this, _logs); + AudioDeviceManager = new StreamAudioDeviceManager(InternalLowLevelClient.RtcSession, this, _logs); SubscribeTo(InternalLowLevelClient); }