Android S5PV210 cameraHAL 录像功能分析
来源:互联网 发布:windows 10 version 编辑:程序博客网 时间:2024/05/20 08:28
/**********************************************************************************
转自:http://blog.csdn.net/kickxxx/article/details/7793843
关键字:SecCameraHWInterface
***********************************************************************************/
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数据。
- Android S5PV210 cameraHAL 录像功能分析
- S5PV210 Android cameraHAL 录像功能分析
- Android S5PV210 cameraHAL 录像功能分析
- Android S5PV210 cameraHAL 录像功能分析
- Android S5PV210 cameraHAL 录像功能分析
- s5pv210加入recovery功能分析
- Android Camera CameraHal.cpp 分析
- Android Camera CameraHal.cpp 分析
- Android Camera CameraHal.cpp 分析
- Android Camera CameraHal.cpp 初始化分析
- Android Camera CameraHal.cpp 初始化分析
- android 录像
- Android:录像
- android-录像
- Android-录像
- Android短信功能分析
- android 截屏功能分析
- 功能分析
- 在线编辑器 日历控件的使用
- Socket通讯机制
- 使用.bat文件或者是cmd里面运行.jar文件
- android platform env setup
- 数组的排序
- Android S5PV210 cameraHAL 录像功能分析
- Android 记事本样式
- HttpClient线上bug
- C++界面库 - Xtreme Toolkit Pro
- 第十四周上机任务---数组大折腾
- 数组法求平均数
- 数组仿真栈
- 指定某一行查询
- Python进阶09 动态类型