initCameras method

Future<void> initCameras({
  1. CameraDescription? cameraDescription,
  2. bool ignoreLocks = false,
})

Initialize cameras instances. 初始化相机实例

Implementation

Future<void> initCameras({
  CameraDescription? cameraDescription,
  bool ignoreLocks = false,
}) {
  if (initializeLock != null && !ignoreLocks) {
    return initializeLock!.future;
  }
  final lock = ignoreLocks ? initializeLock! : Completer<void>();
  if (lock != initializeLock) {
    initializeLock = lock;
  }
  Future(() async {
    // Save the current controller to a local variable.
    final CameraController? c = innerController;
    // Dispose at last to avoid disposed usage with assertions.
    if (c != null) {
      innerController = null;
      await c.dispose();
    }
    // Then request a new frame to unbind the controller from elements.
    safeSetState(() {
      maxAvailableZoom = 1;
      minAvailableZoom = 1;
      currentZoom = 1;
      baseZoom = 1;
      // Meanwhile, cancel the existed exposure point and mode display.
      exposurePointDisplayTimer?.cancel();
      exposureModeDisplayTimer?.cancel();
      exposureFadeOutTimer?.cancel();
      isFocusPointDisplays.value = false;
      isFocusPointFadeOut.value = false;
      lastExposurePoint.value = null;
      currentExposureOffset.value = 0;
      currentExposureSliderOffset.value = 0;
      lockedCaptureOrientation = pickerConfig.lockCaptureOrientation;
    });
    await Future.microtask(() {});
    // When the [cameraDescription] is null, which means this is the first
    // time initializing cameras, so available cameras should be fetched.
    if (cameraDescription == null) {
      cameras = await availableCameras();
    }

    // After cameras fetched, judge again with the list is empty or not to
    // ensure there is at least an available camera for use.
    if (cameraDescription == null && cameras.isEmpty) {
      handleErrorWithHandler(
        CameraException(
          'No CameraDescription found.',
          'No cameras are available in the controller.',
        ),
        StackTrace.current,
        pickerConfig.onError,
      );
      return;
    }

    initFlashModesForCameras();
    final int preferredIndex = cameras.indexWhere(
      (CameraDescription e) =>
          e.lensDirection == pickerConfig.preferredLensDirection,
    );
    final int index;
    if (preferredIndex != -1 && c == null) {
      index = preferredIndex;
      currentCameraIndex = preferredIndex;
    } else {
      index = currentCameraIndex;
    }
    // Initialize the controller with the given resolution preset.
    final description = cameraDescription ?? cameras[index];
    invalidControllerMethods[description] ??= <String>{};
    final CameraController newController = CameraController(
      description,
      pickerConfig.resolutionPreset,
      enableAudio: enableAudio,
      imageFormatGroup: pickerConfig.imageFormatGroup,
    );
    try {
      final Stopwatch stopwatch = Stopwatch()..start();
      await newController.initialize();
      stopwatch.stop();
      realDebugPrint(
        "${stopwatch.elapsed} for controller's initialization.",
      );
      // Call recording preparation first.
      if (shouldPrepareForVideoRecording) {
        stopwatch
          ..reset()
          ..start();
        await newController.prepareForVideoRecording();
        stopwatch.stop();
        realDebugPrint("${stopwatch.elapsed} for recording's preparation.");
      }
      // Then call other asynchronous methods.
      stopwatch
        ..reset()
        ..start();
      await Future.wait(
        <Future<void>>[
          wrapControllerMethod(
            'getExposureOffsetStepSize',
            () => newController.getExposureOffsetStepSize(),
            description: description,
            fallback: exposureStep,
          ).then((value) => exposureStep = value),
          wrapControllerMethod(
            'getMaxExposureOffset',
            () => newController.getMaxExposureOffset(),
            description: description,
            fallback: maxAvailableExposureOffset,
          ).then((value) => maxAvailableExposureOffset = value),
          wrapControllerMethod(
            'getMinExposureOffset',
            () => newController.getMinExposureOffset(),
            description: description,
            fallback: minAvailableExposureOffset,
          ).then((value) => minAvailableExposureOffset = value),
          wrapControllerMethod(
            'getMaxZoomLevel',
            () => newController.getMaxZoomLevel(),
            description: description,
            fallback: maxAvailableZoom,
          ).then((value) => maxAvailableZoom = value),
          wrapControllerMethod(
            'getMinZoomLevel',
            () => newController.getMinZoomLevel(),
            description: description,
            fallback: minAvailableZoom,
          ).then((value) => minAvailableZoom = value),
          wrapControllerMethod(
            'getMinZoomLevel',
            () => newController.getMinZoomLevel(),
            description: description,
            fallback: minAvailableZoom,
          ).then((value) => minAvailableZoom = value),
          if (pickerConfig.lockCaptureOrientation != null)
            wrapControllerMethod<void>(
              'lockCaptureOrientation',
              () => newController.lockCaptureOrientation(
                pickerConfig.lockCaptureOrientation,
              ),
              description: description,
            ),
          // Do not set flash modes for the front camera.
          if (description.lensDirection != CameraLensDirection.front &&
              pickerConfig.preferredFlashMode != FlashMode.auto)
            wrapControllerMethod<void>(
              'setFlashMode',
              () => newController.setFlashMode(
                pickerConfig.preferredFlashMode,
              ),
              description: description,
              onError: () {
                validFlashModes[description]?.remove(
                  pickerConfig.preferredFlashMode,
                );
              },
            ),
        ],
        eagerError: false,
      );
      stopwatch.stop();
      realDebugPrint("${stopwatch.elapsed} for config's update.");
      innerController = newController;
      lock.complete();
    } catch (e, s) {
      accessDenied = e is CameraException && e.code.contains('Access');
      if (!accessDenied) {
        if (!retriedAfterInvalidInitialize) {
          retriedAfterInvalidInitialize = true;
          Future.delayed(Duration.zero, () {
            initCameras(
              cameraDescription: cameraDescription,
              ignoreLocks: true,
            );
          });
        } else {
          retriedAfterInvalidInitialize = false;
          lock.completeError(e, s);
        }
      } else {
        lock.completeError(e, s);
      }
    }
  });
  return lock.future.catchError((e, s) {
    handleErrorWithHandler(e, s, pickerConfig.onError);
  }).whenComplete(() {
    initializeLock = null;
    safeSetState(() {});
  });
}