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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ private IVideoDevice? VideoDeviceService { get; set; }</Pre>
<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="@Localizer["VideoDeviceDownloadText"]" Icon="fa-solid fa-download" OnClick="OnDownload" 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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public partial class VideoDevices : IAsyncDisposable
[Inject, NotNull]
private IVideoDevice? VideoDeviceService { get; set; }

[Inject, NotNull]
private DownloadService? DownloadService { get; set; }

private readonly List<IMediaDeviceInfo> _devices = [];

private List<SelectedItem> _items = [];
Expand Down Expand Up @@ -61,6 +64,15 @@ private async Task OnCapture()
_previewUrl = await VideoDeviceService.GetPreviewUrl();
}

private async Task OnDownload()
{
var stream = await VideoDeviceService.GetPreviewData();
if (stream != null)
{
await DownloadService.DownloadFromStreamAsync("preview.png", stream);
}
}

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

private async Task DisposeAsync(bool disposing)
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -7128,7 +7128,7 @@
"VideoDeviceOpenText": "Open",
"VideoDeviceCloseText": "Close",
"VideoDeviceCaptureText": "Capture",
"VideoDeviceFlipText": "Flip"
"VideoDeviceDownloadText": "Download"
},
"BootstrapBlazor.Server.Components.Samples.AudioDevices": {
"AudioDeviceTitle": "IAudioDevice",
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -7128,7 +7128,7 @@
"VideoDeviceOpenText": "打开设备",
"VideoDeviceCloseText": "关闭设备",
"VideoDeviceCaptureText": "截图",
"VideoDeviceFlipText": "翻转镜头"
"VideoDeviceDownloadText": "下载"
},
"BootstrapBlazor.Server.Components.Samples.AudioDevices": {
"AudioDeviceTitle": "IAudioDevice 音频设备服务",
Expand Down
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-beta02</Version>
<Version>9.6.1-beta03</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ public async Task Capture()
return await module.InvokeAsync<string?>("getPreviewUrl");
}

public async Task<Stream?> GetPreviewData()
{
Stream? ret = null;
var module = await LoadModule();
var stream = await module.InvokeAsync<IJSStreamReference?>("getPreviewData");
if (stream != null)
{
Comment thread
ArgoZhang marked this conversation as resolved.
ret = await stream.OpenReadStreamAsync(stream.Length);
}
return ret;
}

public async Task<bool> Apply(MediaTrackConstraints constraints)
{
var module = await LoadModule();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public Task Capture()
return deviceService.Capture();
}

public Task<Stream?> GetPreviewData()
{
return deviceService.GetPreviewData();
}

public Task<string?> GetPreviewUrl()
{
return deviceService.GetPreviewUrl();
Expand Down
6 changes: 6 additions & 0 deletions src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ public interface IMediaDevices
/// <returns></returns>
Task<string?> GetPreviewUrl();

/// <summary>
/// Gets the stream of the captured image.
/// </summary>
/// <returns></returns>
Task<Stream?> GetPreviewData();

/// <summary>
/// Apply the media track constraints.
/// </summary>
Expand Down
18 changes: 6 additions & 12 deletions src/BootstrapBlazor/Services/MediaDevices/IVideoDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,18 @@ public interface IVideoDevice
/// <returns></returns>
Task Capture();

///// <summary>
///// Preview a still image from the video stream.
///// </summary>
///// <returns></returns>
//Task Preview();

///// <summary>
///// Gets the stream of the captured image.
///// </summary>
///// <returns></returns>
//Task<Stream?> GetPreviewImage();

/// <summary>
/// Gets the preview URL of the captured image.
/// </summary>
/// <returns></returns>
Task<string?> GetPreviewUrl();

/// <summary>
/// Gets the stream of the captured image.
/// </summary>
/// <returns></returns>
Task<Stream?> GetPreviewData();

/// <summary>
/// Apply the media track constraints.
/// </summary>
Expand Down
21 changes: 16 additions & 5 deletions src/BootstrapBlazor/wwwroot/modules/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ export async function enumerateDevices() {
console.log("enumerateDevices() not supported.");
}
else {
await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
ret = await navigator.mediaDevices.enumerateDevices();
try {
await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
ret = await navigator.mediaDevices.enumerateDevices();
}
catch (e) {
console.warn(e);
}
}
return ret;
}
Expand All @@ -25,12 +30,12 @@ export async function open(type, options) {

export async function close(selector) {
const media = registerBootstrapBlazorModule("MediaDevices");
let ret = false;
let ret;
if (media.stream) {
ret = await closeVideoDevice(selector);
}
else {
ret = await stop(selector);
ret = stop(selector);
}
return ret;
}
Expand Down Expand Up @@ -143,11 +148,17 @@ export async function getPreviewUrl() {
const capture = new ImageCapture(track);
const blob = await capture.takePhoto();
url = URL.createObjectURL(blob);
media.previewBlob = blob;
Comment thread
ArgoZhang marked this conversation as resolved.
}
}
return url;
}

export function getPreviewData() {
const media = registerBootstrapBlazorModule("MediaDevices");
return media.previewBlob;
Comment thread
ArgoZhang marked this conversation as resolved.
}

const closeStream = stream => {
if (stream) {
const tracks = stream.getTracks();
Expand Down Expand Up @@ -205,7 +216,7 @@ export async function record(options) {
return ret;
}

export async function stop(selector) {
export function stop(selector) {
let ret = false;
const media = registerBootstrapBlazorModule("MediaDevices");
if (selector) {
Expand Down
8 changes: 8 additions & 0 deletions test/UnitTest/Services/VideoDeviceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone

using Microsoft.JSInterop;
using UnitTest.Mock;

namespace UnitTest.Services;

public class VideoDeviceTest : BootstrapBlazorTestBase
Expand All @@ -27,6 +30,7 @@ public async Task GetDevices_Ok()
public async Task Open_Ok()
Comment thread
ArgoZhang marked this conversation as resolved.
{
Context.JSInterop.Setup<string?>("getPreviewUrl").SetResult("blob:https://test-preview");
Context.JSInterop.Setup<IJSStreamReference?>("getPreviewData").SetResult(new MockJSStreamReference());
Context.JSInterop.Setup<bool>("open", _ => true).SetResult(true);
Context.JSInterop.Setup<bool>("close", _ => true).SetResult(true);
Context.JSInterop.Setup<bool>("apply", _ => true).SetResult(true);
Expand Down Expand Up @@ -58,5 +62,9 @@ public async Task Open_Ok()
await service.Capture();
var url = await service.GetPreviewUrl();
Assert.Equal("blob:https://test-preview", url);

var data = await service.GetPreviewData();
Assert.NotNull(data);
Assert.Equal(4, data.Length);
}
}