Camera1 源码解析系列—— Camera1 takePicture() 流程解析

Posted ByteSaid

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Camera1 源码解析系列—— Camera1 takePicture() 流程解析相关的知识,希望对你有一定的参考价值。

前言

前面几篇文章已经把 Camera 控制流的部分梳理得比较清楚了。在 Camera 流程中,还有一个重要的部分,即数据流。
Camera API 1 中,数据流主要是通过函数回调的方式,依照从下往上的方向,逐层 returnApplications 中。
本篇将数据流与 Camera 的控制流结合起来,从 takePicture() 方法切入,追踪一个比较完整的 Camera 流程。

1 Open 流程

Camera Open 的流程,在之前的一篇文章中已经比较详细地描述了。在这里,再关注一下这个流程中 LibrariesHAL 层的部分。

1.1 CameraService.cpp

  • 路径:frameworks/av/services/camera/libcameraservice/CameraService.cpp
  • connect()
    • 注意这里真正实现逻辑是在 connectHelper() 函数中。
  • connectHelper()
    • 这个函数实现比较长,截取其中的一段。
    • 首先,如果客户端实例已经存在于 MediaRecorder ,则直接将其取出返回。
    • 若不存在,则先获取 deviceVersion,然后再调用 makeClient() 函数创建一个客户端。
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
        int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid,
        int clientPid, apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
        /*out*/sp<CLIENT>& device) 
    binder::Status ret = binder::Status::ok();
    ...
        sp<BasicClient> tmp = nullptr;
        if(!(ret = makeClient(this, cameraCb, clientPackageName,
                cameraId, api1CameraId, facing,
                clientPid, clientUid, getpid(), legacyMode,
                halVersion, deviceVersion, effectiveApiLevel,
                /*out*/&tmp)).isOk()) 
            return ret;
        
        client = static_cast<CLIENT*>(tmp.get());
    ...
    return ret;

  • makeClient()
    • 主要是根据 API 版本以及 HAL 版本来选择生成具体的 Client 实例。
    • 如果是HAL1 + Camera1,则创建的是CameraClient,如果是HAL3 + Camera1,则创建的是Camera2Client
Status CameraService::makeClient(const sp<CameraService>& cameraService,
        const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
        int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
        bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
        /*out*/sp<BasicClient>* client) 

    if (halVersion < 0 || halVersion == deviceVersion) 
        // Default path: HAL version is unspecified by caller, create CameraClient
        // based on device version reported by the HAL.
        switch(deviceVersion) 
          case CAMERA_DEVICE_API_VERSION_1_0:
            if (effectiveApiLevel == API_1)   // Camera1 API route
                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
                *client = new CameraClient(cameraService, tmp, packageName,
                        api1CameraId, facing, clientPid, clientUid,
                        getpid(), legacyMode);
             else  // Camera2 API route
                ALOGW("Camera using old HAL version: %d", deviceVersion);
                return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
                        "Camera device \\"%s\\" HAL version %d does not support camera2 API",
                        cameraId.string(), deviceVersion);
            
            break;
          case CAMERA_DEVICE_API_VERSION_3_0:
          case CAMERA_DEVICE_API_VERSION_3_1:
          case CAMERA_DEVICE_API_VERSION_3_2:
          case CAMERA_DEVICE_API_VERSION_3_3:
          case CAMERA_DEVICE_API_VERSION_3_4:
            if (effectiveApiLevel == API_1)  // Camera1 API route
                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
                *client = new Camera2Client(cameraService, tmp, packageName,
                        cameraId, api1CameraId,
                        facing, clientPid, clientUid,
                        servicePid, legacyMode);
             else  // Camera2 API route
                sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
                        static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
                *client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
                        facing, clientPid, clientUid, servicePid);
            
            break;
          default:
            // Should not be reachable
            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
            return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
                    "Camera device \\"%s\\" has unknown HAL version %d",
                    cameraId.string(), deviceVersion);
        
     else 
        // A particular HAL version is requested by caller. Create CameraClient
        // based on the requested HAL version.
        if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
            halVersion == CAMERA_DEVICE_API_VERSION_1_0) 
            // Only support higher HAL version device opened as HAL1.0 device.
            sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
            *client = new CameraClient(cameraService, tmp, packageName,
                    api1CameraId, facing, clientPid, clientUid,
                    servicePid, legacyMode);
         else 
            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
            ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
                    " opened as HAL %x device", halVersion, deviceVersion,
                    CAMERA_DEVICE_API_VERSION_1_0);
            return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
                    "Camera device \\"%s\\" (HAL version %d) cannot be opened as HAL version %d",
                    cameraId.string(), deviceVersion, halVersion);
        
    
    return Status::ok();

1.2 CameraHardwareInterface.h

  • 路径:frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
  • setCallback()
    • 设置 notify 回调,这用来通知数据已经更新。
    • 设置 data 回调以及 dataTimestamp 回调,对应的是函数指针 mDataCbmDataCvTimestamp
    • 注意到,设置 mDevice->ops 对应回调函数时,传入的不是之前设置的函数指针,而是 __data_cb 这样的函数。在该文件中,实现了 __data_cb ,将回调函数做了一层封装。
/** Set the notification and data callbacks */
void setCallbacks(notify_callback notify_cb,
                  data_callback data_cb,
                  data_callback_timestamp data_cb_timestamp,
                  void* user)

    mNotifyCb = notify_cb;
    mDataCb = data_cb;
    mDataCbTimestamp = data_cb_timestamp;
    mCbUser = user;

    ALOGV("%s(%s)", __FUNCTION__, mName.string());

    if (mDevice->ops->set_callbacks) 
        mDevice->ops->set_callbacks(mDevice,
                               __notify_cb,
                               __data_cb,
                               __data_cb_timestamp,
                               __get_memory,
                               this);
    

  • __data_cb()
    • 对原 callback 函数简单封装,附加了一个防止数组越界判断。
static void __data_cb(int32_t msg_type,
                      const camera_memory_t *data, unsigned int index,
                      camera_frame_metadata_t *metadata,
                      void *user)

    ALOGV("%s", __FUNCTION__);
    CameraHardwareInterface *__this =
            static_cast<CameraHardwareInterface *>(user);
    sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
    if (index >= mem->mNumBufs) 
        ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
             index, mem->mNumBufs);
        return;
    
    __this->mDataCb(msg_type, mem->mBuffers[index], metadata, __this->mCbUser);

2 控制流

2.1 Framework

2.1.1 Camera.java

  • 路径:frameworks/base/core/java/android/hardware/Camera.java
  • takePicture()
    • 设置快门回调。
    • 设置各种类型的图片数据回调。
    • 调用 JNI takePicture 方法。
    • 注意,传入的参数 msgType 是根据相应 CallBack 是否存在而确定的,每种 Callback 应该对应一个二进制中的数位(如 1,10,100 中 1 的位置),于是这里采用 |= 操作给它赋值。
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
        PictureCallback postview, PictureCallback jpeg) 
    mShutterCallback = shutter;
    mRawImageCallback = raw;
    mPostviewCallback = postview;
    mJpegCallback = jpeg;

    // If callback is not set, do not send me callbacks.
    int msgType = 0;
    if (mShutterCallback != null) 
        msgType |= CAMERA_MSG_SHUTTER;
    
    if (mRawImageCallback != null) 
        msgType |= CAMERA_MSG_RAW_IMAGE;
    
    if (mPostviewCallback != null) 
        msgType |= CAMERA_MSG_POSTVIEW_FRAME;
    
    if (mJpegCallback != null) 
        msgType |= CAMERA_MSG_COMPRESSED_IMAGE;
    

    native_takePicture(msgType);
    mFaceDetectionRunning = false;

2.2 Android Runtime

2.2.1 android_hardware_Camera.cpp

  • 路径:frameworks/base/core/jni/android_hardware_Camera.cpp
    • takePicture()
    • 获取已经打开的 camera 实例,调用其 takePicture() 接口。
    • 注意,在这个函数中,对于 RAW_IMAGE 有一些附加操作:
      • 如果设置了 RAWcallback ,则要检查上下文中,是否能找到对应 Buffer
      • 若无法找到 Buffer ,则将 CAMERA_MSG_RAW_IMAGE 的信息去掉,换成 CAMERA_MSG_RAW_IMAGE_NOTIFY
      • 替换后,就只会获得 notification 的消息,而没有对应的图像数据。
static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz, jint msgType)

    ALOGV("takePicture");
    JNICameraContext* context;
    sp<Camera> camera = get_native_camera(env, thiz, &context);
    if (camera == 0) return;

    /*
     * When CAMERA_MSG_RAW_IMAGE is requested, if the raw image callback
     * buffer is available, CAMERA_MSG_RAW_IMAGE is enabled to get the
     * notification _and_ the data; otherwise, CAMERA_MSG_RAW_IMAGE_NOTIFY
     * is enabled to receive the callback notification but no data.
     *
     * Note that CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed to the
     * Java application.
     */
    if (msgType & CAMERA_MSG_RAW_IMAGE) 
        ALOGV("Enable raw image callback buffer");
        if (!context->isRawImageCallbackBufferAvailable()) 
            ALOGV("Enable raw image notification, since no callback buffer exists");
            msgType &= ~CAMERA_MSG_RAW_IMAGE;
            msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY;
        
    

    if (camera->takePicture(msgType) != NO_ERROR) 
        jniThrowRuntimeException(env, "takePicture failed");
        return;
    

2.3 C/C++ Libraries

2.3.1 Camera.cpp

  • 路径:frameworks/av/camera/Camera.cpp
  • takePicture()
    • 获取一个 ICamera,调用其 takePicture 接口。
// take a picture
status_t Camera::takePicture(int msgType)

    ALOGV("takePicture: 0x%x", msgType);
    sp <::android::hardware::ICamera> c = mCamera;
    if (c == 0) return NO_INIT;
    return c->takePicture(msgType);

2.3.2 ICamera.cpp

  • 路径:frameworks/av/camera/ICamera.cpp
  • takePicture()
    • 利用 Binder 机制发送相应指令到服务端。
    • 如果是 HAL1 + Camera1,实际调用到的是 CameraClient::takePicture() 函数。
    • 如果是 HAL3 + Camera1,实际调用到的是 Camera2Client::takePicture() 函数。
// take a picture - returns an IMemory (ref-counted mmap)
status_t takePicture(int msgType)

    ALOGV("takePicture: 0x%x", msgType);
    Parcel data, reply;
    data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
    data.writeInt32(msgType);
    remote()->transact(TAKE_PICTURE, data, &reply);
    status_t ret = reply.readInt32();
    return ret;

2.3.3 CameraClient.cpp

  • 路径:frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
  • takePicture()
    • 注意,CAMERA_MSG_RAW_IMAGE 指令与 CAMERA_MSG_RAW_IMAGE_NOTIFY 指令不能同时有效,需要进行对应的检查。
    • 对传入的指令过滤,只留下与 takePicture() 操作相关的。
    • 调用 CameraHardwareInterface 中的 takePicture() 接口。
// take a picture - image is returned in callback
status_t CameraClient::takePicture(int msgType) 
    LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);

    Mutex::Autolock lock(mLock);
    status_t result = checkPidAndHardware();
    if (result != NO_ERROR) return result;

    if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
        (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) 
        ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
                " cannot be both enabled");
        return BAD_VALUE;
    

    // We only accept picture related message types
    // and ignore other types of messages for takePicture().
    int picMsgType = msgType
                        & (CAMERA_MSG_SHUTTER |
                           CAMERA_MSG_POSTVIEW_FRAME |
                           CAMERA_MSG_RAW_IMAGE |
                           CAMERA_MSG_RAW_IMAGE_NOTIFY |
                           CAMERA_MSG_COMPRESSED_IMAGE);

    enableMsgType(picMsgType);

    return mHardware->takePicture();

2.3.4 Camera2Client.cpp

  • 路径:frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp
  • takePicture()
    • 调用了 mCaptureSequencer->startCapture()
status_t Camera2Client::takePicture(int msgType) 
    ATRACE_CALL();
    Mutex::Autolock icl(mBinderSerializationLock);
    status_t res;
    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
    
        SharedParameters::Lock l(mParameters);
        switch (l.mParameters.state) 
            case Parameters::DISCONNECTED:
            case Parameters::STOPPED:
            case Parameters::WAITING_FOR_PREVIEW_WINDOW:
                ALOGE("%s: Camera %d: Cannot take picture without preview enabled",
                        __FUNCTION__, mCameraId);
                return INVALID_OPERATION;
            case Parameters::PREVIEW:
                // Good to go for takePicture
                res = commandStopFaceDetectionL(l.mParameters);
                if (res != OK) 
                    ALOGE("%s: Camera %d: Unable to stop face detection for still capture",
                            __FUNCTION__, mCameraId);
                    return res;
                
                l.mParameters.state = Parameters::STILL_CAPTURE;
                break;
            case Parameters::RECORD:
                // Good to go for video snapshot
                l.mParameters.state = Parameters::VIDEO_SNAPSHOT;
                break;
            case Parameters::STILL_CAPTURE:
            case Parameters::VIDEO_SNAPSHOT:
                ALOGE("%s: Camera %d: Already taking a picture",
                        __FUNCTION__, mCameraId);
                return INVALID_OPERATION;
        
        ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId);
        res = updateProcessorStream(mJpegProcessor, l.mParameters);
        if (res != OK) 
            ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
                    __FUNCTION__, mCameraId, strerror(-res), res);
            return res;
        
    
    // Need HAL to have correct settings before (possibly) triggering precapture
    syncWithDevice();
    res = mCaptureSequencer->startCapture(msgType);
    if (res != OK) 
        ALOGE("%s: Camera %d: Unable to start capture: %s (%d)",
                __FUNCTION__, mCameraId, strerror(-res), res);
    
    return res;

2.3.4.1 CaptureSequencer.cpp

  • 路径:frameworks/av/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
  • startCapture()
    • 这里 mStartCaptureSignal.signal() 会激活 mStartCaptureSignal 条件。
status_t CaptureSequencer::startCapture(int msgType) 
    ALOGV("%s", __FUNCTION__);
    ATRACE_CALL();
    Mutex::Autolock l(mInputMutex);
    if (mBusy) 
        ALOGE("%s: Already busy capturing!", __FUNCTION__);
        return INVALID_OPERATION;
    
    if (!mStartCapture) 
        mMsgType = msgType;
        mStartCapture = true;
        mStartCaptureSignal.signal();
    
    return OK;

CaptureSequencer 模块是 take picture 操作的重点,在 Camera2Client 中进行了创建,首先来看 CaptureSequencer 线程的 threadLoop 函数:

bool CaptureSequencer::threadLoop() 
 
    sp<Camera2Client> client = mClient.promote();
    if (client == 0) return false;
 
    CaptureState currentState;
    
        Mutex::Autolock l(mStateMutex);
        currentState = mCaptureState;
    
 
    currentState = (this->*kStateManagers[currentState])(client);
 
    Mutex::Autolock l(<

以上是关于Camera1 源码解析系列—— Camera1 takePicture() 流程解析的主要内容,如果未能解决你的问题,请参考以下文章

Camera1 源码解析系列—— Camera1 startPreview() 流程解析

Camera1 源码解析系列—— Camera1 Open() 流程解析

Camera1 源码解析系列—— Camera1 takePicture() 流程解析

Camera1 源码解析系列—— Camera1 takePicture() 流程解析

Camera1 源码解析系列—— Camera1 基本架构

Camera1 源码解析系列—— Camera1 基本架构