[Android P] CameraAPI1 转 HAL3 预览流程(四) — Preview Data

本文详细解析了Android P中从CameraAPI1转到HAL3的预览流程,包括控制流和数据流两部分。控制流涉及Camera2Client、RequestThread等组件,数据流主要关注预览数据的处理和回调。通过对关键函数的分析,揭示了预览数据的获取、处理和回调机制,以及可能存在的卡顿问题的原因。
摘要由CSDN通过智能技术生成

系列文章

总览

预览打开完毕后,就进入了持续预览阶段。

Camera API2 架构下,采用一个 Request 对应一个 Result 的规范,所以在预览期间是需要持续下 Request 来获取预览数据的,而仍然采用 API1 相机应用在 Framework 中也会被转换成这样的形式。

其中,与 Request 密切相关的一个线程是 Camera3Device::RequestThread,它负责持续下预览 Request

Result 从底层返回时,会先回到 Camera3Device,触发 processCaptureResult 并通知到各个 Processor(如 FrameProcessor 和 CallbackProcessor)去进一步处理、上传。

我们现在分析的是开了 preview 以及 callback 两路 stream 的预览流程,其中 APP 一般是拿 callback 这路数据去进行客制化处理,然后进行预览,所以下面的时序中,Result 部分就重点看 callback 数据的回传部分(因为这部分也与我最开始所描述的卡顿问题密切相关)。

previewData

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

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

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

代码分析

我们可以分成两个部分来看:

  1. 下行-控制流(Request);
  2. 上行-数据流(Result)。

控制流部分

严格来说,控制流部分主要是 RequestThread 在负责周转,但由于 setPreviewCallbackFlag 里有影响到它周转流程的逻辑,所以我在这里会先把 Camera2Client 中 setPreviewCallbackFlag 的两种调用情况描述出来,这样在分析 RequestThread::threadLoop 时我们就更能理解它受到了什么影响。

下面主要是讲图示中红框的部分。

control

Camera2Client::setPreviewCallbackFlag

这个函数被调用,一般是有两种情况:

  1. App 调用了 addCallbackBuffer,把用于装填 callback 数据的 buffer 主动传下来时,此时带的参数是 0x05
  2. Callback result 回调上来,到 JNI 处有 copyAndPost动作,如果此帧数据上传后, App 提供的 buffer 用完了,就会被调用,此时带的参数是 0x00
  3. 具体代码就不用看了,最终都会调用到 startPreviewL,这个才是我们需要关注的部分。

Camera2Client::startPreviewL

以参数 0x05 的情况来分析:

  1. 第 4~14 行,主要是状态变更,以及更新参数等动作,这部分在前两篇都有描述,就不赘述了;
  2. 第 19 行,这是一个关键点,由于传入的 previewCallbackFlags 是 0x05,这里计算出来的值是 true
  3. 第 23~37 行,由于 callbacksEnabled 为 true,走入该分支,会调用到 CallbackProcessor 实例的 updateStream 函数,而此处我们需要关注的是第 36 行,把 callback 的 output stream 加入到 stream 列表中
  4. 第 48 行,这里是把 preview 的 output stream 加入到 stream 列表中,此时 stream 列表的 size 为 2
  5. 第 66 行,注意这里,startStream 调用时带的参数有 outputStreams,在这个函数里面它会被传入到新创建的 CaptureRequest 实例中,进而影响到下一次 Request 申请 Hal buffer 的动作。
status_t Camera2Client::startPreviewL(Parameters &params, 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 了,回到其逻辑:

  1. 第 6 行,先检查是否需要暂停循环,如果需要暂停则直接跳过本次 threadLoop,我们此时是不需要的;
  2. 第 11 行,等待这一批要下的 request 获取完毕,具体的逻辑下面会讲到;
  3. 第 22 行,这里主要是检查这次的 request 带的 Session Params 和上一次的是否一致,我们这里主要关注一致的情况,此时会返回 false,不走这个 if 分支;
  4. 第 27 行,为本次 request 准备好送下 HAL 层的 buffer(注意 APP 带下来的 buffer 是停留在 JNI 的);
  5. 第 58 行,把本次 request 发送到 HAL。
bool Camera3Device::RequestThread::threadLoop() {
   
    ATRACE_CALL();
    status_t res;

    // Handle paused state.
    if 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值