Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit f692cce

Browse files
committed
feat: add availableCameras implementation
1 parent 1e3e808 commit f692cce

File tree

1 file changed

+96
-5
lines changed

1 file changed

+96
-5
lines changed

packages/camera/camera_web/lib/src/camera_web.dart

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import 'dart:html' as html;
77
import 'dart:math';
88

99
import 'package:camera_platform_interface/camera_platform_interface.dart';
10+
import 'package:camera_web/src/camera_settings.dart';
11+
import 'package:camera_web/src/types/types.dart';
1012
import 'package:flutter/material.dart';
1113
import 'package:flutter/services.dart';
1214
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
@@ -15,18 +17,94 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
1517
///
1618
/// This class implements the `package:camera` functionality for the web.
1719
class CameraPlugin extends CameraPlatform {
20+
/// Creates a new instance of [CameraPlugin]
21+
/// with the given [cameraSettings] utility.
22+
CameraPlugin({required CameraSettings cameraSettings})
23+
: _cameraSettings = cameraSettings;
24+
1825
/// Registers this class as the default instance of [CameraPlatform].
1926
static void registerWith(Registrar registrar) {
20-
CameraPlatform.instance = CameraPlugin();
27+
CameraPlatform.instance = CameraPlugin(
28+
cameraSettings: CameraSettings(),
29+
);
2130
}
2231

23-
/// The current browser window used to access device cameras.
32+
final CameraSettings _cameraSettings;
33+
34+
/// The current browser window used to access media devices.
2435
@visibleForTesting
25-
html.Window? window;
36+
html.Window? window = html.window;
2637

2738
@override
28-
Future<List<CameraDescription>> availableCameras() {
29-
throw UnimplementedError('availableCameras() is not implemented.');
39+
Future<List<CameraDescription>> availableCameras() async {
40+
final mediaDevices = window?.navigator.mediaDevices;
41+
final cameras = <CameraDescription>[];
42+
43+
// Throw a not supported exception if the current browser window
44+
// does not support any media devices.
45+
if (mediaDevices == null) {
46+
throw CameraException(
47+
CameraErrorCodes.notSupported,
48+
'The camera is not supported on this device.',
49+
);
50+
}
51+
52+
// Request available media devices.
53+
final devices = await mediaDevices.enumerateDevices();
54+
55+
// Filter video input devices.
56+
final videoInputDevices = devices
57+
.whereType<html.MediaDeviceInfo>()
58+
.where((device) => device.kind == MediaDeviceKind.videoInput)
59+
60+
/// The device id property is currently not supported on Internet Explorer.
61+
/// https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/deviceId#browser_compatibility
62+
.where((device) => device.deviceId != null);
63+
64+
// Map video input devices to camera descriptions.
65+
for (final videoInputDevice in videoInputDevices) {
66+
// Get the video stream for the current video input device
67+
// to later use for the available video tracks.
68+
final videoStream = await _getVideoStreamForDevice(
69+
mediaDevices,
70+
videoInputDevice.deviceId!,
71+
);
72+
73+
// Get all video tracks in the video stream
74+
// to later extract the lens direction mode from the first track.
75+
final videoTracks = videoStream.getVideoTracks();
76+
77+
if (videoTracks.isNotEmpty) {
78+
// Get the lens direction from the first available video track.
79+
final lensDirection = _cameraSettings.getLensDirectionForVideoTrack(
80+
videoTracks.first,
81+
);
82+
83+
// Create a camera description.
84+
//
85+
// The name is a camera label with a fallback to a device id if empty.
86+
// This is because the label might not exist if no permissions have been granted.
87+
//
88+
// MediaDeviceInfo.label:
89+
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/label
90+
//
91+
// Sensor orientation is currently not supported.
92+
final cameraLabel = videoInputDevice.label ?? '';
93+
final camera = CameraDescription(
94+
name:
95+
cameraLabel.isNotEmpty ? cameraLabel : videoInputDevice.deviceId!,
96+
lensDirection: lensDirection,
97+
sensorOrientation: 0,
98+
);
99+
100+
cameras.add(camera);
101+
} else {
102+
// Ignore as no video tracks exist in the current video input device.
103+
continue;
104+
}
105+
}
106+
107+
return cameras;
30108
}
31109

32110
@override
@@ -190,4 +268,17 @@ class CameraPlugin extends CameraPlatform {
190268
Future<void> dispose(int cameraId) {
191269
throw UnimplementedError('dispose() is not implemented.');
192270
}
271+
272+
/// Returns a media video stream for the device with the given [deviceId].
273+
Future<html.MediaStream> _getVideoStreamForDevice(
274+
html.MediaDevices mediaDevices,
275+
String deviceId,
276+
) {
277+
// Create camera options with the desired device id.
278+
final cameraOptions = CameraOptions(
279+
video: VideoConstraints(deviceId: deviceId),
280+
);
281+
282+
return mediaDevices.getUserMedia(cameraOptions.toJson());
283+
}
193284
}

0 commit comments

Comments
 (0)