在前面一篇文章中,已經介紹瞭如何使用 CameraX,這篇文章就分析下 CameraX 主要流程的源碼。
本篇分析的源碼版本是1.0.0-alpha06,目前最新的 CameraX 版本是1.0.0-alpha10。java
在 build.gradle 中聲明android
def camerax_version = "1.0.0-alpha06"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
def camerax_view_version = "1.0.0-alpha03"
def camerax_ext_version = "1.0.0-alpha03"
//other
// If you to use the Camera View class
implementation "androidx.camera:camera-view:$camerax_view_version"
// If you to use Camera Extensions
implementation "androidx.camera:camera-extensions:$camerax_ext_version"
複製代碼
其中camera-core
和camera-camera2
是必須使用的庫,使用該庫,能夠輕鬆地使用Camera2 API的功能緩存
首先看下 CameraX 的屬性:session
private static final CameraX INSTANCE = new CameraX();
final CameraRepository mCameraRepository = new CameraRepository();
private final AtomicBoolean mInitialized = new AtomicBoolean(false);
private final UseCaseGroupRepository mUseCaseGroupRepository = new UseCaseGroupRepository();
private final ErrorHandler mErrorHandler = new ErrorHandler();
private CameraFactory mCameraFactory;
private CameraDeviceSurfaceManager mSurfaceManager;
private UseCaseConfigFactory mDefaultConfigFactory;
private Context mContext;
複製代碼
主要看其中幾個重要屬性:架構
CameraX主要使用UseCase
的概念與相機設備進行交互,目前提供的UseCase
:app
CameraX初始化方法:initide
public static void init(Context context, @NonNull AppConfig appConfig) {
INSTANCE.initInternal(context, appConfig);
}
複製代碼
init 是經過ContentProvier配置初始化,具體實現類Camera2Initializer
oop
public final class Camera2Initializer extends ContentProvider {
private static final String TAG = "Camera2Initializer";
@Override
public boolean onCreate() {
Log.d(TAG, "CameraX initializing with Camera2 ...");
CameraX.init(getContext(), Camera2AppConfig.create(getContext()));
return false;
}
...
}
複製代碼
在AndroidMainifest.xml
會自動生成provider配置,ContentProvider的OnCreate調用比Applicantion的 onCreate調用更早。post
<provider android:name="androidx.camera.camera2.impl.Camera2Initializer" android:exported="false" android:multiprocess="true" android:authorities="${applicationId}.camerax-init" android:initOrder="100" />
複製代碼
init 方法傳入的 AppConfig 的 create:gradle
public static AppConfig create(Context context) {
// Create the camera factory for creating Camera2 camera objects
CameraFactory cameraFactory = new Camera2CameraFactory(context);
// Create the DeviceSurfaceManager for Camera2
CameraDeviceSurfaceManager surfaceManager = new Camera2DeviceSurfaceManager(context);
// Create default configuration factory
ExtendableUseCaseConfigFactory configFactory = new ExtendableUseCaseConfigFactory();
configFactory.installDefaultProvider(
ImageAnalysisConfig.class, new ImageAnalysisConfigProvider(cameraFactory, context));
configFactory.installDefaultProvider(
ImageCaptureConfig.class, new ImageCaptureConfigProvider(cameraFactory, context));
configFactory.installDefaultProvider(
VideoCaptureConfig.class, new VideoCaptureConfigProvider(cameraFactory, context));
configFactory.installDefaultProvider(
PreviewConfig.class, new PreviewConfigProvider(cameraFactory, context));
AppConfig.Builder appConfigBuilder =
new AppConfig.Builder()
.setCameraFactory(cameraFactory)
.setDeviceSurfaceManager(surfaceManager)
.setUseCaseConfigFactory(configFactory);
return appConfigBuilder.build();
}
複製代碼
經過 AppConfig.Builder 進行構建,CameraX中的默認屬性都在這裏初始化。後面具體講到某個 UseCase 的時候,詳細分析下具體的ConfigProvider
CameraX真正初始化方法:initInternal
private void initInternal(Context context, AppConfig appConfig) {
if (mInitialized.getAndSet(true)) {
return;
}
mContext = context.getApplicationContext();
mCameraFactory = appConfig.getCameraFactory(null);
if (mCameraFactory == null) {
throw new IllegalStateException(
"Invalid app configuration provided. Missing CameraFactory.");
}
mSurfaceManager = appConfig.getDeviceSurfaceManager(null);
if (mSurfaceManager == null) {
throw new IllegalStateException(
"Invalid app configuration provided. Missing CameraDeviceSurfaceManager.");
}
mDefaultConfigFactory = appConfig.getUseCaseConfigRepository(null);
if (mDefaultConfigFactory == null) {
throw new IllegalStateException(
"Invalid app configuration provided. Missing UseCaseConfigFactory.");
}
mCameraRepository.init(mCameraFactory);
}
複製代碼
直接從 AppConfig 中獲取到具體實例,mCameraFactory對應的實例是Camera2CameraFactory
,mCameraRepository.init(mCameraFactory)進行 Camera 相關的初始化
public void init(CameraFactory cameraFactory) {
synchronized (mCamerasLock) {
try {
Set<String> camerasList = cameraFactory.getAvailableCameraIds();
for (String id : camerasList) {
Log.d(TAG, "Added camera: " + id);
mCameras.put(id, cameraFactory.getCamera(id));
}
...
}
}
複製代碼
getAvailableCameraIds
獲取可用 Camera Id列表,Camera2CameraFactory的getCamera
真正初始化Camera
public BaseCamera getCamera(@NonNull String cameraId) {
Camera camera = new Camera(mCameraManager, cameraId,
mAvailabilityRegistry.getAvailableCameraCount(), sHandler);
mAvailabilityRegistry.registerCamera(camera);
return camera;
}
複製代碼
經過CameraAvailabilityRegistry
的registerCamera
方法進行Camera註冊
到此爲止,CameraX 相關屬性就初始化完成了
從第一個UseCase
預覽(preview)來說解CameraX 生命週期過程,以及數據傳輸流程。
前面一篇文章已經講解過 CameraX 的使用,其中預覽(preivew),會先聲明PreviewConfig
,經過 config 生成Preivew
,preview.setOnPreviewOutputUpdateListener
設置監聽Camera數據流。這一系列流程可以實現,主要經過CameraX.bindToLifecycle
實現
具體流程:
public static void bindToLifecycle(LifecycleOwner lifecycleOwner, UseCase... useCases) {
Threads.checkMainThread();
UseCaseGroupLifecycleController useCaseGroupLifecycleController =
INSTANCE.getOrCreateUseCaseGroup(lifecycleOwner);
UseCaseGroup useCaseGroupToBind = useCaseGroupLifecycleController.getUseCaseGroup();
Collection<UseCaseGroupLifecycleController> controllers =
INSTANCE.mUseCaseGroupRepository.getUseCaseGroups();
//檢查UseCase 只能在一個lifecycle上
for (UseCase useCase : useCases) {
for (UseCaseGroupLifecycleController controller : controllers) {
UseCaseGroup useCaseGroup = controller.getUseCaseGroup();
if (useCaseGroup.contains(useCase) && useCaseGroup != useCaseGroupToBind) {
throw new IllegalStateException(
String.format(
"Use case %s already bound to a different lifecycle.",
useCase));
}
}
}
//onBind監聽回調
for (UseCase useCase : useCases) {
useCase.onBind();
}
calculateSuggestedResolutions(lifecycleOwner, useCases);
for (UseCase useCase : useCases) {
useCaseGroupToBind.addUseCase(useCase);
for (String cameraId : useCase.getAttachedCameraIds()) {
attach(cameraId, useCase);
}
}
useCaseGroupLifecycleController.notifyState();
}
複製代碼
建立 UseCaseGroupLifecycleController,UseCaseGroup控制器,經過Lifecycle組件進行 start 和 stop 操做
UseCaseGroupLifecycleController useCaseGroupLifecycleController =
INSTANCE.getOrCreateUseCaseGroup(lifecycleOwner);
...
private UseCaseGroupLifecycleController getOrCreateUseCaseGroup(LifecycleOwner lifecycleOwner) {
return mUseCaseGroupRepository.getOrCreateUseCaseGroup(
lifecycleOwner, new UseCaseGroupRepository.UseCaseGroupSetup() {
@Override
public void setup(UseCaseGroup useCaseGroup) {
useCaseGroup.setListener(mCameraRepository);
}
});
}
複製代碼
經過UseCaseGroupRepository建立UseCaseGroupLifecycleController
UseCaseGroupLifecycleController getOrCreateUseCaseGroup( LifecycleOwner lifecycleOwner, UseCaseGroupSetup groupSetup) {
UseCaseGroupLifecycleController useCaseGroupLifecycleController;
synchronized (mUseCasesLock) {
//若是有緩存,則直接返回,不然進行建立
useCaseGroupLifecycleController = mLifecycleToUseCaseGroupControllerMap.get(
lifecycleOwner);
if (useCaseGroupLifecycleController == null) {
useCaseGroupLifecycleController = createUseCaseGroup(lifecycleOwner);
groupSetup.setup(useCaseGroupLifecycleController.getUseCaseGroup());
}
}
return useCaseGroupLifecycleController;
}
...
private UseCaseGroupLifecycleController createUseCaseGroup(LifecycleOwner lifecycleOwner) {
...
// Need to add observer before creating UseCaseGroupLifecycleController to make sure
// UseCaseGroups can be stopped before the latest active one is started.
lifecycleOwner.getLifecycle().addObserver(createLifecycleObserver());
UseCaseGroupLifecycleController useCaseGroupLifecycleController =
new UseCaseGroupLifecycleController(lifecycleOwner.getLifecycle());
//建立後,放入 map 緩存
synchronized (mUseCasesLock) {
mLifecycleToUseCaseGroupControllerMap.put(lifecycleOwner,
useCaseGroupLifecycleController);
}
return useCaseGroupLifecycleController;
}
複製代碼
建立UseCaseGroupLifecycleController,並增長Lifecycle生命週期控制:
UseCaseGroupLifecycleController(Lifecycle lifecycle) {
this(lifecycle, new UseCaseGroup());
}
/** Wraps an existing {@link UseCaseGroup} so it is controlled by lifecycle transitions. */
UseCaseGroupLifecycleController(Lifecycle lifecycle, UseCaseGroup useCaseGroup) {
this.mUseCaseGroup = useCaseGroup;
this.mLifecycle = lifecycle;
//綁定Lifecycle
lifecycle.addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart(LifecycleOwner lifecycleOwner) {
synchronized (mUseCaseGroupLock) {
mUseCaseGroup.start();
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop(LifecycleOwner lifecycleOwner) {
synchronized (mUseCaseGroupLock) {
mUseCaseGroup.stop();
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy(LifecycleOwner lifecycleOwner) {
synchronized (mUseCaseGroupLock) {
mUseCaseGroup.clear();
}
}
複製代碼
上面的代碼,增長了ON_START
,ON_STOP
,ON_DESTROY
的生命週期監聽
根據傳入的配置,生成各個UseCase的最佳解決方案。後面的代碼會以Preview
這個 UseCase 展開,其餘 UseCase 代碼邏輯相似。
private static void calculateSuggestedResolutions(LifecycleOwner lifecycleOwner, UseCase... useCases) {
// There will only one lifecycleOwner active. Therefore, only collect use cases belong to
// same lifecycleOwner and calculate the suggested resolutions.
...
// Collect new use cases for different camera devices
for (UseCase useCase : useCases) {
String cameraId = null;
try {
cameraId = getCameraWithCameraDeviceConfig(
(CameraDeviceConfig) useCase.getUseCaseConfig());
} catch (CameraInfoUnavailableException e) {
throw new IllegalArgumentException(
"Unable to get camera id for the camera device config.", e);
}
}
...
// Get suggested resolutions and update the use case session configuration
for (String cameraId : newCameraIdUseCaseMap.keySet()) {
Map<UseCase, Size> suggestResolutionsMap =
getSurfaceManager()
.getSuggestedResolutions(
cameraId,
originalCameraIdUseCaseMap.get(cameraId),
newCameraIdUseCaseMap.get(cameraId));
for (UseCase useCase : newCameraIdUseCaseMap.get(cameraId)) {
Size resolution = suggestResolutionsMap.get(useCase);
Map<String, Size> suggestedCameraSurfaceResolutionMap = new HashMap<>();
suggestedCameraSurfaceResolutionMap.put(cameraId, resolution);
//更新配置
useCase.updateSuggestedResolution(suggestedCameraSurfaceResolutionMap);
}
}
}
複製代碼
每一個 UseCase 會去更新對應配置 updateSuggestedResolution->onSuggestedResolutionUpdated
public void updateSuggestedResolution(Map<String, Size> suggestedResolutionMap) {
Map<String, Size> resolutionMap = onSuggestedResolutionUpdated(suggestedResolutionMap);
...
}
複製代碼
onSuggestedResolutionUpdated針對不一樣的 UseCase 有不一樣的實現,這裏以Preview
爲例
protected Map<String, Size> onSuggestedResolutionUpdated( Map<String, Size> suggestedResolutionMap) {
//獲取前面配置的 config
PreviewConfig config = (PreviewConfig) getUseCaseConfig();
String cameraId = getCameraIdUnchecked(config);
Size resolution = suggestedResolutionMap.get(cameraId);
...
//設置 config
updateConfigAndOutput(config, resolution);
return suggestedResolutionMap;
}
...
private void updateConfigAndOutput(PreviewConfig config, Size resolution) {
String cameraId = getCameraIdUnchecked(config);
//初始化pipeline
mSessionConfigBuilder = createPipeline(config, resolution);
attachToCamera(cameraId, mSessionConfigBuilder.build());
updateOutput(mSurfaceTextureHolder.getSurfaceTexture(), resolution);
}
複製代碼
建立Preview管道,經過 PreviewConfig 的配置,建立對應的顯示Surface和SessionConfig
SessionConfig.Builder createPipeline(PreviewConfig config, Size resolution) {
Threads.checkMainThread();
SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config);
final CaptureProcessor captureProcessor = config.getCaptureProcessor(null);
//擴展的 extensions實現
if (captureProcessor != null) {
CaptureStage captureStage = new CaptureStage.DefaultCaptureStage();
// TODO: To allow user to use an Executor for the processing.
...
} else {
final ImageInfoProcessor processor = config.getImageInfoProcessor(null);
if (processor != null) {
sessionConfigBuilder.addCameraCaptureCallback(new CameraCaptureCallback() {
@Override
public void onCaptureCompleted( @NonNull CameraCaptureResult cameraCaptureResult) {
super.onCaptureCompleted(cameraCaptureResult);
if (processor.process(
new CameraCaptureResultImageInfo(cameraCaptureResult))) {
notifyUpdated();
}
}
});
}
//默認的 Surface
CheckedSurfaceTexture checkedSurfaceTexture = new CheckedSurfaceTexture(resolution);
mSurfaceTextureHolder = checkedSurfaceTexture;
sessionConfigBuilder.addSurface(checkedSurfaceTexture);
}
...
}
複製代碼
這裏就能夠看到咱們熟悉的味道,在Camera2中 用到的Surface
,Session
相關配置,後面會用到相關配置。
在CheckedSurfaceTexture
中會建立FixedSizeSurfaceTexture
用來顯示圖像。
增長數據的監聽
void updateOutput(SurfaceTexture surfaceTexture, Size resolution) {
PreviewConfig useCaseConfig = (PreviewConfig) getUseCaseConfig();
...
PreviewOutput newOutput =
PreviewOutput.create(surfaceTexture, resolution, relativeRotation);
// Only update the output if something has changed
if (!Objects.equals(mLatestPreviewOutput, newOutput)) {
SurfaceTexture oldTexture =
(mLatestPreviewOutput == null)
? null
: mLatestPreviewOutput.getSurfaceTexture();
OnPreviewOutputUpdateListener outputListener = getOnPreviewOutputUpdateListener();
...
if (outputListener != null) {
mSurfaceDispatched = true;
updateListener(outputListener, newOutput);
}
}
}
複製代碼
根據Preview設置的setOnPreviewOutputUpdateListener,獲取到對應的Listener,經過updateListener
方法回調數據。
private void updateListener(OnPreviewOutputUpdateListener listener, PreviewOutput output) {
...
mOutputUpdateExecutor.execute(() -> listener.onUpdated(output));
...
}
複製代碼
調用UseCaseGroupLifecycleController的notifyState,激活 UseCase 狀態,在UseCaseGroupLifecycleController中有增長生命週期的監聽,在ON_START
狀態會調用mUseCaseGroup.start
方法。
void notifyState() {
synchronized (mUseCaseGroupLock) {
if (mLifecycle.getCurrentState().isAtLeast(State.STARTED)) {
mUseCaseGroup.start();
}
for (UseCase useCase : mUseCaseGroup.getUseCases()) {
useCase.notifyState();
}
}
}
複製代碼
void start() {
synchronized (mListenerLock) {
if (mListener != null) {
mListener.onGroupActive(this);
}
mIsActive = true;
}
}
複製代碼
啓動 start 狀態,調用CameraRepository
的onGroupActive
方法:
public void onGroupActive(UseCaseGroup useCaseGroup) {
synchronized (mCamerasLock) {
Map<String, Set<UseCase>> cameraIdToUseCaseMap = useCaseGroup.getCameraIdToUseCaseMap();
for (Map.Entry<String, Set<UseCase>> cameraUseCaseEntry :
cameraIdToUseCaseMap.entrySet()) {
BaseCamera camera = getCamera(cameraUseCaseEntry.getKey());
attachUseCasesToCamera(camera, cameraUseCaseEntry.getValue());
}
}
}
...
private void attachUseCasesToCamera(BaseCamera camera, Set<UseCase> useCases) {
camera.addOnlineUseCase(useCases);
}
複製代碼
camera.addOnlineUseCase
關聯UseCase 和 Camera。
public void addOnlineUseCase(@NonNull final Collection<UseCase> useCases) {
if (useCases.isEmpty()) {
return;
}
// Attaches the surfaces of use case to the Camera (prevent from surface abandon crash)
// addOnlineUseCase could be called with duplicate use case, so we need to filter out
// use cases that are either pending for addOnline or are already online.
// It's ok for two thread to run here, since it‘ll do nothing if use case is already
// pending.
synchronized (mPendingLock) {
for (UseCase useCase : useCases) {
boolean isOnline = isUseCaseOnline(useCase);
if (mPendingForAddOnline.contains(useCase) || isOnline) {
continue;
}
notifyAttachToUseCaseSurfaces(useCase);
mPendingForAddOnline.add(useCase);
}
}
...
updateCaptureSessionConfig();
resetCaptureSession(/*abortInFlightCaptures=*/false);
if (mState == InternalState.OPENED) {
openCaptureSession();
} else {
open();
}
updateCameraControlPreviewAspectRatio(useCases);
}
複製代碼
在addOnlineUseCase方法中,open會去打開Camera設備。
public void open() {
...
switch (mState) {
case INITIALIZED:
openCameraDevice();
break;
case CLOSING:
setState(InternalState.REOPENING);
// If session close has not yet completed, then the camera is still open. We
// can move directly back into an OPENED state.
// If session close is already complete, then the camera is closing. We'll reopen
// the camera in the camera state callback.
// If the camera device is currently in an error state, we need to close the
// camera before reopening, so we cannot directly reopen.
if (!isSessionCloseComplete() && mCameraDeviceError == ERROR_NONE) {
Preconditions.checkState(mCameraDevice != null,
"Camera Device should be open if session close is not complete");
setState(InternalState.OPENED);
openCaptureSession();
}
break;
default:
Log.d(TAG, "open() ignored due to being in state: " + mState);
}
}
...
void openCameraDevice() {
// Check that we have an available camera to open here before attempting
// to open the camera again.
if (!mCameraAvailability.isCameraAvailable()) {
Log.d(TAG, "No cameras available. Waiting for available camera before opening camera: "
+ mCameraId);
setState(InternalState.PENDING_OPEN);
return;
} else {
setState(InternalState.OPENING);
}
...
//真正打開相機
mCameraManager.openCamera(mCameraId, mExecutor, createDeviceStateCallback());
...
}
複製代碼
接下來就是Camera2的預覽流程
CameraX封裝了Camera2的標準預覽流程,這些類都是在 CameraX 庫中
openCameraDevice
的stateCallback
final class StateCallback extends CameraDevice.StateCallback {
@Override
public void onOpened(CameraDevice cameraDevice) {
Log.d(TAG, "CameraDevice.onOpened(): " + cameraDevice.getId());
mCameraDevice = cameraDevice;
mCameraDeviceError = ERROR_NONE;
switch (mState) {
case CLOSING:
case RELEASING:
// No session should have yet been opened, so close camera directly here.
Preconditions.checkState(isSessionCloseComplete());
mCameraDevice.close();
mCameraDevice = null;
break;
case OPENING:
case REOPENING:
setState(InternalState.OPENED);
openCaptureSession();
break;
default:
throw new IllegalStateException(
"onOpened() should not be possible from state: " + mState);
}
}
...
}
...
void openCaptureSession() {
...
mCaptureSession.open(validatingBuilder.build(), mCameraDevice);
...
}
複製代碼
建立CaptureSession
void open(SessionConfig sessionConfig, CameraDevice cameraDevice) throws CameraAccessException, DeferrableSurface.SurfaceClosedException {
synchronized (mStateLock) {
switch (mState) {
case UNINITIALIZED:
throw new IllegalStateException(
"open() should not be possible in state: " + mState);
case INITIALIZED:
//Camera中傳入的essionConfig,默認有TEMPLATE_PREVIEW的 surface 實現
List<DeferrableSurface> surfaces = sessionConfig.getSurfaces();
...
//狀態更新
notifySurfaceAttached();
mState = State.OPENING;
...
SessionConfigurationCompat sessionConfigCompat =
new SessionConfigurationCompat(
SessionConfigurationCompat.SESSION_REGULAR,
outputConfigList,
getExecutor(),
comboCallback);
CaptureRequest captureRequest =
Camera2CaptureRequestBuilder.buildWithoutTarget(
captureConfigBuilder.build(),
cameraDevice);
if (captureRequest != null) {
sessionConfigCompat.setSessionParameters(captureRequest);
}
//建立CaptureSession,CameraDeviceCompat根據 Android 版本有不一樣的實現
CameraDeviceCompat.createCaptureSession(cameraDevice, sessionConfigCompat);
...
}
}
}
複製代碼
在Camera2的使用中,CameraDevice的createCaptureSession能夠建立預覽畫面,CameraX的CaptureSession很好的封裝了這些實現。
在CaptureSession.open
傳入的SessionConfig,是在Camera2AppConfig.create
建立的時候生成
public static AppConfig create(Context context) {
// Create the camera factory for creating Camera2 camera objects
CameraFactory cameraFactory = new Camera2CameraFactory(context);
// PreviewConfig 配置
configFactory.installDefaultProvider(
PreviewConfig.class, new PreviewConfigProvider(cameraFactory, context));
AppConfig.Builder appConfigBuilder =
new AppConfig.Builder()
.setCameraFactory(cameraFactory)
.setDeviceSurfaceManager(surfaceManager)
.setUseCaseConfigFactory(configFactory);
return appConfigBuilder.build();
}
//PreviewConfigProvider配置getConfig
@Override
public PreviewConfig getConfig(LensFacing lensFacing) {
PreviewConfig.Builder builder =
PreviewConfig.Builder.fromConfig(Preview.DEFAULT_CONFIG.getConfig(lensFacing));
// SessionConfig containing all intrinsic properties needed for Preview
SessionConfig.Builder sessionBuilder = new SessionConfig.Builder();
// createCaptureSession中的 preview 設置
sessionBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
// Add options to UseCaseConfig
builder.setDefaultSessionConfig(sessionBuilder.build());
builder.setSessionOptionUnpacker(Camera2SessionOptionUnpacker.INSTANCE);
CaptureConfig.Builder captureBuilder = new CaptureConfig.Builder();
captureBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
builder.setDefaultCaptureConfig(captureBuilder.build());
builder.setCaptureOptionUnpacker(Camera2CaptureOptionUnpacker.INSTANCE);
...
}
複製代碼
CameraDeviceCompat.createCaptureSession
的CameraCaptureSession回調
final class StateCallback extends CameraCaptureSession.StateCallback {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
synchronized (mStateLock) {
switch (mState) {
case UNINITIALIZED:
case INITIALIZED:
case OPENED:
case RELEASED:
throw new IllegalStateException(
"onConfigured() should not be possible in state: " + mState);
case OPENING:
...
// Issue capture request of enableSession if exists.
if (mSessionConfig != null) {
Config implOptions = mSessionConfig.getImplementationOptions();
CameraEventCallbacks eventCallbacks = new Camera2Config(
implOptions).getCameraEventCallback(
CameraEventCallbacks.createEmptyCallback());
//可配置CameraEventCallback的EnableSession回調
List<CaptureConfig> list =
eventCallbacks.createComboCallback().onEnableSession();
if (!list.isEmpty()) {
issueCaptureRequests(setupConfiguredSurface(list));
}
}
//
issueRepeatingCaptureRequests();
issueBurstCaptureRequest();
break;
...
}
}
}
}
複製代碼
開啓Camera預覽
void issueRepeatingCaptureRequests() {
...
CaptureConfig captureConfig = mSessionConfig.getRepeatingCaptureConfig();
...
// The override priority for implementation options
// P1 CameraEventCallback onRepeating options
// P2 SessionConfig options
CaptureConfig.Builder captureConfigBuilder = CaptureConfig.Builder.from(captureConfig);
//建立CaptureRequest
CaptureRequest captureRequest = Camera2CaptureRequestBuilder.build(
captureConfigBuilder.build(), mCameraCaptureSession.getDevice(),
mConfiguredSurfaceMap);
if (captureRequest == null) {
Log.d(TAG, "Skipping issuing empty request for session.");
return;
}
//設置Capture回調
CameraCaptureSession.CaptureCallback comboCaptureCallback =
createCamera2CaptureCallback(
captureConfig.getCameraCaptureCallbacks(),
mCaptureCallback);
CameraCaptureSessionCompat.setSingleRepeatingRequest(mCameraCaptureSession,
captureRequest, mExecutor, comboCaptureCallback);
}
複製代碼
CameraCaptureSessionCompat.setSingleRepeatingRequest 也是區分 Android 版本
private static CameraCaptureSessionCompatImpl chooseImplementation() {
if (Build.VERSION.SDK_INT >= 28) {
return new CameraCaptureSessionCompatApi28Impl();
}
return new CameraCaptureSessionCompatBaseImpl();
}
//CameraCaptureSessionCompatBaseImpl,和平時使用的同樣
public int setSingleRepeatingRequest(@NonNull CameraCaptureSession captureSession, @NonNull CaptureRequest request, @NonNull Executor executor, @NonNull CameraCaptureSession.CaptureCallback listener) throws CameraAccessException {
Preconditions.checkNotNull(captureSession);
// Wrap the executor in the callback
CameraCaptureSession.CaptureCallback cb =
new CameraCaptureSessionCompat.CaptureCallbackExecutorWrapper(executor, listener);
return captureSession.setRepeatingRequest(
request, cb, MainThreadAsyncHandler.getInstance());
}
//CameraCaptureSessionCompatApi28Impl,新版本 API 有些變化
public int setSingleRepeatingRequest(@NonNull CameraCaptureSession captureSession, @NonNull CaptureRequest request, @NonNull Executor executor, @NonNull CameraCaptureSession.CaptureCallback listener) throws CameraAccessException {
Preconditions.checkNotNull(captureSession);
// Call through directly to executor API
return captureSession.setSingleRepeatingRequest(request, executor, listener);
}
複製代碼
從Camera的開啓到預覽,以及讀取各類配置,整個過程到此就完成了,接下來介紹如何拍照,這個流程相對來講比較簡單
拍照的流程:
建立 ImageCaptureRequest,設置cameraId、targetRatio、回調等
private void sendImageCaptureRequest( @Nullable Executor listenerExecutor, OnImageCapturedListener listener) {
String cameraId = getCameraIdUnchecked(mConfig);
// Get the relative rotation or default to 0 if the camera info is unavailable
int relativeRotation = 0;
try {
CameraInfoInternal cameraInfoInternal = CameraX.getCameraInfo(cameraId);
relativeRotation =
cameraInfoInternal.getSensorRotationDegrees(
mConfig.getTargetRotation(Surface.ROTATION_0));
} catch (CameraInfoUnavailableException e) {
Log.e(TAG, "Unable to retrieve camera sensor orientation.", e);
}
Rational targetRatio = mConfig.getTargetAspectRatioCustom(null);
targetRatio = ImageUtil.rotate(targetRatio, relativeRotation);
mImageCaptureRequests.offer(
new ImageCaptureRequest(relativeRotation, targetRatio, listenerExecutor, listener));
if (mImageCaptureRequests.size() == 1) {
issueImageCaptureRequests();
}
}
複製代碼
void issueImageCaptureRequests() {
if (mImageCaptureRequests.isEmpty()) {
return;
}
takePictureInternal();
}
...
//拍照流程
private void takePictureInternal() {
//自定義 Future 調用鏈
FutureChain.from(preTakePicture(state))
.transformAsync{
...
return ImageCapture.this.issueTakePicture(state);
})
.transformAsync{
...
return ImageCapture.this.postTakePicture(state);
})
.addCallback(
...
onTakePictureFinish(null);
)
}
複製代碼
自定義了整個拍照工做流,經過issueTakePicture
進行拍照,postTakePicture
是拍照成功,釋放資源,取消3A。下面重點看下issueTakePicture
流程
ListenableFuture<Void> issueTakePicture(TakePictureState state) {
...
getCurrentCameraControl().submitCaptureRequests(captureConfigs);
...
}
複製代碼
經過CameraControl
提交Capture 請求,CameraControl
具體實現是Camera2CameraControl
。
public void submitCaptureRequests(@NonNull final List<CaptureConfig> captureConfigs) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
submitCaptureRequestsInternal(captureConfigs);
}
});
}
...
void submitCaptureRequestsInternal(final List<CaptureConfig> captureConfigs) {
mControlUpdateListener.onCameraControlCaptureRequests(captureConfigs);
//mControlUpdateListener是Camera 的回調,onCameraControlCaptureRequests 真正實如今 Camera 中
}
//Camera.java
public void onCameraControlUpdateSessionConfig(@NonNull SessionConfig sessionConfig) {
mCameraControlSessionConfig = sessionConfig;
updateCaptureSessionConfig();
}
...
private void updateCaptureSessionConfig() {
...
SessionConfig sessionConfig = validatingBuilder.build();
mCaptureSession.setSessionConfig(sessionConfig);
...
}
}
Camera 獲取 Capture的SessionConfig,經過`CaptureSession`進行狀態控制
複製代碼
void setSessionConfig(SessionConfig sessionConfig) {
synchronized (mStateLock) {
switch (mState) {
case UNINITIALIZED:
throw new IllegalStateException(
"setSessionConfig() should not be possible in state: " + mState);
case INITIALIZED:
case OPENING:
mSessionConfig = sessionConfig;
break;
case OPENED:
mSessionConfig = sessionConfig;
if (!mConfiguredSurfaceMap.keySet().containsAll(sessionConfig.getSurfaces())) {
Log.e(TAG, "Does not have the proper configured lists");
return;
}
Log.d(TAG, "Attempting to submit CaptureRequest after setting");
issueRepeatingCaptureRequests();
break;
case CLOSED:
case RELEASING:
case RELEASED:
throw new IllegalStateException(
"Session configuration cannot be set on a closed/released session.");
}
}
}
複製代碼
在Camera的OPENED狀態,則進行拍照流程
void issueRepeatingCaptureRequests() {
...
CameraCaptureSession.CaptureCallback comboCaptureCallback =
createCamera2CaptureCallback(
captureConfig.getCameraCaptureCallbacks(),
mCaptureCallback);
CameraCaptureSessionCompat.setSingleRepeatingRequest(mCameraCaptureSession,
captureRequest, mExecutor, comboCaptureCallback);
...
}
複製代碼
CameraCaptureSessionCompat
根據 Android 版本有CameraCaptureSessionCompatBaseImpl
和CameraCaptureSessionCompatApi28Impl
兩種實現,最終經過CameraCaptureSession
實現真正的拍照。
拍照完成後,經過最開始設置的 Listener 進行回調
在 Preview 那小節,講解過 bindToLifecycle 流程,這裏的ImageCapture
也是一個UseCase
。在CameraX中的calculateSuggestedResolutions
方法,最終會調用到各個UseCase
的onSuggestedResolutionUpdated
方法。在ImageCapture
的onSuggestedResolutionUpdated
方法,經過createPipeline
建立了拍照數據的回調
SessionConfig.Builder createPipeline(ImageCaptureConfig config, Size resolution) {
...
//和 Camera2的流程一致
mProcessingImageResultThread = new HandlerThread("OnImageAvailableHandlerThread");
mProcessingImageResultThread.start();
mProcessingImageResultHandler = new Handler(mProcessingImageResultThread.getLooper());
...
mImageReader.setOnImageAvailableListener(
new ImageReaderProxy.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReaderProxy imageReader) {
ImageProxy image = null;
try {
image = imageReader.acquireLatestImage();
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to acquire latest image.", e);
} finally {
if (image != null) {
// Call the head request listener to process the captured image.
ImageCaptureRequest imageCaptureRequest;
if ((imageCaptureRequest = mImageCaptureRequests.peek()) != null) {
SingleCloseImageProxy wrappedImage = new SingleCloseImageProxy(
image);
wrappedImage.addOnImageCloseListener(mOnImageCloseListener);
//ImageCaptureRequest設置 Listener
imageCaptureRequest.dispatchImage(wrappedImage);
} else {
// Discard the image if we have no requests.
image.close();
}
}
}
}
},
mProcessingImageResultHandler);
...
}
複製代碼
ImageReader設置了 Camera 數據會調用,並經過ImageCaptureRequest
的dispatchImage
方法進行分發
void dispatchImage(final ImageProxy image) {
try {
mListenerExecutor.execute(new Runnable() {
@Override
public void run() {
Size sourceSize = new Size(image.getWidth(), image.getHeight());
if (ImageUtil.isAspectRatioValid(sourceSize, mTargetRatio)) {
image.setCropRect(
ImageUtil.computeCropRectFromAspectRatio(sourceSize,
mTargetRatio));
}
//真正的回調
mListener.onCaptureSuccess(image, mRotationDegrees);
}
});
} catch (RejectedExecutionException e) {
Log.e(TAG, "Unable to post to the supplied executor.");
// Unable to execute on the supplied executor, close the image.
image.close();
}
}
複製代碼
mListener
是一個封裝Listener,在ImageCapture
中實現
Listener的關係圖:
+-----------------------+
| |
|ImageCapture. |
|OnImageCapturedListener|
| |
+-----------+-----------+
|
|
+-----------v-----------+ +----------------------+
| | | |
| ImageSaver. | | ImageCapture. |
| OnImageSavedListener +------> OnImageSavedListener |
| | | |
+-----------------------+ +----------------------+
複製代碼
OnImageCapturedListener
的實現,其中經過ImageSaver
設置的OnImageSavedListener
回調到最上層的OnImageSavedListener
OnImageCapturedListener imageCaptureCallbackWrapper =
new OnImageCapturedListener() {
@Override
public void onCaptureSuccess(ImageProxy image, int rotationDegrees) {
CameraXExecutors.ioExecutor()
.execute(
new ImageSaver(
image,
saveLocation,
rotationDegrees,
metadata.isReversedHorizontal,
metadata.isReversedVertical,
metadata.location,
executor,
imageSavedListenerWrapper));
}
...
};
// ImageSaver是一個 Runnable,主要 run 的實現
final class ImageSaver implements Runnable {
...
@Override
public void run() {
...
//圖像處理
...
if (saveError != null) {
postError(saveError, errorMessage, exception);
} else {
postSuccess();
}
}
...
private void postSuccess() {
mExecutor.execute(new Runnable() {
@Override
public void run() {
//最外層回調
mListener.onImageSaved(mFile);
}
});
}
}
複製代碼
整個拍照流程和數據回調就講解完畢了。
經過對 CameraX的 Preview 和 ImageCapture的分析,CameraX對Camera2進行完整的封裝,統一參數配置,自動計算Resolution,簡化Camera2的開發,並增長了生命週期控制,對外只暴露了簡單接口。
使用該庫,只須要簡單的幾行代碼就能夠實現之前Camera2複雜的操做。