系列文章
- [Android P] CameraAPI1 转 HAL3 预览流程(一) — 背景概述
- [Android P] CameraAPI1 转 HAL3 预览流程(二) — startPreview
- [Android P] CameraAPI1 转 HAL3 预览流程(三) — setPreviewCallbackFlag
- [Android P] CameraAPI1 转 HAL3 预览流程(四) — Preview Data
总览
注意一下,虽然题目主要写的是 setPreviewCallbackFlag
,但实际上这对于 Camera 应用,它的动作是调用 API1 的 setPreviewCallbackWithBuffer
和 addCallbackBuffer
,这样会把 Preview Callback 的 Buffer 模式设置为手动模式(由 APP 主动带 Callback Buffer 下来,底层数据回来后会被 Copy 到这块 APP Buffer 中)。
本篇文章对这一部分进行的时序分析,有几个前提条件:
- APP 先调用了
startPreview
; - 紧随其后的是调用
addCallbackBuffer
带下 Buffer; - 然后调用了
setPreviewCallbackWithBuffer
,该接口设置 manualMode 为 true,标志 APP 自主带下 Callback Buffer; - 第三点会触发第一次
setPreviewCallbackFlag(0x05)
。
需要注意的几个点:
- 由于 Callback 是单独的一路 stream,所以这里相当于是要打开第二路 stream,会触发 re-configure 机制;
- configureStream 的逻辑是在 CallbackProcessor 中
updateStream
期间触发的,注意这里的时机区别于startPreview
的startStream
。
主要涉及到的类及其对应的代码地址分别是:
- Camera-JNI:
/frameworks/base/core/jni/android_hardware_Camera.cpp
- Camera2Client:
/frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp
- StreamingProcessor:
/frameworks/av/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
- CallbackProcessor:
/frameworks/av/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
- Camera3Device:
/frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
接下来我会照着上面的时序图,结合具体代码进行更深入一些的解释。
代码分析
根据时序图来看,可以分成三个部分来看:
- Camera2Client 中的
setPreviewCallbackFlag
; - CallBackProcessor 中的
updateStream
; - StreamingProcessor 中的
startStream
;
setPreviewCallbackFlag 相关内容
需要注意的是:
- 从时序上看,通常 APP 会先调用到
addCallbackBuffer
,这会使得 JNI 这边有至少一个 Callback Buffer 存在; setPreviewCallbackWithBuffer
会调用到setHasPreviewCallback
这一 native 方法,传下去的 manualBuffer 参数值是 true。
所以接下来从 JNI 部分开始看起。
Camera-JNI::addCallbackBuffer
这个函数主要关注第 8~23 行:
- 第 10 行,把 APP 送下来的 Buffer 放到向量中,此时 size 增加;
- 第 18 行,由于此时 mManualBufferMode 处于初始的 false 状态,所以该分支就此结束。
void JNICameraContext::addCallbackBuffer(
JNIEnv *env, jbyteArray cbb, int msgType)
{
ALOGV("addCallbackBuffer: 0x%x", msgType);
if (cbb != NULL) {
Mutex::Autolock _l(mLock);
switch (msgType) {
case CAMERA_MSG_PREVIEW_FRAME: {
jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb);
mCallbackBuffers.push(callbackBuffer);
ALOGV("Adding callback buffer to queue, %zu total",
mCallbackBuffers.size());
// We want to make sure the camera knows we're ready for the
// next frame. This may have come unset had we not had a
// callbackbuffer ready for it last time.
if (mManualBufferMode && !mManualCameraCallbackSet) {
mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
mManualCameraCallbackSet = true;
}
break;
}
case CAMERA_MSG_RAW_IMAGE: {
jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb);
mRawImageCallbackBuffers.push(callbackBuffer);
break;
}
default: {
jniThrowException(env,
"java/lang/IllegalArgumentException",
"Unsupported message type");
return;
}
}
} else {
ALOGE("Null byte array!");
}
}
Camera-JNI::setHasPreviewCallback
这个函数主要因调用 setPreviewCallbackWithBuffer
接口而触发,该接口可以在 Camera.java 看到,这里不多做解释。
它主要是继续调用 JNI 里的 setCallbackMode
函数(第 13 行),注意 manualBuffer 这一参数值为 true。
static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean manualBuffer)
{
ALOGV("setHasPreviewCallback: installed:%d, manualBuffer:%d", (int)installed, (int)manualBuffer);
// Important: Only install preview_callback if the Java code has called
// setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
// each preview frame for nothing.
JNICameraContext* context;
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
// setCallbackMode will take care of setting the context flags and calling
// camera->setPreviewCallbackFlags within a mutex for us.
context->setCallbackMode(env, installed, manualBuffer);
}
Camera-JNI::setCallbackMode
关于这个函数需要注意的:
- 第 4 行,mManualBufferMode 被设置为 true;
- 第 17~21 行,由于之前调用了
addCallbackBuffer
,此时 mCallbackBuffers 非空,于是会调用到setPreviewCallbackFlags
,注意传入的参数是 CAMERA_FRAME_CALLBACK_FLAG_CAMERA,可以查到这个宏定义对应的数值是 0x05; - 注意下第 20 行,把 mManualCameraCallbackSet 设置为 true;
void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualMode)
{
Mutex::Autolock _l(mLock);
mManualBufferMode = manualMode;
mManualCameraCallbackSet = false;
// In order to limit the over usage of binder threads, all non-manual buffer
// callbacks use CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER mode now.
//
// Continuous callbacks will have the callback re-registered from handleMessage.
// Manual buffer mode will operate as fast as possible, relying on the finite supply
// of buffers for throttling.
if (!installed) {
mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);
clearCallbackBuffers_l(env, &mCallbackBuffers);
} else if (mManualBufferMode) {
if (!mCallbackBuffers.isEmpty()) {
mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
mManualCameraCallbackSet = true;
}
} else {
mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER);
clearCallbackBuffers_l(env, &mCallbackBuffers);
}
}
Camera2Client::setPreviewCallbackFlag
看到这你可能会奇怪,前面调用的是 setPreviewCallbackFlags
,怎么这里就到了 setPreviewCallbackFlag
(缺了一个字母 s
)。这里是因为我省略了 Camera.cpp 的内容,有兴趣可以去看看。
回到正题,这个函数只是个入口,主要是第 9 行进入正题。
void Camera2Client::setPreviewCallbackFlag(int flag) {
ATRACE_CALL();
ALOGV("%s: Camera %d: Flag 0x%x", __FUNCTION__, mCameraId, flag);
Mutex::Autolock icl(mBinderSerializationLock);
if ( checkPid(__FUNCTION__) != OK) return;
SharedParameters::Lock l(mParameters);
setPreviewCallbackFlagL(l.mParameters, flag);
}
Camera2Client::setPreviewCallbackFlagL
该函数主要逻辑如下:
- 第 4~17 行,由于前面已经
startPreview
过,此处 state 为 PREVIEW 状态(即走第 7 行的分支,直接离开 switch); - 第 21 行,之前的 previewCallbackFlags 还是默认的 0x00,此处 flag 带下来 0x05 这个值,于是走入这个分支;
- 第 25 行,更新 previewCallbackFlags 值为 0x05;
- 第 28 行,再次进入
startPreviewL
流程,注意这次 restart 参数值是 true。
void Camera2Client::setPreviewCallbackFlagL(Parameters ¶ms, int flag) {
status_t res = OK;
switch(params.state) {
case Parameters::STOPPED