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

Posted ByteSaid

tags:

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

前言

上一篇关于分析了 hw_get_module() 的调用逻辑,本篇通过追踪 Camera.startPreview() 方法,以加深对控制流的理解。

1 Frameworks

1.1 Camera.java

  • 路径:frameworks/base/core/java/android/hardware/Camera.java
  • startPreview()
    • 给上层 application 提供一个接口。
    • 进入 Runtime 层。
/**
* Starts capturing and drawing preview frames to the screen.
* Preview will not actually start until a surface is supplied
* with @link #setPreviewDisplay(SurfaceHolder) or
* @link #setPreviewTexture(SurfaceTexture).
*
* <p>If @link #setPreviewCallback(Camera.PreviewCallback),
* @link #setOneShotPreviewCallback(Camera.PreviewCallback), or
* @link #setPreviewCallbackWithBuffer(Camera.PreviewCallback) were
* called, @link Camera.PreviewCallback#onPreviewFrame(byte[], Camera)
* will be called when preview data becomes available.
*/
    public native final void startPreview();

2 Android Runtime

2.1 android_hardware_Camera.cpp

  • 路径:frameworks/base/core/jni/android_hardware_Camera.cpp
  • android_hardware_Camera_startPreview()
    • 调用 get_native_camera() 函数获取一个 Camera 实例。
    • 调用 Camera::startPreview()
static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)

    ALOGV("startPreview");
    sp<Camera> camera = get_native_camera(env, thiz, NULL);
    if (camera == 0) return;
    
    if (camera->startPreview() != NO_ERROR) 
        jniThrowRuntimeException(env, "startPreview failed");
        return;
    

  • get_native_camera()
    • DVM 中获取关于 Camera 的上下文。
    • 从上下文信息中获取 Camera 实例。
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)

    sp<Camera> camera;
    Mutex::Autolock _l(sLock);
    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetLongField(thiz, fields.context));
    if (context != NULL) 
        camera = context->getCamera();
    
    ALOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
    if (camera == 0) 
        jniThrowRuntimeException(env,
                "Camera is being used after Camera.release() was called");
    

    if (pContext != NULL) *pContext = context;
    return camera;

3 Libraries

3.1 Camera.cpp

  • 路径:frameworks/av/camera/Camera.cpp
  • startPreview()
    • mCamera 即是在 connect 过程中返回的 CameraClient,它具体实现了 startPreview() 接口。
    • 调用 CameraClient::startPreview()
// start preview mode
status_t Camera::startPreview()

    ALOGV("startPreview");
    sp <::android::hardware::ICamera> c = mCamera;
    if (c == 0) return NO_INIT;
    return c->startPreview();

3.2 CameraClient.cpp

  • 路径:frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
  • startPreview()
    • 通过 startCameraMode 函数进入具体的实现逻辑。
// start preview mode
status_t CameraClient::startPreview() 
    LOG1("startPreview (pid %d)", getCallingPid());
    return startCameraMode(CAMERA_PREVIEW_MODE);

  • startCameraMode()
    • 根据传入的参数 CAMERA_PREVIEW_MODE 确定进入的分支。
    • 调用 startPreviewMode()
// start preview or recording
status_t CameraClient::startCameraMode(camera_mode mode) 
    LOG1("startCameraMode(%d)", mode);
    Mutex::Autolock lock(mLock);
    status_t result = checkPidAndHardware();
    if (result != NO_ERROR) return result;

    switch(mode) 
        case CAMERA_PREVIEW_MODE:
            if (mSurface == 0 && mPreviewWindow == 0) 
                LOG1("mSurface is not set yet.");
                // still able to start preview in this case.
            
            return startPreviewMode();
        case CAMERA_RECORDING_MODE:
            if (mSurface == 0 && mPreviewWindow == 0) 
                ALOGE("mSurface or mPreviewWindow must be set before startRecordingMode.");
                return INVALID_OPERATION;
            
            return startRecordingMode();
        default:
            return UNKNOWN_ERROR;
    

  • startPreviewMode()
    • 如果预览已经存在,则直接返回成功信息。
    • 如果未存在,则继续往下走。
    • mHardwareCameraHardwareInterface 的实例,在 connect 过程的最后被初始化。
    • 通过 mHardware 调用 setPreviewWindow()startPreview() 接口。
    • 进入 HAL 层。
status_t CameraClient::startPreviewMode() 
    LOG1("startPreviewMode");
    status_t result = NO_ERROR;

    // if preview has been enabled, nothing needs to be done
    if (mHardware->previewEnabled()) 
        return NO_ERROR;
    

    if (mPreviewWindow != 0) 
        mHardware->setPreviewScalingMode(
            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
        mHardware->setPreviewTransform(mOrientation);
    
    mHardware->setPreviewWindow(mPreviewWindow);
    result = mHardware->startPreview();
    if (result == NO_ERROR) 
        mCameraService->updateProxyDeviceState(
            ICameraServiceProxy::CAMERA_STATE_ACTIVE,
            String8::format("%d", mCameraId));
    
    return result;

4 HAL

4.1 CameraHardwareInterface.h

  • 路径:frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
  • previewEnable()
    • 通过 mDevice->ops 继续向下调用(不是我们主要追踪的)。
    • mDevice 即是通过 hw_get_module() 相关流程进行初始化的设备实例,它的类型是 camera_device_t
    • 如果 preview 存在,则返回 true
    /**
     * Returns true if preview is enabled.
     */
    int previewEnabled()
    
        ALOGV("%s(%s)", __FUNCTION__, mName.string());
        if (mDevice->ops->preview_enabled)
            return mDevice->ops->preview_enabled(mDevice);
        return false;
    
  • setPreviewWindow()
    • 如果 set_preview_window 函数指针为空,则返回失败信息。
    • 若否,通过 mDevice->ops 继续向下调用(不是我们主要追踪的)。
    /** Set the ANativeWindow to which preview frames are sent */
    status_t setPreviewWindow(const sp<ANativeWindow>& buf)
    
        ALOGV("%s(%s) buf %p", __FUNCTION__, mName.string(), buf.get());
        if (mDevice->ops->set_preview_window) 
            mPreviewWindow = buf;
            if (buf != nullptr) 
                if (mPreviewScalingMode != NOT_SET) 
                    setPreviewScalingMode(mPreviewScalingMode);
                
                if (mPreviewTransform != NOT_SET) 
                    setPreviewTransform(mPreviewTransform);
                
            
            mHalPreviewWindow.user = this;
            ALOGV("%s &mHalPreviewWindow %p mHalPreviewWindow.user %p", __FUNCTION__,
                    &mHalPreviewWindow, mHalPreviewWindow.user);
            return mDevice->ops->set_preview_window(mDevice,
                    buf.get() ? &mHalPreviewWindow.nw : 0);
        
        return INVALID_OPERATION;
    
  • startPreview()
    • start_preview() 函数指针为空,则返回失败信息。
    • 若否,则通过 mDevice 进行下一步操作。
    • 关于 mDevice,我们结合 Camera.open() 流程与 hw_get_module() 相关逻辑,可以知道它的逻辑是这样的:
      • CameraService 启动时,会调用 onFirstRef()module 进行初始化,获取 module 实例。
      • open 过程中,CameraClient 连接 CameraServer 成功时,会实例化 CameraHardwareInterface,并传入 module 实例对其初始化。
      • 在初始化过程中,通过 module 实例对应的 open 方法,我们获得一个 device 实例,即 mDevice,这对应了具体的摄像头设备。
      • 通过 mDevice,我们就可以将对应的指令传达到硬件设备。
    • 通过对 camera_device_t 类型进行追踪,可以找到函数指针的一个具体指向。
    /**
     * Start preview mode.
     */
    status_t startPreview()
    
        ALOGV("%s(%s)", __FUNCTION__, mName.string());
        if (mDevice->ops->start_preview)
            return mDevice->ops->start_preview(mDevice);
        return INVALID_OPERATION;
    

4.2 camera.h

  • 路径:hardware/libhardware/include/hardware/camera.h
  • struct camera_device
    • 这里就声明了我们想要追踪的 camera_device_t
    • ops 对应的类型是 camera_device_ops_t ,这个结构中声明了函数指针。
typedef struct camera_device 
    /**
     * camera_device.common.version must be in the range
     * HARDWARE_DEVICE_API_VERSION(0,0)-(1,FF). CAMERA_DEVICE_API_VERSION_1_0 is
     * recommended.
     */
    hw_device_t common;
    camera_device_ops_t *ops;
    void *priv;
 camera_device_t;
  • struct camera_device_ops
    • 可以看到,所有关于 Camera 设备的操作,对应的函数指针都在这里声明了。但是看不出函数指针具体指向哪里。
    • Linux 下用 find . -name "*.cpp" | xargs grep "start_preview =" 可以找到一些对应的文件,这些文件所处的路径与具体的设备商有关。
    • 在这些文件中,就确定了函数指针的指向。
typedef struct camera_device_ops 
    int (*set_preview_window)(struct camera_device *,
            struct preview_stream_ops *window);

    void (*set_callbacks)(struct camera_device *,
            camera_notify_callback notify_cb,
            camera_data_callback data_cb,
            camera_data_timestamp_callback data_cb_timestamp,
            camera_request_memory get_memory,
            void *user);

    void (*enable_msg_type)(struct camera_device *, int32_t msg_type);

    void (*disable_msg_type)(struct camera_device *, int32_t msg_type);

    int (*msg_type_enabled)(struct camera_device *, int32_t msg_type);

    /**
     * Start preview mode.
     */
    int (*start_preview)(struct camera_device *);

    void (*stop_preview)(struct camera_device *);

    int (*preview_enabled)(struct camera_device *);

    int (*store_meta_data_in_buffers)(struct camera_device *, int enable);

    int (*start_recording)(struct camera_device *);

    void (*stop_recording)(struct camera_device *);

    int (*recording_enabled)(struct camera_device *);

    void (*release_recording_frame)(struct camera_device *,
                    const void *opaque);

    int (*auto_focus)(struct camera_device *);

    int (*cancel_auto_focus)(struct camera_device *);

    int (*take_picture)(struct camera_device *);

    int (*cancel_picture)(struct camera_device *);

    int (*set_parameters)(struct camera_device *, const char *parms);

    char *(*get_parameters)(struct camera_device *);

    void (*put_parameters)(struct camera_device *, char *);

    int (*send_command)(struct camera_device *,
                int32_t cmd, int32_t arg1, int32_t arg2);

    void (*release)(struct camera_device *);

    int (*dump)(struct camera_device *, int fd);
 camera_device_ops_t;

4.3 hardware/ti/omap4-aah/camera

通过 find 指令,找到了一些与函数指针指向有关的文件。从这些文件的路径上看,它们与不同的设备供应商有关。下面去 ti/omap4-aah 子文件夹去一探究竟。

4.3.1 CameraHal_Module.cpp

  • 路径:hardware/ti/omap4-aah/camera/CameraHal_Module.cpp
  • camera_device_open()
    • open 流程中,就指定了 ops 中指针的对应关系。
        memset(camera_device, 0, sizeof(*camera_device));
        memset(camera_ops, 0, sizeof(*camera_ops));

        camera_device->base.common.tag = HARDWARE_DEVICE_TAG;
        camera_device->base.common.version = 0;
        camera_device->base.common.module = (hw_module_t *)(module);
        camera_device->base.common.close = camera_device_close;
        camera_device->base.ops = camera_ops;

        camera_ops->set_preview_window = camera_set_preview_window;
        camera_ops->set_callbacks = camera_set_callbacks;
        camera_ops->enable_msg_type = camera_enable_msg_type;
        camera_ops->disable_msg_type = camera_disable_msg_type;
        camera_ops->msg_type_enabled = camera_msg_type_enabled;
        camera_ops->start_preview = camera_start_preview;
        camera_ops->stop_preview = camera_stop_preview;
        camera_ops->preview_enabled = camera_preview_enabled;
        camera_ops->store_meta_data_in_buffers = camera_store_meta_data_in_buffers;
        camera_ops->start_recording = camera_start_recording;
        camera_ops->stop_recording = camera_stop_recording;
        camera_ops->recording_enabled = camera_recording_enabled;
        camera_ops->release_recording_frame = camera_release_recording_frame;
        camera_ops->auto_focus = camera_auto_focus;
        camera_ops->cancel_auto_focus = camera_cancel_auto_focus;
        camera_ops->take_picture = camera_take_picture;
        camera_ops->cancel_picture = camera_cancel_picture;
        camera_ops->set_parameters = camera_set_parameters;
        camera_ops->get_parameters = camera_get_parameters;
        camera_ops->put_parameters = camera_put_parameters;
        camera_ops->send_command = camera_send_command;
        camera_ops->release = camera_release;
        camera_ops->dump = camera_dump;

        *device = &camera_device->base.common;

        // -------- TI specific stuff --------

        camera_device->cameraid = cameraid;
  • camera_start_preview()
    • 注意 gCameraHalsCameraHal *
    • 通过调用 CameraHal::startPreview() 完成业务逻辑。
int camera_start_preview(struct camera_device * device)

    CAMHAL_LOG_MODULE_FUNCTION_NAME;

    int rv = -EINVAL;
    ti_camera_device_t* ti_dev = NULL;

    if(!device)
        return rv;

    ti_dev = (ti_camera_device_t*) device;

    rv = gCameraHals[ti_dev->cameraid]->startPreview();

    return rv;

4.3.2 CameraHal.cpp

  • 路径:hardware/ti/omap4-aah/camera/CameraHal.cpp
  • 这个文件对应的功能是,将 Camera Hardware Interface 映射到 V4L2
  • 注意两个声明:
    • extern "C" CameraAdapter* OMXCameraAdapter_Factory(size_t);
    • extern "C" CameraAdapter* V4LCameraAdapter_Factory(size_t);
  • 分别对应 OMXV4L 的适配器工厂,这里可能是将 Adapter 模式与 Factory 模式结合使用。
  • startPreview()
    • 源代码中带有大量注释,在这里我将其去掉,只关注调用逻辑。
    • 首先调用了 cameraPreviewInitialization() 函数进行初始化。
    • 通过 CameraAdapter 发送 CAMERA_START_PREVIEW 指令,若成功执行,则完成流程。
status_t CameraHal::startPreview() 
    LOG_FUNCTION_NAME;

    status_t ret = cameraPreviewInitialization();

    if (!mPreviewInitializationDone) return ret;

    mPreviewInitializationDone = false;

    if(mDisplayAdapter.get() != NULL) 
        CAMHAL_LOGDA("Enabling display");
        int width, height;
        mParameters.getPreviewSize(&width, &height);

#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
        ret = mDisplayAdapter->enableDisplay(width, height, &mStartPreview);
#else
        ret = mDisplayAdapter->enableDisplay(width, height, NULL);
#endif

        if ( ret != NO_ERROR ) 
            CAMHAL_LOGEA("Couldn't enable display");
            CAMHAL_ASSERT_X(false,
                "At this stage mCameraAdapter->mStateSwitchLock is still locked, "
                "deadlock is guaranteed");

            goto error;
        
    

    CAMHAL_LOGDA("Starting CameraAdapter preview mode");

    ret = mCameraAdapter->sendCommand(CameraAdapter::CAMERA_START_PREVIEW);

    if(ret!=NO_ERROR) 
        CAMHAL_LOGEA("Couldn't start preview w/ CameraAdapter");
        goto error;
    
    CAMHAL_LOGDA("Started preview");

    mPreviewEnabled = true;
    mPreviewStartInProgress = false;
    return ret;

    error:

        CAMHAL_LOGEA("Performing cleanup after error");

        //Do all the cleanup
        freePreviewBufs();
        mCameraAdapter->sendCommand(CameraAdapter::CAMERA_STOP_PREVIEW);
        if(mDisplayAdapter.get() != NULL) 
            mDisplayAdapter->disableDisplay(false);
        
        mAppCallbackNotifier->stop();
        mPreviewStartInProgress = false;
        mPreviewEnabled = false;
        LOG_FUNCTION_NAME_EXIT;

        return ret;