【Camera专题】HAL层-addChannel和startChannel简析

说在前面的话

最近看源码看得脑壳疼,晕头转向的,我觉得是我看源码的姿势不对,
过分追求细节,想要每一句都懂,实际上这么做,只会打击自己看源码的信心!

算了,就那样吧,人生本来就是一场毫无意义的旅行!

赚钱-买房-买车-结婚-生子-老去-死去-尘土!

一. addChanne简析;

1.1 addChannel(QCAMERA_CH_TYPE_PREVIEW)

hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp

int32_t QCamera2HardwareInterface::preparePreview()
{
    
    
    //添加preview通道
        rc = addChannel(QCAMERA_CH_TYPE_PREVIEW);
}

这是上篇文章没有分析的函数,涉及到数据流,所以本文来继续分析吧!
addChannel()->调用addPreviewChannel()

1.2 addPreviewChannel()

int32_t QCamera2HardwareInterface::addChannel(qcamera_ch_type_enum_t ch_type)
{
    
    
···
    case QCAMERA_CH_TYPE_PREVIEW:
        rc = addPreviewChannel();
···

该函数作用:添加包含 预览流 的 预览通道

int32_t QCamera2HardwareInterface::addPreviewChannel()
{
    
    
`
    CDBG_HIGH("%s :zcf camera_handle=%d ops=%p", __func__,mCameraHandle->camera_handle,mCameraHandle->ops);
    pChannel = new QCameraChannel(mCameraHandle->camera_handle,
                                  mCameraHandle->ops);
···

    // preview only channel, don't need bundle attr and cb
    rc = pChannel->init(NULL, NULL, NULL);
··· 

    // meta data stream always coexists with preview if applicable
    rc = addStreamToChannel(pChannel, CAM_STREAM_TYPE_METADATA,
                            metadata_stream_cb_routine, this);
···

    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);
    }
···
    m_channels[QCAMERA_CH_TYPE_PREVIEW] = pChannel;
    return rc;
}
  • 1.2.1 new QCameraChannel实例
    参数1:uint32_t cam_handle: 唯一标识
    参数2:mm_camera_ops_t *cam_ops:操作集合
    这里的cam_handle和cam_ops是成双成对出现的,
    cam_handle是cam_ops的 唯一标识,可以通过cam_handle获取到cam_ops指针。

    获取方式:cam_obj = mm_camera_util_get_camera_by_handler(camera_handle);

  • 1.2.2 pChannel->init(NULL, NULL, NULL) 初始化
    调用QCameraChannel::init 初始化通道。
    调用流程如下:

QCameraChannel::init->
mm_camera_intf_add_channel->
mm_camera_add_channel->
mm_channel_init->
mm_camera_poll_thread_launch

看一些关键函数:mm_camera_add_channel

uint32_t mm_camera_add_channel(mm_camera_obj_t *my_obj,
                               mm_camera_channel_attr_t *attr,
                               mm_camera_buf_notify_t channel_cb,
                               void *userdata)
{
    
    
    mm_channel_t *ch_obj = NULL;//这里定义mm_channel_t结构体指针
    uint8_t ch_idx = 0; 
    uint32_t ch_hdl = 0; 
···
    if (NULL != ch_obj) {
    
    
        /* 初始化 channel 结构体 */
        memset(ch_obj, 0, sizeof(mm_channel_t));
        ch_hdl = mm_camera_util_generate_handler(ch_idx);
        ALOGE("%s zcf ch_hdl=%d\n", __func__,ch_hdl);
        ch_obj->my_hdl = ch_hdl;
        ch_obj->state = MM_CHANNEL_STATE_STOPPED;
        ch_obj->cam_obj = my_obj;
        pthread_mutex_init(&ch_obj->ch_lock, NULL);
        //继续调用mm_channel_init方法
        mm_channel_init(ch_obj, attr, channel_cb, userdata);
    }
    pthread_mutex_unlock(&my_obj->cam_lock);
    return ch_hdl;
}

这里就初始化了mm_channel_t *ch_obj这个结构体。

扫描二维码关注公众号,回复: 13552674 查看本文章

看一些关键函数:mm_channel_init

int32_t mm_channel_init(mm_channel_t *my_obj,
                        mm_camera_channel_attr_t *attr,
                        mm_camera_buf_notify_t channel_cb,
                        void *userdata)
{
    
    
    int32_t rc = 0; 

    my_obj->bundle.super_buf_notify_cb = channel_cb;
    my_obj->bundle.user_data = userdata;
    if (NULL != attr) {
    
    
        my_obj->bundle.superbuf_queue.attr = *attr;
    }    

    CDBG("%s : Launch data poll thread in channel open", __func__);
    snprintf(my_obj->threadName, THREAD_NAME_SIZE, "DataPoll");
    mm_camera_poll_thread_launch(&my_obj->poll_thread[0],
                                 MM_CAMERA_POLL_TYPE_DATA);

    /* change state to stopped state */
    my_obj->state = MM_CHANNEL_STATE_STOPPED;
    return rc;
}

最终调用到mm_camera_poll_thread_launch
该函数作用:在打开的通道中启动数据轮询线程
接着my_obj->state = MM_CHANNEL_STATE_STOPPED;

  • 1.2.3 addStreamToChannel 数据流类型:CAM_STREAM_TYPE_METADATA
    通道有了,通过addStreamToChannel 把数据流添加到通道里!首先添加的是元数据流

addStreamToChannel(pChannel, CAM_STREAM_TYPE_METADATA,
metadata_stream_cb_routine, this);
这里设置了数据回调函数 metadata_stream_cb_routine,后续数据都会通过这个函数回调到上层!

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

    if (streamType == CAM_STREAM_TYPE_RAW) {
    
    
        prepareRawStream(pChannel);
    }    
    //这里申请内存空间buffer
    QCameraHeapMemory *pStreamInfo = allocateStreamInfoBuf(streamType);
···
    //buffer的个数,元数据type=7,buffer个数为8
    uint8_t minStreamBufNum = getBufNumRequired(streamType);
···
    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()) {
    
    
        //调用addStream添加流
        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);
    }

···
    return rc;
}

先申请 申请内存空间buffer,元数据对应的streamtype 为7,申请的buffer个数: 8 。
然后调用 pChannel->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)
{
    
    
···
    //new一个QCameraStream 实例对象
    QCameraStream *pStream = new QCameraStream(allocator,
                                               m_camHandle,
                                               m_handle,
                                               m_camOps,
                                               paddingInfo,
                                               bDeffAlloc);
···
    //调用初始化方法
    rc = pStream->init(streamInfoBuf, minStreamBufNum,
                       stream_cb, userdata, bDynAllocBuf);
    if (rc == 0) {
    
     
        //初始化成功,添加流
        mStreams.add(pStream);
    } else {
    
    
        delete pStream;
    }    
    return rc;
}

pStream->init初始化成功后,接着mStreams.add(pStream),
这里mStreams是vector容器,往里面添加此pStream 实例,用于维护!

pStream->init 是怎么初始化的,调用流程如下:

mHandle = mCamOps->add_stream(mCamHandle, mChannelHandle);->
(.add_stream = mm_camera_intf_add_stream)
mm_camera_intf_add_stream->
mm_camera_add_stream->
mm_channel_fsm_fn(ch_obj,MM_CHANNEL_EVT_ADD_STREAM,
                                      NULL,(void *)&s_hdl);->
s_hdl = mm_channel_add_stream(my_obj);->
rc = mm_stream_fsm_fn(stream_obj, MM_STREAM_EVT_ACQUIRE, NULL, NULL);->
m_stream_fsm_inited(my_obj, evt, in_val, out_val);

看一些关键函数:mm_channel_add_stream

hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_channel.c

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 结构体指针
    mm_stream_t *stream_obj = NULL;

    CDBG("%s : E", __func__);
    /* 检查流是否可用 */
    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;
        }
    }
···
    /*初始化  mm_stream_t *stream_obj 这个结构体*/
    memset(stream_obj, 0, sizeof(mm_stream_t));
    stream_obj->my_hdl = mm_camera_util_generate_handler(idx);
    CDBG_ERROR("%s: zcf stream_obj->my_hdl=%d", __func__,stream_obj->my_hdl);
    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);
    //设置流的状态为 MM_STREAM_STATE_INITED
    stream_obj->state = MM_STREAM_STATE_INITED;

    /* 申请流  */
    rc = mm_stream_fsm_fn(stream_obj, MM_STREAM_EVT_ACQUIRE, NULL, NULL);
···
    CDBG("%s : stream handle = %d", __func__, s_hdl);
    return s_hdl;
}

看一些关键函数:mm_stream_fsm_inited

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 );
        //打开dev/vedio0或1
        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;
}

这里就去操作dev/vedio0或者1节点了,
然后设置状态为MM_STREAM_STATE_ACQUIRED,表示 流已经 acquired, fd opened

  • 1.2.4 addStreamToChannel 数据流类型:CAM_STREAM_TYPE_PREVIEW
    添加预览流

    和1.2.3一样的,唯一区别就是数据流不同,之前添加的是META流,现在加的是Preview流。

  • 1.2.5 m_channels[QCAMERA_CH_TYPE_PREVIEW] = pChannel;
    通道有了,数据流也加入通道了,最后
    m_channels[QCAMERA_CH_TYPE_PREVIEW] = pChannel;

    把pChannel放在m_channels数组里,后续维护!

    其定义如下:
    QCameraChannel *m_channels[QCAMERA_CH_TYPE_MAX];

到此addChannel(QCAMERA_CH_TYPE_PREVIEW)就分析完了,preparePreview的准备工作也就完成了!

int32_t QCamera2HardwareInterface::preparePreview()
{
    
    
    //添加preview通道
        rc = addChannel(QCAMERA_CH_TYPE_PREVIEW);
}

二. startChanne简析;

2.1 startChannel(QCAMERA_CH_TYPE_PREVIEW)

int QCamera2HardwareInterface::startPreview()
{
    
    
    ATRACE_CALL();
    int32_t rc = NO_ERROR;
    CDBG_HIGH("%s: zcf  E", __func__);
    updateThermalLevel(mThermalLevel);
    // start preview stream 开启数据流
    if (mParameters.isZSLMode() && mParameters.getRecordingHintValue() !=true) {
    
    
        //ZSL模式,添加ZSL数据流
        rc = startChannel(QCAMERA_CH_TYPE_ZSL);
    } else {
    
    
        //非ZSL模式,普通Preview数据流
        rc = startChannel(QCAMERA_CH_TYPE_PREVIEW);
    }
···
    return rc;
}

前面在preparePreview中,调用addChannel(QCAMERA_CH_TYPE_PREVIEW)添加了通道和数据流。
准备工作就完成了,接下来就可以startPreview了,

这里分析普通数据流,非ZSL模式!

调用startChannel(QCAMERA_CH_TYPE_PREVIEW)去启动数据流。
hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp

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;
}
  • 2.1.1 m_channels[ch_type]->config()去配置流
    调用流程:
QCameraChannel::config()->
QCameraStream::configStream(···)->
(.config_stream = mm_camera_intf_config_stream,)
mm_camera_intf_config_stream(···)->
mm_camera_config_stream(my_obj, ch_id, stream_id, config)->
mm_channel_fsm_fn(ch_obj,MM_CHANNEL_EVT_CONFIG_STREAM,
                                   (void *)&payload, NULL)->
 mm_channel_config_stream(···)->
 /* set stream fmt */
mm_stream_fsm_fn(stream_obj,MM_STREAM_EVT_SET_FMT,
                            (void *)config,NULL);->
mm_stream_config(my_obj, config)->
mm_stream_set_fmt(···)->往下就是kernel层了

来看关键函数:mm_stream_set_fmt()

hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c

int32_t mm_stream_set_fmt(mm_stream_t *my_obj)
{
    
    
    int32_t rc = 0; 
    struct v4l2_format fmt; 
    struct msm_v4l2_format_data msm_fmt;
    int i;

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

    if (my_obj->stream_info->dim.width == 0 ||
        my_obj->stream_info->dim.height == 0) {
    
    
        CDBG_ERROR("%s:invalid input[w=%d,h=%d,fmt=%d]\n",
                   __func__,
                   my_obj->stream_info->dim.width,
                   my_obj->stream_info->dim.height,
                   my_obj->stream_info->fmt);
        return -1;
    }

    memset(&fmt, 0, sizeof(fmt));
    memset(&msm_fmt, 0, sizeof(msm_fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    msm_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    msm_fmt.width = (unsigned int)my_obj->stream_info->dim.width;
    msm_fmt.height = (unsigned int)my_obj->stream_info->dim.height;
    msm_fmt.pixelformat = mm_stream_get_v4l2_fmt(my_obj->stream_info->fmt);
    msm_fmt.num_planes = (unsigned char)my_obj->frame_offset.num_planes;
    for (i = 0; i < msm_fmt.num_planes; i++) {
    
    
        msm_fmt.plane_sizes[i] = my_obj->frame_offset.mp[i].len;
    }

    memcpy(fmt.fmt.raw_data, &msm_fmt, sizeof(msm_fmt));
    //通过这个ioctl的方式,设置流到kernel
    rc = ioctl(my_obj->fd, VIDIOC_S_FMT, &fmt);
    return rc;
}

该函数通过V4L2设置流的格式到kernel中!

  • 2.1.2 m_channels[ch_type]->start()启动流;
    流配置好了以后,就要启动通道(启动通道,意味着 启动通道所有的流)!
    hardware/qcom/camera/QCamera2/HAL/QCameraChannel.cpp
int32_t QCameraChannel::start()
{
    
    
···
    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);
···
    return rc;
}

1.调用 mStreams[i]->start();将启动主线程来处理流相关操作。
hardware/qcom/camera/QCamera2/HAL/QCameraStream.cpp

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

2.m_camOps->start_channel(m_camHandle, m_handle)启动通道
调用流程:

m_camOps->start_channel(m_camHandle, m_handle)->
(.start_channel = mm_camera_intf_start_channel,)
mm_camera_intf_start_channel(···)->
mm_camera_start_channel(my_obj, ch_id)->
mm_channel_fsm_fn(ch_obj,MM_CHANNEL_EVT_START,NULL,NULL)->
mm_channel_start(my_obj)->
mm_stream_fsm_fn(s_objs[i],MM_STREAM_EVT_START,NULL, NULL)->
mm_stream_streamon(my_obj)->调用到kernel层,请求内核启动

来看关键函数:mm_stream_streamon()

hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c

调用此函数之前,先设置了一下状态:my_obj->state = MM_STREAM_STATE_ACTIVE;

int32_t mm_stream_streamon(mm_stream_t *my_obj)
{
    
    
    int32_t rc;
    enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

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

    rc = ioctl(my_obj->fd, VIDIOC_STREAMON, &buf_type);
    if (rc < 0) {
    
    
        CDBG_ERROR("%s: ioctl VIDIOC_STREAMON failed: rc=%d\n",
                   __func__, rc);
        /* remove fd from data poll thread in case of failure */
        mm_camera_poll_thread_del_poll_fd(&my_obj->ch_obj->poll_thread[0],
            my_obj->my_hdl, mm_camera_sync_call);
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

通过ioctl的方式,指令为:VIDIOC_STREAMON,
向内核发送v4l2请求,启动一个数据流!

到此,我们就分析结束了,下一篇将重点关注数据流的回调函数!

Stay Hunfry! Stay Foolish!

猜你喜欢

转载自blog.csdn.net/justXiaoSha/article/details/100592574
今日推荐