[Android P] CameraAPI1 转 HAL3 预览流程(三) — setPreviewCallbackFlag

本文深入剖析Android P中,从CameraAPI1的setPreviewCallbackFlag到HAL3的预览流程。分析了Camera-JNI、Camera2Client、StreamingProcessor和CallbackProcessor等关键组件,详细阐述了setPreviewCallbackFlag如何启动第二路stream并触发re-configure机制的过程。
摘要由CSDN通过智能技术生成

系列文章

总览

注意一下,虽然题目主要写的是 setPreviewCallbackFlag,但实际上这对于 Camera 应用,它的动作是调用 API1 的 setPreviewCallbackWithBufferaddCallbackBuffer,这样会把 Preview Callback 的 Buffer 模式设置为手动模式(由 APP 主动带 Callback Buffer 下来,底层数据回来后会被 Copy 到这块 APP Buffer 中)。

本篇文章对这一部分进行的时序分析,有几个前提条件:

  1. APP 先调用了 startPreview
  2. 紧随其后的是调用 addCallbackBuffer 带下 Buffer;
  3. 然后调用了 setPreviewCallbackWithBuffer,该接口设置 manualModetrue,标志 APP 自主带下 Callback Buffer;
  4. 第三点会触发第一次 setPreviewCallbackFlag(0x05)

需要注意的几个点:

  1. 由于 Callback 是单独的一路 stream,所以这里相当于是要打开第二路 stream,会触发 re-configure 机制
  2. configureStream 的逻辑是在 CallbackProcessor 中 updateStream 期间触发的,注意这里的时机区别于 startPreviewstartStream

setPreviewCallbackFlag

主要涉及到的类及其对应的代码地址分别是:

  1. Camera-JNI/frameworks/base/core/jni/android_hardware_Camera.cpp
  2. Camera2Client/frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp
  3. StreamingProcessor/frameworks/av/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
  4. CallbackProcessor/frameworks/av/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
  5. Camera3Device/frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

接下来我会照着上面的时序图,结合具体代码进行更深入一些的解释。

代码分析

根据时序图来看,可以分成三个部分来看:

  1. Camera2Client 中的 setPreviewCallbackFlag
  2. CallBackProcessor 中的 updateStream;
  3. StreamingProcessor 中的 startStream

setPreviewCallbackFlag 相关内容

需要注意的是:

  1. 从时序上看,通常 APP 会先调用到 addCallbackBuffer,这会使得 JNI 这边有至少一个 Callback Buffer 存在;
  2. setPreviewCallbackWithBuffer 会调用到 setHasPreviewCallback 这一 native 方法,传下去的 manualBuffer 参数值是 true。

所以接下来从 JNI 部分开始看起。

Camera-JNI::addCallbackBuffer

这个函数主要关注第 8~23 行:

  1. 第 10 行,把 APP 送下来的 Buffer 放到向量中,此时 size 增加
  2. 第 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

关于这个函数需要注意的:

  1. 第 4 行,mManualBufferMode 被设置为 true
  2. 第 17~21 行,由于之前调用了 addCallbackBuffer,此时 mCallbackBuffers 非空,于是会调用到 setPreviewCallbackFlags注意传入的参数是 CAMERA_FRAME_CALLBACK_FLAG_CAMERA,可以查到这个宏定义对应的数值是 0x05
  3. 注意下第 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

该函数主要逻辑如下:

  1. 第 4~17 行,由于前面已经 startPreview 过,此处 state 为 PREVIEW 状态(即走第 7 行的分支,直接离开 switch);
  2. 第 21 行,之前的 previewCallbackFlags 还是默认的 0x00,此处 flag 带下来 0x05 这个值,于是走入这个分支;
  3. 第 25 行,更新 previewCallbackFlags 值为 0x05
  4. 第 28 行,再次进入 startPreviewL 流程,注意这次 restart 参数值是 true
void Camera2Client::setPreviewCallbackFlagL(Parameters &params, int flag) {
   
    status_t res = OK;

    switch(params.state) {
   
        case Parameters::STOPPED
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值