diff --git a/docs/Components/CallComposite.md b/docs/Components/CallComposite.md index fb771d5..c12e659 100644 --- a/docs/Components/CallComposite.md +++ b/docs/Components/CallComposite.md @@ -92,6 +92,9 @@ To mute the microphone of the current user, call the `MuteAsync()` method on the To unmute the microphone of the current user, call the `UnmuteAsync()` method on the `ICallAdapter`. +### Gets available cameras +To retrieve the list of available camera call the `QueryCamerasAsync()` method. + ### Events You can subsribe to the following asynchronous events using a standard delegate method: - `OnCallEnded`: Occurs then the call is ended. diff --git a/docs/PortedApi.md b/docs/PortedApi.md index 1f0caab..f25b3ad 100644 --- a/docs/PortedApi.md +++ b/docs/PortedApi.md @@ -14,24 +14,24 @@ which has been ported to this library. | offStateChange | TODO | | | getState | TODO | | | dispose | **Done** | | -| holdCall (Beta) | No | Currently in beta | +| holdCall (Beta) | No | Currently in beta in Microsoft library | | joinCall (Deprecated) | No | Deprecated | | joinCall | Partially | Need to wrap the Call returned object | | leaveCall | **Done** | | -| resumeCall (Beta) | No | Currently in beta | +| resumeCall (Beta) | No | Currently in beta in Microsoft library | | startCamera | TODO | | | stopCamera | TODO | | | mute | **Done** | | | unmute | **Done** | | -| startCall (Beta) | No | Currently in beta | +| startCall (Beta) | No | Currently in beta in Microsoft library | | startScreenShare | **Done** | | | stopScreenShare | **Done** | | -| addParticipant (Beta) | No | Currently in beta | +| addParticipant (Beta) | No | Currently in beta in Microsoft library | | removeParticipant | TODO | | | createStreamView | TODO | | | disposeStreamView | TODO | | | askDevicePermission | TODO | | -| queryCameras | TODO | | +| queryCameras | **Done** | | | queryMicrophones | TODO | | | querySpeakers | TODO | | | setCamera | TODO | | diff --git a/src/Communication.UI.Blazor/Calling/CallAdapter.cs b/src/Communication.UI.Blazor/Calling/CallAdapter.cs index 7bb347b..7e344b6 100644 --- a/src/Communication.UI.Blazor/Calling/CallAdapter.cs +++ b/src/Communication.UI.Blazor/Calling/CallAdapter.cs @@ -90,6 +90,14 @@ public async Task StopScreenShareAsync() await this.Module.InvokeVoidAsync("adapterStopScreenShare", this.Id); } + /// + public async Task> QueryCamerasAsync() + { + ObjectDisposedException.ThrowIf(this.callbackEvent is null, this); + + return await this.Module.InvokeAsync>("adapterQueryCameras", this.Id); + } + /// public async ValueTask DisposeAsync() { diff --git a/src/Communication.UI.Blazor/Calling/CallComposite.razor.js b/src/Communication.UI.Blazor/Calling/CallComposite.razor.js index 5d1747e..f4d1e86 100644 --- a/src/Communication.UI.Blazor/Calling/CallComposite.razor.js +++ b/src/Communication.UI.Blazor/Calling/CallComposite.razor.js @@ -69,6 +69,15 @@ export async function adapterMute(id) { await adapter.mute(); } +export async function adapterQueryCameras(id) { + + const adapter = getAdapter(id); + + var cameras = await adapter.queryCameras(); + + return cameras.map(createCameraDevice) +} + export async function adapterUnmute(id) { const adapter = getAdapter(id); @@ -117,4 +126,12 @@ function createRemoteParticipant(remoteParticipant) { identifier: remoteParticipant.identifier, displayName: remoteParticipant.displayName, }; +} + +function createCameraDevice(cameraDevice) { + return { + name: cameraDevice.name, + id: cameraDevice.id, + deviceType: cameraDevice.deviceType, + }; } \ No newline at end of file diff --git a/src/Communication.UI.Blazor/Calling/ICallAdapter.cs b/src/Communication.UI.Blazor/Calling/ICallAdapter.cs index 6948d21..a09f09e 100644 --- a/src/Communication.UI.Blazor/Calling/ICallAdapter.cs +++ b/src/Communication.UI.Blazor/Calling/ICallAdapter.cs @@ -56,6 +56,13 @@ public interface ICallAdapter : IAsyncDisposable /// If the has already been disposed. Task MuteAsync(); + /// + /// Query for available camera devices. + /// + /// A that represents the asynchronous invocation which contains the camera devices available. + /// If the has already been disposed. + Task> QueryCamerasAsync(); + /// /// Start sharing the screen during a call. /// diff --git a/src/Communication.UI.Blazor/Calling/VideoDeviceInfo.cs b/src/Communication.UI.Blazor/Calling/VideoDeviceInfo.cs new file mode 100644 index 0000000..58bb5c9 --- /dev/null +++ b/src/Communication.UI.Blazor/Calling/VideoDeviceInfo.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) P.O.S Informatique. All rights reserved. +// +//----------------------------------------------------------------------- + +namespace PosInformatique.Azure.Communication.UI.Blazor +{ + using System.Text.Json.Serialization; + + /// + /// Information about a camera device. + /// + public class VideoDeviceInfo + { + /// + /// Initializes a new instance of the class. + /// + /// The id of this video device. + /// The name of this video device. + /// The video device type. + public VideoDeviceInfo(string id, string name, VideoDeviceType deviceType) + { + this.Id = id; + this.Name = name; + this.DeviceType = deviceType; + } + + /// + /// Gets the name of this video device. + /// + [JsonPropertyName("name")] + [JsonPropertyOrder(1)] + public string Name { get; } + + /// + /// Gets the id of this video device. + /// + [JsonPropertyName("id")] + [JsonPropertyOrder(2)] + public string Id { get; } + + /// + /// Gets the video device type. + /// + [JsonPropertyName("deviceType")] + [JsonPropertyOrder(3)] + [JsonConverter(typeof(JsonStringEnumConverter))] + public VideoDeviceType DeviceType { get; } + } +} diff --git a/src/Communication.UI.Blazor/Calling/VideoDeviceType.cs b/src/Communication.UI.Blazor/Calling/VideoDeviceType.cs new file mode 100644 index 0000000..31cf51e --- /dev/null +++ b/src/Communication.UI.Blazor/Calling/VideoDeviceType.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) P.O.S Informatique. All rights reserved. +// +//----------------------------------------------------------------------- + +namespace PosInformatique.Azure.Communication.UI.Blazor +{ + /// + /// Type of a video device. + /// + public enum VideoDeviceType + { + /// + /// The type of the device can not be determined. + /// + Unknown = 0, + + /// + /// The camera is simple USB camera. + /// + UsbCamera = 1, + + /// + /// The camera is a capture adapter. + /// + CaptureAdapter = 2, + + /// + /// The camera is virtual (simulated by the host device). + /// + Virtual = 3, + + /// + /// The camera is a screen sharing. + /// + ScreenSharing = 4, + } +} diff --git a/tests/Communication.UI.Blazor.Demo/Pages/Home.razor b/tests/Communication.UI.Blazor.Demo/Pages/Home.razor index 38cf8d2..483a7d7 100644 --- a/tests/Communication.UI.Blazor.Demo/Pages/Home.razor +++ b/tests/Communication.UI.Blazor.Demo/Pages/Home.razor @@ -60,7 +60,15 @@ - + +
+ +
cameras; + private string userId; private string groupIdLocator; @@ -39,6 +41,8 @@ public Home() this.groupIdLocator = "76FC6C87-2D4C-49C8-B909-7E3819A88621"; this.displayName = "John Doe"; + this.cameras = []; + this.cameraButton = true; this.devicesButton = true; this.endCallButton = true; @@ -122,6 +126,8 @@ private async Task LoadAsync() this.callAdapter.OnMicrophoneMuteChanged += this.OnMicrophoneMuteChanged; this.callAdapter.OnParticipantJoined += this.OnParticipantJoined; this.callAdapter.OnParticipantLeft += this.OnParticipantLeft; + + this.cameras = await this.callAdapter.QueryCamerasAsync(); } private async Task MuteAsync() diff --git a/tests/Communication.UI.Blazor.Tests/Calling/VideoDeviceInfoTest.cs b/tests/Communication.UI.Blazor.Tests/Calling/VideoDeviceInfoTest.cs new file mode 100644 index 0000000..a816eba --- /dev/null +++ b/tests/Communication.UI.Blazor.Tests/Calling/VideoDeviceInfoTest.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) P.O.S Informatique. All rights reserved. +// +//----------------------------------------------------------------------- + +namespace PosInformatique.Azure.Communication.UI.Blazor.Tests +{ + public class VideoDeviceInfoTest + { + [Fact] + public void Constructor() + { + var deviceInfo = new VideoDeviceInfo("The id", "The name", VideoDeviceType.CaptureAdapter); + + deviceInfo.DeviceType.Should().Be(VideoDeviceType.CaptureAdapter); + deviceInfo.Id.Should().Be("The id"); + deviceInfo.Name.Should().Be("The name"); + } + + [Fact] + public void Serialization() + { + var deviceInfo = new VideoDeviceInfo("The id", "The name", VideoDeviceType.CaptureAdapter); + + deviceInfo.Should().BeJsonSerializableInto(new + { + name = "The name", + id = "The id", + deviceType = "CaptureAdapter", + }); + } + + [Fact] + public void Deserialization() + { + var json = new + { + name = "The name", + id = "The id", + deviceType = "CaptureAdapter", + }; + + json.Should().BeJsonDeserializableInto(new VideoDeviceInfo("The id", "The name", VideoDeviceType.CaptureAdapter)); + } + } +} \ No newline at end of file