diff --git a/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor b/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor index 8cf435af1fc..73c72d0bf9b 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor @@ -18,6 +18,7 @@ private IVideoDevice? VideoDeviceService { get; set; } + diff --git a/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs index 0635c200592..46d1b06ae95 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs @@ -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 _devices = []; private List _items = []; @@ -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) diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 22785ecd4e6..d9d406a223e 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -7128,7 +7128,7 @@ "VideoDeviceOpenText": "Open", "VideoDeviceCloseText": "Close", "VideoDeviceCaptureText": "Capture", - "VideoDeviceFlipText": "Flip" + "VideoDeviceDownloadText": "Download" }, "BootstrapBlazor.Server.Components.Samples.AudioDevices": { "AudioDeviceTitle": "IAudioDevice", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 5e3cc128f15..f35f000f138 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -7128,7 +7128,7 @@ "VideoDeviceOpenText": "打开设备", "VideoDeviceCloseText": "关闭设备", "VideoDeviceCaptureText": "截图", - "VideoDeviceFlipText": "翻转镜头" + "VideoDeviceDownloadText": "下载" }, "BootstrapBlazor.Server.Components.Samples.AudioDevices": { "AudioDeviceTitle": "IAudioDevice 音频设备服务", diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 1a7f7ef8660..d7b84e4d7b4 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 9.6.1-beta02 + 9.6.1-beta03 diff --git a/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs b/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs index 37e124abed0..2e4914bb1ea 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs @@ -45,6 +45,18 @@ public async Task Capture() return await module.InvokeAsync("getPreviewUrl"); } + public async Task GetPreviewData() + { + Stream? ret = null; + var module = await LoadModule(); + var stream = await module.InvokeAsync("getPreviewData"); + if (stream != null) + { + ret = await stream.OpenReadStreamAsync(stream.Length); + } + return ret; + } + public async Task Apply(MediaTrackConstraints constraints) { var module = await LoadModule(); diff --git a/src/BootstrapBlazor/Services/MediaDevices/DefaultVideoDevice.cs b/src/BootstrapBlazor/Services/MediaDevices/DefaultVideoDevice.cs index b4af4b6de4a..e68dbae597d 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/DefaultVideoDevice.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/DefaultVideoDevice.cs @@ -37,6 +37,11 @@ public Task Capture() return deviceService.Capture(); } + public Task GetPreviewData() + { + return deviceService.GetPreviewData(); + } + public Task GetPreviewUrl() { return deviceService.GetPreviewUrl(); diff --git a/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs b/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs index 8cd7345d6c4..67466fabca4 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs @@ -43,6 +43,12 @@ public interface IMediaDevices /// Task GetPreviewUrl(); + /// + /// Gets the stream of the captured image. + /// + /// + Task GetPreviewData(); + /// /// Apply the media track constraints. /// diff --git a/src/BootstrapBlazor/Services/MediaDevices/IVideoDevice.cs b/src/BootstrapBlazor/Services/MediaDevices/IVideoDevice.cs index c197c87bf37..b72bcf73221 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/IVideoDevice.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/IVideoDevice.cs @@ -36,24 +36,18 @@ public interface IVideoDevice /// Task Capture(); - ///// - ///// Preview a still image from the video stream. - ///// - ///// - //Task Preview(); - - ///// - ///// Gets the stream of the captured image. - ///// - ///// - //Task GetPreviewImage(); - /// /// Gets the preview URL of the captured image. /// /// Task GetPreviewUrl(); + /// + /// Gets the stream of the captured image. + /// + /// + Task GetPreviewData(); + /// /// Apply the media track constraints. /// diff --git a/src/BootstrapBlazor/wwwroot/modules/media.js b/src/BootstrapBlazor/wwwroot/modules/media.js index 53944134c3d..cef7f694731 100644 --- a/src/BootstrapBlazor/wwwroot/modules/media.js +++ b/src/BootstrapBlazor/wwwroot/modules/media.js @@ -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; } @@ -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; } @@ -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; } } return url; } +export function getPreviewData() { + const media = registerBootstrapBlazorModule("MediaDevices"); + return media.previewBlob; +} + const closeStream = stream => { if (stream) { const tracks = stream.getTracks(); @@ -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) { diff --git a/test/UnitTest/Services/VideoDeviceTest.cs b/test/UnitTest/Services/VideoDeviceTest.cs index 500100ea64f..64e4b616273 100644 --- a/test/UnitTest/Services/VideoDeviceTest.cs +++ b/test/UnitTest/Services/VideoDeviceTest.cs @@ -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 @@ -27,6 +30,7 @@ public async Task GetDevices_Ok() public async Task Open_Ok() { Context.JSInterop.Setup("getPreviewUrl").SetResult("blob:https://test-preview"); + Context.JSInterop.Setup("getPreviewData").SetResult(new MockJSStreamReference()); Context.JSInterop.Setup("open", _ => true).SetResult(true); Context.JSInterop.Setup("close", _ => true).SetResult(true); Context.JSInterop.Setup("apply", _ => true).SetResult(true); @@ -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); } }