Camera系统进行视频录制的过程,涉及到camera driver,camera HAL,camera Service,camera Java 几层
camera 录像功能一般是和preview功能同时进行的,samsung HAL的录像线程是借助preview线程来实现的,在这个线程中FIMC0获取preview使用的原始数据,同时FIMC2获取录像需要的原始数据,这里之所以使用两个FIMC获取数据,我想是因为preview和录像需要的原始数据是有差异的,而S5PV210恰好又支持两个fimc控制器同时输出一个sensor的BT656信号。
libcamera/SecCameraHWInterface.cpp
802 status_t CameraHardwareSec::startRecording()
803 {
804 LOGV("%s :", __func__);
805
806 if (mRecordRunning == false) {
807 if (mSecCamera->startRecord() < 0) {
808 LOGE("ERR(%s):Fail on mSecCamera->startRecord()", __func__);
809 // sw5771.park : temporary fix
810 // with HDMI, fimc2 is conflict...
811 #ifdef BOARD_USES_HDMI
812 #else
813 return UNKNOWN_ERROR;
814 #endif
815 }
816 mRecordRunning = true;
817 }
818 return NO_ERROR;
819 }
807 mSecCamera->startRecord 设置并启动recording对应的fimc控制器
816 设置启动标志,previewThread线程中会通过mRecordRunning判断是否需要录像处理
libcamera/SecCamera.cpp:SecCamera::startRecord()
1084 int SecCamera::startRecord(void)
1085 {
1086 int ret, i;
1087
1088 LOGV("%s :", __func__);
1089
1090 // aleady started
1091 if (m_flag_record_start > 0) {
1092 LOGE("ERR(%s):Recording was already started\n", __func__);
1093 return 0;
1094 }
1095
1096 if (m_cam_fd2 <= 0) {
1097 LOGE("ERR(%s):Camera was closed\n", __func__);
1098 return -1;
1099 }
1100
1101 /* enum_fmt, s_fmt sample */
1102 ret = fimc_v4l2_enum_fmt(m_cam_fd2, V4L2_PIX_FMT_NV12T);
1103 CHECK(ret);
1104
1105 LOGI("%s: m_recording_width = %d, m_recording_height = %d\n",
1106 __func__, m_recording_width, m_recording_height);
1107
1108 ret = fimc_v4l2_s_fmt(m_cam_fd2, m_recording_width, m_recording_height,
1109 V4L2_PIX_FMT_NV12T, 0);
1110 CHECK(ret);
1111
1112 ret = fimc_v4l2_reqbufs(m_cam_fd2, V4L2_BUF_TYPE_VIDEO_CAPTURE, MAX_BUFFERS);
1113 CHECK(ret);
1114
1115 ret = this->m_setCameraAngle(m_cam_fd2);
1116 CHECK(ret);
1117
1118 /* start with all buffers in queue */
1119 for (i = 0; i < MAX_BUFFERS; i++) {
1120 ret = fimc_v4l2_qbuf(m_cam_fd2, i);
1121 CHECK(ret);
1122 }
1123
1124 ret = fimc_v4l2_streamon(m_cam_fd2);
1125 CHECK(ret);
1126
1127 // Get and throw away the first frame since it is often garbled.
1128 memset(&m_events_c2, 0, sizeof(m_events_c2));
1129 m_events_c2.fd = m_cam_fd2;
1130 m_events_c2.events = POLLIN | POLLERR;
1131 ret = fimc_poll(&m_events_c2);
1132 CHECK(ret);
1133
1134 m_flag_record_start = 1;
1135
1136 LOGE("(%s): end\n", __func__);
1137 return 0;
1138 }
1102 检查设备是否支持NV12T格式,这里使用的是m_cam_fd2,对应着FIMC2控制器
接下来的流程类似于普通拍照时camera设备的初始化流程
1108 ~ 1109 设置录像的宽度,高度和格式
1112 申请拍照用的queue buffer
1118 ~ 1112 把这些buffer加入等待队列
1124 启动FIMC2的capture stream on 模式,FIMC2开始抓取数据,FIMC2每获取一帧数据都会唤醒调用fimc_poll阻塞的进程
1128 ~ 1132 等待第一帧的到来,并且忽略掉,因为第一帧的数据经常错乱
821 void CameraHardwareSec::stopRecording()
822 {
823 LOGV("%s :", __func__);
824
825 if (mRecordRunning == true) {
826 if (mSecCamera->stopRecord() < 0) {
827 LOGE("ERR(%s):Fail on mSecCamera->stopRecord()", __func__);
828 return;
829 }
830 mRecordRunning = false;
831 }
832 }
826 停止FIMC2设备的capture stream
830 mRecordRunning = false,会停止previewThread中针对record的处理
841 void CameraHardwareSec::releaseRecordingFrame(const sp<IMemory>& mem)
842 {
843 ssize_t offset;
844 sp<IMemoryHeap> heap = mem->getMemory(&offset, NULL);
845 struct ADDRS *addrs = (struct ADDRS *)((uint8_t *)heap->base() + offset);
846
847 mSecCamera->releaseRecordFrame(addrs->buf_idx);
848 }
buffer数据要传送给上层处理,在上层处理完之前,驱动层和HAL不能修改这个buffer,直到上层应用调用releaseRecordingFrame接口释放buffer
847 mSecCamera->releaseRecordFrame会把addrs->buf_idx对应的buffer加到队列中
542 int CameraHardwareSec::previewThread()
543 {
544 int index;
545 nsecs_t timestamp;
546 unsigned int phyYAddr;
547 unsigned int phyCAddr;
548 struct ADDRS *addrs;
549
550 index = mSecCamera->getPreview();
551 if (index < 0) {
552 LOGE("ERR(%s):Fail on SecCamera->getPreview()", __func__);
553 return UNKNOWN_ERROR;
554 }
555 mSkipFrameLock.lock();
556 if (mSkipFrame > 0) {
557 mSkipFrame--;
558 mSkipFrameLock.unlock();
559 return NO_ERROR;
560 }
561 mSkipFrameLock.unlock();
562 gInterlace++;
563 //if (gInterlace % 8) {
564 // return NO_ERROR;
565 //}
566
567 timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
568
569 phyYAddr = mSecCamera->getPhyAddrY(index);
570 phyCAddr = mSecCamera->getPhyAddrC(index);
571
572 if (phyYAddr == 0xffffffff || phyCAddr == 0xffffffff) {
573 LOGE("ERR(%s):Fail on SecCamera getPhyAddr Y addr = %0x C addr = %0x", __func__, phyYAddr, phyCAddr);
574 return UNKNOWN_ERROR;
575 }
576
577 int width, height, frame_size, offset;
578
579 mSecCamera->getPreviewSize(&width, &height, &frame_size);
580
581 offset = (frame_size + mSizeOfADDRS) * index;
582 sp<MemoryBase> buffer = new MemoryBase(mPreviewHeap, offset, frame_size);
583
584 memcpy(static_cast<unsigned char *>(mPreviewHeap->base()) + (offset + frame_size ), &phyYAddr, 4);
585 memcpy(static_cast<unsigned char *>(mPreviewHeap->base()) + (offset + frame_size + 4), &phyCAddr, 4);
586
587 #if defined(BOARD_USES_OVERLAY)
588 if (mUseOverlay) {
589 int ret;
590 overlay_buffer_t overlay_buffer;
591
592 mOverlayBufferIdx ^= 1;
593 memcpy(static_cast<unsigned char*>(mPreviewHeap->base()) + offset + frame_size + sizeof(phyYAddr) + sizeof(phyCA ddr),
594 &mOverlayBufferIdx, sizeof(mOverlayBufferIdx));
595
596 ret = mOverlay->queueBuffer((void*)(static_cast<unsigned char *>(mPreviewHeap->base()) + (offset + frame_size))) ;
597
598 if (ret == -1 ) {
599 LOGE("ERR(%s):overlay queueBuffer fail", __func__);
600 } else if (ret != ALL_BUFFERS_FLUSHED) {
601 ret = mOverlay->dequeueBuffer(&overlay_buffer);
602 if (ret == -1) {
603 LOGE("ERR(%s):overlay dequeueBuffer fail", __func__);
604 }
605 }
606 }
607 #endif
608
609 // Notify the client of a new frame.
610 if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) {
611 mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie);
612 }
613
614 if (mRecordRunning == true) {
615 // sw5771.park : temporary fix
616 // with HDMI, fimc2 is conflict...
617 #ifdef BOARD_USES_HDMI
618 int preview_index = index;
619
620 index = mSecCamera->getRecordFrame();
621 if (index < 0) {
622 LOGE("ERR(%s):Fail on SecCamera->getRecordFrame()", __func__);
623 index = preview_index;
624 //return UNKNOWN_ERROR;
625 } else {
626 phyYAddr = mSecCamera->getRecPhyAddrY(index);
627 phyCAddr = mSecCamera->getRecPhyAddrC(index);
628 }
629 #else
630 index = mSecCamera->getRecordFrame();
631 if (index < 0) {
632 LOGE("ERR(%s):Fail on SecCamera->getRecordFrame()", __func__);
633 return UNKNOWN_ERROR;
634 }
635
636 phyYAddr = mSecCamera->getRecPhyAddrY(index);
637 phyCAddr = mSecCamera->getRecPhyAddrC(index);
638 #endif
639
640 if (phyYAddr == 0xffffffff || phyCAddr == 0xffffffff) {
641 LOGE("ERR(%s):Fail on SecCamera getRectPhyAddr Y addr = %0x C addr = %0x", __func__, phyYAddr, phyCAddr);
642 return UNKNOWN_ERROR;
643 }
644
645 addrs = (struct ADDRS *)mRecordHeap->base();
646
647 sp<MemoryBase> buffer = new MemoryBase(mRecordHeap, mSizeOfADDRS * index, mSizeOfADDRS);
648 addrs[index].addr_y = phyYAddr;
649 addrs[index].addr_cbcr = phyCAddr;
650 addrs[index].buf_idx = index;
651
652 // Notify the client of a new frame.
653 if (mMsgEnabled & CAMERA_MSG_VIDEO_FRAME) {
654 mDataCbTimestamp(timestamp, CAMERA_MSG_VIDEO_FRAME, buffer, mCallbackCookie);
655 } else {
656 mSecCamera->releaseRecordFrame(index);
657 }
658 }
659
660 return NO_ERROR;
661 }
614 ~ 658是和record相关的代码
630 获取record frame,getRecordFrame()会阻塞在FIMC2设备节点的poll函数上,直到有可用数据,返回值index是可用buffer的索引
636 ~ 637 获取buffer对应的物理地址,这里不太明白为什么录像要获取物理地址,也许是传给上层后视频编码过程能够处理物理地址吧
653 上层如果需要处理数据那么就要设置 mMsgEnable为CAMERA_MSG_VIDEO_FRAME,这样HAL层就会通过mDataCbTimestamp上传数据,上层处理完后要负责调用releaseRecordFrame接口释放buffer
656 上层不做处理,那么直接释放buffer(重新入队),不过实在想像不出有什么场景会不做处理
三星S5PV210由于硬件的特殊性,因此可以在preview中启动两个camera capture stream,并且两个FIMC控制器fimc1 fime2从同一个camera sensor获取数据,这两份数据一份用来preview,另外一份用来recording,而其他平台的preview 和recording一般都是共享同一份raw数据。