注意一下,虽然题目主要写的是 setPreviewCallbackFlag
,但实际上这对于 Camera 应用,它的动作是调用 API1 的 setPreviewCallbackWithBuffer
和 addCallbackBuffer
,这样会把 Preview Callback 的 Buffer 模式设置为手动模式(由 APP 主动带 Callback Buffer 下来,底层数据回来后会被 Copy 到这块 APP Buffer 中)。
本篇文章对这一部分进行的时序分析,有几个前提条件:
startPreview
;addCallbackBuffer
带下 Buffer;setPreviewCallbackWithBuffer
,该接口设置 manualMode 为 true,标志 APP 自主带下 Callback Buffer;setPreviewCallbackFlag(0x05)
。需要注意的几个点:
updateStream
期间触发的,注意这里的时机区别于 startPreview
的 startStream
。主要涉及到的类及其对应的代码地址分别是:
/frameworks/base/core/jni/android_hardware_Camera.cpp
/frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp
/frameworks/av/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
/frameworks/av/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
/frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
接下来我会照着上面的时序图,结合具体代码进行更深入一些的解释。
根据时序图来看,可以分成三个部分来看:
setPreviewCallbackFlag
;updateStream
;startStream
;需要注意的是:
addCallbackBuffer
,这会使得 JNI 这边有至少一个 Callback Buffer 存在;setPreviewCallbackWithBuffer
会调用到 setHasPreviewCallback
这一 native 方法,传下去的 manualBuffer 参数值是 true。所以接下来从 JNI 部分开始看起。
这个函数主要关注第 8~23 行:
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!");
}
}
这个函数主要因调用 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);
}
关于这个函数需要注意的:
addCallbackBuffer
,此时 mCallbackBuffers 非空,于是会调用到 setPreviewCallbackFlags
,注意传入的参数是 CAMERA_FRAME_CALLBACK_FLAG_CAMERA,可以查到这个宏定义对应的数值是 0x05;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);
}
}
看到这你可能会奇怪,前面调用的是 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);
}
该函数主要逻辑如下:
startPreview
过,此处 state 为 PREVIEW 状态(即走第 7 行的分支,直接离开 switch);startPreviewL
流程,注意这次 restart 参数值是 true。void Camera2Client::setPreviewCallbackFlagL(Parameters ¶ms, int flag) {
status_t res = OK;
switch(params.state) {
case Parameters::STOPPED:
case Parameters::WAITING_FOR_PREVIEW_WINDOW:
case Parameters::PREVIEW:
case Parameters::STILL_CAPTURE:
// OK
break;
default:
if (flag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
ALOGE("%s: Camera %d: Can't use preview callbacks "
"in state %d", __FUNCTION__, mCameraId, params.state);
return;
}
}
// NOTE: N Lines are omitted here
if (params.previewCallbackFlags != (uint32_t)flag) {
// NOTE: N Lines are omitted here
params.previewCallbackFlags = flag;
if (params.state == Parameters::PREVIEW) {
res = startPreviewL(params, true);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to refresh request in state %s",
__FUNCTION__, mCameraId,
Parameters::getStateName(params.state));
}
}
}
}
该函数主要逻辑如下:
startPreview
过, 这里能拿到当前 preview stream 的 ID,相关逻辑需要关注的信息不多,就不深入去看了;updateStream
,此处就是后续关注的重点了,由于需要新的这一路 callback stream,此处会有相关的创建新 stream 的逻辑,包括触发 re-config 动作;startStream
就不会触发 configureStream 的动作了,需要注意的是它会调用到 setRepeatingRequests
;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) {
// 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());
// NOTE: N Lines are omitted here
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
}
// NOTE: N Lines are omitted here
params.state = Parameters::PREVIEW;
return OK;
}
这里主要就是更新一个 callback stream,触发 re-config 动作。
此处逻辑如下:
onFrameAvailable
回调密切相关,但由于此处关系略复杂,就不深入去分析了,有兴趣可以自己看看;status_t CallbackProcessor::updateStream(const Parameters ¶ms) {
// NOTE: N Lines are omitted here
if (!mCallbackToApp && mCallbackConsumer == 0) {
// Create CPU buffer queue endpoint, since app hasn't given us one
// Make it async to avoid disconnect deadlocks
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
mCallbackConsumer = new CpuConsumer(consumer, kCallbackHeapCount);
mCallbackConsumer->setFrameAvailableListener(this);
mCallbackConsumer->setName(String8("Camera2-CallbackConsumer"));
mCallbackWindow = new Surface(producer);
}
// NOTE: N Lines are omitted here
if (mCallbackStreamId == NO_STREAM) {
ALOGV("Creating callback stream: %d x %d, format 0x%x, API format 0x%x",
params.previewWidth, params.previewHeight,
callbackFormat, params.previewFormat);
res = device->createStream(mCallbackWindow,
params.previewWidth, params.previewHeight, callbackFormat,
HAL_DATASPACE_V0_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId,
String8());
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
"%s (%d)", __FUNCTION__, mId,
strerror(-res), res);
return res;
}
}
return OK;
}
实际上 createStream
有两个,这里只看最后执行的那一个:
configureStreamsLocked
,这里面就会触发 re-config 情况。注意第 89 行的函数会把 Pause 住的 RequestThread 重新开起来。status_t Camera3Device::createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
// NOTE: N Lines are omitted here
bool wasActive = false;
switch (mStatus) {
case STATUS_ERROR:
CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
case STATUS_UNCONFIGURED:
case STATUS_CONFIGURED:
// OK
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
res = internalPauseAndWaitLocked(maxExpectedDuration);
if (res != OK) {
SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
}
wasActive = true;
break;
default:
SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
assert(mStatus != STATUS_ACTIVE);
sp<Camera3OutputStream> newStream;
// NOTE: N Lines are omitted here
if (format == HAL_PIXEL_FORMAT_BLOB) {
// NOTE: N Lines are omitted here
} else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
// NOTE: N Lines are omitted here
} else if (isShared) {
// NOTE: N Lines are omitted here
} else if (consumers.size() == 0 && hasDeferredConsumer) {
// NOTE: N Lines are omitted here
} else {
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, format, dataSpace, rotation,
mTimestampOffset, physicalCameraId, streamSetId);
}
size_t consumerCount = consumers.size();
for (size_t i = 0; i < consumerCount; i++) {
int id = newStream->getSurfaceId(consumers[i]);
if (id < 0) {
SET_ERR_L("Invalid surface id");
return BAD_VALUE;
}
if (surfaceIds != nullptr) {
surfaceIds->push_back(id);
}
}
newStream->setStatusTracker(mStatusTracker);
newStream->setBufferManager(mBufferManager);
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);
return res;
}
*id = mNextStreamId++;
mNeedConfig = true;
// Continue captures if active at start
if (wasActive) {
ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__);
// Reuse current operating mode and session parameters for new stream config
res = configureStreamsLocked(mOperatingMode, mSessionParams);
if (res != OK) {
CLOGE("Can't reconfigure device for new stream %d: %s (%d)",
mNextStreamId, strerror(-res), res);
return res;
}
internalResumeLocked();
}
ALOGV("Camera %s: Created new stream", mId.string());
return OK;
}
此处逻辑如下:
createStream
的流程中已经被改成 STATUS_CONFIGURED 了,所以此处不会被 return,如果还是 STATUS_ACTIVE 的话这里就触发 return,就不会继续 config 了;createStream
里面有设置 mNeedConfig 为 true,所以此处不会触发 return;startConfiguration
的动作;finishConfiguration
动作;status_t Camera3Device::configureStreamsLocked(int operatingMode,
const CameraMetadata& sessionParams, bool notifyRequestThread) {
ATRACE_CALL();
status_t res;
if (mStatus != STATUS_UNCONFIGURED && mStatus != STATUS_CONFIGURED) {
CLOGE("Not idle");
return INVALID_OPERATION;
}
// NOTE: N Lines are omitted here
if (!mNeedConfig) {
ALOGV("%s: Skipping config, no stream changes", __FUNCTION__);
return OK;
}
// NOTE: N Lines are omitted here
mPreparerThread->pause();
// NOTE: N Lines are omitted here
for (size_t i = 0; i < mOutputStreams.size(); i++) {
// Don't configure bidi streams twice, nor add them twice to the list
if (mOutputStreams[i].get() ==
static_cast<Camera3StreamInterface*>(mInputStream.get())) {
config.num_streams--;
continue;
}
camera3_stream_t *outputStream;
outputStream = mOutputStreams.editValueAt(i)->startConfiguration();
if (outputStream == NULL) {
CLOGE("Can't start output stream configuration");
cancelStreamsConfigurationLocked();
return INVALID_OPERATION;
}
streams.add(outputStream);
if (outputStream->format == HAL_PIXEL_FORMAT_BLOB &&
outputStream->data_space == HAL_DATASPACE_V0_JFIF) {
size_t k = i + ((mInputStream != nullptr) ? 1 : 0); // Input stream if present should
// always occupy the initial entry.
bufferSizes[k] = static_cast<uint32_t>(
getJpegBufferSize(outputStream->width, outputStream->height));
}
}
config.streams = streams.editArray();
// Do the HAL configuration; will potentially touch stream
// max_buffers, usage, priv fields.
const camera_metadata_t *sessionBuffer = sessionParams.getAndLock();
res = mInterface->configureStreams(sessionBuffer, &config, bufferSizes);
sessionParams.unlock(sessionBuffer);
// NOTE: N Lines are omitted here
for (size_t i = 0; i < mOutputStreams.size(); i++) {
sp<Camera3OutputStreamInterface> outputStream =
mOutputStreams.editValueAt(i);
if (outputStream->isConfiguring() && !outputStream->isConsumerConfigurationDeferred()) {
res = outputStream->finishConfiguration();
if (res != OK) {
CLOGE("Can't finish configuring output stream %d: %s (%d)",
outputStream->getId(), strerror(-res), res);
cancelStreamsConfigurationLocked();
return BAD_VALUE;
}
}
}
// Request thread needs to know to avoid using repeat-last-settings protocol
// across configure_streams() calls
if (notifyRequestThread) {
mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration, sessionParams);
}
// NOTE: N Lines are omitted here
mNeedConfig = false;
internalUpdateStatusLocked((mDummyStreamId == NO_STREAM) ?
STATUS_CONFIGURED : STATUS_UNCONFIGURED);
ALOGV("%s: Camera %s: Stream configuration complete", __FUNCTION__, mId.string());
// tear down the deleted streams after configure streams.
mDeletedStreams.clear();
auto rc = mPreparerThread->resume();
if (rc != OK) {
SET_ERR_L("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
return rc;
}
return OK;
}
这部分与 startPreview 流程比较重复,不过由于某些变量值不同,需要执行的逻辑少了许多。
此处逻辑如下:
prepareHalRequest
时所准备的 buffer 个数;status_t StreamingProcessor::startStream(StreamType type,
const Vector<int32_t> &outputStreams) {
// NOTE: N Lines are omitted here
CameraMetadata &request = (type == PREVIEW) ?
mPreviewRequest : mRecordingRequest;
res = request.update(
ANDROID_REQUEST_OUTPUT_STREAMS,
outputStreams);
// NOTE: N Lines are omitted here
res = device->setStreamingRequest(request);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to set preview request to start preview: "
"%s (%d)",
__FUNCTION__, mId, strerror(-res), res);
return res;
}
mActiveRequest = type;
mPaused = false;
mActiveStreamIds = outputStreams;
return OK;
}
这里主要作用是转换 request 的形式,并调用到 setStreamingRequestList
。
status_t Camera3Device::setStreamingRequest(const CameraMetadata &request,
int64_t* /*lastFrameNumber*/) {
ATRACE_CALL();
List<const PhysicalCameraSettingsList> requestsList;
std::list<const SurfaceMap> surfaceMaps;
convertToRequestList(requestsList, surfaceMaps, request);
return setStreamingRequestList(requestsList, /*surfaceMap*/surfaceMaps,
/*lastFrameNumber*/NULL);
}
此处主要是对接到 submitRequestsHelper
。
status_t Camera3Device::setStreamingRequestList(
const List<const PhysicalCameraSettingsList> &requestsList,
const std::list<const SurfaceMap> &surfaceMaps, int64_t *lastFrameNumber) {
ATRACE_CALL();
return submitRequestsHelper(requestsList, surfaceMaps, /*repeating*/true, lastFrameNumber);
}
此处逻辑如下:
setRepeatingRequest
,这是 API2 启预览的接口;status_t Camera3Device::submitRequestsHelper(
const List<const PhysicalCameraSettingsList> &requests,
const std::list<const SurfaceMap> &surfaceMaps,
bool repeating,
/*out*/
int64_t *lastFrameNumber) {
// NOTE: N Lines are omitted here
RequestList requestList;
res = convertMetadataListToRequestListLocked(requests, surfaceMaps,
repeating, /*out*/&requestList);
if (res != OK) {
// error logged by previous call
return res;
}
if (repeating) {
res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber);
} else {
res = mRequestThread->queueRequestList(requestList, lastFrameNumber);
}
if (res == OK) {
waitUntilStateThenRelock(/*active*/true, kActiveTimeout);
if (res != OK) {
SET_ERR_L("Can't transition to active in %f seconds!",
kActiveTimeout/1e9);
}
ALOGV("Camera %s: Capture request %" PRId32 " enqueued", mId.string(),
(*(requestList.begin()))->mResultExtras.requestId);
} else {
CLOGE("Cannot queue request. Impossible.");
return BAD_VALUE;
}
return res;
}
此处比较关键的是第 16 行,目的是获取 CaptureRequest 实例。
status_t Camera3Device::convertMetadataListToRequestListLocked(
const List<const PhysicalCameraSettingsList> &metadataList,
const std::list<const SurfaceMap> &surfaceMaps,
bool repeating,
RequestList *requestList) {
if (requestList == NULL) {
CLOGE("requestList cannot be NULL.");
return BAD_VALUE;
}
int32_t burstId = 0;
List<const PhysicalCameraSettingsList>::const_iterator metadataIt = metadataList.begin();
std::list<const SurfaceMap>::const_iterator surfaceMapIt = surfaceMaps.begin();
for (; metadataIt != metadataList.end() && surfaceMapIt != surfaceMaps.end();
++metadataIt, ++surfaceMapIt) {
sp<CaptureRequest> newRequest = setUpRequestLocked(*metadataIt, *surfaceMapIt);
if (newRequest == 0) {
CLOGE("Can't create capture request");
return BAD_VALUE;
}
newRequest->mRepeating = repeating;
// NOTE: N Lines are omitted here
requestList->push_back(newRequest);
ALOGV("%s: requestId = %" PRId32, __FUNCTION__, newRequest->mResultExtras.requestId);
}
// NOTE: N Lines are omitted here
return OK;
}
此处逻辑如下:
startPreview
时一样,就不赘述了。sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
const PhysicalCameraSettingsList &request, const SurfaceMap &surfaceMap) {
status_t res;
if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
// This point should only be reached via API1 (API2 must explicitly call configureStreams)
// so unilaterally select normal operating mode.
res = filterParamsAndConfigureLocked(request.begin()->metadata,
CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE);
// Stream configuration failed. Client might try other configuraitons.
if (res != OK) {
CLOGE("Can't set up streams: %s (%d)", strerror(-res), res);
return NULL;
} else if (mStatus == STATUS_UNCONFIGURED) {
// Stream configuration successfully configure to empty stream configuration.
CLOGE("No streams configured");
return NULL;
}
}
sp<CaptureRequest> newRequest = createCaptureRequest(request, surfaceMap);
return newRequest;
}
再次来到 setRepeatingRequests
:
signal
,会直接影响到 waitForNextRequestLocked
。status_t Camera3Device::RequestThread::setRepeatingRequests(
const RequestList &requests,
/*out*/
int64_t *lastFrameNumber) {
ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
if (lastFrameNumber != NULL) {
*lastFrameNumber = mRepeatingLastFrameNumber;
}
mRepeatingRequests.clear();
mRepeatingRequests.insert(mRepeatingRequests.begin(),
requests.begin(), requests.end());
unpauseForNewRequests();
mRepeatingLastFrameNumber = hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES;
return OK;
}
这片文章主要介绍了 Android P 版本下,相机 APP 调用 Camera API1 的 startPreview
后,继续调用 addCallbackBuffer
以及 setPreviewCallbackWithBuffer
,Framework 层是转换成 HAL3 的逻辑时序。
而下一篇文章将会介绍到,成功进入持续预览状态(两路 stream,preview && callback)后,Framework 是如何持续下 Request 的,以及它收到对应的 Result 后,数据向 APP 流动的时序。