availableCameras method Null safety
override
Completes with a list of available cameras.
This method returns an empty list when no cameras are available.
Implementation
@override
Future<List<CameraDescription>> availableCameras() async {
try {
final html.MediaDevices? mediaDevices = window?.navigator.mediaDevices;
final List<CameraDescription> cameras = <CameraDescription>[];
// Throw a not supported exception if the current browser window
// does not support any media devices.
if (mediaDevices == null) {
throw PlatformException(
code: CameraErrorCode.notSupported.toString(),
message: 'The camera is not supported on this device.',
);
}
// Request video and audio permissions.
final html.MediaStream cameraStream =
await _cameraService.getMediaStreamForOptions(
const CameraOptions(
audio: AudioConstraints(enabled: true),
),
);
// Release the camera stream used to request video and audio permissions.
cameraStream
.getVideoTracks()
.forEach((html.MediaStreamTrack videoTrack) => videoTrack.stop());
// Request available media devices.
final List<dynamic> devices = await mediaDevices.enumerateDevices();
// Filter video input devices.
final Iterable<html.MediaDeviceInfo> videoInputDevices = devices
.whereType<html.MediaDeviceInfo>()
.where((html.MediaDeviceInfo device) =>
device.kind == MediaDeviceKind.videoInput)
/// The device id property is currently not supported on Internet Explorer:
/// https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/deviceId#browser_compatibility
.where(
(html.MediaDeviceInfo device) =>
device.deviceId != null && device.deviceId!.isNotEmpty,
);
// Map video input devices to camera descriptions.
for (final html.MediaDeviceInfo videoInputDevice in videoInputDevices) {
// Get the video stream for the current video input device
// to later use for the available video tracks.
final html.MediaStream videoStream = await _getVideoStreamForDevice(
videoInputDevice.deviceId!,
);
// Get all video tracks in the video stream
// to later extract the lens direction from the first track.
final List<html.MediaStreamTrack> videoTracks =
videoStream.getVideoTracks();
if (videoTracks.isNotEmpty) {
// Get the facing mode from the first available video track.
final String? facingMode =
_cameraService.getFacingModeForVideoTrack(videoTracks.first);
// Get the lens direction based on the facing mode.
// Fallback to the external lens direction
// if the facing mode is not available.
final CameraLensDirection lensDirection = facingMode != null
? _cameraService.mapFacingModeToLensDirection(facingMode)
: CameraLensDirection.external;
// Create a camera description.
//
// The name is a camera label which might be empty
// if no permissions to media devices have been granted.
//
// MediaDeviceInfo.label:
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/label
//
// Sensor orientation is currently not supported.
final String cameraLabel = videoInputDevice.label ?? '';
final CameraDescription camera = CameraDescription(
name: cameraLabel,
lensDirection: lensDirection,
sensorOrientation: 0,
);
final CameraMetadata cameraMetadata = CameraMetadata(
deviceId: videoInputDevice.deviceId!,
facingMode: facingMode,
);
cameras.add(camera);
camerasMetadata[camera] = cameraMetadata;
// Release the camera stream of the current video input device.
for (final html.MediaStreamTrack videoTrack in videoTracks) {
videoTrack.stop();
}
} else {
// Ignore as no video tracks exist in the current video input device.
continue;
}
}
return cameras;
} on html.DomException catch (e) {
throw CameraException(e.name, e.message);
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
} on CameraWebException catch (e) {
_addCameraErrorEvent(e);
throw CameraException(e.code.toString(), e.description);
}
}