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)