Skip to content

Commit fde404c

Browse files
feat(IVideoDevice): add IVideoDevice service (#5940)
* refactor: 增加 IMediaDevices 服务 * doc: 更新示例 * feat: 增加 media 脚本 * feat: 增加 IMediaDeviceInfo 接口 * doc: 更新示例 * refactor: 增加 getUserMedia 方法 * feat: 增加视频功能 * doc: 增加示例 * feat: 增加 IMeidaVideo 接口服务 * doc: 更新示例 * refactor: 优化 registerBootstrapBlazorModule 方法 * feat: 增加 drawImage 方法 * refactor: 移除 GetDisplayMedia 方法 * feat: 精简代码 * doc: 更新示例 * refactor: 重构 IMediaVideo 接口 * refactor: 更新脚本 * refactor: 重构代码 * doc: 更新示例 * refactor: 更新接口 * refactor: 增加镜头翻转功能 * refactor: 更改服务名称 * feat: 更新 open close 方法实现逻辑 * doc: 更新示例 * feat: 实现 GetPreviewUrl 方法 * doc: 更新示例 * refactor: 精简代码 * refactor: 增加翻转功能 * doc: 更新示例代码 * chore: 增加源码映射文件 * doc: 增加菜单 * doc: 更改示例文档名称 * doc: 更新文档 * test: 增加单元测试 * chore: bump version 9.6.1-beta01 Co-Authored-By: Vincent <[email protected]> * refactor: 撤销 Flip 方法 * doc: 撤销更改 --------- Co-authored-by: Vincent <[email protected]>
1 parent 0e5d5d4 commit fde404c

File tree

19 files changed

+664
-13
lines changed

19 files changed

+664
-13
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@page "/video-device"
2+
@inject IStringLocalizer<VideoDevices> Localizer
3+
4+
<h3>@Localizer["VideoDeviceTitle"]</h3>
5+
6+
<h4>@Localizer["VideoDeviceIntro"]</h4>
7+
8+
<Pre>[Inject, NotNull]
9+
private IBluetooth? BluetoothService { get; set; }</Pre>
10+
11+
<DemoBlock Title="@Localizer["BaseUsageTitle"]"
12+
Introduction="@Localizer["BaseUsageIntro"]"
13+
Name="Normal">
14+
<div class="row form-inline g-3">
15+
<div class="col-12">
16+
<Button Text="@Localizer["VideoDeviceRequestText"]" Icon="fa-solid fa-photo-film" OnClick="OnRequestDevice"></Button>
17+
<Button Text="@Localizer["VideoDeviceOpenText"]" Icon="fa-solid fa-play" OnClick="OnOpenVideo" class="ms-2"></Button>
18+
<Button Text="@Localizer["VideoDeviceCloseText"]" Icon="fa-solid fa-stop" OnClick="OnCloseVideo" class="ms-2"></Button>
19+
<Button Text="@Localizer["VideoDeviceCaptureText"]" Icon="fa-solid fa-camera" OnClick="OnCapture" class="ms-2"></Button>
20+
<Button Text="@Localizer["VideoDeviceFlipText"]" Icon="fa-solid fa-camera-rotate" OnClick="OnFlip" class="ms-2"></Button>
21+
</div>
22+
<div class="col-12">
23+
<Select Items="@_items" @bind-Value="_deviceId" DisplayText="Devices" ShowLabel="true"></Select>
24+
</div>
25+
</div>
26+
27+
<video class="bb-video" muted playsinline autoplay></video>
28+
29+
@if (!string.IsNullOrEmpty(_previewUrl))
30+
{
31+
<img class="bb-image" src="@_previewUrl" />
32+
}
33+
</DemoBlock>
34+
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Server.Components.Samples;
7+
8+
/// <summary>
9+
/// VideoDevice Component
10+
/// </summary>
11+
public partial class VideoDevices
12+
{
13+
[Inject, NotNull]
14+
private IVideoDevice? VideoDeviceService { get; set; }
15+
16+
private readonly List<IMediaDeviceInfo> _devices = [];
17+
18+
private List<SelectedItem> _items = [];
19+
20+
private string? _deviceId;
21+
22+
private string? _previewUrl;
23+
24+
private async Task OnRequestDevice()
25+
{
26+
var devices = await VideoDeviceService.GetDevices();
27+
if (devices != null)
28+
{
29+
_devices.AddRange(devices);
30+
_items = [.. _devices.Select(i => new SelectedItem(i.DeviceId, i.Label))];
31+
}
32+
}
33+
34+
private async Task OnOpenVideo()
35+
{
36+
if (!string.IsNullOrEmpty(_deviceId))
37+
{
38+
var constraints = new MediaTrackConstraints
39+
{
40+
DeviceId = _deviceId,
41+
VideoSelector = ".bb-video"
42+
};
43+
await VideoDeviceService.Open(constraints);
44+
}
45+
}
46+
47+
private async Task OnCloseVideo()
48+
{
49+
_previewUrl = "";
50+
await VideoDeviceService.Close(".bb-video");
51+
}
52+
53+
private async Task OnCapture()
54+
{
55+
_previewUrl = await VideoDeviceService.GetPreviewUrl();
56+
}
57+
58+
private async Task OnFlip()
59+
{
60+
await VideoDeviceService.Flip();
61+
}
62+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.bb-video {
2+
min-height: 240px;
3+
height: auto;
4+
width: auto;
5+
transform: scaleX(-1);
6+
margin-top: 1rem;
7+
display: block;
8+
}
9+
10+
.bb-image {
11+
border: 1px solid var(--bs-border-color);
12+
border-radius: var(--bs-border-radius);
13+
transform: scaleX(-1);
14+
margin-top: 1rem;
15+
display: block;
16+
}

src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,12 @@ void AddServices(DemoMenuItem item)
15491549
Url = "title"
15501550
},
15511551
new()
1552+
{
1553+
IsNew = true,
1554+
Text = Localizer["VideoDevices"],
1555+
Url = "video-device"
1556+
},
1557+
new()
15521558
{
15531559
Text = Localizer["WebSerial"],
15541560
Url = "web-serial"

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4920,7 +4920,8 @@
49204920
"UniverSheet": "UniverSheet",
49214921
"ShieldBadge": "ShieldBadge",
49224922
"OtpInput": "OtpInput",
4923-
"TotpService": "ITotpService"
4923+
"TotpService": "ITotpService",
4924+
"VideoDevices": "IVideoDevice"
49244925
},
49254926
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
49264927
"TablesHeaderTitle": "Header grouping function",
@@ -7116,5 +7117,16 @@
71167117
"UrlDesc": "Use your iPhone's camera or the QR code scanning function to scan the QR code below, then open the <code>Safari</code> browser and open the current address",
71177118
"WiFiDesc": "Use your iPhone's camera or the QR code scanner to scan the QR code below to automatically join the <code>WiFi</code> network.",
71187119
"EmailDesc": "Use your iPhone's camera or the QR code scanner to scan the QR code below, then open the <code>Email</code> app to send an email."
7120+
},
7121+
"BootstrapBlazor.Server.Components.Samples.VideoDevices": {
7122+
"VideoDeviceTitle": "IVideoDevice",
7123+
"VideoDeviceIntro": "Get video equipment operation capabilities through this service",
7124+
"BaseUsageTitle": "Basic usage",
7125+
"BaseUsageIntro": "Perform different operations by calling different API methods",
7126+
"VideoDeviceRequestText": "List",
7127+
"VideoDeviceOpenText": "Open",
7128+
"VideoDeviceCloseText": "Close",
7129+
"VideoDeviceCaptureText": "Capture",
7130+
"VideoDeviceFlipText": "Flip"
71197131
}
71207132
}

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4920,7 +4920,8 @@
49204920
"UniverSheet": "表格组件 UniverSheet",
49214921
"ShieldBadge": "徽章组件 ShieldBadge",
49224922
"OtpInput": "验证码输入框 OtpInput",
4923-
"TotpService": "时间密码验证服务 ITotpService"
4923+
"TotpService": "时间密码验证服务 ITotpService",
4924+
"VideoDevices": "视频设备服务 IVideoDevice"
49244925
},
49254926
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
49264927
"TablesHeaderTitle": "表头分组功能",
@@ -7116,5 +7117,16 @@
71167117
"UrlDesc": "使用 <code>iPhone</code> 手机相机或者扫描二维码功能扫描下方二维码后,打开 <code>Safari</code> 浏览器并且打开当前地址",
71177118
"WiFiDesc": "使用 <code>iPhone</code> 手机相机或者扫描二维码功能扫描下方二维码后,自动加入 <code>WiFi</code> 网络",
71187119
"EmailDesc": "使用 <code>iPhone</code> 手机相机或者扫描二维码功能扫描下方二维码后,打开 <code>Email</code> 应用发送邮件"
7120+
},
7121+
"BootstrapBlazor.Server.Components.Samples.VideoDevices": {
7122+
"VideoDeviceTitle": "IVideoDevice 视频设备服务",
7123+
"VideoDeviceIntro": "通过此服务获得视频设备操作能力",
7124+
"BaseUsageTitle": "基本用法",
7125+
"BaseUsageIntro": "通过调用不同的 api 方法进行不同操作",
7126+
"VideoDeviceRequestText": "枚举设备",
7127+
"VideoDeviceOpenText": "打开设备",
7128+
"VideoDeviceCloseText": "关闭设备",
7129+
"VideoDeviceCaptureText": "截图",
7130+
"VideoDeviceFlipText": "翻转镜头"
71197131
}
71207132
}

src/BootstrapBlazor.Server/docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@
230230
"univer-sheet": "UniverSheets",
231231
"shield-badge": "ShieldBadges",
232232
"opt-input": "OtpInputs",
233-
"otp-service": "OtpServices"
233+
"otp-service": "OtpServices",
234+
"video-device": "VideoDevices"
234235
},
235236
"video": {
236237
"table": "BV1ap4y1x7Qn?p=1",

src/BootstrapBlazor/BootstrapBlazor.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>9.6.1-beta02</Version>
4+
<Version>9.6.1-beta01</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/Camera/Camera.razor.js

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ const play = (camera, option = {}) => {
4040
...option
4141
}
4242
navigator.mediaDevices.getUserMedia(constrains).then(stream => {
43+
const supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
44+
console.log(supportedConstraints);
45+
4346
camera.video = { deviceId: option.video.deviceId };
4447
camera.video.element = camera.el.querySelector('video')
4548
camera.video.element.srcObject = stream
@@ -136,22 +139,46 @@ export function download(id, fileName) {
136139
createEl.remove();
137140
}
138141

139-
export function resize(id, width, height) {
142+
export async function resize(id, width, height) {
140143
const camera = Data.get(id)
141144
if (camera === null || camera.video === void 0) {
142145
return
143146
}
144147

145-
const constrains = {
146-
video: {
147-
deviceId: camera.video.deviceId,
148-
width: { ideal: width },
149-
height: { ideal: height }
150-
}
148+
const constraints = {
149+
facingMode: "environment",
150+
width: { ideal: width },
151+
height: { ideal: height }
151152
}
153+
await camera.video.track.applyConstraints(constraints);
154+
}
152155

153-
stopDevice(camera)
154-
play(camera, constrains)
156+
157+
export async function flip(id) {
158+
const camera = Data.get(id)
159+
if (camera === null || camera.video === void 0) {
160+
return
161+
}
162+
163+
const { track } = camera.video;
164+
if (track) {
165+
const constraints = track.getConstraints();
166+
if (constraints.facingMode === void 0) {
167+
constraints.facingMode = { exact: "environment" }
168+
}
169+
const { exact } = constraints.facingMode;
170+
if (exact === void 0) {
171+
constraints.facingMode = { ideal: "environment" }
172+
}
173+
else if (exact === "user") {
174+
constraints.facingMode = { ideal: "environment" }
175+
}
176+
else {
177+
constraints.facingMode = { ideal: "user" }
178+
}
179+
console.log(constraints);
180+
await track.applyConstraints(constraints);
181+
}
155182
}
156183

157184
export function dispose(id) {

src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ public static IServiceCollection AddBootstrapBlazor(this IServiceCollection serv
8686
services.TryAddScoped<IBrowserFingerService, DefaultBrowserFingerService>();
8787
services.TryAddScoped<ISerialService, DefaultSerialService>();
8888
services.TryAddScoped<IBluetooth, DefaultBluetooth>();
89+
services.TryAddScoped<IMediaDevices, DefaultMediaDevices>();
90+
services.TryAddScoped<IVideoDevice, DefaultVideoDevice>();
8991
services.AddScoped<TabItemTextOptions>();
9092
services.AddScoped<DialogService>();
9193
services.AddScoped<MaskService>();

0 commit comments

Comments
 (0)