1. 先看下HAL3的预览状态机流程

默认状态          MM_CHANNEL_STATE_NOTUSED                      MM_STREAM_STATE_NOTUSED
add_channel       MM_CHANNEL_STATE_STOPPED                      MM_STREAM_STATE_INITEDMM_CHANNEL_EVT_ADD_STREAM                       MM_STREAM_EVT_ACQUIREMM_STREAM_STATE_ACQUIRED====>mm_stream_t *my_obj;my_obj->fd = "/dev/%s" (open这个设备)<====
map_stream_buf    CAM_MAPPING_BUF_TYPE_STREAM_INFOMM_CHANNEL_EVT_MAP_STREAM_BUF
config_streamMM_CHANNEL_EVT_CONFIG_STREAMMM_STREAM_EVT_SET_FMTMM_STREAM_STATE_CFG
start_channelMM_CHANNEL_EVT_STARTMM_CHANNEL_STATE_ACTIVE                        MM_STREAM_EVT_GET_BUF====>"CAM_SuperBuf","CAM_SuperBufCB"线程run<====MM_STREAM_STATE_BUFFEDMM_STREAM_EVT_REG_BUF====>    VIDIOC_REQBUFSmm_stream_qbuf"add fd and poll" <====                    MM_STREAM_STATE_REG MM_STREAM_EVT_START MM_STREAM_STATE_ACTIVE    ====>  "CAM_StrmAppData" 线程runmm_stream_streamonVIDIOC_STREAMON<====

2. 开启预览的详细流程

我们这篇文章重点分析HAL3的预览数据流
在Camera 初始化(Open)二(HAL3中Open过程)的最后一步有创建一个channel,在框架层调用configureStreamsLocked(Camera 初始化(Preview) 一(Framework->HAL3))的时候,会在HAL3利用之前创建的channel构造一个QCamera3MetadataChannel以及QCamera3RegularChannel的对象。

hardware\qcom\camera\qcamera2\hal3\QCamera3HWI.cpp

int QCamera3HardwareInterface::configureStreamsPerfLocked(camera3_stream_configuration_t *streamList)
{...for (size_t i = 0; i < streamList->num_streams; i++) {camera3_stream_t *newStream = streamList->streams[i];if (!stream_exists && newStream->stream_type != CAMERA3_STREAM_INPUT) {//new streamstream_info_t* stream_info;stream_info = (stream_info_t* )malloc(sizeof(stream_info_t));if (!stream_info) {LOGE("Could not allocate stream info");rc = -ENOMEM;pthread_mutex_unlock(&mMutex);return rc;}stream_info->stream = newStream;stream_info->status = VALID;stream_info->channel = NULL;mStreamInfo.push_back(stream_info);}}mMetadataChannel = new QCamera3MetadataChannel(mCameraHandle->camera_handle,mChannelHandle, mCameraHandle->ops, captureResultCb,setBufferErrorStatus, &padding_info, metadataFeatureMask, this);rc = mMetadataChannel->initialize(IS_TYPE_NONE);...for (size_t i = 0; i < streamList->num_streams; i++) {camera3_stream_t *newStream = streamList->streams[i];     ...switch (newStream->format) {case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:     ...channel = new QCamera3RegularChannel(mCameraHandle->camera_handle,mChannelHandle, mCameraHandle->ops, captureResultCb,setBufferErrorStatus, &gCamCapability[mCameraId]->padding_info,this,newStream,(cam_stream_type_t)mStreamConfigInfo.type[mStreamConfigInfo.num_streams],mStreamConfigInfo.postprocess_mask[mStreamConfigInfo.num_streams],mMetadataChannel,bufferCount);newStream->max_buffers = channel->getNumBuffers();newStream->priv = channel;break;}           ...
}

注意QCamera3RegularChannelQCamera3MetadataChannel中的mChannelHandle都是指向在Camera Open的时候创建的Channel
至此,我们得到了2条派生的channel,QCamera3RegularChannelQCamera3MetadataChannel,与之对应我们同时还会得到2条Stream,CAM_STREAM_TYPE_METADATACAM_STREAM_TYPE_PREVIEW

当框架第一次下发Capture Request到HAL3的时候,HAL3会完成QCamera3RegularChannel的初始化,并启动所有的Channel

第一步 channel initialize

第二步 channel start

hardware\qcom\camera\qcamera2\hal3\QCamera3HWI.cpp

int QCamera3HardwareInterface::processCaptureRequest(camera3_capture_request_t *request,List<InternalRequest> &internallyRequestedStreams)
{...if (mState == CONFIGURED) {...for (List<stream_info_t *>::iterator it = mStreamInfo.begin();it != mStreamInfo.end(); it++) {QCamera3Channel *channel = (QCamera3Channel *)(*it)->stream->priv;...rc = channel->initialize(is_type);}}...rc = mMetadataChannel->start();...for (List<stream_info_t *>::iterator it = mStreamInfo.begin();it != mStreamInfo.end(); it++) {QCamera3Channel *channel = (QCamera3Channel *)(*it)->stream->priv;LOGH("Start Processing Channel mask=%d",channel->getStreamTypeMask());rc = channel->start();if (rc < 0) {LOGE("channel start failed");pthread_mutex_unlock(&mMutex);goto error_exit;}}           if (mChannelHandle) {rc = mCameraHandle->ops->start_channel(mCameraHandle->camera_handle,mChannelHandle);}...mState = STARTED;...
}

2.1 channel initialize

hardware\qcom\camera\qcamera2\hal3\QCamera3Channel.cpp

int32_t QCamera3Channel::addStream(cam_stream_type_t streamType,cam_format_t streamFormat,cam_dimension_t streamDim,cam_rotation_t streamRotation,uint8_t minStreamBufNum,cam_feature_mask_t postprocessMask,cam_is_type_t isType,uint32_t batchSize)
{int32_t rc = NO_ERROR;if (m_numStreams >= 1) {LOGE("Only one stream per channel supported in v3 Hal");return BAD_VALUE;}if (m_numStreams >= MAX_STREAM_NUM_IN_BUNDLE) {LOGE("stream number (%d) exceeds max limit (%d)",m_numStreams, MAX_STREAM_NUM_IN_BUNDLE);return BAD_VALUE;}QCamera3Stream *pStream = new QCamera3Stream(m_camHandle,m_handle,m_camOps,&mPaddingInfo,this);if (pStream == NULL) {LOGE("No mem for Stream");return NO_MEMORY;}LOGD("batch size is %d", batchSize);rc = pStream->init(streamType, streamFormat, streamDim, streamRotation,NULL, minStreamBufNum, postprocessMask, isType, batchSize,streamCbRoutine, this);if (rc == 0) {mStreams[m_numStreams] = pStream;m_numStreams++;} else {delete pStream;}return rc;
}int32_t QCamera3RegularChannel::initialize(cam_is_type_t isType)
{...rc = QCamera3Channel::addStream(mStreamType,mStreamFormat,streamDim,mRotation,mNumBufs,mPostProcMask,mIsType,mBatchSize);...
}

注意这里stream的回调函数streamCbRoutine

hardware\qcom\camera\qcamera2\hal3\QCamera3Stream.cpp

int32_t QCamera3Stream::init(cam_stream_type_t streamType,cam_format_t streamFormat,cam_dimension_t streamDim,cam_rotation_t streamRotation,cam_stream_reproc_config_t* reprocess_config,uint8_t minNumBuffers,cam_feature_mask_t postprocess_mask,cam_is_type_t is_type,uint32_t batchSize,hal3_stream_cb_routine stream_cb,void *userdata)
{mm_camera_stream_config_t stream_config;...mHandle = mCamOps->add_stream(mCamHandle, mChannelHandle);// Configure the stream...rc = mCamOps->map_stream_buf(mCamHandle,mChannelHandle, mHandle, CAM_MAPPING_BUF_TYPE_STREAM_INFO,0, -1, mStreamInfoBuf->getFd(0), (size_t)bufSize,mStreamInfoBuf->getPtr(0));...stream_config.stream_info = mStreamInfo;stream_config.mem_vtbl = mMemVtbl;stream_config.padding_info = mPaddingInfo;stream_config.userdata = this;stream_config.stream_cb = dataNotifyCB;stream_config.stream_cb_sync = NULL;...rc = mCamOps->config_stream(mCamHandle,mChannelHandle, mHandle, &stream_config);...mDataCB = stream_cb; //streamCbRoutine回调mUserData = userdata; //QCamera3Channel对象mBatchSize = batchSize;
}

2.2 channel start

hardware\qcom\camera\qcamera2\hal3\QCamera3Channel.cpp

int32_t QCamera3Channel::start()
{...for (uint32_t i = 0; i < m_numStreams; i++) {if (mStreams[i] != NULL) {mStreams[i]->start();}}
}

将这条信道中的所有Stream开启

hardware\qcom\camera\qcamera2\hal3\QCamera3Stream.cpp

int32_t QCamera3Stream::start()
{int32_t rc = 0;mDataQ.init();mTimeoutFrameQ.clear();if (mBatchSize)mFreeBatchBufQ.init();rc = mProcTh.launch(dataProcRoutine, this);return rc;
}

这里会开启stream的数据处理线程dataProcRoutine,线程名称:“CAM_PREVIEW”
当然对于metadata的那条stream也是同理,只是线程名称为:“CAM_METADATA”

最后启动我们最早的Channel

hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\Mm_camera_interface.c

int32_t mm_camera_start_channel(mm_camera_obj_t *my_obj, uint32_t ch_id)
{int32_t rc = -1;mm_channel_t * ch_obj =mm_camera_util_get_channel_by_handler(my_obj, ch_id);if (NULL != ch_obj) {pthread_mutex_lock(&ch_obj->ch_lock);pthread_mutex_unlock(&my_obj->cam_lock);rc = mm_channel_fsm_fn(ch_obj,MM_CHANNEL_EVT_START,NULL,NULL);} else {pthread_mutex_unlock(&my_obj->cam_lock);}return rc;
}

这俩部分的细节,可以参考上面的状态机流程梳理代码即可。

3. 预览数据如何返回

hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\Mm_camera_thread.c

static void *mm_camera_poll_fn(mm_camera_poll_thread_t *poll_cb)
{int rc = 0, i;if (NULL == poll_cb) {LOGE("poll_cb is NULL!\n");return NULL;}LOGD("poll type = %d, num_fd = %d poll_cb = %p\n",poll_cb->poll_type, poll_cb->num_fds,poll_cb);do {for(i = 0; i < poll_cb->num_fds; i++) {poll_cb->poll_fds[i].events = POLLIN|POLLRDNORM|POLLPRI;}rc = poll(poll_cb->poll_fds, poll_cb->num_fds, poll_cb->timeoutms);if(rc > 0) {if ((poll_cb->poll_fds[0].revents & POLLIN) &&(poll_cb->poll_fds[0].revents & POLLRDNORM)) {/* if we have data on pipe, we only process pipe in this iteration */LOGD("cmd received on pipe\n");mm_camera_poll_proc_pipe(poll_cb);} else {for(i=1; i<poll_cb->num_fds; i++) {/* Checking for ctrl events */if ((poll_cb->poll_type == MM_CAMERA_POLL_TYPE_EVT) &&(poll_cb->poll_fds[i].revents & POLLPRI)) {LOGD("mm_camera_evt_notify\n");if (NULL != poll_cb->poll_entries[i-1].notify_cb) {poll_cb->poll_entries[i-1].notify_cb(poll_cb->poll_entries[i-1].user_data);}}if ((MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) &&(poll_cb->poll_fds[i].revents & POLLIN) &&(poll_cb->poll_fds[i].revents & POLLRDNORM)) {LOGD("mm_stream_data_notify\n");if (NULL != poll_cb->poll_entries[i-1].notify_cb) {poll_cb->poll_entries[i-1].notify_cb(poll_cb->poll_entries[i-1].user_data);}}}}} else {/* in error case sleep 10 us and then continue. hard coded here */usleep(10);continue;}} while ((poll_cb != NULL) && (poll_cb->state == MM_CAMERA_POLL_TASK_STATE_POLL));return NULL;
}

当有预览数据来的时候,会调用到下面这个函数

poll_cb->poll_entries[i-1].notify_cb(poll_cb->poll_entries[i-1].user_data);

这个notify_cb调用的是什么呢?不急看下面

hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\Mm_camera_stream.c

int32_t mm_stream_qbuf(mm_stream_t *my_obj, mm_camera_buf_def_t *buf)
{...//只会在第一个buf入列的时候会调用一次rc = mm_camera_poll_thread_add_poll_fd(&my_obj->ch_obj->poll_thread[0],idx, my_obj->my_hdl, my_obj->fd, mm_stream_data_notify,(void*)my_obj, mm_camera_async_call);...rc = ioctl(my_obj->fd, VIDIOC_QBUF, &buffer);...
}

如上我们看到了这条Stream的回调函数mm_stream_data_notify

int32_t mm_camera_poll_thread_add_poll_fd(mm_camera_poll_thread_t * poll_cb,uint8_t idx, uint32_t handler, int32_t fd, mm_camera_poll_notify_t notify_cb,void* userdata, mm_camera_call_type_t call_type)
{int32_t rc = -1;if (MAX_STREAM_NUM_IN_BUNDLE > idx) {poll_cb->poll_entries[idx].fd = fd;poll_cb->poll_entries[idx].handler = handler;poll_cb->poll_entries[idx].notify_cb = notify_cb; //这个地方poll_cb->poll_entries[idx].user_data = userdata; //指向stream对象自身/* send poll entries updated signal to poll thread */if (call_type == mm_camera_sync_call ) {rc = mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED);} else {rc = mm_camera_poll_sig_async(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED_ASYNC );}} else {LOGE("invalid handler %d (%d)", handler, idx);}return rc;
}

看下这个回调函数

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

mm_stream_data_notify:mm_camera_buf_info_t buf_info;memset(&buf_info, 0, sizeof(mm_camera_buf_info_t));rc = mm_stream_read_msm_frame(my_obj, &buf_info,(uint8_t)my_obj->frame_offset.num_planes);...mm_stream_handle_rcvd_buf(my_obj, &buf_info, has_cb);

mm_stream_read_msm_frame:

 memset(&vb,  0,  sizeof(vb));vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;vb.memory = V4L2_MEMORY_USERPTR;vb.m.planes = &planes[0];vb.length = num_planes;//从缓存队列中取出一个bufrc = ioctl(my_obj->fd, VIDIOC_DQBUF, &vb);//缓冲的buf个数自减my_obj->queued_buffer_count--;/*一旦缓存的buf个数减为0,说明这条stream已经没有数据了,那么将这条stream从poll里面拿掉*/if (0 == my_obj->queued_buffer_count) {mm_camera_poll_thread_del_poll_fd(&my_obj->ch_obj->poll_thread[0],my_obj->my_hdl, mm_camera_async_call);}int32_t idx = vb.index;/*将缓存的buf取出来,放入buf_info中,并填充buf_info的其他字段一定要留意的是这儿传递的是buf的地址啊,buf_info->buf这个东东是个指针,所以以后我们操作buf_info->buf,窗口里面的显示的图形也会变化的*/buf_info->buf = &my_obj->buf[idx];buf_info->frame_idx = vb.sequence;buf_info->stream_id = my_obj->my_hdl;buf_info->buf->stream_id = my_obj->my_hdl;//buf的索引buf_info->buf->buf_idx = idx;buf_info->buf->frame_idx = vb.sequence;buf_info->buf->ts.tv_sec  = vb.timestamp.tv_sec;buf_info->buf->ts.tv_nsec = vb.timestamp.tv_usec * 1000;//通过ION调用,将这块共享buf cleanrc = my_obj->mem_vtbl.clean_invalidate_buf(idx,my_obj->mem_vtbl.user_data);//取出buf的索引uint32_t idx = buf_info.buf->buf_idx;//如之前的描述has_cb = 1;for (i = 0; i < MM_CAMERA_STREAM_BUF_CB_MAX; i++) {if(NULL != my_obj->buf_cb[i].cb) {/* for every CB, add ref count */has_cb = 1;break;}}//由于buf已经取出,因此标记当前buf已不存在与kernel中my_obj->buf_status[idx].in_kernel = 0;//preview的channel,my_obj->is_bundled是false的,下面的代码不运行if (my_obj->is_bundled) {/* need to add into super buf since bundled, add ref count */my_obj->buf_status[idx].buf_refcnt++;}//增加当前buf的引用计数为1my_obj->buf_status[idx].buf_refcnt =(uint8_t)(my_obj->buf_status[idx].buf_refcnt + has_cb);

mm_stream_handle_rcvd_buf:

 //同理preview channel不运行if (my_obj->is_bundled) {rc = mm_stream_notify_channel(my_obj->ch_obj, buf_info);if (rc < 0) {CDBG_ERROR("%s: Unable to notify channel", __func__);}}//当前stream也没有link,下面的代码也不运行if(my_obj->is_linked) {/* need to add into super buf for linking, add ref count */my_obj->buf_status[buf_info->buf->buf_idx].buf_refcnt++;rc = mm_stream_notify_channel(my_obj->linked_obj, buf_info);if (rc < 0) {CDBG_ERROR("%s: Unable to notify channel", __func__);}}//previewSkipCnt为0,preview的channel不运行if (my_obj->ch_obj->previewSkipCnt &&my_obj->stream_info->stream_type == CAM_STREAM_TYPE_PREVIEW) {my_obj->ch_obj->previewSkipCnt--;CDBG_HIGH("%s: Skipping preview frame, pending skip count %d", __func__,my_obj->ch_obj->previewSkipCnt);mm_stream_buf_done(my_obj, buf_info->buf);return;}/*has_cb为真,my_obj->cmd_thread.is_active这个参数是在stream on之前,开启stream callback的数据       处理线程的时候赋值为true的,因此进入分支*/if(has_cb && my_obj->cmd_thread.is_active) {mm_camera_cmdcb_t* node = NULL;/* send cam_sem_post to wake up cmd thread to dispatch dataCB *///申请一个nodenode = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));if (NULL != node) {memset(node, 0, sizeof(mm_camera_cmdcb_t));//命令类型是处理数据的回调node->cmd_type = MM_CAMERA_CMD_TYPE_DATA_CB;//预览数据的buf传递 node->u.buf = *buf_info;/* enqueue to cmd thread *///将node插入链表中cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);/* wake up cmd thread *///通过内嵌的条件量唤醒等待的stream cmd线程cam_sem_post(&(my_obj->cmd_thread.cmd_sem));} else {CDBG_ERROR("%s: No memory for mm_camera_node_t", __func__);}}

这里的my_obj->cmd_thread指向下面"CAM_StrmAppData"线程

int32_t mm_stream_fsm_reg(mm_stream_t * my_obj,mm_stream_evt_type_t evt,void * in_val,void * out_val)
{case MM_STREAM_EVT_START:...mm_camera_cmd_thread_launch(&my_obj->cmd_thread,mm_stream_dispatch_app_data,(void *)my_obj);====>mm_camera_cmd_thread:...do {接收到条件量后跳出等待状态ret = cam_sem_wait(&cmd_thread->cmd_sem);} while (ret != 0);case MM_CAMERA_CMD_TYPE_DATA_CB://调用回调函数,处理完成后再次进入循环等待状态cmd_thread->cb(node, cmd_thread->user_data);====>mm_stream_dispatch_app_data:mm_camera_super_buf_t super_buf;buf_info = &cmd_cb->u.buf;memset(&super_buf, 0, sizeof(mm_camera_super_buf_t));//填充super_bufsuper_buf.num_bufs = 1;//取出buf的指针,赋值给super_buf.bufs[0]super_buf.bufs[0] = buf_info->buf;super_buf.camera_handle = my_obj->ch_obj->cam_obj->my_hdl;super_buf.ch_id = my_obj->ch_obj->my_hdl;//增加buf的引用计数my_obj->buf_status[buf_info->buf->buf_idx].buf_refcnt++;/* callback *//*触发stream的buf回调函数,先关注这俩个参数super_buf是我们在这个函数中构造的,需要特别注意的是*/super_buf.bufs[0] = buf_info->buf;//这里面就是我们buf的指针//my_obj->buf_cb[i].user_data 这个指向QCamera3Stream对象my_obj->buf_cb[i].cb(&super_buf,my_obj->buf_cb[i].user_data);<====            <====

这里说明一下my_obj->buf_cb[i].cb的来源

hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\Mm_camera_stream.c

int32_t mm_stream_config(mm_stream_t *my_obj,mm_camera_stream_config_t *config)
{...if (config->stream_cb_sync != NULL) {/* SYNC callback is always placed at index 0*/my_obj->buf_cb[cb_index].cb = config->stream_cb_sync;my_obj->buf_cb[cb_index].user_data = config->userdata;my_obj->buf_cb[cb_index].cb_count = -1; /* infinite by default */my_obj->buf_cb[cb_index].cb_type = MM_CAMERA_STREAM_CB_TYPE_SYNC;cb_index++;}my_obj->buf_cb[cb_index].cb = config->stream_cb;my_obj->buf_cb[cb_index].user_data = config->userdata;my_obj->buf_cb[cb_index].cb_count = -1; /* infinite by default */my_obj->buf_cb[cb_index].cb_type = MM_CAMERA_STREAM_CB_TYPE_ASYNC;...

MM_CAMERA_STREAM_CB_TYPE_SYNC类型的callback一般是没有的,MM_CAMERA_STREAM_CB_TYPE_ASYNC都是有的

hardware\qcom\camera\qcamera2\hal3\QCamera3Stream.cpp

int32_t QCamera3Stream::init(cam_stream_type_t streamType,cam_format_t streamFormat,cam_dimension_t streamDim,cam_rotation_t streamRotation,cam_stream_reproc_config_t* reprocess_config,uint8_t minNumBuffers,cam_feature_mask_t postprocess_mask,cam_is_type_t is_type,uint32_t batchSize,hal3_stream_cb_routine stream_cb,void *userdata)
{...// Configure the streamstream_config.stream_info = mStreamInfo;stream_config.mem_vtbl = mMemVtbl;stream_config.padding_info = mPaddingInfo;stream_config.userdata = this;stream_config.stream_cb = dataNotifyCB;stream_config.stream_cb_sync = NULL;rc = mCamOps->config_stream(mCamHandle,mChannelHandle, mHandle, &stream_config);mDataCB = stream_cb;      //指向streamCbRoutine  ...

dataNotifyCB:

         //我们之前分析过userdata指向的是QCameraStream对象,因此做强制转换QCameraStream* stream = (QCameraStream *)userdata;//分配一个帧bufmm_camera_super_buf_t *frame =(mm_camera_super_buf_t *)malloc(sizeof(mm_camera_super_buf_t));/*这个地方聊一下为什么要malloc一个指针,而且拷贝了传入参数,而不是简单的指针传递,是因为随后我们会触发数据处理的callback,而这个调用是异步的,如果这儿传递指针,像这样frame= recvd_frame,那么问题就来了,*recvd_frame这个传入参数是从栈里面分配的,后面的processDataNotify这个函数调用后就结束了,真实的数据处理还没开始呢,然后由于调用的结束*recvd_frame这个变量的生命周期已经结束了,那么随后开启的数据处理的时候指针就飞掉了,所以我们要从堆里面重新分配一块内存,把它拷贝过来*/*frame = *recvd_frame;//强调一下frame-> bufs[0]就是我们的预览数据stream->processDataNotify(frame);

processDataNotify:

     //将当前预览的frame预览数据塞入mDataQ的链表中if (mDataQ.enqueue((void *)frame)) {//唤醒stream指向的处于条件量等待状态的线程rc = mProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE);} else {...}

这儿把数据发送到了mProcTh的线程,即”CAM_PREVIEW“,这个线程是在stream启动的时候创建的
dataProcRoutine:

void *QCamera3Stream::dataProcRoutine(void *data)
{...ret = cam_sem_wait(&cmdThread->cmd_sem);...case CAMERA_CMD_TYPE_DO_NEXT_JOB:{LOGD("Do next job");mm_camera_super_buf_t *frame =(mm_camera_super_buf_t *)pme->mDataQ.dequeue();if (NULL != frame) {//我们一般是CAM_STREAM_BUF_TYPE_MPLANE,这儿一般不走if (UNLIKELY(frame->bufs[0]->buf_type ==CAM_STREAM_BUF_TYPE_USERPTR)) {pme->handleBatchBuffer(frame);} else if (pme->mDataCB != NULL) {  //走这儿pme->mDataCB(frame, pme, pme->mUserData);} else {// no data cb routine, return buf herepme->bufDone(frame->bufs[0]->buf_idx);}}}break;...

pme的类型是QCamera3Stream,可以查看QCamera3RegularChannel->initialize的流程可知,pme->mDataCB(…)将调用streamCbRoutine,另外 pme->mUserData 指向的是QCamera3RegularChannel对象

void QCamera3Channel::streamCbRoutine(mm_camera_super_buf_t *super_frame,QCamera3Stream *stream, void *userdata)
{QCamera3Channel *channel = (QCamera3Channel *)userdata;if (channel == NULL) {LOGE("invalid channel pointer");return;}channel->streamCbRoutine(super_frame, stream);
}

所以函数流程走向QCamera3ProcessingChannel::streamCbRoutine
hardware\qcom\camera\qcamera2\hal3\QCamera3Channel.cpp

void QCamera3ProcessingChannel::streamCbRoutine(mm_camera_super_buf_t *super_frame,QCamera3Stream *stream)
{...resultBuffer = (buffer_handle_t *)mMemory.getBufferHandle(frameIndex);resultFrameNumber = mMemory.getFrameNumber(frameIndex);...result.stream = mCamera3Stream;result.buffer = resultBuffer;...result.status = CAMERA3_BUFFER_STATUS_OK;result.acquire_fence = -1;result.release_fence = -1;...if (0 <= resultFrameNumber) {if (mChannelCB) {mChannelCB(NULL, &result, (uint32_t)resultFrameNumber, false, mUserData);}} else {LOGE("Bad frame number");}...
}

最后的mChannelCB调用的是captureResultCb

Camera 初始化(Preview) 二(HAL3的流程)相关推荐

  1. Netty 内存池(二)内存申请流程

    Netty 内存池(二)内存申请流程 上期带大家了解了一下Netty内存池都有哪些重要的对象,以及这些对象的作用是什么,本期就带大家分析一下线程是如何申请一块内存的.本期由于涉及到了很多变量和位运算, ...

  2. c语言 统计已初始化的二维数组a[3][4]中非零元素的个数(用指针实现)

    统计已初始化的二维数组a[3][4]中非零元素的个数(用指针实现) [输入输出示例] 1 0 2 3↙ 2 3 0 4↙ 0 1 0 2↙ nonezero=8 ` #include<stdio ...

  3. ros开发增加clion常用模板及初始化配置(二)

    ros开发增加clion常用模板及初始化配置(二) 在需要共享的文件夹内用控制台运行这个命令开启服务器,共享自己的文件,局域网电脑游览器登入这台电脑的ip加8000端口号即可访问 python -m ...

  4. 科目二 倒车入库 流程记录 LTS

    科目二 倒车入库 流程记录 LTS 好文章 https://blog.csdn.net/sinat_35907936/article/details/107874815 实景图 过渡 偶数 车道 比 ...

  5. activiti学习(二十一)——流程虚拟机源码分析(三)——从进入到离开userTask

    前言 承接上文<activiti学习(二十)--流程虚拟机源码分析(二)--从开始节点离开到下个节点前>,假设execution接下来进入的节点是userTask,本文分析一下进入user ...

  6. 安卓 camera API2 下发Reuqest的接口流程

    安卓 camera API2 下发Reuqest的接口流程 framework层:CameraDeviceClient中与下发Request相关接口 CameraDeviceClient中实现了ICa ...

  7. MATLAB中怎样初始化(创建)二维、三维、四维以及多维矩阵,各维度的索引顺序是怎样的?

    目录 1 在MATLAB中初始化二维矩阵 2 在MATLAB中初始化三维矩阵 3 在MATLAB中初始化四维矩阵 4 在MATLAB中初始化N维矩阵 1 在MATLAB中初始化二维矩阵 在MATLAB ...

  8. Android Framework层Power键关机流程(二,关机流程)

    二,关机流程 从前一篇博文我们知道,当用户长按Power键时会弹出(关机.重启,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框.那么从选项对话框到关机确认对话框又是一个什么流程呢.下面我 ...

  9. 《c primer pius》第十章第6题,编写一个程序,初始化一个二维double数组,并利用练习2中的任一函数来把这个数组复制到另一个二维数组(因为二维数组是数组的数组,所以可以使用处理一维数组的

    <c primer pius>第十章第6题,编写一个程序,初始化一个二维double数组,并利用练习2中的任一函数来把这个数组复制到另一个二维数组(因为二维数组是数组的数组,所以可以使用处 ...

  10. 科目二 直角转弯 流程记录 LTS

    科目二 直角转弯 流程记录 LTS 实景图 S弯人的肩膀和车屁股完全出来之后,不要着急回正,和直角弯距离很近,先去顺直角弯的点,雨刷器顺上之后回正方向.并且在进入直角弯之前就要开启转向灯,怕它弹回去可 ...

最新文章

  1. 数字图像处理知识点总结
  2. 机器学习知识点(三十)LDA话题模型Java实现
  3. 关于Oracle Insert 语句的子查询 和 with check option的用法
  4. [云炬创业基础笔记]第六章商业模式测试14
  5. 【struts2】struts2配置文件—struts.properties
  6. 你不知道的微软奇怪的研究案例
  7. 【word基础知识】如何将论文中的引文标签和参考文献编号自动关联
  8. 编程常用英语词汇 | GitHub
  9. Django基础—— 4.项目目录结构
  10. Devexpress GridView 提交焦点列
  11. 【ftp 上传文件失败】
  12. 2020北京邮电大学计算机学院803初试经验分享
  13. LaTeX使用excel2latex插入表格
  14. App Clips 新特性
  15. webman apidoc安装、生成接口文档
  16. Container with most water(盛水最多的容器)
  17. 38.6歌曲计算机,目前最火的6首歌曲, 38度6垫底, 沙漠骆驼第二, 第一百听不厌
  18. 武汉python自动化测试招聘,python自动化测试工程师招聘_Python-自动化测试面试
  19. C# EF查询结果转换成子类 SelectToType
  20. Java 基础知识测试-1

热门文章

  1. 知识点篇:7)企业标准体系制定要求
  2. 【Android笔记】Android引用第三方依赖包library报错解决方法
  3. 记录HttpWebRequest辅助类
  4. 配置sysklogd 接收远程系統日志
  5. 使用java.text包格式化数字和日期
  6. Java并发容器,底层原理深入分析
  7. 单目标决策---决策的分类
  8. 设计模式之禅——模板方法模式钩子方法
  9. IDC:“互联网+流通”将进一步释放活力
  10. python学习之继承