OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析1_初始化

系统
HarmonyOS 相机驱动框架模型对上实现相机HDI接口,对下实现相机Pipeline模型,管理相机各个硬件设备。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

说明

  • 解析源码基于 OpenHarmony-v3.0-LTS 版本,代码结构和部分源码与当前维护的主线分支有差别。
  • 解析示例代码 drivers\peripheral\camera\hal\init\demo_main.cpp

1. 简介

HarmonyOS 相机驱动框架模型对上实现相机HDI接口,对下实现相机Pipeline模型,管理相机各个硬件设备。

各层的基本概念如下:

HDI实现层,对上实现OHOS相机标准南向接口。

框架层,对接HDI实现层的控制、流的转发,实现数据通路的搭建、管理相机各个硬件设备等功能。

适配层,屏蔽底层芯片和OS差异,支持多平台适配。

2. 框架图

OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析1_初始化-鸿蒙HarmonyOS技术社区

3. 代码结构

  1. drivers\peripheral\camera 
  2. ├── hal 
  3. │   ├── adapter                //芯片和平台适配层  
  4. │   ├── buffer_manager         //buffer管理 
  5. │   ├── device_manager         //设备控制接口定义 
  6. │   ├── hdi_impl               //HDI Implementation实现 
  7. │   │   ├── include 
  8. │   │   ├── src 
  9. │   │   │   ├── camera_device              //Device 
  10. │   │   │   ├── camera_host                //Host 
  11. │   │   │   ├── offline_stream_operator    //OffineStreamOperator 
  12. │   │   │   └── stream_operator            //StreamOperator 
  13. │   │   └── test 
  14. │   ├── include 
  15. │   ├── init                                //HiSpark 3516 demo 代码 
  16. │   ├── pipeline_core                        
  17. │   │   ├── host_stream 
  18. │   │   │   ├── include 
  19. │   │   │   └── src 
  20. │   │   ├── include 
  21. │   │   ├── ipp                            //ipp 
  22. │   │   │   ├── include 
  23. │   │   │   └── src 
  24. │   │   ├── nodes                          //基础通用node 
  25. │   │   │   ├── include 
  26. │   │   │   └── src 
  27. │   │   │       ├── dummy_node 
  28. │   │   │       ├── fork_node 
  29. │   │   │       ├── merge_node 
  30. │   │   │       ├── node_base 
  31. │   │   │       ├── sensor_node 
  32. │   │   │       ├── sink_node 
  33. │   │   │       ├── source_node 
  34. │   │   │       └── transform_node 
  35. │   │   ├── pipeline_impl                //pipeline构建实现 
  36. │   │   │   ├── include 
  37. │   │   │   └── src 
  38. │   │   │       ├── builder 
  39. │   │   │       ├── dispatcher 
  40. │   │   │       ├── parser 
  41. │   │   │       └── strategy 
  42. │   │   │           └── config 
  43. │   │   ├── src                          //PipelineCore 
  44. │   │   └── utils 
  45. ├── hal_c                                //hal层 C实现接口 
  46. │   ├── hdi_cif 
  47. │   │   ├── include 
  48. │   │   └── src 
  49. │   └── include 
  50. └── interfaces                            //Camera Host框架HDI接口 
  51.     └── include                            
  52.         ├── callback                      //框架涉及的所有callback接口 
  53.         │   ├── device 
  54.         │   ├── host 
  55.         │   └── operator 
  56.         ├── client 
  57.         └── server 

4. 源码解析

上层demo代码相对简单,这里只贴出关键部分代码

  1. int main(int argc, char** argv) 
  2.     RetCode rc = RC_OK; 
  3.  
  4.     auto mainDemo = std::make_shared<Hos3516Demo>(); 
  5.     rc = mainDemo->InitSensors(); 
  6.     if (rc == RC_ERROR) { 
  7.         CAMERA_LOGE("main test: mainDemo->InitSensors() error\n"); 
  8.         return -1; 
  9.     } 
  10.     rc = mainDemo->InitCameraDevice(); 
  11.     if (rc == RC_ERROR) { 
  12.         CAMERA_LOGE("main test: mainDemo->InitCameraDevice() error\n"); 
  13.         return -1; 
  14.     } 
  15.  
  16.     ...... 
  17.  
  18.     return RC_OK; 

4.1 Hos3516Demo::InitSensors()

4.1.1 上层代码

上层获取HDI层的ICameraHost:设备管理接口,并设置Host层的回调。

  1. constexpr const char *DEMO_SERVICE_NAME = "camera_service"
  2.  
  3. RetCode Hos3516Demo::InitSensors() 
  4.     int rc = 0; 
  5.  
  6.     CAMERA_LOGD("demo test: InitSensors enter"); 
  7.  
  8.     if (demoCameraHost_ != nullptr) { 
  9.         return RC_OK; 
  10.     } 
  11.  
  12.     demoCameraHost_ = ICameraHost::Get(DEMO_SERVICE_NAME); 
  13.     if (demoCameraHost_ == nullptr) { 
  14.         CAMERA_LOGE("demo test: ICameraHost::Get error"); 
  15.         return RC_ERROR; 
  16.     } 
  17.  
  18.     hostCallback_ = new CameraHostCallback(); 
  19.     rc = demoCameraHost_->SetCallback(hostCallback_); 
  20.     if (rc != Camera::NO_ERROR) { 
  21.         CAMERA_LOGE("demo test: demoCameraHost_->SetCallback(hostCallback_) error"); 
  22.         return RC_ERROR; 
  23.     } 
  24.  
  25.     CAMERA_LOGD("demo test: InitSensors exit"); 
  26.  
  27.     return RC_OK; 

4.1.2 时序图

OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析1_初始化-鸿蒙HarmonyOS技术社区

CameraHostProxy 提供上层调用的代理接口。

CameraHostStub 提供对应代理接口的HDI层的 ‘桩’。

CameraHostImpl 是接口层的具体实现。

Proxy-Stub 负责上下接口参数的数据转换等工作。

4.1.3 ICameraHost::Get(const char *serviceName)

从ServiceManager中取出CameraHostProxy。并返回ICameraHost接口类指针

  1. //drivers\peripheral\camera\interfaces\include\client\camera_host_proxy.cpp 
  2. sptr<ICameraHost> ICameraHost::Get(const char *serviceName) 
  3.     do { 
  4.         using namespace OHOS::HDI::ServiceManager::V1_0; 
  5.         auto servMgr = IServiceManager::Get(); 
  6.         if (servMgr == nullptr) { 
  7.             HDF_LOGE("%s: IServiceManager failed!", __func__); 
  8.             break; 
  9.         } 
  10.  
  11.         auto remote = servMgr->GetService(serviceName); 
  12.         if (remote != nullptr) { 
  13.             sptr<CameraHostProxy> hostSptr = iface_cast<CameraHostProxy>(remote); 
  14.             return hostSptr; 
  15.         } 
  16.         HDF_LOGE("%s: GetService failed! serviceName = %s", __func__, serviceName); 
  17.     } while(false); 
  18.  
  19.     HDF_LOGE("%s: get %s failed!", __func__, serviceName); 
  20.     return nullptr; 

4.1.4 CameraHostProxy::SetCallback(const OHOS::sptr &callback)

ICameraHostCallback 回调接口类定义了下面两个回调接口,在CameraHostCallback类中具体实现并通过IPC通讯最终设置到CameraHostImpl

  1. virtual void OnCameraStatus(const std::string &cameraId, CameraStatus status) = 0; 
  2. virtual void OnFlashlightStatus(const std::string &cameraId, FlashlightStatus status) = 0; 

 client::host_proxy发起IPC —> server::host_service_stub

注意 这里有个坑! 通常 SendRequest() 和 OnRemoteRequest() 是一对。sendRequest()在client::XXX_proxy调用,OnRemoteRequest() 在server::XXX_stub调用。但是CamereaHost 的OnRemoteRequest是由 HDF中的 HdfCameraService来接收 通过Dispatch接口再调用具体的CameraHostStub!!

这里的proxy和stub 的cmdID 宏定义名字不一样,不太利于代码阅读。

  1. //drivers\peripheral\camera\interfaces\include\client\camera_host_proxy.cpp 
  2. CamRetCode CameraHostProxy::SetCallback(const OHOS::sptr<ICameraHostCallback> &callback) 
  3.     ...... 
  4.  
  5.     int32_t ret = Remote()->SendRequest(CMD_CAMERA_HOST_REMOTE_SET_CALLBACK, data, reply, option); 
  6.  
  7.     ...... 
  8.  
  9.     return static_cast<CamRetCode>(reply.ReadInt32()); 
  1. //drivers\peripheral\camera\interfaces\include\server\camera_host_service_stub.cpp 
  2. int32_t CameraHostStub::CameraHostStubSetCallback( 
  3.     MessageParcel &data, MessageParcel &reply, MessageOption &option
  4.     bool flag = data.ReadBool(); 
  5.     sptr<ICameraHostCallback> hostCallback = nullptr; 
  6.     if (flag) { 
  7.         sptr<IRemoteObject> remoteObj = data.ReadRemoteObject(); 
  8.         hostCallback = OHOS::iface_cast<ICameraHostCallback>(remoteObj); 
  9.     } 
  10.     CamRetCode ret = cameraHost_->SetCallback(hostCallback); 
  11.     if (!reply.WriteInt32(static_cast<int32_t>(ret))) { 
  12.         HDF_LOGE("%s: write retcode failed", __func__); 
  13.         return HDF_FAILURE; 
  14.     } 
  15.  
  16.     return HDF_SUCCESS; 

 最终调用到HDI 接口层的camera_host 完成callback的设置。

  1. //drivers\peripheral\camera\hal\hdi_impl\src\camera_host\camera_host_impl.cpp 
  2. /** 
  3.  * @brief 设置Host回调函数 
  4.  * 
  5.  * @param callback Host回调函数 
  6.  * @return CamRetCode 
  7.  */ 
  8. CamRetCode CameraHostImpl::SetCallback(const OHOS::sptr<ICameraHostCallback> &callback) 
  9.     DFX_LOCAL_HITRACE_BEGIN; 
  10.  
  11.     if (callback == nullptr) { 
  12.         CAMERA_LOGW("host callback is null."); 
  13.         return INVALID_ARGUMENT; 
  14.     } 
  15.  
  16.     cameraHostCallback_ = callback; 
  17.  
  18.     DFX_LOCAL_HITRACE_END; 
  19.     return NO_ERROR; 

4.2 Hos3516Demo::InitCameraDevice()

4.2.1 上层代码

获取CameraIds 、获取默认前置的摄像头设备的ability、创建CameraDeviceCallbak 并OpenCamera

  1. RetCode Hos3516Demo::InitCameraDevice() 
  2.     int rc = 0; 
  3.  
  4.     CAMERA_LOGD("demo test: InitCameraDevice enter"); 
  5.  
  6.     if (demoCameraHost_ == nullptr) { 
  7.         CAMERA_LOGE("demo test: InitCameraDevice demoCameraHost_ == nullptr"); 
  8.         return RC_ERROR; 
  9.     } 
  10.  
  11.     (void)demoCameraHost_->GetCameraIds(cameraIds_); 
  12.     if (cameraIds_.empty()) { 
  13.         return RC_ERROR; 
  14.     } 
  15.     const std::string cameraId = cameraIds_.front(); 
  16.     demoCameraHost_->GetCameraAbility(cameraId, ability_); 
  17.  
  18.     sptr<CameraDeviceCallback> callback = new CameraDeviceCallback(); 
  19.     rc = demoCameraHost_->OpenCamera(cameraIds_.front(), callback, demoCameraDevice_); 
  20.     if (rc != Camera::NO_ERROR || demoCameraDevice_ == nullptr) { 
  21.         CAMERA_LOGE("demo test: InitCameraDevice OpenCamera failed"); 
  22.         return RC_ERROR; 
  23.     } 
  24.  
  25.     CAMERA_LOGD("demo test: InitCameraDevice exit"); 
  26.  
  27.     return RC_OK; 

4.2.2 时序图

OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析1_初始化-鸿蒙HarmonyOS技术社区

4.2.3 GetCameraIds()和GetCameraAblilty()

先来看下两个简单的设备信息获取接口。

获取系统配置中的cameraIds和CameraAbility

IPC 的过程和setcallback一样 此处 略去 IPC相关代码和时序图

直接看实现CameraHostImpl::GetCameraIds() 和 CameraHostImpl::GetCameraAbility(),调用了CameraHostConfig::GetCameraIds()和CameraHostConfig::GetCameraAbility()

  1. 、、drivers\peripheral\camera\hal\hdi_impl\src\camera_host\camera_host_impl.cpp 
  2. /** 
  3.  * @brief 获取当前可用的Camera设备列表 
  4.  * 
  5.  * @param ids 返回的当前可用的设备列表 
  6.  */ 
  7. CamRetCode CameraHostImpl::GetCameraIds(std::vector<std::string> &cameraIds) 
  8.     DFX_LOCAL_HITRACE_BEGIN; 
  9.  
  10.     CameraHostConfig *config = CameraHostConfig::GetInstance(); 
  11.     if (config == nullptr) { 
  12.         return INVALID_ARGUMENT; 
  13.     } 
  14.     RetCode rc = config->GetCameraIds(cameraIds); 
  15.     if (rc != RC_OK) { 
  16.         return INVALID_ARGUMENT; 
  17.     } 
  18.  
  19.     DFX_LOCAL_HITRACE_END; 
  20.     return NO_ERROR; 
  21. /** 
  22.  * @brief 获取Camera的能力集 
  23.  * 
  24.  * @param cameraId 要获取的Camera设备id 
  25.  * @param ability Camera设备的能力集 
  26.  * @return CamRetCode 
  27.  * @see CameraAbility 
  28.  */ 
  29. CamRetCode CameraHostImpl::GetCameraAbility(const std::string &cameraId, 
  30.     std::shared_ptr<CameraAbility> &ability) 
  31.     DFX_LOCAL_HITRACE_BEGIN; 
  32.     CameraHostConfig *config = CameraHostConfig::GetInstance(); 
  33.     if (config == nullptr) { 
  34.         return INVALID_ARGUMENT; 
  35.     } 
  36.     RetCode rc = config->GetCameraAbility(cameraId, ability); 
  37.     if (rc != RC_OK) { 
  38.         return INVALID_ARGUMENT; 
  39.     } 
  40.     DFX_LOCAL_HITRACE_END; 
  41.     return NO_ERROR; 

CameraHostConfig负责从camera_host_config.hcb文件读出设备驱动信息。

  1. //drivers\peripheral\camera\hal\hdi_impl\src\camera_host\camera_host_config.cpp 
  2. RetCode CameraHostConfig::ReadConfigFile() 
  3.     std::unique_ptr<HcsDeal> hcsDeal = std::make_unique<HcsDeal>(CONFIG_PATH_NAME); 
  4.     if (hcsDeal == nullptr) { 
  5.         CAMERA_LOGE("make HcsDeal failed. [pathname = %{public}s]", CONFIG_PATH_NAME.c_str()); 
  6.         return RC_ERROR; 
  7.     } 
  8.  
  9.     RetCode rc = hcsDeal->Init(); 
  10.     if (rc != RC_OK) { 
  11.         CAMERA_LOGE("hcs deal init failed. [pathname = %{public}s]", CONFIG_PATH_NAME.c_str()); 
  12.         return rc; 
  13.     } 
  14.  
  15.     rc = hcsDeal->GetCameraId(cameraIdMap_); 
  16.     if (rc != RC_OK || cameraIdMap_.empty()) { 
  17.         CAMERA_LOGE("config camera id not found. [pathname = %{public}s]", CONFIG_PATH_NAME.c_str()); 
  18.         return rc; 
  19.     } 
  20.  
  21.     rc = hcsDeal->GetMetadata(cameraAbilityMap_); 
  22.     if (rc != RC_OK || cameraAbilityMap_.empty()) { 
  23.         CAMERA_LOGE("config camera ability not found. [pathname = %{public}s]", CONFIG_PATH_NAME.c_str()); 
  24.         return rc; 
  25.     } 
  26.  
  27.     return RC_OK; 
  28. RetCode CameraHostConfig::GetCameraIds(std::vector<std::string> &cameraIds) 
  29.     auto itr = cameraAbilityMap_.begin(); 
  30.     for (; itr != cameraAbilityMap_.end(); itr++) { 
  31.         cameraIds.push_back(itr->first); 
  32.     } 
  33.  
  34.     return RC_OK; 
  35. RetCode CameraHostConfig::GetCameraAbility( 
  36.     const std::string &cameraId, std::shared_ptr<CameraAbility> &ability) 
  37.     auto itr = cameraAbilityMap_.find(cameraId); 
  38.     if (itr != cameraAbilityMap_.end()) { 
  39.         ability = itr->second
  40.         return RC_OK; 
  41.     } 
  42.  
  43.     return RC_ERROR; 

hcb文件是由hcs文件通过build_hcs.py工具生成的具体生成的相关代码在驱动芯片适配的BUILD.gn中

  1. //drivers/peripheral/camera/hal/adapter/chipset/hispark_taurus/BUILD.gn 
  2. action("build_camera_host_config") { 
  3.   script = "$hdf_framework_path/tools/hc-gen/build_hcs.py" 
  4.   sources = [ rebase_path( 
  5.           "//vendor/hisilicon/Hi3516DV300/hdf_config/uhdf/camera/hal/mpp/hispark_taurus/hdi_impl/camera_host_config.hcs") ] 
  6.   outputs = [ "$target_gen_dir/hdi_impl/camera_host_config.hcb" ] 
  7.   args = [ 
  8.     "-o"
  9.     rebase_path(outputs[0]), 
  10.     sources[0], 
  11.   ] 
  12.  
  13. ohos_prebuilt_etc("camera_host_config.hcb") { 
  14.   deps = [ ":build_camera_host_config" ] 
  15.   hcs_outputs = get_target_outputs(":build_camera_host_config"
  16.   source = hcs_outputs[0] 
  17.   relative_install_dir = "hdfconfig" 
  18.   subsystem_name = "hdf" 
  19.   part_name = "hdf" 
  1. //vendor/hisilicon/Hi3516DV300/hdf_config/uhdf/camera/hal/mpp/hispark_taurus/hdi_impl/camera_host_config.hcs 
  2. root { 
  3.     module="sample"
  4.     camera_host_config { 
  5.         match_attr = "camera_host_interface"
  6.         .... 
  7.         ability_01 :: ability { 
  8.             logicCameraId = "lcam001"
  9.             physicsCameraIds = [ 
  10.                 "CAMERA_FIRST"
  11.                 "CAMERA_SECOND" 
  12.             ]; 
  13.             metadata { 
  14.                 aeAvailableAntiBandingModes = [ 
  15.                     "OHOS_CAMERA_AE_ANTIBANDING_MODE_OFF" 
  16.                 ]; 
  17.             ...... 

4.2.4 OpenCamera()

先简单说下callback。

这里会先创建一个CameraDeviceCallback设备回调 作为Camera设备的回调接口。CameraDeviceCallback的接口类定义在

  1. //drivers\peripheral\camera\interfaces\include\icamera_device_callback.h 
  2. virtual void OnError(ErrorType type, int32_t errorCode) = 0; 
  3. virtual void OnResult(uint64_t timestamp, const std::shared_ptr<CameraStandard::CameraMetadata> &result) = 0; 

开始OpenCamera()。还是通过一次ipc 最后到HDI的camera_host

CameraHostImpl::OpenCamera 先从cameraDeviceMap_中查找cameraId对应的CameraDeviceImpl,然后通过CameraDeviceImpl设置device的callback.

再通过cameraId从CameraHostConfig查出对应的phyCameraIds,在调用CameraPowerUp接口完成上电.

最后把CameraDevice的status set true.

至此上层的相机初始化就完成了。

  1. /** 
  2.  * @brief 打开Camera设备 
  3.  * 
  4.  * @param cameraId 要打开的Camera设备id 
  5.  * @param callback Camera设备的回调函数 
  6.  * @param camera 返回的Camera设备接口 
  7.  * @return CamRetCode 
  8.  * @see ICameraDeviceCallback 
  9.  * @see ICameraDevice 
  10.  */ 
  11. CamRetCode CameraHostImpl::OpenCamera(const std::string &cameraId, 
  12.     const OHOS::sptr<ICameraDeviceCallback> &callback, 
  13.     OHOS::sptr<ICameraDevice> &device) 
  14.     CAMERA_LOGD("OpenCamera entry"); 
  15.     DFX_LOCAL_HITRACE_BEGIN; 
  16.      
  17.     //这里说明参数callback必须设置!!! 
  18.     if (CameraIdInvalid(cameraId) != RC_OK || callback == nullptr) { 
  19.         CAMERA_LOGW("open camera id is empty or callback is null."); 
  20.         return INVALID_ARGUMENT; 
  21.     } 
  22.  
  23.     auto itr = cameraDeviceMap_.find(cameraId); 
  24.     if (itr == cameraDeviceMap_.end()) { 
  25.         CAMERA_LOGE("camera device not found."); 
  26.         return INSUFFICIENT_RESOURCES; 
  27.     } 
  28.     CAMERA_LOGD("OpenCamera cameraId find success."); 
  29.  
  30.     std::shared_ptr<CameraDeviceImpl> cameraDevice = 
  31.         std::static_pointer_cast<CameraDeviceImpl>(itr->second); 
  32.     if (cameraDevice == nullptr) { 
  33.         CAMERA_LOGE("camera device is null."); 
  34.         return INSUFFICIENT_RESOURCES; 
  35.     } 
  36.  
  37.     CamRetCode ret = cameraDevice->SetCallback(callback); 
  38.     if (ret != NO_ERROR) { 
  39.         CAMERA_LOGW("set camera device callback faild."); 
  40.         return ret; 
  41.     } 
  42.  
  43.     CameraHostConfig *config = CameraHostConfig::GetInstance(); 
  44.     if (config == nullptr) { 
  45.         return INVALID_ARGUMENT; 
  46.     } 
  47.     std::vector<std::string> phyCameraIds; 
  48.     RetCode rc = config->GetPhysicCameraIds(cameraId, phyCameraIds); 
  49.     if (rc != RC_OK) { 
  50.         CAMERA_LOGE("get physic cameraId failed."); 
  51.         return DEVICE_ERROR; 
  52.     } 
  53.  
  54.     if (CameraPowerUp(cameraId, phyCameraIds) != RC_OK) { 
  55.         CAMERA_LOGE("camera powerup failed."); 
  56.         CameraPowerDown(phyCameraIds); 
  57.         return DEVICE_ERROR; 
  58.     } 
  59.  
  60.     auto sptrDevice = deviceBackup_.find(cameraId); 
  61.     if (sptrDevice == deviceBackup_.end()) { 
  62.         deviceBackup_[cameraId] = cameraDevice.get(); 
  63.     } 
  64.     device = deviceBackup_[cameraId]; 
  65.  
  66.     cameraDevice->SetStatus(true); 
  67.     CAMERA_LOGD("open camera success."); 
  68.     DFX_LOCAL_HITRACE_END; 
  69.     return NO_ERROR; 

5 这里留下了两个问题

cameraDeviceMap_中的值什么时候设置的?

CameraPowerUp() 如何完成上电的功能?

将在下一章节进行解析

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-02-14 13:52:04

OpenHarmor系统鸿蒙

2022-02-25 15:33:45

图像采集鸿蒙Camera相机

2022-02-08 15:07:45

OpenHarmor鸿蒙操作系统

2022-02-17 16:47:40

OpenharmonIPC通信鸿蒙

2022-06-22 09:14:23

事件打点HiSysEvent

2022-02-21 15:38:57

Openharmon操作系统鸿蒙

2022-04-21 11:26:31

鸿蒙操作系统

2023-02-13 15:54:49

2022-03-28 15:40:34

harmony鸿蒙操作系统

2011-06-17 15:29:44

C#对象初始化器集合初始化器

2022-07-14 19:03:33

IPC服务鸿蒙

2022-07-04 16:41:16

IPC通信HiTrace

2021-10-11 08:51:05

Linux console Linux 系统

2023-02-28 15:49:09

鸿蒙应用开发

2021-10-20 19:14:30

缓存CacheCPU

2010-02-06 14:40:50

C++初始化和赋值

2023-01-31 09:12:16

CPU芯片缓存

2023-11-12 23:08:17

C++初始化

2022-04-01 15:18:04

HarmonyHDF 驱动鸿蒙

2023-10-10 15:33:55

机器学习相似性度量
点赞
收藏

51CTO技术栈公众号