@@ -43,6 +43,7 @@ class CameraPlugin extends CameraPlatform {
43
43
@visibleForTesting
44
44
final Map <int , Camera > cameras = < int , Camera > {};
45
45
int _textureCounter = 1 ;
46
+ List <CameraDescription >? _availableCameras;
46
47
47
48
/// Metadata associated with each camera description.
48
49
/// Populated in [availableCameras] .
@@ -81,48 +82,60 @@ class CameraPlugin extends CameraPlatform {
81
82
@visibleForTesting
82
83
html.Window ? window = html.window;
83
84
84
- @override
85
- Future <List <CameraDescription >> availableCameras () async {
86
- try {
87
- final html.MediaDevices ? mediaDevices = window? .navigator.mediaDevices;
88
- final List <CameraDescription > cameras = < CameraDescription > [];
85
+ Future <List <html.MediaDeviceInfo >> _videoInputDevices () async {
86
+ final html.MediaDevices ? mediaDevices = window? .navigator.mediaDevices;
89
87
90
- // Throw a not supported exception if the current browser window
91
- // does not support any media devices.
92
- if (mediaDevices == null ) {
93
- throw PlatformException (
94
- code: CameraErrorCode .notSupported.toString (),
95
- message: 'The camera is not supported on this device.' ,
96
- );
97
- }
88
+ // Throw a not supported exception if the current browser window
89
+ // does not support any media devices.
90
+ if (mediaDevices == null ) {
91
+ throw PlatformException (
92
+ code: CameraErrorCode .notSupported.toString (),
93
+ message: 'The camera is not supported on this device.' ,
94
+ );
95
+ }
98
96
99
- // Request video permissions only.
100
- final html.MediaStream cameraStream =
101
- await _cameraService.getMediaStreamForOptions (const CameraOptions ());
97
+ // Request available media devices.
98
+ final List <dynamic > devices = await mediaDevices.enumerateDevices ();
102
99
103
- // Release the camera stream used to request video permissions.
104
- cameraStream
105
- .getVideoTracks ()
106
- .forEach ((html.MediaStreamTrack videoTrack) => videoTrack.stop ());
100
+ // Filter video input devices.
101
+ final Iterable <html.MediaDeviceInfo > videoInputDevices = devices
102
+ .whereType< html.MediaDeviceInfo > ()
103
+ .where ((html.MediaDeviceInfo device) =>
104
+ device.kind == MediaDeviceKind .videoInput)
107
105
108
- // Request available media devices.
109
- final List <dynamic > devices = await mediaDevices.enumerateDevices ();
106
+ /// The device id property is currently not supported on Internet Explorer:
107
+ /// https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/deviceId#browser_compatibility
108
+ .where (
109
+ (html.MediaDeviceInfo device) =>
110
+ device.deviceId != null && device.deviceId! .isNotEmpty,
111
+ );
110
112
111
- // Filter video input devices.
112
- final Iterable <html.MediaDeviceInfo > videoInputDevices = devices
113
- .whereType< html.MediaDeviceInfo > ()
114
- .where ((html.MediaDeviceInfo device) =>
115
- device.kind == MediaDeviceKind .videoInput)
113
+ return videoInputDevices.toList ();
114
+ }
116
115
117
- /// The device id property is currently not supported on Internet Explorer:
118
- /// https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/deviceId#browser_compatibility
119
- .where (
120
- (html.MediaDeviceInfo device) =>
121
- device.deviceId != null && device.deviceId! .isNotEmpty,
122
- );
116
+ @override
117
+ Future <List <CameraDescription >> availableCameras () async {
118
+ if (_availableCameras != null ) {
119
+ return _availableCameras! ;
120
+ }
121
+ try {
122
+ final List <CameraDescription > cameraDescriptions = < CameraDescription > [];
123
+
124
+ final bool hasPermission = cameras.isNotEmpty;
125
+ if (! hasPermission) {
126
+ // Request video permissions only.
127
+ final html.MediaStream cameraStream = await _cameraService
128
+ .getMediaStreamForOptions (const CameraOptions ());
129
+
130
+ // Release the camera stream used to request video permissions.
131
+ cameraStream
132
+ .getVideoTracks ()
133
+ .forEach ((html.MediaStreamTrack videoTrack) => videoTrack.stop ());
134
+ }
123
135
124
136
// Map video input devices to camera descriptions.
125
- for (final html.MediaDeviceInfo videoInputDevice in videoInputDevices) {
137
+ for (final html.MediaDeviceInfo videoInputDevice
138
+ in await _videoInputDevices ()) {
126
139
// Get the video stream for the current video input device
127
140
// to later use for the available video tracks.
128
141
final html.MediaStream videoStream = await _getVideoStreamForDevice (
@@ -134,54 +147,57 @@ class CameraPlugin extends CameraPlatform {
134
147
final List <html.MediaStreamTrack > videoTracks =
135
148
videoStream.getVideoTracks ();
136
149
137
- if (videoTracks.isNotEmpty) {
138
- // Get the facing mode from the first available video track.
139
- final String ? facingMode =
140
- _cameraService.getFacingModeForVideoTrack (videoTracks.first);
141
-
142
- // Get the lens direction based on the facing mode.
143
- // Fallback to the external lens direction
144
- // if the facing mode is not available.
145
- final CameraLensDirection lensDirection = facingMode != null
146
- ? mapFacingModeToLensDirection (facingMode)
147
- : CameraLensDirection .external ;
148
-
149
- // Create a camera description.
150
- //
151
- // The name is a camera label which might be empty
152
- // if no permissions to media devices have been granted.
153
- //
154
- // MediaDeviceInfo.label:
155
- // https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/label
156
- //
157
- // Sensor orientation is currently not supported.
158
- final String cameraLabel = videoInputDevice.label ?? '' ;
159
- final CameraDescription camera = CameraDescription (
160
- name: cameraLabel,
161
- lensDirection: lensDirection,
162
- sensorOrientation: 0 ,
163
- );
164
-
165
- final CameraMetadata cameraMetadata = CameraMetadata (
166
- deviceId: videoInputDevice.deviceId! ,
167
- facingMode: facingMode,
168
- );
169
-
170
- cameras.add (camera);
171
-
172
- camerasMetadata[camera] = cameraMetadata;
150
+ if (videoTracks.isEmpty) {
151
+ // Ignore as no video tracks exist in the current video input device.
152
+ continue ;
153
+ }
154
+
155
+ // Get the facing mode from the first available video track.
156
+ final String ? facingMode =
157
+ _cameraService.getFacingModeForVideoTrack (videoTracks.first);
158
+
159
+ // Get the lens direction based on the facing mode.
160
+ // Fallback to the external lens direction
161
+ // if the facing mode is not available.
162
+ final CameraLensDirection lensDirection = facingMode != null
163
+ ? mapFacingModeToLensDirection (facingMode)
164
+ : CameraLensDirection .external ;
165
+
166
+ // Create a camera description.
167
+ //
168
+ // The name is a camera label which might be empty
169
+ // if no permissions to media devices have been granted.
170
+ //
171
+ // MediaDeviceInfo.label:
172
+ // https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/label
173
+ //
174
+ // Sensor orientation is currently not supported.
175
+ final String cameraLabel = videoInputDevice.label ?? '' ;
176
+ final CameraDescription camera = CameraDescription (
177
+ name: cameraLabel,
178
+ lensDirection: lensDirection,
179
+ sensorOrientation: 0 ,
180
+ );
173
181
182
+ final CameraMetadata cameraMetadata = CameraMetadata (
183
+ deviceId: videoInputDevice.deviceId! ,
184
+ facingMode: facingMode,
185
+ );
186
+
187
+ cameraDescriptions.add (camera);
188
+
189
+ camerasMetadata[camera] = cameraMetadata;
190
+
191
+ if (! hasPermission) {
174
192
// Release the camera stream of the current video input device.
175
193
for (final html.MediaStreamTrack videoTrack in videoTracks) {
176
194
videoTrack.stop ();
177
195
}
178
- } else {
179
- // Ignore as no video tracks exist in the current video input device.
180
- continue ;
181
196
}
182
197
}
183
198
184
- return cameras;
199
+ _availableCameras = cameraDescriptions;
200
+ return cameraDescriptions;
185
201
} on html.DomException catch (e) {
186
202
throw CameraException (e.name, e.message);
187
203
} on PlatformException catch (e) {
0 commit comments