1. 概述
为了更好的为学习Camera HAL做铺垫,首先从应用Camera2作为切入点,了解应用启动Camera,拍照,录像的流程,从而更好的学习整条通路。对比以往的笔记,省略大篇幅的代码的粘贴,将关键步骤保留以流程图展示,保证清晰的学习思路。所有学习代码均截取自https://cs.android.com/.
本文涉及的内容在Framework层以上,包括如下目录:
1
2
3
4
5
6
7
8
android/packages/apps/Camera2 #应用目录
android/frameworks/ex/camera2 # Camera2 API
android/frameworks/base/core/java/android/hardware/camera2 # Camera涉及的Framework代码
- frameworks/base/core/java/android/hardware/camera2/CameraManager.java
- frameworks/base/core/java/android/hardware/camera2/CameraDevice.java
- frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
- frameworks/base/core/java/android/hardware/camera2/CameraCaptureSession.java
- frameworks/base/core/java/android/hardware/camera2/CameraCaptureSession.java
Framework中涉及重要类包括如下:
1
2
3
4
- CameraManager: 管理手机上的所有摄像头设备,它的作用主要是获取摄像头列表和打开指定的摄像头
- CameraDevice: 具体的摄像头设备,它有一系列参数(预览尺寸、拍照尺寸等),可以通过CameraManager的getCameraCharacteristics()方法获取。它的作用主要是创建CameraCaptureSession和CaptureRequest
- CameraCaptureSession: 相机捕获会话,用于处理拍照和预览的工作(很重要)
- CaptureRequest: 捕获请求,定义输出缓冲区以及显示界面(TextureView或SurfaceView)等
Camera2中的Activity包括如下:
1
2
3
4
5
1. com.android.camera.CameraActivity(应用启动Activity)
2. com.android.camera.PermissionsActivity(获取应用权限)
3. com.android.camera.CaptureActivity(空实现,用于接收IMAGE_CAPTURE,VIDEO_CAMERA,VIDEO_CAPTURE)
4. com.android.camera.SecureCameraActivity(空实现,用于接收STILL_IMAGE_CAMERA_SECURE,IMAGE_CAPTURE_SECURE)
5. com.android.camera.settings.CameraSettingsActivity(设置Activity)
其中CaptureActivity和SecureCameraActivity都为空实现,只是在AndroidManifest.xml中接收intents,是为了不影响CameraActivity的工作。
如果需要在其他应用中调用相机,只需要实现如下逻辑:
1
2
3
4
5
6
7
8
9
10
/*
新建MediaStore.ACTION_IMAGE_CAPTURE的intent,MediaStore.ACTION_IMAGE_CAPTURE是标准的
获取图片并返回结果的Intent。
*/
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//应用需要将具体图片路径转化为URI,并作为参数传入intent
fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
//启动能够处理该类型intent的应用功能,即调用Camera2。应用在onActivityResult中处理返回结果
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
2. Camera2初始化
Camera2启动时,对应的Activity为CameraActivity,包括对照相机的打开,参数设置,预览等,都需要在该Activit中实现,现对其进行分析。
2.1 CameraActivity简述
1.CameraActivity继承了QuickActivity,该类实现了基本的Activity操作,子类只需要实现如下方法即可(CaptureActivity同样继承了QuickActivity):
- onCreateTasks
- onStartTasks
- onResumeTasks
- onPauseTasks
- onStopTasks
- onDestroyTasks
2.CameraActivity实现回调类CameraAgent.CameraOpenCallback,该方法定义了以下回调方法,从而能够通知到上层相机的连接状态:
- public void onCameraOpened(CameraProxy camera);
- public void onCameraDisabled(int cameraId);
- public void onDeviceOpenFailure(int cameraId, String info);
- public void onDeviceOpenedAlready(int cameraId, String info);
- public void onReconnectionFailure(CameraAgent mgr, String info);
2.2 Profiler分析器
Camera2的应用采用了Profile进行日志打印,以CameraActivity方法为例,在onCreateTasks的使用流程如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
初始化Profiler,Profilers内嵌了多个LogginProfiler和GuardingProfiler,前者用于区分不同打印等级,可以进行日志打印。
后者可以限定时间,在该限定时间内的打印不进行打印,超过的则将其日志打印出来。
*/
private final Profiler mProfiler = Profilers.instance().guard();
...
@Override
public void onCreateTasks(Bundle state) {
Profile profile = mProfiler.create("CameraActivity.onCreateTasks").start();
...//初始化
profile.mark();//记录上次时间
....//另一部分初始化
profile.mark("Glide.setup");
.... //另一部分初始化
profile.stop();
}
其优势是可以统计启动过程的时间耗费,start()时开始计时,调用mark时将会计算由start()到mark()阶段使用的时间,并从mark开始新的计时开端,直到下一个mark或者stop。以下为其中的日志打印实例:
1
2
3
4
CameraActivity.onCreateTasks - START
CameraActivity.onCreateTasks - [0.048ms] Glide.setup
.....
CameraActivity.onCreateTasks - STOP
通过该分析器,能够对比出应用启动流程,在性能优化时可以作为重要参考。
2.3 CameraActivity启动流程
2.3.1 CamerActivity.onCreateTasks
CamerActivity的onCreateTasks流程图如下所示:
1.CameraActivity启动时调用父类QuickActivity的onCreate,随后进入CameraActivity的onCreateTasks方法。
1
2
3
4
5
6
7
8
9
# packages/apps/Camera2/src/com/android/camera/util/QuickActivity.java
@Override
protected final void onCreate(Bundle bundle) {
...
super.onCreate(bundle);
mMainHandler = new Handler(getMainLooper());
onCreateTasks(bundle);
...
}
2.通过OneCameraModule分别创建了两个对象,mOneCameraOpener以及mOneCameraManager。前者用于打开Camera设备,后者为管理硬件Camera的Manager。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# packages/apps/Camera2/src/com/android/camera/CameraActivity.java
...
try {
mOneCameraOpener = OneCameraModule.provideOneCameraOpener(
mFeatureConfig,
mAppContext,
mActiveCameraDeviceTracker,
ResolutionUtil.getDisplayMetrics(this));
mOneCameraManager = OneCameraModule.provideOneCameraManager();
} catch (OneCameraException e) {
...
mFatalErrorHandler.onGenericCameraAccessFailure();
}
...
3.新建了CameraController,可用于获取相机参数(getCharacteristics()),关闭相机(closeCamera()),获取相机个数(getNumberOfCameras()),判断是前置摄像头还是后置摄像头(isFrontFacingCamera()/isBackFacingCamera()),获取第一个前置摄像头/后置摄像头的ID(getFirstFrontCameraId()/getFirstBackCameraId())等等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# packages/apps/Camera2/src/com/android/camera/CameraActivity.java
...
try {
mCameraController = new CameraController(mAppContext, this, mMainHandler,
CameraAgentFactory.getAndroidCameraAgent(mAppContext,
CameraAgentFactory.CameraApi.API_1),
CameraAgentFactory.getAndroidCameraAgent(mAppContext,
CameraAgentFactory.CameraApi.AUTO),
mActiveCameraDeviceTracker);
mCameraController.setCameraExceptionHandler(
new CameraExceptionHandler(mCameraExceptionCallback, mMainHandler));
} catch (AssertionError e) {
mFatalErrorHandler.onGenericCameraAccessFailure();
}
...
当然CameraController能够获到这些相机的参数属性,大部分都离不开CameraManager的协助。通过传入CameraAgentFactor工厂类,最终获取到CameraManager。为了看清CameraController是怎么调用其中的getCharacteristics接口,可以参考如下流程图:
4.新建了ModuleManagerImpl,并作为参数传入ModulesInfo.setupModules中。
1
2
3
4
5
# packages/apps/Camera2/src/com/android/camera/CameraActivity.java
...
mModuleManager = new ModuleManagerImpl();
ModulesInfo.setupModules(mAppContext, mModuleManager, mFeatureConfig);
...
ModuleInfo的setupModules会注册多种Module,包括CaptureModule,PhotoModule,VideoModule等等。这是为下一步Module的初始化做准备。
5.设置当前Module并初始化。期间CamerAppUI准备初始化工作。
1
2
3
4
5
6
# packages/apps/Camera2/src/com/android/camera/CameraActivity.java
...
setModuleFromModeIndex(getModeIndex());
mCameraAppUI.prepareModuleUI();
mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
...
setModuleFromModeIndex会根据getModeIndex的值,获取对应的ModuleAgent。紧接着通过ModuleAgent调用getModuleId获取mCurrentModeIndex。最后通过ModuleAgent的createModule创建对应Module对。以拍照为例,即新建了CaptureModule.紧接着,调用CaptureModule的init方法进行初始化。后续的拍照按键的点击CaptureModule关系紧密。
此外,mCameraAppUI调用prepareModuoleUI,初始化应用控件,其中如下的mTextureView将用于相机的预览。
1
mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
2.3.2 Camera设备open流程
当CaptureModule初始化时,会初始化一个PreviewStatusListener类型的listener,用于监听TextureView是否准备完毕。该TextureView上述提到的mCameraAppUI中的preview_content
控件,专门用于预览。
1
2
3
4
5
6
7
8
9
10
11
12
13
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/camera_app_root"
android:background="@android:color/black"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextureView
android:id="@+id/preview_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
...
/>
当SurfaceTexture已准备好时,CaptureModule调用reopenCamera,并最终触发打开Camera设备。当打开完成后,回调onCameraOpened被调用,上层应用此时可以进行预览动作。
2.4 拍照流程
当设备打开完毕后,CaptureModule的回调方法onCameraOpened被调用,会在回调中调用startPreview,表明可以进行预览操作了,如下:
1
2
3
4
5
6
7
8
9
10
11
12
# packages/apps/Camera2/src/com/android/camera/CaptureModule.java
camera.startPreview(new Surface(getPreviewSurfaceTexture()),
new CaptureReadyCallback() {
@Override
public void onSetupFailed() {
...
}
@Override
public void onReadyForCapture() {
...
}
});
经过重重调用,最终会在OneCameraImpl中创建一个CaptureSession,后续正是依靠这个CaptureSession才完成拍照。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# packages/apps/Camera2/src/com/android/camera/CaptureModule.java
private void setup(Surface previewSurface, final CaptureReadyCallback listener) {
...
//创建CapturSession,成功后,通过onConfigured获取session。该session是拍照的关键
mDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigureFailed(CameraCaptureSession session) {
listener.onSetupFailed();
}
@Override
public void onConfigured(CameraCaptureSession session) {
mCaptureSession = session;
....
}
@Override
public void onClosed(CameraCaptureSession session) {
super.onClosed(session);
}
}, mCameraHandler);
...
}
之后,通过点击应用的拍照按键表明拍照动作的开始,其流程图如下:
1.点击按键,触发CameraActivity的onKeyDown方法,直调用到CapturModule的onKeyDown方法。 2.CaptureModule的onKeyDown方法调用onShutterButtonClick,从而重复啊一连串拍照流程takePicture。 3.利用之前提到的CameraCaptureSession,完成capture动作 4.拍照完成后,回调方法onCaptureCompleted调用。
至此,简单的分析了下Camera2的初始化和拍照流程,后续将会从Framework的角度进行分析。