diff --git a/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor b/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor index 7462a9f529c..a947fd7d90a 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor @@ -17,6 +17,7 @@ private IAudioDevice? AudioDeviceService { get; set; } +
diff --git a/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor.cs index 11d84ce3788..a5f57b8e344 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/AudioDevices.razor.cs @@ -13,6 +13,9 @@ public partial class AudioDevices : IAsyncDisposable [Inject, NotNull] private IAudioDevice? AudioDeviceService { get; set; } + [Inject, NotNull] + private DownloadService? DownloadService { get; set; } + private readonly List _devices = []; private List _items = []; @@ -21,6 +24,8 @@ public partial class AudioDevices : IAsyncDisposable private bool _isOpen = false; + private bool _isDownload = false; + private async Task OnRequestDevice() { var devices = await AudioDeviceService.GetDevices(); @@ -49,8 +54,18 @@ private async Task OnOpen() private async Task OnClose() { - _isOpen = false; await AudioDeviceService.Close(".bb-audio"); + _isOpen = false; + _isDownload = true; + } + + private async Task OnDownload() + { + var stream = await AudioDeviceService.GetData(); + if (stream != null) + { + await DownloadService.DownloadFromStreamAsync($"data_{DateTime.Now:HHmmss}.wav", stream); + } } private async Task DisposeAsync(bool disposing) diff --git a/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs index 46d1b06ae95..f25c4b56680 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/VideoDevices.razor.cs @@ -69,7 +69,7 @@ private async Task OnDownload() var stream = await VideoDeviceService.GetPreviewData(); if (stream != null) { - await DownloadService.DownloadFromStreamAsync("preview.png", stream); + await DownloadService.DownloadFromStreamAsync($"preview_{DateTime.Now:HHmmss}.png", stream); } } diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index d9d406a223e..a3aed59c1b9 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -7137,6 +7137,9 @@ "BaseUsageIntro": "Perform different operations by calling different API methods", "AudioDeviceRequestText": "List", "AudioDeviceOpenText": "Record", - "AudioDeviceCloseText": "Stop" + "AudioDeviceCloseText": "Stop", + "AudioDevicePauseText": "Pause", + "AudioDeviceResumeText": "Resume", + "AudioDeviceDownloadText": "Download" } } diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index f35f000f138..a8881f92588 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -7139,6 +7139,7 @@ "AudioDeviceOpenText": "录音", "AudioDeviceCloseText": "停止", "AudioDevicePauseText": "暂停", - "AudioDeviceResumeText": "恢复" + "AudioDeviceResumeText": "恢复", + "AudioDeviceDownloadText": "下载" } } diff --git a/src/BootstrapBlazor/Services/MediaDevices/DefaultAudioDevice.cs b/src/BootstrapBlazor/Services/MediaDevices/DefaultAudioDevice.cs index 05708cea6cc..95b0420bfe2 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/DefaultAudioDevice.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/DefaultAudioDevice.cs @@ -31,4 +31,9 @@ public Task Close(string? selector) { return deviceService.Close(selector); } + + public Task GetData() + { + return deviceService.GetAudioData(); + } } diff --git a/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs b/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs index 2e4914bb1ea..682c58bc570 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/DefaultMediaDevices.cs @@ -62,4 +62,16 @@ public async Task Apply(MediaTrackConstraints constraints) var module = await LoadModule(); return await module.InvokeAsync("apply", constraints); } + + public async Task GetAudioData() + { + Stream? ret = null; + var module = await LoadModule(); + var stream = await module.InvokeAsync("getAudioData"); + if (stream != null) + { + ret = await stream.OpenReadStreamAsync(stream.Length); + } + return ret; + } } diff --git a/src/BootstrapBlazor/Services/MediaDevices/IAudioDevice.cs b/src/BootstrapBlazor/Services/MediaDevices/IAudioDevice.cs index dc8f9f68731..8f4685a1402 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/IAudioDevice.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/IAudioDevice.cs @@ -29,4 +29,10 @@ public interface IAudioDevice /// /// Task Close(string? selector); + + /// + /// Gets the stream of the audio. + /// + /// + Task GetData(); } diff --git a/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs b/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs index 67466fabca4..99da23a9a25 100644 --- a/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs +++ b/src/BootstrapBlazor/Services/MediaDevices/IMediaDevices.cs @@ -55,4 +55,10 @@ public interface IMediaDevices /// /// Task Apply(MediaTrackConstraints constraints); + + /// + /// Gets the stream of the audio. + /// + /// + Task GetAudioData(); } diff --git a/src/BootstrapBlazor/wwwroot/modules/media.js b/src/BootstrapBlazor/wwwroot/modules/media.js index cef7f694731..ef4e514a551 100644 --- a/src/BootstrapBlazor/wwwroot/modules/media.js +++ b/src/BootstrapBlazor/wwwroot/modules/media.js @@ -202,6 +202,7 @@ export async function record(options) { audio.classList.remove("d-none"); audio.classList.remove("hidden"); audio.removeAttribute("hidden"); + media.audioBlob = blob; } } delete media.audioSelector; @@ -233,3 +234,8 @@ export function stop(selector) { } return ret; } + +export function getAudioData() { + const media = registerBootstrapBlazorModule("MediaDevices"); + return media.audioBlob +} diff --git a/test/UnitTest/Services/AudioDeviceTest.cs b/test/UnitTest/Services/AudioDeviceTest.cs index e1a5136c397..9d07721b5b2 100644 --- a/test/UnitTest/Services/AudioDeviceTest.cs +++ b/test/UnitTest/Services/AudioDeviceTest.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 AudioDeviceTest : BootstrapBlazorTestBase @@ -28,6 +31,7 @@ public async Task Open_Ok() { Context.JSInterop.Setup("open", _ => true).SetResult(true); Context.JSInterop.Setup("close", _ => true).SetResult(true); + Context.JSInterop.Setup("getAudioData").SetResult(new MockJSStreamReference()); var service = Context.Services.GetRequiredService(); var options = new MediaTrackConstraints() @@ -40,5 +44,9 @@ public async Task Open_Ok() var close = await service.Close(".bb-audio"); Assert.True(close); + + var data = await service.GetData(); + Assert.NotNull(data); + Assert.Equal(4, data.Length); } }