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数据。