Media Module之Camera(三) Camera预览

分析Camera预览的过程,我是通过从底层向上层分析的,从jni->Native->HAL->v4l2->java。

Camera预览流程图

3.1 JNI

在java framework层中调用native方法:
frameworks/base/core/java/androidhardware/Camera.java

public native final void startPreview();

进入JNI层调用相关方法:
frameworks/base/core/jni/android_hardware_Camera.cpp.

static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
{
    ALOGV("startPreview");
    //获取native层中的Camera.cpp的实例
    sp<Camera> camera = get_native_camera(env, thiz, NULL);
    if (camera == 0) return;
    //调用native的startPreview()方法,并判断其返回值
    if (camera->startPreview() != NO_ERROR) {
        jniThrowRuntimeException(env, "startPreview failed");
        return;
    }
}

3.2 Native层

进入native层,首先调用的是在JNI应用的startPreview()方法:

// start preview mode
status_t Camera::startPreview()
{
    ALOGV("startPreview");
    sp <ICamera> c = mCamera;
    if (c == 0) return NO_INIT;
    return c->startPreview();
}

根据前面所分析的结果,其实就会调用到CameraService中CameraClient.cpp中

status_t CameraClient::startPreview() {
    LOG1("startPreview (pid %d)", getCallingPid());
    return startCameraMode(CAMERA_PREVIEW_MODE);
}
    CAMERA_PREVIEW_MODE,枚举类型是在CameraClient.h中定义的 。之后调用startCameraMode方法:
// 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方法:

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) {
        //适配显示窗口的大小
        native_window_set_scaling_mode(mPreviewWindow.get(),
                NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
        //调整帧数据的方向
        native_window_set_buffers_transform(mPreviewWindow.get(),
                mOrientation);
    }
    //设置mPreviewWindow为显示窗口
    mHardware->setPreviewWindow(mPreviewWindow);
    //HAL层启动预览    
    result = mHardware->startPreview();
    if (result == NO_ERROR) {
        mCameraService->updateProxyDeviceState(
            ICameraServiceProxy::CAMERA_STATE_ACTIVE,
            String8::format("%d", mCameraId));
    }
    return result;
}

其中mPreviewWindow是数据将要投射的预览窗口,是从应用层中的启动预览时设置的
。native_window_set_scaling_mode方法是设置窗口的缩放比例,native_window_set_buffers_transform设置数据的放向。之后启动了HAL层的启动预览的方法。

3.3 HAL层

下面进入到HAL层的处理frameworks\av\services\camera\libcameraservice\device1\ CameraHardwareInterface.h

/** 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;
        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;
}

status_t startPreview()
{
    ALOGV("%s(%s)", __FUNCTION__, mName.string());
    if (mDevice->ops->start_preview)
        return mDevice->ops->start_preview(mDevice);
    return INVALID_OPERATION;
}

在HAL的接口定义中没有具体实现方法,交给了不同厂商的HAL层进行处理。对于LN50B63的hal层具体实现的代码路径:hardware\qcom\camera\QCamera2\HAL\,并且接下来分析的都是Non-ZSL模式,也就是标准的模式。ZSL模式是快速拍照模式,其处理模式与标准模式不同。

首先从hardware\qcom\camera\QCamera2\HAL\QCameraStateMachine.cpp状态机的初始状态procEvtPreviewStoppedState开始。

int32_t QCameraStateMachine::procEvtPreviewStoppedState(qcamera_sm_evt_enum_t evt,
                                                        void *payload)
{
    int32_t rc = NO_ERROR;
    qcamera_api_result_t result;
    memset(&result, 0, sizeof(qcamera_api_result_t));
    switch (evt) {
    ... ...
    case QCAMERA_SM_EVT_START_PREVIEW:
    {
        ALOGE("bluedai procEvtPreviewStoppedState");
        if (m_parent->mPreviewWindow == NULL) {
            rc = m_parent->preparePreview();
            if(rc == NO_ERROR) {
                // preview window is not set yet, move to previewReady state
                m_state = QCAMERA_SM_STATE_PREVIEW_READY;
            } else {
                ALOGE("%s: preparePreview failed",__func__);
            }
        } else {
            rc = m_parent->preparePreview();
            if (rc == NO_ERROR) {
                rc = m_parent->startPreview();
                if (rc != NO_ERROR) {
                    m_parent->unpreparePreview();
                } else {
                    // start preview success, move to previewing state
                    m_state = QCAMERA_SM_STATE_PREVIEWING;
                }
            }
        }
        result.status = rc;
        result.request_api = evt;
        result.result_type = QCAMERA_API_RESULT_TYPE_DEF;
        m_parent->signalAPIResult(&result);
    }
    break;

如果mPreviewWindow的值是空,调用QCamera2HardwareInterface的preparepreview方法;如果mPreviewWindow不为空,则直接开启QCamera2HardwareInterface的startPreview方法。

QCamera2HardwareInterface是HAL层方法的接口,真正的实现是在hardware\qcom\camera\QCamera2\HAL\ QCamera2HWI.cpp中。

int32_t QCamera2HardwareInterface::preparePreview()
{
    ALOGE("bluedai qcamera2hwi preparePreview");
    ATRACE_CALL();
    int32_t rc = NO_ERROR;

    if (mParameters.isZSLMode() && mParameters.getRecordingHintValue() !=true) {
        rc = addChannel(QCAMERA_CH_TYPE_ZSL);
        if (rc != NO_ERROR) {
            return rc;
        }
    } else {
        bool recordingHint = mParameters.getRecordingHintValue();
        if(recordingHint) {
            //stop face detection,longshot,etc if turned ON in Camera mode
            int32_t arg; //dummy arg
#ifndef VANILLA_HAL
            if (isLongshotEnabled()) {
                sendCommand(CAMERA_CMD_LONGSHOT_OFF, arg, arg);
            }
#endif
            if (mParameters.isFaceDetectionEnabled()) {
                sendCommand(CAMERA_CMD_STOP_FACE_DETECTION, arg, arg);
            }
#ifndef VANILLA_HAL
            if (mParameters.isHistogramEnabled()) {
                sendCommand(CAMERA_CMD_HISTOGRAM_OFF, arg, arg);
            }
#endif

            cam_dimension_t videoSize;
            mParameters.getVideoSize(&videoSize.width, &videoSize.height);
            if (!is4k2kResolution(&videoSize) && !mParameters.isLowPowerEnabled()) {
               rc = addChannel(QCAMERA_CH_TYPE_SNAPSHOT);
               if (rc != NO_ERROR) {
                   return rc;
               }
            }
            rc = addChannel(QCAMERA_CH_TYPE_VIDEO);
            if (rc != NO_ERROR) {
                delChannel(QCAMERA_CH_TYPE_SNAPSHOT);
                return rc;
            }
        }

        rc = addChannel(QCAMERA_CH_TYPE_PREVIEW);
        if (rc != NO_ERROR) {
            if (recordingHint) {
                delChannel(QCAMERA_CH_TYPE_SNAPSHOT);
                delChannel(QCAMERA_CH_TYPE_VIDEO);
            }
            return rc;
        }

        if (!recordingHint) {
            waitDefferedWork(mMetadataJob);
        }
    }

    return rc;
}

首先判断拍照的模式是否是ZSL模式,然后调用的其实是addChannel方法,在addChannel方法中,调用了addPreviewChannel方法。

int32_t QCamera2HardwareInterface::addPreviewChannel()
{
    int32_t rc = NO_ERROR;
    QCameraChannel *pChannel = NULL;

    if (m_channels[QCAMERA_CH_TYPE_PREVIEW] != NULL) {
        CDBG_HIGH("%s : Preview Channel already added and so delete it", __func__);
        delete m_channels[QCAMERA_CH_TYPE_PREVIEW];
        m_channels[QCAMERA_CH_TYPE_PREVIEW] = NULL;
    }

    pChannel = new QCameraChannel(mCameraHandle->camera_handle,
                                  mCameraHandle->ops);
    if (NULL == pChannel) {
        ALOGE("%s: no mem for preview channel", __func__);
        return NO_MEMORY;
    }

    // preview only channel, don't need bundle attr and cb
    rc = pChannel->init(NULL, NULL, NULL);
    if (rc != NO_ERROR) {
        ALOGE("%s: init preview channel failed, ret = %d", __func__, rc);
        delete pChannel;
        return rc;
    }

    // meta data stream always coexists with preview if applicable
    rc = addStreamToChannel(pChannel, CAM_STREAM_TYPE_METADATA,
                            metadata_stream_cb_routine, this);
    if (rc != NO_ERROR) {
        ALOGE("%s: add metadata stream failed, ret = %d", __func__, rc);
        delete pChannel;
        return rc;
    }

    if (isNoDisplayMode()) {
        rc = addStreamToChannel(pChannel, CAM_STREAM_TYPE_PREVIEW,
                                nodisplay_preview_stream_cb_routine, this);
    } else {
        rc = addStreamToChannel(pChannel, CAM_STREAM_TYPE_PREVIEW,
                                preview_stream_cb_routine, this);
    }
    if (rc != NO_ERROR) {
        ALOGE("%s: add preview stream failed, ret = %d", __func__, rc);
        delete pChannel;
        return rc;
    }

    m_channels[QCAMERA_CH_TYPE_PREVIEW] = pChannel;
    return rc;
}

在addPreviewChannel方法中,对QCameraChannel进行了实例化,并且调用了初始化方法init方法,然后调用addStreamToChannel方法,把数据加载到对应的管道中。维护了m_channels数组,最后的时候将QCAMERA_CH_TYPE_PREVIEW的元素设置成了当前的QCameraChannel对象。

int32_t QCamera2HardwareInterface::addStreamToChannel(QCameraChannel *pChannel,
                                                      cam_stream_type_t streamType,
                                                      stream_cb_routine streamCB,
                                                      void *userData)
{
    ALOGE("bluedai qcamera2hwi addStreamToChannel");
    int32_t rc = NO_ERROR;

    if (streamType == CAM_STREAM_TYPE_RAW) {
        prepareRawStream(pChannel);
    }
    QCameraHeapMemory *pStreamInfo = allocateStreamInfoBuf(streamType);
    if (pStreamInfo == NULL) {
        ALOGE("%s: no mem for stream info buf", __func__);
        return NO_MEMORY;
    }
    uint8_t minStreamBufNum = getBufNumRequired(streamType);
    bool bDynAllocBuf = false;
    if (isZSLMode() && streamType == CAM_STREAM_TYPE_SNAPSHOT) {
        bDynAllocBuf = true;
    }

    if ( ( streamType == CAM_STREAM_TYPE_SNAPSHOT ||
            streamType == CAM_STREAM_TYPE_POSTVIEW ||
            streamType == CAM_STREAM_TYPE_METADATA ||
            streamType == CAM_STREAM_TYPE_RAW) &&
            !isZSLMode() &&
            !isLongshotEnabled() &&
            !mParameters.getRecordingHintValue()) {
        rc = pChannel->addStream(*this,
                pStreamInfo,
                minStreamBufNum,
                &gCamCapability[mCameraId]->padding_info,
                streamCB, userData,
                bDynAllocBuf,
                true);

        // Queue buffer allocation for Snapshot and Metadata streams
        if ( !rc ) {
            DefferWorkArgs args;
            DefferAllocBuffArgs allocArgs;

            memset(&args, 0, sizeof(DefferWorkArgs));
            memset(&allocArgs, 0, sizeof(DefferAllocBuffArgs));
            allocArgs.type = streamType;
            allocArgs.ch = pChannel;
            args.allocArgs = allocArgs;

            if (streamType == CAM_STREAM_TYPE_SNAPSHOT) {
                mSnapshotJob = queueDefferedWork(CMD_DEFF_ALLOCATE_BUFF,
                        args);

                if ( mSnapshotJob == -1) {
                    rc = UNKNOWN_ERROR;
                }
            } else if (streamType == CAM_STREAM_TYPE_METADATA) {
                mMetadataJob = queueDefferedWork(CMD_DEFF_ALLOCATE_BUFF,
                        args);

                if ( mMetadataJob == -1) {
                    rc = UNKNOWN_ERROR;
                }
            } else if (streamType == CAM_STREAM_TYPE_RAW) {
                mRawdataJob = queueDefferedWork(CMD_DEFF_ALLOCATE_BUFF,
                        args);

                if ( mRawdataJob == -1) {
                    rc = UNKNOWN_ERROR;
                }
            }
        }
    } else {
        rc = pChannel->addStream(*this,
                pStreamInfo,
                minStreamBufNum,
                &gCamCapability[mCameraId]->padding_info,
                streamCB, userData,
                bDynAllocBuf,
                false);
    }

    if (rc != NO_ERROR) {
        ALOGE("%s: add stream type (%d) failed, ret = %d",
              __func__, streamType, rc);
        pStreamInfo->deallocate();
        delete pStreamInfo;
        // Returning error will delete corresponding channel but at the same time some of
        // deffered streams in same channel might be still in process of allocating buffers
        // by CAM_defrdWrk thread.
        waitDefferedWork(mMetadataJob);
        waitDefferedWork(mPostviewJob);
        waitDefferedWork(mSnapshotJob);
        waitDefferedWork(mRawdataJob);
        return rc;
    }

    return rc;
}

可以看出,在此方法中设置的Snapshot和Metadata流,并且分配buffer。然后调用
hardware\qcom\camera\QCamera2\HAL\ QCameraChannel.cpp的addStream方法。

int32_t QCameraChannel::addStream(QCameraAllocator &allocator,
                                  QCameraHeapMemory *streamInfoBuf,
                                  uint8_t minStreamBufNum,
                                  cam_padding_info_t *paddingInfo,
                                  stream_cb_routine stream_cb,
                                  void *userdata,
                                  bool bDynAllocBuf,
                                  bool bDeffAlloc)
{
    int32_t rc = NO_ERROR;
    if (mStreams.size() >= MAX_STREAM_NUM_IN_BUNDLE) {
        ALOGE("%s: stream number (%d) exceeds max limit (%d)",
              __func__, mStreams.size(), MAX_STREAM_NUM_IN_BUNDLE);
        return BAD_VALUE;
    }
    QCameraStream *pStream = new QCameraStream(allocator,
                                               m_camHandle,
                                               m_handle,
                                               m_camOps,
                                               paddingInfo,
                                               bDeffAlloc);
    if (pStream == NULL) {
        ALOGE("%s: No mem for Stream", __func__);
        return NO_MEMORY;
    }

    rc = pStream->init(streamInfoBuf, minStreamBufNum,
                       stream_cb, userdata, bDynAllocBuf);
    if (rc == 0) {
        mStreams.add(pStream);
    } else {
        delete pStream;
    }
    return rc;
}

实例化QCameraStream,并且调用初始化init方法,其中mStreams 是装有QCameraStream 的容器,在vector中添加此pStream 实例。
hardware\qcom\camera\QCamera2\HAL\ QCameraStream.cpp

int32_t QCameraStream::init(QCameraHeapMemory *streamInfoBuf,
                            uint8_t minNumBuffers,
                            stream_cb_routine stream_cb,
                            void *userdata,
                            bool bDynallocBuf)
{
    int32_t rc = OK;
    ssize_t bufSize = BAD_INDEX;

    mHandle = mCamOps->add_stream(mCamHandle, mChannelHandle);
    if (!mHandle) {
        ALOGE("add_stream failed");
        rc = UNKNOWN_ERROR;
        goto done;
    }

    // assign and map stream info memory
    mStreamInfoBuf = streamInfoBuf;
    mStreamInfo = reinterpret_cast<cam_stream_info_t *>(mStreamInfoBuf->getPtr(0));
    mNumBufs = minNumBuffers;

    bufSize = mStreamInfoBuf->getSize(0);
    if (BAD_INDEX != bufSize) {
        rc = mCamOps->map_stream_buf(mCamHandle,
                mChannelHandle, mHandle, CAM_MAPPING_BUF_TYPE_STREAM_INFO,
                0, -1, mStreamInfoBuf->getFd(0), (uint32_t)bufSize);
        if (rc < 0) {
            ALOGE("Failed to map stream info buffer");
            goto err1;
        }
    } else {
        ALOGE("Failed to retrieve buffer size (bad index)");
        goto err1;
    }

    // Calculate buffer size for deffered allocation
    if (mDefferedAllocation) {
        rc = calcOffset(mStreamInfo);
        if (rc < 0) {
            ALOGE("%s : Failed to calculate stream offset", __func__);
            goto err1;
        }
    } else {
        rc = configStream();
        if (rc < 0) {
            ALOGE("%s : Failed to config stream ", __func__);
            goto err1;
        }
    }

    mDataCB = stream_cb;
    mUserData = userdata;
    mDynBufAlloc = bDynallocBuf;
    return 0;

err1:
    mCamOps->delete_stream(mCamHandle, mChannelHandle, mHandle);
    mHandle = 0;
    mStreamInfoBuf = NULL;
    mStreamInfo = NULL;
    mNumBufs = 0;
done:
    return rc;
}

mCamOps->add_stream,其具体的调用顺序是:
mm_camera_interface.c (mm_camera_intf_add_stream()) ->
mm_camera.c (mm_camera_add_stream()) ->
mm_camera_channel.c (mm_channel_fsm_fn()) ->
mm_channel_fsm_fn_stopped() -> mm_channel_add_stream()
接下来分析
hardware\qcom\camera\QCamera2\stack\mm-camera-interface\src\mm_camera_channel.c中的mm_channel_add_stream方法:

uint32_t mm_channel_add_stream(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    uint8_t idx = 0;
    uint32_t s_hdl = 0;
    mm_stream_t *stream_obj = NULL;

    CDBG("%s : E", __func__);
    /* check available stream */
    for (idx = 0; idx < MAX_STREAM_NUM_IN_BUNDLE; idx++) {
        if (MM_STREAM_STATE_NOTUSED == my_obj->streams[idx].state) {
            stream_obj = &my_obj->streams[idx];
            break;
        }
    }
    if (NULL == stream_obj) {
        CDBG_ERROR("%s: streams reach max, no more stream allowed to add", __func__);
        return s_hdl;
    }

    /* initialize stream object */
    memset(stream_obj, 0, sizeof(mm_stream_t));
    stream_obj->my_hdl = mm_camera_util_generate_handler(idx);
    stream_obj->ch_obj = my_obj;
    pthread_mutex_init(&stream_obj->buf_lock, NULL);
    pthread_mutex_init(&stream_obj->cb_lock, NULL);
    pthread_mutex_init(&stream_obj->cmd_lock, NULL);
    stream_obj->state = MM_STREAM_STATE_INITED;

    /* acquire stream */
    rc = mm_stream_fsm_fn(stream_obj, MM_STREAM_EVT_ACQUIRE, NULL, NULL);
    if (0 == rc) {
        s_hdl = stream_obj->my_hdl;
    } else {
        /* error during acquire, de-init */
        pthread_mutex_destroy(&stream_obj->buf_lock);
        pthread_mutex_destroy(&stream_obj->cb_lock);
        pthread_mutex_destroy(&stream_obj->cmd_lock);
        memset(stream_obj, 0, sizeof(mm_stream_t));
    }
    CDBG("%s : stream handle = %d", __func__, s_hdl);
    return s_hdl;
}

可以看出,此方法首先去确认可用的流,然后初始化流对象,最后调用mm_stream_fsm_fn
去获取流。
在mm_camera_stream.c首先初始化调用的是mm_stream_fsm_inited方法,然后操作video node,然后设置状态为MM_STREAM_STATE_ACQUIRED并调用mm_stream_fsm_acquired方法。
hardware\qcom\camera\QCamera2\stack\mm-camera-interface\src\mm_camera_stream.c

int32_t mm_stream_fsm_inited(mm_stream_t *my_obj,
                             mm_stream_evt_type_t evt,
                             void * in_val,
                             void * out_val)
{
    int32_t rc = 0;
    char dev_name[MM_CAMERA_DEV_NAME_LEN];
    char t_devname[MM_CAMERA_DEV_NAME_LEN];
    const char *temp_dev_name = NULL;

    CDBG("%s: E, my_handle = 0x%x, fd = %d, state = %d",
         __func__, my_obj->my_hdl, my_obj->fd, my_obj->state);

    switch(evt) {
    case MM_STREAM_EVT_ACQUIRE:
        if ((NULL == my_obj->ch_obj) ||
                ((NULL != my_obj->ch_obj) && (NULL == my_obj->ch_obj->cam_obj))) {
            CDBG_ERROR("%s: NULL channel or camera obj\n", __func__);
            rc = -1;
            break;
        }
        temp_dev_name = mm_camera_util_get_dev_name(my_obj->ch_obj->cam_obj->my_hdl);
        if (temp_dev_name == NULL) {
            CDBG_ERROR("%s: dev name is NULL",__func__);
            rc = -1;
            break;
        }
        strlcpy(t_devname, temp_dev_name, sizeof(t_devname));
        snprintf(dev_name, sizeof(dev_name), "/dev/%s",t_devname );

        my_obj->fd = open(dev_name, O_RDWR | O_NONBLOCK);
        if (my_obj->fd <= 0) {
            CDBG_ERROR("%s: open dev returned %d\n", __func__, my_obj->fd);
            rc = -1;
            break;
        }
        CDBG("%s: open dev fd = %d\n", __func__, my_obj->fd);
        rc = mm_stream_set_ext_mode(my_obj);
        if (0 == rc) {
            my_obj->state = MM_STREAM_STATE_ACQUIRED;
        } else {
            /* failed setting ext_mode
             * close fd */
            close(my_obj->fd);
            my_obj->fd = 0;
            break;
        }
        break;
    default:
        CDBG_ERROR("%s: invalid state (%d) for evt (%d), in(%p), out(%p)",
                   __func__, my_obj->state, evt, in_val, out_val);
        break;
    }
    return rc;
}

返回到QCameraStream::init方法中,继续调用了configStream()方法,去配置流的相关属性。

int32_t QCameraStream::configStream()
{
    int rc = NO_ERROR;

    // Configure the stream
    mm_camera_stream_config_t stream_config;
    stream_config.stream_info = mStreamInfo;
    stream_config.mem_vtbl = mMemVtbl;
    stream_config.stream_cb = dataNotifyCB;
    stream_config.padding_info = mPaddingInfo;
    stream_config.userdata = this;
    rc = mCamOps->config_stream(mCamHandle,
                mChannelHandle, mHandle, &stream_config);

    return rc;
}

mm_camera_interface.c (mm_camera_intf_config_stream()) ->
mm_camera.c (mm_camera_config_stream()) ->
mm_camera_channel.c (mm_channel_config_stream()) ->
mm_camera_stream.c(mm_stream_fsm_acquired) ->
mm_stream_config()

在mm_stream_config()方法中,

int32_t mm_stream_config(mm_stream_t *my_obj,
                         mm_camera_stream_config_t *config)
{
    int32_t rc = 0;
    CDBG("%s: E, my_handle = 0x%x, fd = %d, state = %d",
         __func__, my_obj->my_hdl, my_obj->fd, my_obj->state);
    my_obj->stream_info = config->stream_info;
    my_obj->buf_num = (uint8_t) config->stream_info->num_bufs;
    my_obj->mem_vtbl = config->mem_vtbl;
    my_obj->padding_info = config->padding_info;
    /* cd through intf always palced at idx 0 of buf_cb */
    my_obj->buf_cb[0].cb = config->stream_cb;
    my_obj->buf_cb[0].user_data = config->userdata;
    my_obj->buf_cb[0].cb_count = -1; /* infinite by default */

    rc = mm_stream_sync_info(my_obj);
    if (rc == 0) {
        rc = mm_stream_set_fmt(my_obj);
    }
    return rc;
}

在此方法中,向kernel中发送sync请求,并且通过V4L2设置流的格式。

接下来分析start_preview的HAL流程。
在所有的preview的准备工作结束之后,到QCamera2HWI.cpp类的startPreview()方法中,

int QCamera2HardwareInterface::startPreview()
{
    CDBG_HIGH("bluedai qcamera2hwi startPreview");
    ATRACE_CALL();
    int32_t rc = NO_ERROR;
    CDBG_HIGH("%s: E", __func__);
    updateThermalLevel(mThermalLevel);
    // start preview stream
    if (mParameters.isZSLMode() && mParameters.getRecordingHintValue() !=true) {
        rc = startChannel(QCAMERA_CH_TYPE_ZSL);
    } else {
        rc = startChannel(QCAMERA_CH_TYPE_PREVIEW);
        /*
          CAF needs cancel auto focus to resume after snapshot.
          Focus should be locked till take picture is done.
          In Non-zsl case if focus mode is CAF then calling cancel auto focus
          to resume CAF.
        */
        cam_focus_mode_type focusMode = mParameters.getFocusMode();
        if (focusMode == CAM_FOCUS_MODE_CONTINOUS_PICTURE)
            mCameraHandle->ops->cancel_auto_focus(mCameraHandle->camera_handle);
    }
#ifdef TARGET_TS_MAKEUP
    if (mMakeUpBuf == NULL) {
        int pre_width, pre_height;
        mParameters.getPreviewSize(&pre_width, &pre_height);
        mMakeUpBuf = new unsigned char[pre_width*pre_height*3/2];
        CDBG_HIGH("prewidht=%d,preheight=%d",pre_width, pre_height);
    }
#endif
    CDBG_HIGH("%s: X", __func__);
    return rc;
}

接着就调用了startChannel方法,在m_channels数组中取出对应的QCameraChannel对象,并且调用config()和start()方法。

int32_t QCamera2HardwareInterface::startChannel(qcamera_ch_type_enum_t ch_type)
{
    int32_t rc = UNKNOWN_ERROR;
    if (m_channels[ch_type] != NULL) {
        rc = m_channels[ch_type]->config();
        if (NO_ERROR == rc) {
            rc = m_channels[ch_type]->start();
        }
    }
    return rc;
}

在QCameraChannel的start方法中,

int32_t QCameraChannel::start()
{
    int32_t rc = NO_ERROR;

    if (mStreams.size() > 1) {
        // there is more than one stream in the channel
        // we need to notify mctl that all streams in this channel need to be bundled
        cam_bundle_config_t bundleInfo;
        memset(&bundleInfo, 0, sizeof(bundleInfo));
        rc = m_camOps->get_bundle_info(m_camHandle, m_handle, &bundleInfo);
        if (rc != NO_ERROR) {
            ALOGE("%s: get_bundle_info failed", __func__);
            return rc;
        }
        if (bundleInfo.num_of_streams > 1) {
            for (int i = 0; i < bundleInfo.num_of_streams; i++) {
                QCameraStream *pStream = getStreamByServerID(bundleInfo.stream_ids[i]);
                if (pStream != NULL) {
                    if (pStream->isTypeOf(CAM_STREAM_TYPE_METADATA)) {
                        // Skip metadata for reprocess now because PP module cannot handle meta data
                        // May need furthur discussion if Imaginglib need meta data
                        continue;
                    }

                    cam_stream_parm_buffer_t param;
                    memset(&param, 0, sizeof(cam_stream_parm_buffer_t));
                    param.type = CAM_STREAM_PARAM_TYPE_SET_BUNDLE_INFO;
                    param.bundleInfo = bundleInfo;
                    rc = pStream->setParameter(param);
                    if (rc != NO_ERROR) {
                        ALOGE("%s: stream setParameter for set bundle failed", __func__);
                        return rc;
                    }
                }
            }
        }
    }

    for (size_t i = 0; i < mStreams.size(); i++) {
        if ((mStreams[i] != NULL) &&
                (m_handle == mStreams[i]->getChannelHandle())) {
            mStreams[i]->start();
        }
    }
    rc = m_camOps->start_channel(m_camHandle, m_handle);

    if (rc != NO_ERROR) {
        for (size_t i = 0; i < mStreams.size(); i++) {
            if ((mStreams[i] != NULL) &&
                    (m_handle == mStreams[i]->getChannelHandle())) {
                mStreams[i]->stop();
            }
        }
    } else {
        m_bIsActive = true;
        for (size_t i = 0; i < mStreams.size(); i++) {
            if (mStreams[i] != NULL) {
                mStreams[i]->cond_signal();
            }
        }
    }

    return rc;
}

在start方法中,最主要的是两个,一个是调用了QCameraStream的start()方法,一个是调用了start_channel()方法。
首先看QCameraStream::start(),启动了主线程,处理流相关的操作。

int32_t QCameraStream::start()
{
    int32_t rc = 0;
    rc = mProcTh.launch(dataProcRoutine, this);
    if (rc == NO_ERROR) {
        m_bActive = true;
    }
    pthread_mutex_init(&m_lock, NULL);
    pthread_cond_init(&m_cond, NULL);
    return rc;
}

mm_camera_intf_start_channel() ->
mm_camera_start_channel() ->
mm_channel_fsm_fn_stopped() ->
mm_channel_start()
mm_channel_fsm_fn_active()

在start_channel()方法中,会继续调用到mm_camera_channel.c中的mm_channel_start方法。

int32_t mm_channel_start(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    int i, j;
    mm_stream_t *s_objs[MAX_STREAM_NUM_IN_BUNDLE] = {NULL};
    uint8_t num_streams_to_start = 0;
    mm_stream_t *s_obj = NULL;
    int meta_stream_idx = 0;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;

    for (i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if (my_obj->streams[i].my_hdl > 0) {
            s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                          my_obj->streams[i].my_hdl);
            if (NULL != s_obj) {
                stream_type = s_obj->stream_info->stream_type;
                /* remember meta data stream index */
                if ((stream_type == CAM_STREAM_TYPE_METADATA) &&
                        (s_obj->ch_obj == my_obj)) {
                    meta_stream_idx = num_streams_to_start;
                }
                s_objs[num_streams_to_start++] = s_obj;
            }
        }
    }

    if (meta_stream_idx > 0 ) {
        /* always start meta data stream first, so switch the stream object with the first one */
        s_obj = s_objs[0];
        s_objs[0] = s_objs[meta_stream_idx];
        s_objs[meta_stream_idx] = s_obj;
    }

    if (NULL != my_obj->bundle.super_buf_notify_cb) {
        /* need to send up cb, therefore launch thread */
        /* init superbuf queue */
        mm_channel_superbuf_queue_init(&my_obj->bundle.superbuf_queue);
        my_obj->bundle.superbuf_queue.num_streams = num_streams_to_start;
        my_obj->bundle.superbuf_queue.expected_frame_id = 0;
        my_obj->bundle.superbuf_queue.expected_frame_id_without_led = 0;
        for (i = 0; i < num_streams_to_start; i++) {
            /* Only bundle streams that belong to the channel */
            if(s_objs[i]->ch_obj == my_obj) {
                /* set bundled flag to streams */
                s_objs[i]->is_bundled = 1;
            }
            /* init bundled streams to invalid value -1 */
            my_obj->bundle.superbuf_queue.bundled_streams[i] = s_objs[i]->my_hdl;
        }
        /* launch cb thread for dispatching super buf through cb */
        snprintf(my_obj->cb_thread.threadName, THREAD_NAME_SIZE, "CAM_SuperBuf");
        mm_camera_cmd_thread_launch(&my_obj->cb_thread,
                                    mm_channel_dispatch_super_buf,
                                    (void*)my_obj);

        /* launch cmd thread for super buf dataCB */
        snprintf(my_obj->cmd_thread.threadName, THREAD_NAME_SIZE, "CAM_SuperBufCB");
        mm_camera_cmd_thread_launch(&my_obj->cmd_thread,
                                    mm_channel_process_stream_buf,
                                    (void*)my_obj);
        /* set flag to TRUE */
        my_obj->bundle.is_active = TRUE;
    }
    for (i = 0; i < num_streams_to_start; i++) {
        /* stream that are linked to this channel should not be started */
        if (s_objs[i]->ch_obj != my_obj) {
            pthread_mutex_lock(&s_objs[i]->linked_stream->buf_lock);
            s_objs[i]->linked_stream->linked_obj = my_obj;
            s_objs[i]->linked_stream->is_linked = 1;
            pthread_mutex_unlock(&s_objs[i]->linked_stream->buf_lock);
            continue;
        }
        /* all streams within a channel should be started at the same time */
        if (s_objs[i]->state == MM_STREAM_STATE_ACTIVE) {
            CDBG_ERROR("%s: stream already started idx(%d)", __func__, i);
            rc = -1;
            break;
        }
        /* allocate buf */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_GET_BUF,
                              NULL,
                              NULL);
        if (0 != rc) {
            CDBG_ERROR("%s: get buf failed at idx(%d)", __func__, i);
            break;
        }
        /* reg buf */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_REG_BUF,
                              NULL,
                              NULL);
        if (0 != rc) {
            CDBG_ERROR("%s: reg buf failed at idx(%d)", __func__, i);
            break;
        }
        /* start stream */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_START,
                              NULL,
                              NULL);
        if (0 != rc) {
            CDBG_ERROR("%s: start stream failed at idx(%d)", __func__, i);
            break;
        }
    }
    return rc;
}

可以看出,在此方法中,launch了super buf分发和streams流程的线程,之后调用mm_camera_stream.c里面处理stream的相关方法。

case MM_STREAM_EVT_START:
    {
        uint8_t has_cb = 0;
        uint8_t i;
        /* launch cmd thread if CB is not null */
        pthread_mutex_lock(&my_obj->cb_lock);
        for (i = 0; i < MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
            if(NULL != my_obj->buf_cb[i].cb) {
                has_cb = 1;
                break;
            }
        }
        pthread_mutex_unlock(&my_obj->cb_lock);

        pthread_mutex_lock(&my_obj->cmd_lock);
        if (has_cb) {
            snprintf(my_obj->cmd_thread.threadName, THREAD_NAME_SIZE, "CAM_StrmAppData");
            mm_camera_cmd_thread_launch(&my_obj->cmd_thread,
                                        mm_stream_dispatch_app_data,
                                        (void *)my_obj);
        }
        pthread_mutex_unlock(&my_obj->cmd_lock);

        my_obj->state = MM_STREAM_STATE_ACTIVE;
        rc = mm_stream_streamon(my_obj);
        if (0 != rc) {
            /* failed stream on, need to release cmd thread if it's launched */
            pthread_mutex_lock(&my_obj->cmd_lock);
            if (has_cb) {
                mm_camera_cmd_thread_release(&my_obj->cmd_thread);
            }
            pthread_mutex_unlock(&my_obj->cmd_lock);
            my_obj->state = MM_STREAM_STATE_REG;
            break;
        }
    }
    break;

在此方法中,launch分发数据的线程,向kernel发送V4L2请求。

3.4 preview数据流

在上面分析的QCamera2HardwareInterface::addPreviewChannel()方法中,其中注册了preview_stream_cb_routine回调,此回调就是处理预览数据的。

rc = addStreamToChannel(pChannel, CAM_STREAM_TYPE_PREVIEW,
                        preview_stream_cb_routine, this);

preview_stream_cb_routine方法在
hardware\qcom\camera\QCamera2\HAL\QCamera2HWICallbacks.cpp中实现。

void QCamera2HardwareInterface::preview_stream_cb_routine(mm_camera_super_buf_t *super_frame,
                                                          QCameraStream * stream,
                                                          void *userdata)
{
    ATRACE_CALL();
    CDBG("[KPI Perf] %s : BEGIN", __func__);
    int err = NO_ERROR;
    QCamera2HardwareInterface *pme = (QCamera2HardwareInterface *)userdata;
    QCameraGrallocMemory *memory = (QCameraGrallocMemory *)super_frame->bufs[0]->mem_info;
    ... ...
    
    // Display the buffer.
    CDBG("%p displayBuffer %d E", pme, idx);
    int dequeuedIdx = memory->displayBuffer(idx);
    if (dequeuedIdx < 0 || dequeuedIdx >= memory->getCnt()) {
        CDBG_HIGH("%s: Invalid dequeued buffer index %d from display",
              __func__, dequeuedIdx);
    } else {
        // Return dequeued buffer back to driver
        err = stream->bufDone((uint32_t)dequeuedIdx);
        if ( err < 0) {
            ALOGE("stream bufDone failed %d", err);
        }
    }

    // Handle preview data callback
    if (pme->mDataCb != NULL &&
            (pme->msgTypeEnabledWithLock(CAMERA_MSG_PREVIEW_FRAME) > 0)) {
        int32_t rc = pme->sendPreviewCallback(stream, memory, idx);
        if (NO_ERROR != rc) {
            ALOGE("%s: Preview callback was not sent succesfully", __func__);
        }

    }

    free(super_frame);
    CDBG("[KPI Perf] %s : END", __func__);
    return;
}

通过注释即可看出,对用在屏幕上投射的buffer和对上层应用去实现的data callback也做了相应处理。

callback主要有三种类型
notifyCallback
dataCallback
dataTimestampCallback

在之前分析client与server端相互连接时,在连接成功之后会返回一个new client,然后在client的构造函数中就对camera设置了notifyCallback ,dataCallback ,dataTimestampCallback三个回调,返回底层数据用于处理。
frameworks\av\services\camera\libcameraservice\api1\CameraClient.cpp

status_t CameraClient::initialize(CameraModule *module) {
    int callingPid = getCallingPid();
    status_t res;

    LOG1("CameraClient::initialize E (pid %d, id %d)", callingPid, mCameraId);

    // Verify ops permissions
    res = startCameraOps();
    if (res != OK) {
        return res;
    }

    char camera_device_name[10];
    snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);

    mHardware = new CameraHardwareInterface(camera_device_name);
    res = mHardware->initialize(module);
    if (res != OK) {
        ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
                __FUNCTION__, mCameraId, strerror(-res), res);
        mHardware.clear();
        return res;
    }

    mHardware->setCallbacks(notifyCallback,
            dataCallback,
            dataCallbackTimestamp,
            (void *)(uintptr_t)mCameraId);

    // Enable zoom, error, focus, and metadata messages by default
    enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
                  CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);

    LOG1("CameraClient::initialize X (pid %d, id %d)", callingPid, mCameraId);
    return OK;
}

注册回调之后,需要到CameraClient中去找具体的实现。

void CameraClient::dataCallback(int32_t msgType,
        const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
    LOG2("dataCallback(%d)", msgType);

    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
    if (client.get() == nullptr) return;

    if (!client->lockIfMessageWanted(msgType)) return;
    if (dataPtr == 0 && metadata == NULL) {
        ALOGE("Null data returned in data callback");
        client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
        return;
    }

    switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {
        case CAMERA_MSG_PREVIEW_FRAME:
            client->handlePreviewData(msgType, dataPtr, metadata);
            break;
        case CAMERA_MSG_POSTVIEW_FRAME:
            client->handlePostview(dataPtr);
            break;
        case CAMERA_MSG_RAW_IMAGE:
            client->handleRawPicture(dataPtr);
            break;
        case CAMERA_MSG_COMPRESSED_IMAGE:
            client->handleCompressedPicture(dataPtr);
            break;
        default:
            client->handleGenericData(msgType, dataPtr, metadata);
            break;
    }
}

继续分析handlePreviewData方法。

void CameraClient::handlePreviewData(int32_t msgType,
                                              const sp<IMemory>& mem,
                                              camera_frame_metadata_t *metadata) {
    ssize_t offset;
    size_t size;
    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);

    // local copy of the callback flags
    int flags = mPreviewCallbackFlag;

    // is callback enabled?
    if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
        // If the enable bit is off, the copy-out and one-shot bits are ignored
        LOG2("frame callback is disabled");
        mLock.unlock();
        return;
    }

    // hold a strong pointer to the client
    sp<ICameraClient> c = mRemoteCallback;

    // clear callback flags if no client or one-shot mode
    if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
        LOG2("Disable preview callback");
        mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
                                  CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
                                  CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
        disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
    }

    if (c != 0) {
        // Is the received frame copied out or not?
        if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
            LOG2("frame is copied");
            copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);
        } else {
            LOG2("frame is forwarded");
            mLock.unlock();
            c->dataCallback(msgType, mem, metadata);
        }
    } else {
        mLock.unlock();
    }
}

copyFrameAndPostCopiedFrame方法就是两个buff区preview数据的投递。

void CameraClient::copyFrameAndPostCopiedFrame(
        int32_t msgType, const sp<ICameraClient>& client,
        const sp<IMemoryHeap>& heap, size_t offset, size_t size,
        camera_frame_metadata_t *metadata) {
    LOG2("copyFrameAndPostCopiedFrame");
    // It is necessary to copy out of pmem before sending this to
    // the callback. For efficiency, reuse the same MemoryHeapBase
    // provided it's big enough. Don't allocate the memory or
    // perform the copy if there's no callback.
    // hold the preview lock while we grab a reference to the preview buffer
    sp<MemoryHeapBase> previewBuffer;

    if (mPreviewBuffer == 0) {
        mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
    } else if (size > mPreviewBuffer->virtualSize()) {
        mPreviewBuffer.clear();
        mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
    }
    if (mPreviewBuffer == 0) {
        ALOGE("failed to allocate space for preview buffer");
        mLock.unlock();
        return;
    }
    previewBuffer = mPreviewBuffer;
    ... ...
    memcpy(previewBufferBase, (uint8_t *) heapBase + offset, size);
    sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
    if (frame == 0) {
        ALOGE("failed to allocate space for frame callback");
        mLock.unlock();
        return;
    }
    mLock.unlock();
    client->dataCallback(msgType, frame, metadata);
}

将数据处理成frame,继续调用客户端client->dataCallback
frameworks\av\camera\Camera.cpp

void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata)
{
    sp<CameraListener> listener;
    {
        Mutex::Autolock _l(mLock);
        listener = mListener;
    }
    if (listener != NULL) {
        listener->postData(msgType, dataPtr, metadata);
    }
}

通过listener的方式来往上层甩数据,此处的listener是在JNI中

static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
    ... ...
    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    context->incStrong((void*)android_hardware_Camera_native_setup);
    camera->setListener(context);
    ... ...
}

继续之前的postData方法。

void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr,
                                camera_frame_metadata_t *metadata)
{
    ... ...
    // return data based on callback type
    switch (dataMsgType) {
        case CAMERA_MSG_VIDEO_FRAME:
            // should never happen
            break;

        // For backward-compatibility purpose, if there is no callback
        // buffer for raw image, the callback returns null.
        case CAMERA_MSG_RAW_IMAGE:
            ALOGV("rawCallback");
            if (mRawImageCallbackBuffers.isEmpty()) {
                env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
                        mCameraJObjectWeak, dataMsgType, 0, 0, NULL);
            } else {
                copyAndPost(env, dataPtr, dataMsgType);
            }
            break;

        // There is no data.
        case 0:
            break;

        default:
            ALOGV("dataCallback(%d, %p)", dataMsgType, dataPtr.get());
            copyAndPost(env, dataPtr, dataMsgType);
            break;
    }
    ... ...
}

继续走到copyAndPost方法。

void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
{
    jbyteArray obj = NULL;

    // allocate Java byte array and copy data
    if (dataPtr != NULL) {
        ssize_t offset;
        size_t size;
        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
        ALOGV("copyAndPost: off=%zd, size=%zu", offset, size);
        uint8_t *heapBase = (uint8_t*)heap->base();

        if (heapBase != NULL) {
            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);

            if (msgType == CAMERA_MSG_RAW_IMAGE) {
                obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size);
            } else if (msgType == CAMERA_MSG_PREVIEW_FRAME && mManualBufferMode) {
                obj = getCallbackBuffer(env, &mCallbackBuffers, size);

                if (mCallbackBuffers.isEmpty()) {
                    ALOGV("Out of buffers, clearing callback!");
                    mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);
                    mManualCameraCallbackSet = false;

                    if (obj == NULL) {
                        return;
                    }
                }
            } else {
                ALOGV("Allocating callback buffer");
                obj = env->NewByteArray(size);
            }

            if (obj == NULL) {
                ALOGE("Couldn't allocate byte array for JPEG data");
                env->ExceptionClear();
            } else {
                env->SetByteArrayRegion(obj, 0, size, data);
            }
        } else {
            ALOGE("image heap is NULL");
        }
    }

    // post image data to Java
    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
            mCameraJObjectWeak, msgType, 0, 0, obj);
    if (obj) {
        env->DeleteLocalRef(obj);
    }
}

先建立一个byte数组obj,将data缓存数据存储进obj数组,CallStaticVoidMethod是c调用java函数,最后执行是在Camera.java框架中的postEventFromNative方法中。
frameworks\base\core\java\android\hardware\Camera.java

private static void postEventFromNative(Object camera_ref,
                                        int what, int arg1, int arg2, Object obj)
{
    Camera c = (Camera)((WeakReference)camera_ref).get();
    if (c == null)
        return;

    if (c.mEventHandler != null) {
        Message m = c.mEventHandler.obtainMessage(what, arg1, arg2, obj);
        c.mEventHandler.sendMessage(m);
    }
}

然后通过handleMessage去处理接收到的消息。

public void handleMessage(Message msg) {
    switch(msg.what) {
    ... ...
    case CAMERA_MSG_PREVIEW_FRAME:
        PreviewCallback pCb = mPreviewCallback;
        if (pCb != null) {
            if (mOneShot) {
                // Clear the callback variable before the callback
                // in case the app calls setPreviewCallback from
                // the callback function
                mPreviewCallback = null;
            } else if (!mWithBuffer) {
                // We're faking the camera preview mode to prevent
                // the app from being flooded with preview frames.
                // Set to oneshot mode again.
                setHasPreviewCallback(true, false);
            }
            pCb.onPreviewFrame((byte[])msg.obj, mCamera);
        }
        return;
    .... ....

至此,在camera.java中处理了数据的回调,并且在此handleMessage方法中也有对其他方法的回调,例如快门按键,拍照数据回调等。默认是没有previewcallback这个回调的,除非app层设置了setPreviewCallback,则可以将数据回调到上层。数据采集区与显示区两个缓存区的buff投递,是在HAL层进行处理的。

之后,在app层中,使用surfaceview去显示其preview数据即可,具体的实现在
frameworks\base\core\java\android\hardware\Camera.java中的setPreviewDisplay方法。此处的操作在之前所分析的初始化流程中也有体现,即在底层传入的surfaceview直接显示在显示区中的数据,而不用向上层去投递数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容