今年一进公司就开始搞相机开发,一直是在搞相机应用层,不过自己的学习心一直没停,闲暇之余就研究一下相机的framework、HAL层的东西,平时工作中碰到的和自己接触到的,相机中最复杂的就是预览了,有了一些体会也不想放下,所以决定写一系列关于Android相机的博客,把自己学习到的东西记录下来。
说起Android相机的东西,从应用层的角度来看,基本就是四个重要的节点了:openCamera、createCaptureSession、preview、capture,最复杂的就是preview了,要理解preview,那么就要求大家对Android的View显示系统有一定的理解,才能更好的理解相机的预览。相机的预览其实就是使用预览区的SurfaceView对应的surface创建一条预览流,然后framework从预览surface当中获取到显示buffer,这里用于显示的buffer会根据数量来获取,华为手机的相机framework+HAL两部分一般总共需要7个buffer,每个buffer都对应预览区的一屏的大小,它就是HAL、算法各层填充完毕后,要交给SurfaceFlinger用于显示的预览区大小的所有像素点的byte数组,这7个buffer每次在CameraServer进程获取一个,然后通过HIDL下发给CameraDaemon进程,交给算法、HAL层进行着色渲染,完成后再通过CameraServer进程交给SurfaceFlinger,最后显示在屏幕上,这样不断的轮转,我们就看到了预览区会不断的变动,这里的buffer轮转也就是相机最核心的部分了。我们后期的博客具体在讲关于buffer轮转的知识。
这节我们先来说说从应用层调用openCamera之后的执行逻辑。openCamera的方法实现是在frameworks\base\core\java\android\hardware\camera2\CameraManager.java类中完成的,CameraManager和我们应用中经常使用的WindowManager、PackageManager一样,都可以通过context上下文获取到,它也是在frameworks\base\core\java\android\app\SystemServiceRegistry.java类中通过Context.CAMERA_SERVICE键值注册的,源码如下:
我们拿到CameraManager类的对象之后,就可以调用它的openCamera方法来打开相机了,CameraManager类的openCamera方法的源码如下:
@RequiresPermission(android.Manifest.permission.CAMERA)
public void openCamera(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
}
这个方法的实现很简单,就是调用openCameraForUid来进一步处理,我们先来看看调用该方法需要传递的参数,第一个表示要打开的目标Camera的id,华为手机上该值一般有两个:0和1,0表示后摄,当然也是主摄,1表示前摄,我们怎么知道该值的取值呢?可以通过调用CameraManager类的getCameraIdList()方法来获取,该方法会将当前已经注册成功的camera硬件对应的id列表返回给我们应用层,硬件注册都是驱动层的东西了,那一步离我们现在的阶段还很远。我们再来看一下第二个参数CameraDevice.StateCallback
,它是定义在
frameworks\base\core\java\android\hardware\camera2\CameraDevice.java类中的一个内部类,StateCallback类的定义源码如下:
public static abstract class StateCallback {
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device is in use already.
*
* <p>
* This error can be produced when opening the camera fails due to the camera
* being used by a higher-priority camera API client.
* </p>
*
* @see #onError
*/
public static final int ERROR_CAMERA_IN_USE = 1;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device could not be opened
* because there are too many other open camera devices.
*
* <p>
* The system-wide limit for number of open cameras has been reached,
* and more camera devices cannot be opened until previous instances are
* closed.
* </p>
*
* <p>
* This error can be produced when opening the camera fails.
* </p>
*
* @see #onError
*/
public static final int ERROR_MAX_CAMERAS_IN_USE = 2;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device could not be opened due to a device
* policy.
*
* @see android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean)
* @see #onError
*/
public static final int ERROR_CAMERA_DISABLED = 3;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device has encountered a fatal error.
*
* <p>The camera device needs to be re-opened to be used again.</p>
*
* @see #onError
*/
public static final int ERROR_CAMERA_DEVICE = 4;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera service has encountered a fatal error.
*
* <p>The Android device may need to be shut down and restarted to restore
* camera function, or there may be a persistent hardware problem.</p>
*
* <p>An attempt at recovery <i>may</i> be possible by closing the
* CameraDevice and the CameraManager, and trying to acquire all resources
* again from scratch.</p>
*
* @see #onError
*/
public static final int ERROR_CAMERA_SERVICE = 5;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ERROR_"}, value =
{ERROR_CAMERA_IN_USE,
ERROR_MAX_CAMERAS_IN_USE,
ERROR_CAMERA_DISABLED,
ERROR_CAMERA_DEVICE,
ERROR_CAMERA_SERVICE })
public @interface ErrorCode {};
/**
* The method called when a camera device has finished opening.
*
* <p>At this point, the camera device is ready to use, and
* {@link CameraDevice#createCaptureSession} can be called to set up the first capture
* session.</p>
*
* @param camera the camera device that has become opened
*/
public abstract void onOpened(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has been closed with
* {@link CameraDevice#close}.
*
* <p>Any attempt to call methods on this CameraDevice in the
* future will throw a {@link IllegalStateException}.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
* @param camera the camera device that has become closed
*/
public void onClosed(@NonNull CameraDevice camera) {
// Default empty implementation
}
/**
* The method called when a camera device is no longer available for
* use.
*
* <p>This callback may be called instead of {@link #onOpened}
* if opening the camera fails.</p>
*
* <p>Any attempt to call methods on this CameraDevice will throw a
* {@link CameraAccessException}. The disconnection could be due to a
* change in security policy or permissions; the physical disconnection
* of a removable camera device; or the camera being needed for a
* higher-priority camera API client.</p>
*
* <p>There may still be capture callbacks that are invoked
* after this method is called, or new image buffers that are delivered
* to active outputs.</p>
*
* <p>The default implementation logs a notice to the system log
* about the disconnection.</p>
*
* <p>You should clean up the camera with {@link CameraDevice#close} after
* this happens, as it is not recoverable until the camera can be opened
* again. For most use cases, this will be when the camera again becomes
* {@link CameraManager.AvailabilityCallback#onCameraAvailable available}.
* </p>
*
* @param camera the device that has been disconnected
*/
public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has encountered a serious error.
*
* <p>This callback may be called instead of {@link #onOpened}
* if opening the camera fails.</p>
*
* <p>This indicates a failure of the camera device or camera service in
* some way. Any attempt to call methods on this CameraDevice in the
* future will throw a {@link CameraAccessException} with the
* {@link CameraAccessException#CAMERA_ERROR CAMERA_ERROR} reason.
* </p>
*
* <p>There may still be capture completion or camera stream callbacks
* that will be called after this error is received.</p>
*
* <p>You should clean up the camera with {@link CameraDevice#close} after
* this happens. Further attempts at recovery are error-code specific.</p>
*
* @param camera The device reporting the error
* @param error The error code.
*
* @see #ERROR_CAMERA_IN_USE
* @see #ERROR_MAX_CAMERAS_IN_USE
* @see #ERROR_CAMERA_DISABLED
* @see #ERROR_CAMERA_DEVICE
* @see #ERROR_CAMERA_SERVICE
*/
public abstract void onError(@NonNull CameraDevice camera,
@ErrorCode int error); // Must implement
}
我们从这个类所定义的方法就能够非常清楚的看到,它这几个回调的意图了:onOpened就是成功打开camera之后的回调,而且它会返回一个CameraDevice camera对象给我们应用层,基本上操作相机所有重要的工作都是由它来中转实现的,所以应用层拿到这个对象之后,就可以使用它作很多其他的工作了,在接下来的分析过程中,我们也会看到,这个对象在framework中是怎么构建好,然后又是怎么回传给我们应用层的;onClosed方法就是当相机关闭时的回调了;onDisconnected方法就是相机断开连接时的回调;onError方法就是相机出错时的回调了。
我们再来看看最后一个参数Handler handler,为什么要传一个Handler进来呢?它的目的就是为了保证线程不切换,假如我们在应用层在工作线程B中执行openCamera的方法,同时将线程B对应的Handler对象传进来,那么打开成功之后,framework为了保证线程同步,也会使用该handler对象,将消息发送到线程B的Looper循环上,这就是传入一个Handler对象的原因了,我们在后面的分析过程中会看到它的使用。
好,参数分析完了,我们继续来看一下openCameraForUid方法的实现,源码如下:
public void openCameraForUid(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
int clientUid)
throws CameraAccessException {
if (cameraId == null) {
throw new IllegalArgumentException("cameraId was null");
} else if (callback == null) {
throw new Illega