摘要

本文通过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仓库。

image-20230725205100856

 

 

二、OpenHarmony camera预览拍照的适配修改:

1、适配框图介绍:

OH适配代码的开发集中在Camera_host层的OH部分,其中改造最多的是OH V4L2适配层。

image-20230720172156677

 

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);
}
......

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐