OpenHarmony 适配SL8541E camera预览拍照适配指导
摘要 本文通过camera预览拍照适配实践操作梳理了OpenHarmony Release 3.2.1适配8541E芯片camera的方法。 本文描述的camera厂商库适配对接流程,不止适用于8541e,也适用于该芯片厂家的其他芯片,如7863、7885等,虽然部分具体的接口或底层驱动会有差异。一、camera方案考量 OpenHarmon
摘要
本文通过camera预览拍照适配实践操作梳理了OpenHarmony Release 3.2.1适配8541E芯片camera的方法。
本文描述的camera厂商库适配对接流程,不止适用于8541e,也适用于该芯片厂家的其他芯片,如7863、7885等,虽然部分具体的接口或底层驱动会有差异。
一、camera方案考量
OpenHarmony HDI 框架提供了两种适配平台代码,分别是MPP和V4L2。其中MPP适配示例是针对海思芯片,考虑到8541E 厂商虽然自己实现了自有camera驱动框架,但也针对该自有camera驱动框架对外提供了V4L2 adapter模块,故而选择OpenHarmony HDI V4L2框架对该芯片camera做适配。OH适配代码主要集中在OH Camera_host层,涉及厂商产品的device_board以及vendor仓库下camera部分以及drivers_peripheral_camera仓库。
二、OpenHarmony camera预览拍照的适配修改:
1、适配框图介绍:
OH适配代码的开发集中在Camera_host层的OH部分,其中改造最多的是OH V4L2适配层。
2、适配介绍:
2.1、原厂闭源算法库,camera驱动以及V4L2适配接口库适配:
联系原厂将对应的camera算法库、底层驱动库以及V4L2适配接口库、ION或DMA辅助接口库和有限开源源码(获得授权能拿到)编译移植到OpenHarmony下。将编译好的camera 闭源so库文件、对应的配置文件、库源码添加到vendor/厂商名/产品名/camera目录下。闭源库和配置文件添加build.gn把他们打包进系统镜像中的指定目录。参与产品编译的库源码对应的添加build.gn,并被使用到的地方所依赖。
2.1.1、配置camera_host uhdf设备节点:
参照gitee社区RK3568 camera_host配置,在“vendor/厂商名/产品名/hdf_config/uhdf/device_info.hcs”文件下配置camera_host设备节点。默认从RK3568移植过来的device_info.hcs已经配置好了camera_host设备节点。
hdi_server :: host { hostName = "camera_host"; priority = 50; gid = ["camera_host", "uhdf_driver", "vendor_mpp_driver"]; camera_device :: device { device0 :: deviceNode { policy = 2; priority = 100; moduleName = "libcamera_host_service_1.0.z.so"; serviceName = "camera_service"; } } ......
2.1.2、camera适配源码框架建立:
拷贝RK3568工程里vendor/hihope/rk3568/hdf_config/uhdf/下camera文件夹以及其下对应的文件,到“vendor/厂商名/产品名/hdf_config/uhdf/下。其它模块编译时指定的依赖前述模块的路径也随同更改。
拷贝RK3568工程里device/board/hihope/rk3568/下camera文件夹以及其下对应的文件,到“device/board/厂商名/产品名/“下。其它模块编译时指定的依赖前述模块的路径也随同更改。
确认vendor/厂商名/产品名/product.gni在芯片整理移植时是否更改了device.gni的查找路径:import("//device/board/厂商名/${device_name}/device.gni")。
确认device/board/厂商名/产品名/device.gni里指定的device下的camera相关路径,是否正确的指到了device/board/厂商名/产品名/下。
将config.hcs里RK相关标记去掉(仍使用RK相关的编码库可以例外):RKCodec均修改为Codec,RKFace均修改为Face,RKExif均修改为Exif,确保都改完全。
将device/board/厂商名/产品名/camera/pipeline_core/src/node下rk_codec_node.h,rk_codec_node.cpp,rk_exif_node.h,rk_exif_node.cpp,rk_face_node.h,rk_face_node.cpp源文件名称均去掉rk_。然后对应的build.gn相关文件名称以及其它模块指定的依赖上述模块的路径也随同更改。
将前述修改了文件名的源码中的带RK的类名前缀相关(最好包含打印信息以及注释一并修改),修改为与HCS里的node名称前缀一致(见下面的CodecNode, {"Codec"}示例)。
注:要仔细无疏漏,不然camera_host系统启动出问题,排查耗时更多。上述模块更改后,建议删除out/产品名文件夹整体重新编译一次。后三步去除RK相关信息的修改,仍使用RK芯片的可以例外。
vendor/厂商名/产品名/hdf_config/uhdf/camera/pipeline_core/config.hcs ...... fork_2 :: node_spec { name = "fork#1"; status = "new"; in_port_0 :: port_spec { name = "in0"; peer_port_name = "out0"; peer_port_node_name = "fork#0"; direction = 0; } out_port_0 :: port_spec { name = "out0"; peer_port_name = "in0"; peer_port_node_name = "Codec#0"; direction = 1; } out_port_1 :: port_spec { name = "out1"; peer_port_name = "in0"; peer_port_node_name = "Face#0"; direction = 1; } } ...... Codec_2 :: node_spec { name = "Codec#1"; status = "new"; in_port_0 :: port_spec { name = "in0"; peer_port_name = "out0"; peer_port_node_name = "ipp#0"; direction = 0; } out_port_0 :: port_spec { name = "out0"; peer_port_name = "in0"; peer_port_node_name = "Exif#0"; direction = 1; } } Exif_1 :: node_spec { name = "Exif#0"; status = "new"; in_port_0 :: port_spec { name = "in0"; peer_port_name = "out0"; peer_port_node_name = "Codec#1"; direction = 0; } ...... device/board/厂商名/产品名/camera/pipeline_core/src/node/codec_node.cpp ...... RetCode CodecNode::Capture(const int32_t streamId, const int32_t captureId) { CAMERA_LOGV("CodecNode::Capture"); return RC_OK; } RetCode CodecNode::CancelCapture(const int32_t streamId) { CAMERA_LOGI("CodecNode::CancelCapture streamid = %{public}d", streamId); return RC_OK; } REGISTERNODE(CodecNode, {"Codec"}) } // namespace OHOS::Camera
2.1.3、device/board/厂商名/产品名/camera文件树以及说明:
. ├── BUILD.gn ├── demo │ └── include │ └── project_camera_demo.h//camera_host框架的调试测试程序ohos_camera_demo ├── device_manager │ ├── BUILD.gn │ ├── include │ │ ├── 前sensor.h//前置摄像头sensor特性头文件 │ │ ├── 后sensor.h//后置摄像头sensor特性头文件 │ │ └── project_hardware.h//camera ID与驱动名映射头文件 │ └── src │ ├── 前sensor.cpp//前置摄像头sensor特性源文件 │ └── 后sensor.cpp//前置摄像头sensor特性源文件 ├── driver_adapter │ ├── BUILD.gn │ ├── include │ │ ├── v4l2_buffer.h//OH v4l2适配层数据缓存接口头文件 │ │ ├── v4l2_client.h//芯片原厂v4l2适配接口库头文件 │ │ ├── v4l2_common.h//OH v4l2适配层公共头文件 │ │ ├── v4l2_control.h//OH v4l2适配层控制指令接口头文件 │ │ ├── v4l2_dev.h//OH v4l2适配层设备接口头文件 │ │ ├── v4l2_fileformat.h//OH v4l2适配层文件格式接口头文件 │ │ ├── v4l2_stream.h//OH v4l2适配层流处理接口头文件 │ │ ├── v4l2_temp.h │ │ └── v4l2_uvc.h//OH v4l2适配层uvc接口头文件 │ ├── main_test │ │ ├── BUILD.gn │ │ ├── project_v4l2_main.h//OH v4l2_main测试程序配置头文件 │ │ └── v4l2_main.cpp//OH v4l2_main测试程序源文件 │ ├── src │ │ ├── v4l2_buffer.cpp//OH v4l2适配层数据缓存接口源文件 │ │ ├── v4l2_control//OH v4l2适配层控制指令接口源文件 │ │ ├── v4l2_dev.cpp//OH v4l2适配层设备接口源文件 │ │ ├── v4l2_fileformat.cpp//OH v4l2适配层文件格式接口源文件 │ │ ├── v4l2_stream.cpp//OH v4l2适配层流处理接口源文件 │ │ └── v4l2_uvc.cpp//OH v4l2适配层uvc接口源文件 │ └── test │ ├── BUILD.gn │ ├── unittest │ ├── include │ │ └── utest_v4l2_dev.h//OH v4l2适配层单元测试头文件 │ └── src │ └── utest_v4l2_dev.cpp//OH v4l2适配层单元测试源文件 └── pipeline_core ├── BUILD.gn └── src ├── ipp_algo_example │ └── ipp_algo_example.c//图像修图调整算法源文件(预留修图接口,用于客户自行增补图像修正算法) └── node ├── BufferAllocator2//芯片原厂DMA缓冲区分配相关头文件(不使用DMA分配图像缓存,不需要移植进来,具体根据芯片原厂移植指导进行) │ ├── BufferAllocator2.h │ ├── BufferAllocatorWrapper2.h │ └── dmabufheap-defs2.h ├── BufferAllocator2.cpp//芯片原厂DMA缓冲区分配相关源文件(使用ion分配图像缓存,不需要移植进来,,具体根据芯片原厂移植指导进行) ├── BufferAllocatorWrapper2.cpp//芯片原厂DMA缓冲区分配相关源文件(使用ion分配图像缓存,不需要移植进来,具体根据芯片原厂移植指导进行) ├── MemDmabuf2.cpp//芯片原厂DMA缓冲区分配相关源文件(使用ion分配图像缓存,不需要移植进来,具体根据芯片原厂移植指导进行) ├── MemDmabuf2.h//芯片原厂DMA缓冲区分配相关源文件(使用ion分配图像缓存,不需要移植进来,具体根据芯片原厂移植指导进行) ├── MemObject2.cpp//芯片原厂DMA缓冲区分配相关源文件(使用ion分配图像缓存,不需要移植进来,具体根据芯片原厂移植指导进行) ├── MemObject2.h//芯片原厂DMA缓冲区分配相关源文件(使用ion分配图像缓存,不需要移植进来,具体根据芯片原厂移植指导进行) ├── avc_enc_api.h//芯片原厂H264编码库api头文件,具体根据芯片原厂移植指导进行 ├── avch264enc.cpp//芯片原厂H264编码库编码源文件,具体根据芯片原厂移植指导进行 ├── avch264enc.h//芯片原厂H264编码库编码头文件,具体根据芯片原厂移植指导进行 ├── codec_node.cpp//编码节点源文件 ├── codec_node.h//编码节点头文件 ├── colorfunc.c//YUV转RGBA8888 neon汇编加速指令芯片原厂源文件,具体根据芯片原厂移植指导进行 ├── colorfunc.h//YUV转RGBA8888 neon汇编加速指令芯片原厂头文件,具体根据芯片原厂移植指导进行 ├── exif_node.cpp//扩展节点源文件 ├── exif_node.h//扩展节点头文件 ├── face_node.cpp//人脸节点源文件 ├── face_node.h//人脸节点头文件 ├── ion │ ├── ion.c//芯片原厂ION缓冲区处理相关源文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) │ └── ion.h//芯片原厂ION缓冲区处理相关头文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) ├── ion.h//芯片原厂ION缓冲区处理相关头文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) ├── ion_4.12.h//芯片原厂ION缓冲区处理相关头文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) ├── jpeg_api.h//芯片原厂JPEG编码相关API头文件(具体根据芯片原厂移植指导进行) ├── jpeg_codec.cpp//芯片原厂JPEG编码处理相关源文件(具体根据芯片原厂移植指导进行) ├── linux │ ├── ion.h//芯片原厂ION缓冲区处理相关源文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) │ ├── ion_4.12.h//芯片原厂ION缓冲区处理相关源文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) │ ├── ion_4.19.h//芯片原厂ION缓冲区处理相关源文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) │ └── ion_test.h//芯片原厂ION缓冲区处理相关源文件(使用ion分配图像缓存,需要移植进来,具体根据芯片原厂移植指导进行) ├── md5.c//MD5算法函数库,被H264编码库使用,具体参看芯片原厂指导 ├── sci_types.h ├── sprd_ion.h ├── util.cpp//被H264编码库使用,具体参看芯片原厂指导 ├── util.h └── utils ├── Log.h └── atomic.h
2.2、适配源码修改细节说明:
2.2.1、device/board/厂商名/产品名/camera/demo/include/project_camera_demo.h:
namespace OHOS::Camera { #define CAMERA_PREVIEW_WIDTH 640 #define CAMERA_PREVIEW_HEIGHT 480 #define CAMERA_CAPTURE_WIDTH 640// 1280 将对应分辨率更改为与预览一致,修改仅影响camera_host框架的调试测试程序ohos_camera_demo #define CAMERA_CAPTURE_HEIGHT 480// 960 将对应分辨率更改为与预览一致 #define CAMERA_VIDEO_WIDTH 640// 1280 将对应分辨率更改为与预览一致 #define CAMERA_VIDEO_HEIGHT 480// 960 将对应分辨率更改为与预览一致 #define CAMERA_CAPTURE_ENCODE_TYPE ENCODE_TYPE_JPEG #define CAMERA_VIDEO_ENCODE_TYPE ENCODE_TYPE_H264 #define CAMERA_FORMAT PIXEL_FMT_RGBA_8888 } // namespace OHOS::Camera
2.2.2、device/board/厂商名/产品名/camera/device_manager/include/project_hardware.h:
std::vector<HardwareConfiguration> hardware = { {CAMERA_FIRST, DM_M_SENSOR, DM_C_SENSOR, (std::string) "rkisp_v5"},//"rkisp_v5"修改为后摄对应的sensor驱动名称, {CAMERA_FIRST, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, {CAMERA_FIRST, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"}, {CAMERA_SECOND, DM_M_SENSOR, DM_C_SENSOR, (std::string) "Imx600"},//"Imx600"修改为前摄对应的sensor驱动名称 {CAMERA_SECOND, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, {CAMERA_SECOND, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"} };
2.2.3、device/board/厂商名/产品名/camera/device_manager:
2.2.3.1、修改适配sensor特性文件对应文件名称:
device/board/厂商名/产品名/camera/device_manager/include/前摄sensor名.h device/board/厂商名/产品名/camera/device_manager/include/后摄sensor名.h device/board/厂商名/产品名/camera/device_manager/src/前摄sensor名.cpp device/board/厂商名/产品名/camera/device_manager/src/后摄sensor名.cpp
2.2.3.2、修改sensor特性文件中对应的类名Imx600为新的sensor类名,如相关修改:
class 新的sensor类名 : public ISensor { DECLARE_SENSOR(新的sensor类名) public: 新的sensor类名(); virtual ~新的sensor类名(); void Init(Camera::CameraMetadata& camera_meta_data); void InitPhysicalSize(Camera::CameraMetadata& camera_meta_data); void InitAntiBandingModes(Camera::CameraMetadata& camera_meta_data); void InitAeFpsTarget(Camera::CameraMetadata& camera_meta_data); void InitCompensationRange(Camera::CameraMetadata& camera_meta_data); void InitSensitivityRange(Camera::CameraMetadata& camera_meta_data); };
2.2.3.3、修改device/board/厂商名/产品名/camera/device_manager/BUILD.gn,对应上新修改的文件名:
ohos_shared_library("camera_device_manager") { sources = [ "$camera_path/adapter/platform/v4l2/src/device_manager/enumerator_manager.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/flash_controller.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/flash_manager.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/idevice_manager.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/isp_controller.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/isp_manager.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/sensor_controller.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/sensor_manager.cpp", "$camera_path/adapter/platform/v4l2/src/device_manager/v4l2_device_manager.cpp", "$camera_path/device_manager/src/icontroller.cpp", "$camera_path/device_manager/src/imanager.cpp", "$camera_path/device_manager/src/isensor.cpp", "$board_camera_path/device_manager/src/新的sensor.cpp",//"src/rkispv5.cpp",原rkispv5.cpp替换为新的sensor.cpp ]
2.2.3.4、device/board/厂商名/产品名/camera/driver_adapter文件夹下新增V4L2适配层头文件、以及V4L2适配层cpp文件。
适配层头文件文件夹:device/board/厂商名/产品名/camera/driver_adapter/include拷贝以下文件: v4l2_buffer.h、 v4l2_common.h、 v4l2_control.h、 v4l2_dev.h、 v4l2_fileformat.h、 v4l2_stream.h、 v4l2_temp.h、 v4l2_uvc.h 增补芯片原厂v4l2适配库v4l2适配库对应的头文件:v4l2_client.h,由芯片原厂授权ISV提供。 并由芯片原厂授权ISV提供v4l2适配库对应的so或源代码,并提供更底层的闭源库so,。 适配层源文件文件夹:device/board/厂商名/产品名/camera/driver_adapter/src拷贝以下文件: v4l2_buffer.cpp、 v4l2_control.cpp、 v4l2_dev.cpp、 v4l2_fileformat.cpp、 v4l2_stream.cpp、 v4l2_uvc.cpp
2.2.3.5、V4L2适配层代码修改:
注:由于社区OpenHarmony camera代码的演进增修,代码细节不一定能对上,另限于篇幅,代码修改细节无法贴全,因此下面的代码部分类伪代码,重点梳理关键节点以及思路,适配时参照修改点进行修改,并通过编译补充相关代码细节:
1、device/board/厂商名/产品名/camera/driver_adapter/include/v4l2_common.h ...... #include <vector> #include "v4l2_client.h"//新增芯片原厂v4l2适配库对接库头文件 #if defined(V4L2_UTEST) || defined (V4L2_MAIN_TEST) #include "v4l2_temp.h" #else #include <stream.h> #endif namespace OHOS::Camera { #define MAXSTREAMCOUNT 4 #define MAXVIDEODEVICE 24 #define DEVICENAMEX "/dev/video" #define SPRDDRIVERNAME "sprd_video"//新增芯片原厂驱动名定义 #define MAXUVCNODE 10 #define UNISOCV4L2OPENFLAG 3//新增芯片原厂库openflag ......
2、device/board/厂商名/产品名/camera/driver_adapter/include/v4l2_dev.h ...... RetCode SetCallback(BufCallback cb); RetCode Flush(const std::string& cameraID); void SetMemoryType(uint8_t &memType); static RetCode Init(std::vector<std::string>& cameraIDs); static void Release(); //新增芯片原厂v4l2接口库释放函数 static std::map<std::string, std::string> deviceMatch; static std::map<std::string, int> fdMatch; static std::mutex deviceFdLock_; static V4l2AdapterInterface *v4l2Handle_; //新增芯片原厂v4l2接口句柄定义 static UnisocV4l2ClientFactory global_UnisocV4l2ClientFactory; //新增芯片原厂v4l2接口库工厂类对象 static std::map<std::string, std::string> CreateDevMap() { std::map<std::string, std::string> tmp_map; tmp_map.insert(std::pair<std::string, std::string>("INVALID", "INVALID")); return tmp_map; } static std::map<std::string, int> CreateFdMap() { std::map<std::string, int> tmp_map; tmp_map.insert(std::pair<std::string, int>("INVALID", 0)); return tmp_map; } static V4l2AdapterInterface *CreateV4l2AdapterInterface() //新增芯片原厂v4l2接口库句柄创建函数 { V4l2AdapterInterface *v4l2Handle = global_UnisocV4l2ClientFactory.create(); return v4l2Handle; } private: int GetCurrentFd(const std::string& cameraID); void loopBuffers(); RetCode CreateEpoll(int fd, const unsigned int streamNumber); void EraseEpoll(int fd); RetCode ConfigFps(const int fd, DeviceFormat& format, V4l2FmtCmd command); std::thread* streamThread_ = nullptr; unsigned int streamNumber_ = 0; unsigned int fdNum_ = 0; struct pollfd fds_[MAXSTREAMCOUNT]; std::mutex pollLock_; std::shared_ptr<HosV4L2Buffers> myBuffers_ = nullptr; std::shared_ptr<HosV4L2Streams> myStreams_ = nullptr; std::shared_ptr<HosFileFormat> myFileFormat_ = nullptr; std::shared_ptr<HosV4L2Control> myControl_ = nullptr; enum v4l2_memory memoryType_ = V4L2_MEMORY_MMAP; //unisoc camera, V4L2_MEMORY_USERPTR; enum v4l2_buf_type bufferType_ = V4L2_BUF_TYPE_PRIVATE; ......
3、将v4l2_buffer.cpp、v4l2_control.cpp、v4l2_dev.cpp、v4l2_fileformat.cpp、v4l2_stream.cpp、v4l2_uvc.cpp文件中使用HosV4L2Dev::v4l2Handle_->ioctl替换掉ioctl函数
4、drivers/peripheral/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp ...... RetCode HosV4L2Buffers::V4L2QueueBuffer(int fd, const std::shared_ptr<FrameSpec>& frameSpec) { struct v4l2_buffer buf = {}; struct v4l2_plane planes[1] = {}; if (frameSpec == nullptr) { CAMERA_LOGE("V4L2QueueBuffer: frameSpec is NULL\n"); return RC_ERROR; } if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { buf.m.planes = planes; } MakeInqueueBuffer(buf, frameSpec); bufferLock_.lock();//新增 auto itr = queueBuffers_.find(fd);//新增 if (itr != queueBuffers_.end()) {//新增 int rc = HosV4L2Dev::v4l2Handle_->ioctl(fd, VIDIOC_QBUF, &buf); if (rc < 0) { bufferLock_.unlock(); CAMERA_LOGE("HosV4L2Dev::v4l2Handle_->ioctl VIDIOC_QBUF failed: %{public}s\n", strerror(errno)); return RC_ERROR; } itr->second[buf.index] = frameSpec; bufferLock_.unlock();//新增 CAMERA_LOGD("insert frameMap fd = %{public}d buf.index = %{public}d\n", fd, buf.index); } else {//新增 int rc = HosV4L2Dev::v4l2Handle_->ioctl(fd, VIDIOC_QBUF, &buf);// if (rc < 0) { bufferLock_.unlock();//新增 CAMERA_LOGE("ioctl VIDIOC_QBUF failed: %s\n", strerror(errno)); return RC_ERROR; } FrameMap frameMap; frameMap.insert(std::make_pair(buf.index, frameSpec)); queueBuffers_.insert(std::make_pair(fd, frameMap)); bufferLock_.unlock();//新增 CAMERA_LOGD("insert fd = %{public}d buf.index = %{public}d\n", fd, buf.index); }//新增 bufCont++;//新增 CAMERA_LOGD("V4L2QueueBuffer success bufCont = %{public}d\n", bufCont); return RC_OK; } ......
5、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_buffer.cpp ...... RetCode HosV4L2Buffers::V4L2DequeueBuffer(int fd) { struct v4l2_buffer buf = {}; struct v4l2_plane planes[1] = {}; while (bufCont < 1) {//新增下面一段,当前系统缓冲区为空时,暂停去收取图像数据---start usleep(20000); if (bufCont < 1) { return RC_ERROR; } else { break; } }//新增---end buf.type = bufferType_; buf.memory = memoryType_; CAMERA_LOGD("V4L2DequeueBuffer memoryType_ = %{public}d\n", (int)memoryType_); if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { buf.m.planes = planes; buf.length = 1; } int rc = HosV4L2Dev::v4l2Handle_->ioctl(fd, VIDIOC_DQBUF, &buf); CAMERA_LOGD("V4L2DequeueBuffer memoryType_ buf.index == %{public}d \n", buf.index); if (rc < 0) { CAMERA_LOGE("HosV4L2Dev::v4l2Handle_->ioctl VIDIOC_DQBUF failed: %{public}s\n", strerror(errno)); return RC_ERROR; } if (memoryType_ == V4L2_MEMORY_MMAP) { if (adapterBufferMap_[buf.index].userBufPtr && adapterBufferMap_[buf.index].start) { if ( memcpy_s(adapterBufferMap_[buf.index].userBufPtr, adapterBufferMap_[buf.index].length, adapterBufferMap_[buf.index].start, adapterBufferMap_[buf.index].length) != 0 ) {//新增报错返回值判断 rc = -3; } } else {//新增报错返回值判断 rc = -2; } } else {//新增报错返回值判断 rc = -1; } bufferLock_.lock();//新增---加锁保护 auto IterMap = queueBuffers_.find(fd); if (IterMap == queueBuffers_.end()) { bufferLock_.unlock();//新增---返回时配套解锁 CAMERA_LOGE("std::map queueBuffers_ no fd\n"); return RC_ERROR; } auto& bufferMap = IterMap->second; auto Iter = bufferMap.find(buf.index); if (Iter == bufferMap.end()) { bufferLock_.unlock();//新增---返回时配套解锁 CAMERA_LOGE("V4L2DequeueBuffer buf.index == %{public}d is not find in FrameMap\n", buf.index); return RC_ERROR; } if (dequeueBuffer_ == nullptr) { bufferMap.erase(Iter); bufferLock_.unlock();//新增---返回时配套解锁 CAMERA_LOGE("V4L2DequeueBuffer buf.index == %{public}d no callback\n", buf.index); return RC_ERROR; } std::shared_ptr<FrameSpec> framebuff = Iter->second; bufferMap.erase(Iter); bufferLock_.unlock();//新增---使用完毕配套解锁 if (rc < 0) {//新增---数据操作无效,但内存仍需要流转去回收 framebuff->buffer_->SetBufferStatus(CAMERA_BUFFER_STATUS_INVALID); } dequeueBuffer_(framebuff); bufCont--;//新增---缓冲区计数减 CAMERA_LOGD("V4L2DequeueBuffer success bufCont = %{public}d\n", bufCont); return RC_OK; } ......
6、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_buffer.cpp RetCode HosV4L2Buffers::V4L2ReleaseBuffers(int fd) { CAMERA_LOGE("HosV4L2Buffers::V4L2ReleaseBuffers\n"); bufferLock_.lock(); queueBuffers_.erase(fd); bufferLock_.unlock(); for (auto &mem : adapterBufferMap_) { if (mem.second.start) { if (HosV4L2Dev::v4l2Handle_->munmap(mem.second.start, mem.second.length) < 0) { // return RC_ERROR;//注释掉错误码退出 } } } adapterBufferMap_.clear(); return V4L2ReqBuffers(fd, 0); }
7、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_buffer.cpp RetCode HosV4L2Buffers::Flush(int fd) { CAMERA_LOGD("HosV4L2Buffers::Flush\n"); return RC_OK; } void HosV4L2Buffers::ReSetBufCont()//新增缓冲区数量归0函数---start { bufCont = 0; }//新增缓冲区数量归0函数---end
8、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_dev.cpp std::map<std::string, std::string> HosV4L2Dev::deviceMatch = HosV4L2Dev::CreateDevMap(); std::map<std::string, int> HosV4L2Dev::fdMatch = HosV4L2Dev::CreateFdMap(); std::mutex HosV4L2Dev::deviceFdLock_ = {}; V4l2AdapterInterface *HosV4L2Dev::v4l2Handle_ = global_UnisocV4l2ClientFactory.create();//新增芯片原厂V4L2接口库句柄创建 static constexpr uint32_t WATING_TIME = 1000 * 100; HosV4L2Dev::HosV4L2Dev() {} ...... RetCode HosV4L2Dev::Init(std::vector<std::string>& cameraIDs) { auto myFileFormat = std::make_shared<HosFileFormat>(); if (myFileFormat == nullptr) { CAMERA_LOGE("error: InitMatch: myFileFormat_ make_shared is NULL\n"); return RC_ERROR; } myFileFormat->V4L2MatchDevice(cameraIDs); return RC_OK; } void HosV4L2Dev::Release()//新增芯片原厂V4L2接口库句柄释放函数---start { if (v4l2Handle_ != nullptr) { delete v4l2Handle_; v4l2Handle_ = nullptr; } }//新增芯片原厂V4L2接口库句柄释放函数---end ......
9、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_dev.cpp ...... RetCode HosV4L2Dev::ReqBuffers(const std::string& cameraID, unsigned int buffCont) { int rc, fd; fd = GetCurrentFd(cameraID); if (fd < 0) { CAMERA_LOGE("error: ReqBuffers: GetCurrentFd error\n"); return RC_ERROR; } if (myBuffers_ == nullptr) { myBuffers_ = std::make_shared<HosV4L2Buffers>(memoryType_, bufferType_); if (myBuffers_ == nullptr) { CAMERA_LOGE("error: Creatbuffer: myBuffers_ make_shared is NULL\n"); return RC_ERROR; } } if (buffCont > 0) {//初始化缓冲区数量---start myBuffers_->ReSetBufCont(); }//初始化缓冲区数量---end rc = myBuffers_->V4L2ReqBuffers(fd, buffCont); if (rc == RC_ERROR) { CAMERA_LOGE("error: Creatbuffer: V4L2ReqBuffers error\n"); return RC_ERROR; } return RC_OK; } ......
10、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_dev.cpp ...... void HosV4L2Dev::loopBuffers() { int nfds, rc; CAMERA_LOGD("!!! loopBuffers enter\n"); prctl(PR_SET_NAME, "v4l2_loopbuffer"); CAMERA_LOGD("loopBuffers: poll streamNumber_ = %d\n", streamNumber_); while (streamNumber_ > 0) { /*nfds = epoll_wait(epollFd_, events, MAXSTREAMCOUNT, -1); CAMERA_LOGD("loopBuffers: epoll_wait rc = %{public}d streamNumber_ == %{public}d\n", nfds, streamNumber_); for (int n = 0; nfds > 0; ++n, --nfds) { if ((events[n].events & EPOLLIN) && (events[n].data.fd != eventFd_)) {*/芯片原厂适配库不支持上述机制,需要注释掉上面这一段。 CHECK_IF_PTR_NULL_RETURN_VOID(myBuffers_); rc = myBuffers_->V4L2DequeueBuffer(fds_[0].fd); if (rc == RC_ERROR) { CAMERA_LOGE("loopBuffers: myBuffers_->V4L2DequeueBuffer return error == %d\n", rc); // continue; } /*} else { CAMERA_LOGD("loopBuffers: epoll invalid events = 0x%x or eventFd exit = %d\n", events[n].events, (events[n].data.fd == eventFd_)); usleep(WATING_TIME); } }*/芯片原厂适配库不支持上述机制,需要配合上面段注释掉上面这一段。 } CAMERA_LOGD("!!! loopBuffers exit\n"); } ......
11、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_dev.cpp ...... RetCode HosV4L2Dev::StartStream(const std::string& cameraID) { int rc, fd; fd = GetCurrentFd(cameraID); if (fd < 0) { CAMERA_LOGE("error: ReqBuffers: GetCurrentFd error\n"); return RC_ERROR; } if (myStreams_ == nullptr) { myStreams_ = std::make_shared<HosV4L2Streams>(bufferType_); if (myStreams_ == nullptr) { CAMERA_LOGE("error: StartStream: myStreams_ make_shared is NULL\n"); return RC_ERROR; } } rc = myStreams_->V4L2StreamOn(fd); if (rc == RC_ERROR) { CAMERA_LOGE("error: StartStream: V4L2StreamOn error\n"); return RC_ERROR; } //rc = CreateEpoll(fd, streamNumber_);//芯片原厂V4L2接口库不支持epoll机制,没用到 //if (rc == RC_ERROR) { // CAMERA_LOGE("StartStream: CreateEpoll error\n"); // return RC_ERROR; //} if (streamNumber_ == 0) { streamNumber_++; streamThread_ = new (std::nothrow) std::thread(&HosV4L2Dev::loopBuffers, this); if (streamThread_ == nullptr) { streamNumber_--; CAMERA_LOGE("V4L2 StartStream start thread failed\n"); return RC_ERROR; } } return RC_OK; } RetCode HosV4L2Dev::StopStream(const std::string& cameraID) { int rc, fd; if (myStreams_ == nullptr) { CAMERA_LOGE("error: StopStream: myStreams_ is NULL\n"); return RC_ERROR; } if (streamThread_ == nullptr) { CAMERA_LOGE("StopStream thread is stopped\n"); return RC_ERROR; } streamNumber_ -= 1; CAMERA_LOGD("HosV4L2Dev::StopStream streamNumber_ = %d\n", streamNumber_); if (streamNumber_ == 0) { CAMERA_LOGD("waiting loopBuffers stop\n"); streamThread_->join(); } fd = GetCurrentFd(cameraID); if (fd < 0) { CAMERA_LOGE("error: ReqBuffers: GetCurrentFd error\n"); return RC_ERROR; } rc = myStreams_->V4L2StreamOff(fd); if (rc == RC_ERROR) { CAMERA_LOGE("error: StartStream: V4L2StreamOn error\n"); return RC_ERROR; } //EraseEpoll(fd);////芯片原厂V4L2接口库不支持epoll机制,没用到 if (streamNumber_ == 0) { delete streamThread_; streamThread_ = nullptr; } return RC_OK; } ......
11、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_fileformat.cpp ...... RetCode HosFileFormat::V4L2GetCapability(int fd, const std::string& devName, std::string& cameraId) { struct v4l2_capability cap = {}; int rc = HosV4L2Dev::v4l2Handle_->ioctl(fd, VIDIOC_QUERYCAP, &cap); if (rc < 0) { return RC_ERROR; } if (!(cap.capabilities & V4L2_CAP_STREAMING)) { return RC_ERROR; } if (!((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) || (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))) { return RC_ERROR; } if (SPRDDRIVERNAME != std::string((char*)cap.driver)) {//修改,来校验芯片原厂驱动名 return RC_ERROR; } std::lock_guard<std::mutex> l(HosV4L2Dev::deviceFdLock_); auto itr = HosV4L2Dev::deviceMatch.find(cameraId);////修改,来校验芯片原厂设备是否有重复插入 if (itr == HosV4L2Dev::deviceMatch.end()) {//修改,来校验芯片原厂设备是否有重复插入 HosV4L2Dev::deviceMatch.insert(std::make_pair(cameraId, devName)); } else { CAMERA_LOGE("v4l2 device cameraId = %{public}s repeat Insert error\n", cameraId.c_str()); } CAMERA_LOGD("v4l2 driver name = %{public}s\n", cap.driver); CAMERA_LOGD("v4l2 capabilities = 0x%{public}x\n", cap.capabilities); CAMERA_LOGD("v4l2 card: %{public}s\n", cap.card); CAMERA_LOGD("v4l2 bus info: %{public}s\n", cap.bus_info); return RC_OK; } ...... int HosFileFormat::V4L2OpenDevice(const std::string& deviceName) { size_t len = deviceName.length(); if (len == 0) { CAMERA_LOGD("V4L2OpenDevice deviceName length is 0\n"); } int rc = 0; char devName[PATH_MAX] = {0}; CAMERA_LOGD("V4L2OpenDevice deviceName = %{public}s\n", deviceName.c_str()); if ((sprintf_s(devName, sizeof(devName), "%s", deviceName.c_str())) < 0) { CAMERA_LOGE("%s: sprintf devName failed 1", __func__); } CAMERA_LOGD("V4L2OpenDevice %{public}s\n", devName); // devName = realpath(deviceName.c_str(), absPath);//虚拟设备点,无法校验设备路径 // if (devName == nullptr) { // CAMERA_LOGE("V4L2OpenDevice realpath error\n"); // return RCERRORFD; // } rc = HosV4L2Dev::v4l2Handle_->open(devName, UNISOCV4L2OPENFLAG); if ( rc <= 0) { CAMERA_LOGE("V4L2OpenDevice open error\n"); } return rc; } ......
12、device/board/厂商名/产品名/camera/driver_adapter/src/v4l2_fileformat.cpp ...... void HosFileFormat::V4L2MatchDevice(std::vector<std::string>& cameraIDs) { struct stat st = {}; char devName[16] = {0}; std::string name = DEVICENAMEX; int fd = 0; int rc = 0; size_t sensorNum = cameraIDs.size();// for (size_t i = 0; i < sensorNum; ++i) {//虚拟设备点,无法枚举节点,通过配置camera数量来match camera if ((sprintf_s(devName, sizeof(devName), "%s%d", name.c_str(), i)) < 0) { CAMERA_LOGE("%s: sprintf devName failed", __func__); } fd = HosV4L2Dev::v4l2Handle_->open(devName, UNISOCV4L2OPENFLAG); if (fd < 0) { CAMERA_LOGE("%s: open devName %s failed", __func__, devName); continue; } rc = V4L2GetCapability(fd, devName, cameraIDs.at(i)); if (rc == RC_ERROR) { CAMERA_LOGE("%s: V4L2GetCapability fail", __func__); HosV4L2Dev::v4l2Handle_->close(fd); continue; } CAMERA_LOGD("%s: open devName = %s end", __func__, devName); HosV4L2Dev::v4l2Handle_->close(fd); //break;//注释掉,不然match后摄后直接就退出了 } CAMERA_LOGD("%s: open sensorNum = %d all end", __func__, (int)sensorNum); } ......
更多推荐
所有评论(0)