availableCameras method

  1. @override
Future<List<CameraDescription>> availableCameras()

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 permissions only.
    final html.MediaStream cameraStream =
        await _cameraService.getMediaStreamForOptions(const CameraOptions());

    // Release the camera stream used to request video 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);
  }
}