Skip to content

Commit

Permalink
Adds the QueryCamerasAsync() API.
Browse files Browse the repository at this point in the history
  • Loading branch information
GillesTourreau committed Jun 11, 2024
1 parent 1700787 commit f2c5869
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 6 deletions.
3 changes: 3 additions & 0 deletions docs/Components/CallComposite.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 5 additions & 5 deletions docs/PortedApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand Down
8 changes: 8 additions & 0 deletions src/Communication.UI.Blazor/Calling/CallAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ public async Task StopScreenShareAsync()
await this.Module.InvokeVoidAsync("adapterStopScreenShare", this.Id);
}

/// <inheritdoc />
public async Task<IReadOnlyList<VideoDeviceInfo>> QueryCamerasAsync()
{
ObjectDisposedException.ThrowIf(this.callbackEvent is null, this);

return await this.Module.InvokeAsync<IReadOnlyList<VideoDeviceInfo>>("adapterQueryCameras", this.Id);
}

/// <inheritdoc />
public async ValueTask DisposeAsync()
{
Expand Down
17 changes: 17 additions & 0 deletions src/Communication.UI.Blazor/Calling/CallComposite.razor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
};
}
7 changes: 7 additions & 0 deletions src/Communication.UI.Blazor/Calling/ICallAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ public interface ICallAdapter : IAsyncDisposable
/// <exception cref="ObjectDisposedException">If the <see cref="CallAdapter"/> has already been disposed.</exception>
Task MuteAsync();

/// <summary>
/// Query for available camera devices.
/// </summary>
/// <returns>A <see cref="Task"/> that represents the asynchronous invocation which contains the camera devices available.</returns>
/// <exception cref="ObjectDisposedException">If the <see cref="CallAdapter"/> has already been disposed.</exception>
Task<IReadOnlyList<VideoDeviceInfo>> QueryCamerasAsync();

/// <summary>
/// Start sharing the screen during a call.
/// </summary>
Expand Down
51 changes: 51 additions & 0 deletions src/Communication.UI.Blazor/Calling/VideoDeviceInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//-----------------------------------------------------------------------
// <copyright file="VideoDeviceInfo.cs" company="P.O.S Informatique">
// Copyright (c) P.O.S Informatique. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace PosInformatique.Azure.Communication.UI.Blazor
{
using System.Text.Json.Serialization;

/// <summary>
/// Information about a camera device.
/// </summary>
public class VideoDeviceInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="VideoDeviceInfo"/> class.
/// </summary>
/// <param name="id">The id of this video device.</param>
/// <param name="name">The name of this video device.</param>
/// <param name="deviceType">The video device type.</param>
public VideoDeviceInfo(string id, string name, VideoDeviceType deviceType)
{
this.Id = id;
this.Name = name;
this.DeviceType = deviceType;
}

/// <summary>
/// Gets the name of this video device.
/// </summary>
[JsonPropertyName("name")]
[JsonPropertyOrder(1)]
public string Name { get; }

/// <summary>
/// Gets the id of this video device.
/// </summary>
[JsonPropertyName("id")]
[JsonPropertyOrder(2)]
public string Id { get; }

/// <summary>
/// Gets the video device type.
/// </summary>
[JsonPropertyName("deviceType")]
[JsonPropertyOrder(3)]
[JsonConverter(typeof(JsonStringEnumConverter))]
public VideoDeviceType DeviceType { get; }
}
}
39 changes: 39 additions & 0 deletions src/Communication.UI.Blazor/Calling/VideoDeviceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//-----------------------------------------------------------------------
// <copyright file="VideoDeviceType.cs" company="P.O.S Informatique">
// Copyright (c) P.O.S Informatique. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace PosInformatique.Azure.Communication.UI.Blazor
{
/// <summary>
/// Type of a video device.
/// </summary>
public enum VideoDeviceType
{
/// <summary>
/// The type of the device can not be determined.
/// </summary>
Unknown = 0,

/// <summary>
/// The camera is simple USB camera.
/// </summary>
UsbCamera = 1,

/// <summary>
/// The camera is a capture adapter.
/// </summary>
CaptureAdapter = 2,

/// <summary>
/// The camera is virtual (simulated by the host device).
/// </summary>
Virtual = 3,

/// <summary>
/// The camera is a screen sharing.
/// </summary>
ScreenSharing = 4,
}
}
10 changes: 9 additions & 1 deletion tests/Communication.UI.Blazor.Demo/Pages/Home.razor
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,15 @@

<button @onclick="this.StartScreenShareAsync" disabled="@(this.callAdapter is null)">Start screen share</button>
<button @onclick="this.StopScreenShareAsync" disabled="@(this.callAdapter is null)">Stop screen share</button>

</div>
<div>
<label for="cameras">Available cameras:</label>
<select name="cameras" id="cameras">
@foreach (var camera in this.cameras)
{
<option value="@camera.Id">@camera.Name (Id: @camera.Id, Type: @camera.DeviceType)</option>
}
</select>
</div>

<CallComposite Adapter="this.callAdapter"
Expand Down
6 changes: 6 additions & 0 deletions tests/Communication.UI.Blazor.Demo/Pages/Home.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public partial class Home

private ICallAdapter? callAdapter;

private IReadOnlyList<VideoDeviceInfo> cameras;

private string userId;

private string groupIdLocator;
Expand All @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
47 changes: 47 additions & 0 deletions tests/Communication.UI.Blazor.Tests/Calling/VideoDeviceInfoTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//-----------------------------------------------------------------------
// <copyright file="VideoDeviceInfoTest.cs" company="P.O.S Informatique">
// Copyright (c) P.O.S Informatique. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

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));
}
}
}

0 comments on commit f2c5869

Please sign in to comment.