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 web.MediaDevices mediaDevices = window.navigator.mediaDevices;
    final List<CameraDescription> cameras = <CameraDescription>[];

    // Request video permissions only.
    final web.MediaStream cameraStream =
        await _cameraService.getMediaStreamForOptions(const CameraOptions());

    // Release the camera stream used to request video permissions.
    cameraStream
        .getVideoTracks()
        .toDart
        .forEach((web.MediaStreamTrack videoTrack) => videoTrack.stop());

    // Request available media devices.
    final List<web.MediaDeviceInfo> devices =
        (await mediaDevices.enumerateDevices().toDart).toDart;

    // Filter video input devices.
    final Iterable<web.MediaDeviceInfo> videoInputDevices = devices
        .where(
          (web.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((web.MediaDeviceInfo device) => device.deviceId.isNotEmpty);

    // Map video input devices to camera descriptions.
    for (final web.MediaDeviceInfo videoInputDevice in videoInputDevices) {
      // Get the video stream for the current video input device
      // to later use for the available video tracks.
      final web.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<web.MediaStreamTrack> videoTracks =
          videoStream.getVideoTracks().toDart;

      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 CameraDescription camera = CameraDescription(
          name: videoInputDevice.label,
          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 web.MediaStreamTrack videoTrack in videoTracks) {
          videoTrack.stop();
        }
      } else {
        // Ignore as no video tracks exist in the current video input device.
        continue;
      }
    }

    return cameras;
  } on web.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);
  }
}