系列文章
- [Android P] CameraAPI1 转 HAL3 预览流程(一) — 背景概述
- [Android P] CameraAPI1 转 HAL3 预览流程(二) — startPreview
- [Android P] CameraAPI1 转 HAL3 预览流程(三) — setPreviewCallbackFlag
- [Android P] CameraAPI1 转 HAL3 预览流程(四) — Preview Data
总览
预览打开完毕后,就进入了持续预览阶段。
Camera API2 架构下,采用一个 Request 对应一个 Result 的规范,所以在预览期间是需要持续下 Request 来获取预览数据的,而仍然采用 API1 相机应用在 Framework 中也会被转换成这样的形式。
其中,与 Request 密切相关的一个线程是 Camera3Device::RequestThread,它负责持续下预览 Request。
Result 从底层返回时,会先回到 Camera3Device,触发 processCaptureResult
并通知到各个 Processor(如 FrameProcessor 和 CallbackProcessor)去进一步处理、上传。
我们现在分析的是开了 preview 以及 callback 两路 stream 的预览流程,其中 APP 一般是拿 callback 这路数据去进行客制化处理,然后进行预览,所以下面的时序中,Result 部分就重点看 callback 数据的回传部分(因为这部分也与我最开始所描述的卡顿问题密切相关)。
主要涉及到的类及其对应的代码地址分别是:
- Camera-JNI:
/frameworks/base/core/jni/android_hardware_Camera.cpp
- Camera2Client:
/frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp
- FrameProcessor:
/frameworks/av/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
- CallbackProcessor:
/frameworks/av/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
- Camera3Device:
/frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
接下来我会照着上面的时序图,结合具体代码进行更深入一些的解释。
代码分析
我们可以分成两个部分来看:
- 下行-控制流(Request);
- 上行-数据流(Result)。
控制流部分
严格来说,控制流部分主要是 RequestThread 在负责周转,但由于 setPreviewCallbackFlag
里有影响到它周转流程的逻辑,所以我在这里会先把 Camera2Client 中 setPreviewCallbackFlag
的两种调用情况描述出来,这样在分析 RequestThread::threadLoop
时我们就更能理解它受到了什么影响。
下面主要是讲图示中红框的部分。
Camera2Client::setPreviewCallbackFlag
这个函数被调用,一般是有两种情况:
- App 调用了
addCallbackBuffer
,把用于装填 callback 数据的 buffer 主动传下来时,此时带的参数是 0x05; - Callback result 回调上来,到 JNI 处有
copyAndPost
动作,如果此帧数据上传后, App 提供的 buffer 用完了,就会被调用,此时带的参数是 0x00; - 具体代码就不用看了,最终都会调用到
startPreviewL
,这个才是我们需要关注的部分。
Camera2Client::startPreviewL
以参数 0x05 的情况来分析:
- 第 4~14 行,主要是状态变更,以及更新参数等动作,这部分在前两篇都有描述,就不赘述了;
- 第 19 行,这是一个关键点,由于传入的 previewCallbackFlags 是 0x05,这里计算出来的值是 true;
- 第 23~37 行,由于 callbacksEnabled 为 true,走入该分支,会调用到 CallbackProcessor 实例的
updateStream
函数,而此处我们需要关注的是第 36 行,把 callback 的 output stream 加入到 stream 列表中; - 第 48 行,这里是把 preview 的 output stream 加入到 stream 列表中,此时 stream 列表的 size 为 2;
- 第 66 行,注意这里,
startStream
调用时带的参数有 outputStreams,在这个函数里面它会被传入到新创建的 CaptureRequest 实例中,进而影响到下一次 Request 申请 Hal buffer 的动作。
status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) {
// NOTE: N Lines are omitted here
params.state = Parameters::STOPPED;
int lastPreviewStreamId = mStreamingProcessor->getPreviewStreamId();
res = mStreamingProcessor->updatePreviewStream(params);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to update preview stream: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
}
bool previewStreamChanged = mStreamingProcessor->getPreviewStreamId() != lastPreviewStreamId;
// NOTE: N Lines are omitted here
Vector<int32_t> outputStreams;
bool callbacksEnabled = (params.previewCallbackFlags &
CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) ||
params.previewCallbackSurface;
if (callbacksEnabled) {
// Can't have recording stream hanging around when enabling callbacks,
// since it exceeds the max stream count on some devices.
if (mStreamingProcessor->getRecordingStreamId() != NO_STREAM) {
// NOTE: N Lines are omitted here
}
res = mCallbackProcessor->updateStream(params);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to update callback stream: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
}
outputStreams.push(getCallbackStreamId());
} else if (previewStreamChanged && mCallbackProcessor->getStreamId() != NO_STREAM) {
// NOTE: N Lines are omitted here
}
if (params.useZeroShutterLag() &&
getRecordingStreamId() == NO_STREAM) {
// NOTE: N Lines are omitted here
} else {
mZslProcessor->deleteStream();
}
outputStreams.push(getPreviewStreamId());
if (params.isDeviceZslSupported) {
// If device ZSL is supported, resume preview buffers that may be paused
// during last takePicture().
mDevice->dropStreamBuffers(false, getPreviewStreamId());
}
if (!params.recordingHint) {
if (!restart) {
res = mStreamingProcessor->updatePreviewRequest(params);
if (res != OK) {
ALOGE("%s: Camera %d: Can't set up preview request: "
"%s (%d)", __FUNCTION__, mCameraId,
strerror(-res), res);
return res;
}
}
res = mStreamingProcessor->startStream(StreamingProcessor::PREVIEW,
outputStreams);
} else {
// NOTE: N Lines are omitted here
}
if (res != OK) {
ALOGE("%s: Camera %d: Unable to start streaming preview: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
}
params.state = Parameters::PREVIEW;
return OK;
}
Camera3Device::RequestThread::threadLoop
回到控制流的正题,即 RequestThread 的运转逻辑。
RequestThread 实例实际是在 Camera3Device::initializeCommonLocked
中创建并 run 起来的,这是 openCamera 的流程,有兴趣可以去看看。
线程 run 起来后就是循环调用 threadLoop
了,回到其逻辑:
- 第 6 行,先检查是否需要暂停循环,如果需要暂停则直接跳过本次
threadLoop
,我们此时是不需要的; - 第 11 行,等待这一批要下的 request 获取完毕,具体的逻辑下面会讲到;
- 第 22 行,这里主要是检查这次的 request 带的 Session Params 和上一次的是否一致,我们这里主要关注一致的情况,此时会返回 false,不走这个 if 分支;
- 第 27 行,为本次 request 准备好送下 HAL 层的 buffer(注意 APP 带下来的 buffer 是停留在 JNI 的);
- 第 58 行,把本次 request 发送到 HAL。
bool Camera3Device::RequestThread::threadLoop() {
ATRACE_CALL();
status_t res;
// Handle paused state.
if