Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ private IBluetooth? BluetoothService { get; set; }</Pre>
Name="Normal">
<div class="row form-inline g-3">
<div class="col-12">
<Button Text="@Localizer["VideoDeviceRequestText"]" Icon="fa-solid fa-photo-film" OnClick="OnRequestDevice"></Button>
<Button Text="@Localizer["VideoDeviceOpenText"]" Icon="fa-solid fa-play" OnClick="OnOpenVideo" IsDisabled="_isOpen || string.IsNullOrEmpty(_deviceId)" class="ms-2"></Button>
<Button Text="@Localizer["VideoDeviceCloseText"]" Icon="fa-solid fa-stop" OnClick="OnCloseVideo" IsDisabled="!_isOpen" class="ms-2"></Button>
<Button Text="@Localizer["VideoDeviceCaptureText"]" Icon="fa-solid fa-camera" OnClick="OnCapture" IsDisabled="!_isOpen" class="ms-2"></Button>
<div class="bb-actions">
<Button Text="@Localizer["VideoDeviceRequestText"]" Icon="fa-solid fa-photo-film" OnClick="OnRequestDevice"></Button>
<Button Text="@Localizer["VideoDeviceOpenText"]" Icon="fa-solid fa-play" OnClick="OnOpenVideo" IsDisabled="_isOpen || string.IsNullOrEmpty(_deviceId)"></Button>
<Button Text="@Localizer["VideoDeviceCloseText"]" Icon="fa-solid fa-stop" OnClick="OnCloseVideo" IsDisabled="!_isOpen"></Button>
<Button Text="@Localizer["VideoDeviceCaptureText"]" Icon="fa-solid fa-camera" OnClick="OnCapture" IsDisabled="!_isOpen"></Button>
<Button Text="QVGA" IsDisabled="!_isOpen" OnClickWithoutRender="() => OnApply(320, 240)"></Button>
<Button Text="VGA" IsDisabled="!_isOpen" OnClickWithoutRender="() => OnApply(640, 480)"></Button>
<Button Text="HD" IsDisabled="!_isOpen" OnClickWithoutRender="() => OnApply(1280, 960)"></Button>
</div>
</div>
<div class="col-12">
<Select Items="@_items" @bind-Value="_deviceId" DisplayText="Devices" ShowLabel="true"></Select>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ private async Task OnCapture()
_previewUrl = await VideoDeviceService.GetPreviewUrl();
}

private Task OnApply(int width, int height) => VideoDeviceService.Apply(new MediaTrackConstraints() { Width = width, Height = height });

private async Task DisposeAsync(bool disposing)
{
if (disposing)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
.bb-video {
.bb-actions {
display: flex;
flex-wrap: wrap;
gap: .5rem .5rem;
}

.bb-video {
min-height: 240px;
height: auto;
width: auto;
margin-top: 1rem;
margin: 1rem;
display: block;
}

.bb-image {
border: 1px solid var(--bs-border-color);
border-radius: var(--bs-border-radius);
margin-top: 1rem;
margin: 1rem 1rem 0 1rem;
display: block;
width: calc(100% - 2rem);
max-width: 640px;
}
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>9.6.1-beta01</Version>
<Version>9.6.1-beta02</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,10 @@ public async Task Capture()
var module = await LoadModule();
return await module.InvokeAsync<string?>("getPreviewUrl");
}

public async Task<bool> Apply(MediaTrackConstraints constraints)
{
var module = await LoadModule();
return await module.InvokeAsync<bool>("apply", constraints);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ public Task Capture()
{
return deviceService.GetPreviewUrl();
}

public Task<bool> Apply(MediaTrackConstraints constraints)
{
return deviceService.Apply(constraints);
}
}
7 changes: 7 additions & 0 deletions src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,11 @@ public interface IMediaDevices
/// </summary>
/// <returns></returns>
Task<string?> GetPreviewUrl();

/// <summary>
/// Apply the media track constraints.
/// </summary>
/// <param name="constraints"></param>
/// <returns></returns>
Task<bool> Apply(MediaTrackConstraints constraints);
}
7 changes: 7 additions & 0 deletions src/BootstrapBlazor/Services/MediaDevices/IVideoDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,11 @@ public interface IVideoDevice
/// </summary>
/// <returns></returns>
Task<string?> GetPreviewUrl();

/// <summary>
/// Apply the media track constraints.
/// </summary>
/// <param name="constraints"></param>
/// <returns></returns>
Task<bool> Apply(MediaTrackConstraints constraints);
}
35 changes: 35 additions & 0 deletions src/BootstrapBlazor/wwwroot/modules/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,41 @@ export async function close(videoSelector) {
return ret;
}

export async function apply(options) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Return value is never updated on success.

ret remains false even on success; update it (or return true) when constraints apply successfully.

Suggested implementation:

            ret = true;
        }
    } catch (ex) {

Please verify that inserting ret = true at this position fits the overall logic, meaning that the constraints have been applied by this point. You might need to adjust the placement if additional logic should occur before returning a successful value.

let ret = false;
try {
const media = registerBootstrapBlazorModule("MediaDevices");
const { stream } = media;
if (stream && stream.active) {
const tracks = stream.getVideoTracks();
if (tracks) {
const track = tracks[0];
const settings = track.getSettings();
const { aspectRatio } = settings;
if (options.width) {
settings.width = {
exact: options.width,
};
settings.height = {
exact: Math.floor(options.width / aspectRatio)
};
}
Comment on lines +91 to +98
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Inconsistency between JS applied constraints and provided parameters.

The function ignores options.height even when passed; either apply options.height to the constraints or update the interface to explicitly reflect that only width is used.

Suggested change
if (options.width) {
settings.width = {
exact: options.width,
};
settings.height = {
exact: Math.floor(options.width / aspectRatio)
};
}
if (options.width) {
settings.width = {
exact: options.width,
};
if (options.height) {
settings.height = {
exact: options.height,
};
} else {
settings.height = {
exact: Math.floor(options.width / aspectRatio)
};
}
}

if (options.facingMode) {
settings.facingMode = {
ideal: options.facingMode,
}
}
console.log(settings);
await track.applyConstraints(settings);
}
}
}
catch (err) {
console.error("Error apply constraints media devices.", err);
}
return ret;
}

export async function getPreviewUrl() {
let url = null;
const media = registerBootstrapBlazorModule("MediaDevices");
Expand Down
4 changes: 4 additions & 0 deletions test/UnitTest/Services/VideoDeviceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public async Task Open_Ok()
Context.JSInterop.Setup<string?>("getPreviewUrl").SetResult("blob:https://test-preview");
Context.JSInterop.Setup<bool>("open", _ => true).SetResult(true);
Context.JSInterop.Setup<bool>("close", _ => true).SetResult(true);
Context.JSInterop.Setup<bool>("apply", _ => true).SetResult(true);

var service = Context.Services.GetRequiredService<IVideoDevice>();
var options = new MediaTrackConstraints()
Expand All @@ -44,6 +45,9 @@ public async Task Open_Ok()
var close = await service.Close(".bb-video");
Assert.True(close);

var apply = await service.Apply(new MediaTrackConstraints() { Width = 640, Height = 480, VideoSelector = ".bb-video" });
Assert.True(apply);
Comment on lines 45 to +49
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Consider testing failure scenarios for the Apply method.

Add tests for when the JS interop call for apply returns false and when it throws an exception.


Assert.Equal("test-device-id", options.DeviceId);
Assert.Equal("user", options.FacingMode);
Assert.Equal(640, options.Height);
Expand Down