initCameras method
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(() {});
});
}