@@ -7,6 +7,8 @@ import 'dart:html' as html;
7
7
import 'dart:math' ;
8
8
9
9
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' ;
10
12
import 'package:flutter/material.dart' ;
11
13
import 'package:flutter/services.dart' ;
12
14
import 'package:flutter_web_plugins/flutter_web_plugins.dart' ;
@@ -15,18 +17,94 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
15
17
///
16
18
/// This class implements the `package:camera` functionality for the web.
17
19
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
+
18
25
/// Registers this class as the default instance of [CameraPlatform] .
19
26
static void registerWith (Registrar registrar) {
20
- CameraPlatform .instance = CameraPlugin ();
27
+ CameraPlatform .instance = CameraPlugin (
28
+ cameraSettings: CameraSettings (),
29
+ );
21
30
}
22
31
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.
24
35
@visibleForTesting
25
- html.Window ? window;
36
+ html.Window ? window = html.window ;
26
37
27
38
@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;
30
108
}
31
109
32
110
@override
@@ -190,4 +268,17 @@ class CameraPlugin extends CameraPlatform {
190
268
Future <void > dispose (int cameraId) {
191
269
throw UnimplementedError ('dispose() is not implemented.' );
192
270
}
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
+ }
193
284
}
0 commit comments