Camera 初始化(Preview) 二(HAL3的流程)
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;} ...
}
注意QCamera3RegularChannel
和QCamera3MetadataChannel
中的mChannelHandle都是指向在Camera Open的时候创建的Channel
至此,我们得到了2条派生的channel,QCamera3RegularChannel
和QCamera3MetadataChannel
,与之对应我们同时还会得到2条Stream,CAM_STREAM_TYPE_METADATA
和CAM_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的流程)相关推荐
- Netty 内存池(二)内存申请流程
Netty 内存池(二)内存申请流程 上期带大家了解了一下Netty内存池都有哪些重要的对象,以及这些对象的作用是什么,本期就带大家分析一下线程是如何申请一块内存的.本期由于涉及到了很多变量和位运算, ...
- c语言 统计已初始化的二维数组a[3][4]中非零元素的个数(用指针实现)
统计已初始化的二维数组a[3][4]中非零元素的个数(用指针实现) [输入输出示例] 1 0 2 3↙ 2 3 0 4↙ 0 1 0 2↙ nonezero=8 ` #include<stdio ...
- ros开发增加clion常用模板及初始化配置(二)
ros开发增加clion常用模板及初始化配置(二) 在需要共享的文件夹内用控制台运行这个命令开启服务器,共享自己的文件,局域网电脑游览器登入这台电脑的ip加8000端口号即可访问 python -m ...
- 科目二 倒车入库 流程记录 LTS
科目二 倒车入库 流程记录 LTS 好文章 https://blog.csdn.net/sinat_35907936/article/details/107874815 实景图 过渡 偶数 车道 比 ...
- activiti学习(二十一)——流程虚拟机源码分析(三)——从进入到离开userTask
前言 承接上文<activiti学习(二十)--流程虚拟机源码分析(二)--从开始节点离开到下个节点前>,假设execution接下来进入的节点是userTask,本文分析一下进入user ...
- 安卓 camera API2 下发Reuqest的接口流程
安卓 camera API2 下发Reuqest的接口流程 framework层:CameraDeviceClient中与下发Request相关接口 CameraDeviceClient中实现了ICa ...
- MATLAB中怎样初始化(创建)二维、三维、四维以及多维矩阵,各维度的索引顺序是怎样的?
目录 1 在MATLAB中初始化二维矩阵 2 在MATLAB中初始化三维矩阵 3 在MATLAB中初始化四维矩阵 4 在MATLAB中初始化N维矩阵 1 在MATLAB中初始化二维矩阵 在MATLAB ...
- Android Framework层Power键关机流程(二,关机流程)
二,关机流程 从前一篇博文我们知道,当用户长按Power键时会弹出(关机.重启,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框.那么从选项对话框到关机确认对话框又是一个什么流程呢.下面我 ...
- 《c primer pius》第十章第6题,编写一个程序,初始化一个二维double数组,并利用练习2中的任一函数来把这个数组复制到另一个二维数组(因为二维数组是数组的数组,所以可以使用处理一维数组的
<c primer pius>第十章第6题,编写一个程序,初始化一个二维double数组,并利用练习2中的任一函数来把这个数组复制到另一个二维数组(因为二维数组是数组的数组,所以可以使用处 ...
- 科目二 直角转弯 流程记录 LTS
科目二 直角转弯 流程记录 LTS 实景图 S弯人的肩膀和车屁股完全出来之后,不要着急回正,和直角弯距离很近,先去顺直角弯的点,雨刷器顺上之后回正方向.并且在进入直角弯之前就要开启转向灯,怕它弹回去可 ...
最新文章
- 数字图像处理知识点总结
- 机器学习知识点(三十)LDA话题模型Java实现
- 关于Oracle Insert 语句的子查询 和 with check option的用法
- [云炬创业基础笔记]第六章商业模式测试14
- 【struts2】struts2配置文件—struts.properties
- 你不知道的微软奇怪的研究案例
- 【word基础知识】如何将论文中的引文标签和参考文献编号自动关联
- 编程常用英语词汇 | GitHub
- Django基础—— 4.项目目录结构
- Devexpress GridView 提交焦点列
- 【ftp 上传文件失败】
- 2020北京邮电大学计算机学院803初试经验分享
- LaTeX使用excel2latex插入表格
- App Clips 新特性
- webman apidoc安装、生成接口文档
- Container with most water(盛水最多的容器)
- 38.6歌曲计算机,目前最火的6首歌曲, 38度6垫底, 沙漠骆驼第二, 第一百听不厌
- 武汉python自动化测试招聘,python自动化测试工程师招聘_Python-自动化测试面试
- C# EF查询结果转换成子类 SelectToType
- Java 基础知识测试-1