当前位置: 首页 > news >正文

门户网站具有什么特点html登录注册页面

门户网站具有什么特点,html登录注册页面,兰州网站建设王道下拉強,如何找到app的开发者功能简介 OpenHarmony相机驱动框架模型对上实现相机HDI#xff08;Hardware Device Interface#xff09;接口#xff0c;对下实现相机Pipeline模型#xff0c;管理相机各个硬件设备。 该驱动框架模型内部分为三层#xff0c;依次为HDI实现层、框架层和设备适配层。各层基…功能简介 OpenHarmony相机驱动框架模型对上实现相机HDIHardware Device Interface接口对下实现相机Pipeline模型管理相机各个硬件设备。 该驱动框架模型内部分为三层依次为HDI实现层、框架层和设备适配层。各层基本概念如下 HDI实现层实现OHOSOpenHarmony Operation System相机标准南向接口。框架层对接HDI实现层的控制、流的转发实现数据通路的搭建管理相机各个硬件设备等功能。设备适配层屏蔽底层芯片和OSOperation System差异支持多平台适配。 运作机制 Camera模块主要包含服务、设备的初始化数据通路的搭建流的配置、创建、下发、捕获等具体运作机制参考以下图文解析 图 1 基于HDF驱动框架的Camera驱动模型  系统启动时创建camera_host进程。进程创建后首先枚举底层设备创建也可以通过配置表创建管理设备树的DeviceManager类及其内部各个底层设备的对象创建对应的CameraHost类实例并且将其注册到UHDF用户态HDF驱动框架服务中方便相机服务层通过UHDF服务获取底层CameraDeviceHost的服务从而操作硬件设备。 Service通过CameraDeviceHost服务获取CameraHost实例CameraHost可以获取底层的Camera能力开启闪光灯、调用Open接口打开Camera创建连接、创建DeviceManager负责底层硬件模块上电、创建CameraDevice向上提供设备控制接口。创建CameraDevice时会实例化PipelineCore的各个子模块其中StreamPipelineCore负责创建PipelineMetaQueueManager负责上报metaData。 Service通过CameraDevice模块配置流、创建Stream类。StreamPipelineStrategy模块通过上层下发的模式和查询配置表创建对应流的Node连接方式StreamPipelineBuilder模块创建Node实例并且连接返回该Pipeline给StreamPipelineDispatcher。StreamPipelineDispatcher提供统一的Pipeline调用管理。 Service通过Stream控制整个流的操作AttachBufferQueue接口将从显示模块申请的BufferQueue下发到底层由CameraDeviceDriverModel自行管理buffer当Capture接口下发命令后底层开始向上传递buffer。Pipeline的IspNode依次从BufferQueue获取指定数量buffer然后下发到底层ISPImage Signal Processor图像信号处理器硬件ISP填充完之后将buffer传递给CameraDeviceDriverModelCameraDeviceDriverModel通过循环线程将buffer填充到已经创建好的Pipeline中各个Node处理后通过回调传递给上层同时buffer返回BufferQueue等待下一次下发。 Service通过Capture接口下发拍照命令。ChangeToOfflineStream接口查询拍照buffer位置如果ISP已经出图并且图像数据已经送到IPP node可以将普通拍照流转换为离线流否则直接走关闭流程。ChangeToOfflineStream接口通过传递StreamInfo使离线流获取到普通流的流信息并且通过配置表确认离线流的具体Node连接方式创建离线流的Node连接如果已创建则通过CloseCamera释放非离线流所需的Node等待buffer从底层Pipeline回传到上层再释放持有的Pipeline相关资源。 Service通过CameraDevice的UpdateSettings接口向下发送CaptureSetting参数CameraDeviceDriverModel通过StreamPipelineDispatcher模块向各个Node转发StartStreamingCapture和Capture接口携带的CaptureSetting通过StreamPipelineDispatcher模块向该流所属的Node转发。 Service通过EnableResult和DisableResult接口控制底层metaData的上报。如果需要底层metaData上报pipeline会创建CameraDeviceDriverModel内部的一个Bufferqueue用来收集和传递metaData根据StreamPipelineStrategy模块查询配置表并通过StreamPipelineBuilder创建和连接NodeMetaQueueManager下发buffer至底层底层相关Node填充数据MetaQueueManager模块再调用上层回调传递给上层。 Service调用CameraDevice的Close接口CameraDevice调用对应的DeviceManager模块对各个硬件下电如果此时在Ipp的SubPipeline中存在OfflineStream则需要保留OfflineStream直到执行完毕。 动态帧率控制。在StreamOperator中起一个CollectBuffer线程CollectBuffer线程从每一路stream的BufferQueue中获取buffer如果某一路流的帧率需要控制为sensor出帧帧率的1/n可以根据需求控制每一帧的buffer打包并决定是否collect此路流的buffer比如sensor出帧帧率为120fps预览流的帧率为30fpsCollectBuffer线程collect预览流的buffer时每隔4fps collect一次。 开发指导 场景介绍 Camera模块主要针对相机预览、拍照、视频流等场景对这些场景下的相机操作进行封装使开发者更易操作相机硬件提高开发效率。 接口说明 注以下接口列举的为IDL接口描述生成的对应C语言函数接口接口声明见idl文件/drivers/interface/camera/v1_1/获取路径为drivers_interface: 暂无描述 - Gitee.com。 在HDI使用中下发的配置参数不能超出GetCameraAbility上报的能力范围。即使通过UpdateSettings、CommitStreams、Capture等接口可以下发超出该范围的配置参数且接口调用不会返回失败但设置后的行为是不确定的。 icamera_device.h 功能描述接口名称获取流控制器int32_t GetStreamOperator_V1_1( const sptrOHOS::HDI::Camera::V1_0::IStreamOperatorCallback callbackObj, sptrOHOS::HDI::Camera::V1_1::IStreamOperator streamOperator ) icamera_host.h 功能描述接口名称打开Camera设备int32_t OpenCamera_V1_1( const std::string cameraId, const sptrOHOS::HDI::Camera::V1_0::ICameraDeviceCallback callbackObj, sptrOHOS::HDI::Camera::V1_1::ICameraDevice device )预启动摄像头设备int32_t PreLaunch(const PrelaunchConfig config) istream_operator.h 功能描述接口名称查询是否支持添加参数对应的流int32_t IsStreamsSupported_V1_1( OperationMode mode, const std::vectoruint8_t modeSetting, const std::vectorStreamInfo_V1_1 infos, StreamSupportType type )创建流int32_t CreateStreams_V1_1(const std::vectorStreamInfo_V1_1 streamInfos) 开发步骤 Camera驱动的开发过程主要包含以下步骤 注册CameraHost 定义Camera的HdfDriverEntry结构体该结构体中定义了CameraHost初始化的方法代码目录drivers/peripheral/camera/interfaces/hdi_ipc/camera_host_driver.cpp。 struct HdfDriverEntry g_cameraHostDriverEntry {.moduleVersion 1,.moduleName camera_service,.Bind HdfCameraHostDriverBind,.Init HdfCameraHostDriverInit,.Release HdfCameraHostDriverRelease, }; HDF_INIT(g_cameraHostDriverEntry); // 将Camera的HdfDriverEntry结构体注册到HDF上 初始化Host服务 步骤1中提到的HdfCameraHostDriverBind接口提供了CameraServiceDispatch和CameraHostStubInstance的注册。CameraServiceDispatch接口是远端调用CameraHost的方法如OpenCamera()SetFlashlight()等CameraHostStubInstance接口是Camera设备的初始化在开机时被调用。 static int HdfCameraHostDriverBind(struct HdfDeviceObject *deviceObject) {HDF_LOGI(HdfCameraHostDriverBind enter);auto *hdfCameraHostHost new (std::nothrow) HdfCameraHostHost;if (hdfCameraHostHost nullptr) {HDF_LOGE(%{public}s: failed to create HdfCameraHostHost object, __func__);return HDF_FAILURE;}hdfCameraHostHost-ioService.Dispatch CameraHostDriverDispatch; // 提供远端CameraHost调用方法hdfCameraHostHost-ioService.Open NULL;hdfCameraHostHost-ioService.Release NULL;auto serviceImpl ICameraHost::Get(true);if (serviceImpl nullptr) {HDF_LOGE(%{public}s: failed to get of implement service, __func__);delete hdfCameraHostHost;return HDF_FAILURE;}hdfCameraHostHost-stub OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,ICameraHost::GetDescriptor()); // 初始化Camera设备if (hdfCameraHostHost-stub nullptr) {HDF_LOGE(%{public}s: failed to get stub object, __func__);delete hdfCameraHostHost;return HDF_FAILURE;}deviceObject-service hdfCameraHostHost-ioService;return HDF_SUCCESS; } 下面的函数是远端CameraHost调用的方法 int32_t CameraHostStub::CameraHostServiceStubOnRemoteRequest(int cmdId, MessageParcel data,MessageParcel reply, MessageOption option) {switch(cmdId) {case CMD_CAMERA_HOST_SET_CALLBACK: {return CameraHostStubSetCallback(data, reply, option);}case CMD_CAMERA_HOST_GET_CAMERAID: {return CameraHostStubGetCameraIds(data, reply, option);}case CMD_CAMERA_HOST_GET_CAMERA_ABILITY: {return CameraHostStubGetCameraAbility(data, reply, option);}case CMD_CAMERA_HOST_OPEN_CAMERA: {return CameraHostStubOpenCamera(data, reply, option);}case CMD_CAMERA_HOST_SET_FLASH_LIGHT: {return CameraHostStubSetFlashlight(data, reply, option);}default: {HDF_LOGE(%s: not support cmd %d, __func__, cmdId);return HDF_ERR_INVALID_PARAM;}}return HDF_SUCCESS; } CameraHostStubInstance()接口最终调用CameraHostImpl::Init()方法该方法会获取物理Camera并对DeviceManager和PipelineCore进行初始化。 获取Host服务 调用Get()接口从远端CameraService中获取CameraHost对象。get()方法如下 sptrICameraHost ICameraHost::Get(const char *serviceName) {do {using namespace OHOS::HDI::ServiceManager::V1_0;auto servMgr IServiceManager::Get();if (servMgr nullptr) {HDF_LOGE(%s: IServiceManager failed!, __func__);break;}auto remote servMgr-GetService(serviceName); // 根据serviceName名称获取CameraHostif (remote ! nullptr) {sptrCameraHostProxy hostSptr iface_castCameraHostProxy(remote); // 将CameraHostProxy对象返回给调用者该对象中包含OpenCamera()等方法。return hostSptr;}HDF_LOGE(%s: GetService failed! serviceName %s, __func__, serviceName);} while(false);HDF_LOGE(%s: get %s failed!, __func__, serviceName);return nullptr; } 打开设备 CameraHostProxy对象中有五个方法分别是SetCallback、GetCameraIds、GetCameraAbility、OpenCamera和SetFlashlight。下面着重描述OpenCamera接口。 CameraHostProxy的OpenCamera()接口通过CMD_CAMERA_HOST_OPEN_CAMERA调用远端CameraHostStubOpenCamera()接口并获取ICameraDevice对象。 int32_t CameraHostProxy::OpenCamera(const std::string cameraId, const sptrICameraDeviceCallback callbackObj,sptrICameraDevice device) {MessageParcel cameraHostData;MessageParcel cameraHostReply;MessageOption cameraHostOption(MessageOption::TF_SYNC);if (!cameraHostData.WriteInterfaceToken(ICameraHost::GetDescriptor())) {HDF_LOGE(%{public}s: failed to write interface descriptor!, __func__);return HDF_ERR_INVALID_PARAM;}if (!cameraHostData.WriteCString(cameraId.c_str())) {HDF_LOGE(%{public}s: write cameraId failed!, __func__);return HDF_ERR_INVALID_PARAM;}if (!cameraHostData.WriteRemoteObject(OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(callbackObj, ICameraDeviceCallback::GetDescriptor()))) {HDF_LOGE(%{public}s: write callbackObj failed!, __func__);return HDF_ERR_INVALID_PARAM;}int32_t cameraHostRet Remote()-SendRequest(CMD_CAMERA_HOST_OPEN_CAMERA, cameraHostData, cameraHostReply, cameraHostOption);if (cameraHostRet ! HDF_SUCCESS) {HDF_LOGE(%{public}s failed, error code is %{public}d, __func__, cameraHostRet);return cameraHostRet;}device hdi_facecastICameraDevice(cameraHostReply.ReadRemoteObject());return cameraHostRet; } Remote()-SendRequest调用上文提到的CameraHostServiceStubOnRemoteRequest()根据cmdId进入CameraHostStubOpenCamera()接口最终调用CameraHostImpl::OpenCamera()该接口获取了CameraDevice并对硬件进行上电等操作。 int32_t CameraHostImpl::OpenCamera(const std::string cameraId, const sptrICameraDeviceCallback callbackObj,sptrICameraDevice device) {CAMERA_LOGD(OpenCamera entry);DFX_LOCAL_HITRACE_BEGIN;if (CameraIdInvalid(cameraId) ! RC_OK || callbackObj nullptr) {CAMERA_LOGW(open camera id is empty or callback is null.);return INVALID_ARGUMENT;}auto itr cameraDeviceMap_.find(cameraId);if (itr cameraDeviceMap_.end()) {CAMERA_LOGE(camera device not found.);return INSUFFICIENT_RESOURCES;}CAMERA_LOGD(OpenCamera cameraId find success.);std::shared_ptrCameraDeviceImpl cameraDevice itr-second;if (cameraDevice nullptr) {CAMERA_LOGE(camera device is null.);return INSUFFICIENT_RESOURCES;}CamRetCode ret cameraDevice-SetCallback(callbackObj);CHECK_IF_NOT_EQUAL_RETURN_VALUE(ret, HDI::Camera::V1_0::NO_ERROR, ret);CameraHostConfig *config CameraHostConfig::GetInstance();CHECK_IF_PTR_NULL_RETURN_VALUE(config, INVALID_ARGUMENT);std::vectorstd::string phyCameraIds;RetCode rc config-GetPhysicCameraIds(cameraId, phyCameraIds);if (rc ! RC_OK) {CAMERA_LOGE(get physic cameraId failed.);return DEVICE_ERROR;}if (CameraPowerUp(cameraId, phyCameraIds) ! RC_OK) { // 对Camera硬件上电CAMERA_LOGE(camera powerup failed.);CameraPowerDown(phyCameraIds);return DEVICE_ERROR;}auto sptrDevice deviceBackup_.find(cameraId);if (sptrDevice deviceBackup_.end()) { #ifdef CAMERA_BUILT_ON_OHOS_LITEdeviceBackup_[cameraId] cameraDevice; #elsedeviceBackup_[cameraId] cameraDevice.get(); #endif}device deviceBackup_[cameraId];cameraDevice-SetStatus(true);CAMERA_LOGD(open camera success.);DFX_LOCAL_HITRACE_END;return HDI::Camera::V1_0::NO_ERROR; } 获取流 CameraDeviceImpl定义了GetStreamOperator、UpdateSettings、SetResultMode和GetEnabledResult等方法获取流操作方法如下 int32_t CameraDeviceImpl::GetStreamOperator(const sptrIStreamOperatorCallback callbackObj,sptrIStreamOperator streamOperator) {HDI_DEVICE_PLACE_A_WATCHDOG;DFX_LOCAL_HITRACE_BEGIN;if (callbackObj nullptr) {CAMERA_LOGW(input callback is null.);return INVALID_ARGUMENT;}spCameraDeciceCallback_ callbackObj;if (spStreamOperator_ nullptr) { #ifdef CAMERA_BUILT_ON_OHOS_LITE// 这里创建一个spStreamOperator_ 对象传递给调用者以便对stream进行各种操作spStreamOperator_ std::make_sharedStreamOperator(spCameraDeciceCallback_, shared_from_this()); #elsespStreamOperator_ new(std::nothrow) StreamOperator(spCameraDeciceCallback_, shared_from_this()); #endifif (spStreamOperator_ nullptr) {CAMERA_LOGW(create stream operator failed.);return DEVICE_ERROR;}spStreamOperator_-Init();ismOperator_ spStreamOperator_;}streamOperator ismOperator_; #ifndef CAMERA_BUILT_ON_OHOS_LITECAMERA_LOGI(CameraDeviceImpl %{public}s: line: %{public}d, __FUNCTION__, __LINE__);pipelineCore_-GetStreamPipelineCore()-SetCallback([this](const std::shared_ptrCameraMetadata metadata) {OnMetadataChanged(metadata);}); #endifDFX_LOCAL_HITRACE_END;return HDI::Camera::V1_0::NO_ERROR; } 创建流 调用CreateStreams创建流前需要填充StreamInfo结构体具体内容如下 using StreamInfo struct _StreamInfo {int streamId_; int width_; // 数据流宽int height_; // 数据流高int format_; // 数据流格式如PIXEL_FMT_YCRCB_420_SPint dataSpace_; StreamIntent intent_; // StreamIntent 如PREVIEWbool tunneledMode_;BufferProducerSequenceable bufferQueue_; // 数据流bufferQueue可用streamCustomer-CreateProducer()接口创建int minFrameDuration_;EncodeType encodeType_; }; CreateStreams()接口是StreamOperatorStreamOperatorImpl类是StreamOperator的基类类中的方法该接口的主要作用是创建一个StreamBase对象通过StreamBase的Init方法初始化CreateBufferPool等操作。 int32_t StreamOperator::CreateStreams(const std::vectorStreamInfo streamInfos) {PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_);DFX_LOCAL_HITRACE_BEGIN;for (const auto it : streamInfos) {CHECK_IF_NOT_EQUAL_RETURN_VALUE(CheckStreamInfo(it), true, INVALID_ARGUMENT);CAMERA_LOGI(streamId:%{public}d and format:%{public}d and width:%{public}d and height:%{public}d,it.streamId_, it.format_, it.width_, it.height_);if (streamMap_.count(it.streamId_) 0) {CAMERA_LOGE(stream [id %{public}d] has already been created., it.streamId_);return INVALID_ARGUMENT;}std::shared_ptrIStream stream StreamFactory::Instance().CreateShared( // 创建Stream实例IStream::g_availableStreamType[it.intent_], it.streamId_, it.intent_, pipelineCore_, messenger_);if (stream nullptr) {CAMERA_LOGE(create stream [id %{public}d] failed., it.streamId_);return INSUFFICIENT_RESOURCES;}StreamConfiguration scg;StreamInfoToStreamConfiguration(scg, it);RetCode rc stream-ConfigStream(scg);if (rc ! RC_OK) {CAMERA_LOGE(configure stream %{public}d failed, it.streamId_);return INVALID_ARGUMENT;}if (!scg.tunnelMode (it.bufferQueue_)-producer_ ! nullptr) {CAMERA_LOGE(stream [id:%{public}d] is not tunnel mode, cant bind a buffer producer, it.streamId_);return INVALID_ARGUMENT;}if ((it.bufferQueue_)-producer_ ! nullptr) {auto tunnel std::make_sharedStreamTunnel();CHECK_IF_PTR_NULL_RETURN_VALUE(tunnel, INSUFFICIENT_RESOURCES);rc tunnel-AttachBufferQueue((it.bufferQueue_)-producer_);CHECK_IF_NOT_EQUAL_RETURN_VALUE(rc, RC_OK, INVALID_ARGUMENT);if (stream-AttachStreamTunnel(tunnel) ! RC_OK) {CAMERA_LOGE(attach buffer queue to stream [id %{public}d] failed, it.streamId_);return INVALID_ARGUMENT;}}{std::lock_guardstd::mutex l(streamLock_);streamMap_[stream-GetStreamId()] stream;}CAMERA_LOGI(create stream success [id:%{public}d] [type:%{public}s], stream-GetStreamId(),IStream::g_availableStreamType[it.intent_].c_str());}DFX_LOCAL_HITRACE_END;return HDI::Camera::V1_0::NO_ERROR;} 配置流 CommitStreams()是配置流的接口必须在创建流之后调用其主要作用是初始化Pipeline和创建Pipeline。 int32_t StreamOperator::CommitStreams(OperationMode mode, const std::vectoruint8_t modeSetting) {CAMERA_LOGV(enter);CHECK_IF_PTR_NULL_RETURN_VALUE(streamPipeline_, DEVICE_ERROR);PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_);if (modeSetting.empty()) {CAMERA_LOGE(input vector is empty);return INVALID_ARGUMENT;}DFX_LOCAL_HITRACE_BEGIN;std::vectorStreamConfiguration configs {};{std::lock_guardstd::mutex l(streamLock_);std::transform(streamMap_.begin(), streamMap_.end(), std::back_inserter(configs),[](auto iter) { return iter.second-GetStreamAttribute(); });}std::shared_ptrCameraMetadata setting;MetadataUtils::ConvertVecToMetadata(modeSetting, setting);DynamicStreamSwitchMode method streamPipeline_-CheckStreamsSupported(mode, setting, configs);if (method DYNAMIC_STREAM_SWITCH_NOT_SUPPORT) {return INVALID_ARGUMENT;}if (method DYNAMIC_STREAM_SWITCH_NEED_INNER_RESTART) {std::lock_guardstd::mutex l(streamLock_);for (auto it : streamMap_) {it.second-StopStream();}}{std::lock_guardstd::mutex l(streamLock_);for (auto it : streamMap_) {if (it.second-CommitStream() ! RC_OK) {CAMERA_LOGE(commit stream [id %{public}d] failed., it.first);return DEVICE_ERROR;}}}RetCode rc streamPipeline_-PreConfig(setting); // 设备流配置if (rc ! RC_OK) {CAMERA_LOGE(prepare mode settings failed);return DEVICE_ERROR;}rc streamPipeline_-CreatePipeline(mode); // 创建一个pipelineif (rc ! RC_OK) {CAMERA_LOGE(create pipeline failed.);return INVALID_ARGUMENT;}DFX_LOCAL_HITRACE_END;return HDI::Camera::V1_0::NO_ERROR; } 捕获图像 在调用Capture()接口前需要先填充CaptureInfo结构体具体内容如下 using CaptureInfo struct _CaptureInfo {int[] streamIds_; // 需要Capture的streamIdsunsigned char[] captureSetting_; // 这里填充camera ability 可通过CameraHost 的GetCameraAbility()接口获取bool enableShutterCallback_; }; StreamOperator中的Capture方法主要是捕获数据流 int32_t StreamOperator::Capture(int32_t captureId, const CaptureInfo info, bool isStreaming) {CHECK_IF_EQUAL_RETURN_VALUE(captureId 0, true, INVALID_ARGUMENT);PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_);DFX_LOCAL_HITRACE_BEGIN;for (auto id : info.streamIds_) {std::lock_guardstd::mutex l(streamLock_);auto it streamMap_.find(id);if (it streamMap_.end()) {return INVALID_ARGUMENT;}}{std::lock_guardstd::mutex l(requestLock_);auto itr requestMap_.find(captureId);if (itr ! requestMap_.end()) {return INVALID_ARGUMENT;}}std::shared_ptrCameraMetadata captureSetting;MetadataUtils::ConvertVecToMetadata(info.captureSetting_, captureSetting);CaptureSetting setting captureSetting;auto request std::make_sharedCaptureRequest(captureId, info.streamIds_.size(), setting,info.enableShutterCallback_, isStreaming);for (auto id : info.streamIds_) {RetCode rc streamMap_[id]-AddRequest(request);if (rc ! RC_OK) {return DEVICE_ERROR;}}{std::lock_guardstd::mutex l(requestLock_);requestMap_[captureId] request;}return HDI::Camera::V1_0::NO_ERROR; } 取消捕获和释放离线流 StreamOperator类中的CancelCapture()接口的主要作用是根据captureId取消数据流的捕获。 int32_t StreamOperator::CancelCapture(int32_t captureId) {CHECK_IF_EQUAL_RETURN_VALUE(captureId 0, true, INVALID_ARGUMENT);PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_);DFX_LOCAL_HITRACE_BEGIN;std::lock_guardstd::mutex l(requestLock_);auto itr requestMap_.find(captureId); // 根据captureId 在Map中查找对应的CameraCapture对象if (itr requestMap_.end()) {CAMERA_LOGE(cant cancel capture [id %{public}d], this capture doesnt exist, captureId);return INVALID_ARGUMENT;}RetCode rc itr-second-Cancel(); // 调用CameraCapture中Cancel方法结束数据捕获if (rc ! RC_OK) {return DEVICE_ERROR;}requestMap_.erase(itr); // 擦除该CameraCapture对象DFX_LOCAL_HITRACE_END;return HDI::Camera::V1_0::NO_ERROR; } StreamOperator类中的ReleaseStreams接口的主要作用是释放之前通过CreateStream()和CommitStreams()接口创建的流并销毁Pipeline。 int32_t StreamOperator::ReleaseStreams(const std::vectorint32_t streamIds) {PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_);DFX_LOCAL_HITRACE_BEGIN;for (auto id : streamIds) {std::lock_guardstd::mutex l(streamLock_);auto it streamMap_.find(id);if (it streamMap_.end()) {continue;}if (it-second-IsRunning()) {it-second-StopStream();}it-second-DumpStatsInfo();streamMap_.erase(it);}for (auto id : streamIds) {CHECK_IF_EQUAL_RETURN_VALUE(id 0, true, INVALID_ARGUMENT);}DFX_LOCAL_HITRACE_END;return HDI::Camera::V1_0::NO_ERROR; } 关闭Camera设备 调用CameraDeviceImpl中的Close()来关闭CameraDevice该接口调用deviceManager中的PowerDown()来给设备下电。 开发实例 在/drivers/peripheral/camera/test/demo目录下有一个关于Camera的demo开机后会在/vendor/bin下生成可执行文件ohos_camera_demo该demo可以完成Camera的预览拍照等基础功能。下面我们就以此demo为例讲述怎样用HDI接口去编写预览PreviewOn()和拍照CaptureON()的用例可参考ohos_camera_demo。 在main函数中构造一个CameraDemo 对象该对象中有对Camera初始化、启停流、释放等控制的方法。下面mainDemo-InitSensors()函数为初始化CameraHostmainDemo-InitCameraDevice()函数为初始化CameraDevice。 int main(int argc, char** argv) {RetCode rc RC_OK;auto mainDemo std::make_sharedCameraDemo();rc mainDemo-InitSensors(); // 初始化CameraHostif (rc RC_ERROR) {CAMERA_LOGE(main test: mainDemo-InitSensors() error\n);return -1;}rc mainDemo-InitCameraDevice(); // 初始化CameraDeviceif (rc RC_ERROR) {CAMERA_LOGE(main test: mainDemo-InitCameraDevice() error\n);return -1;}rc PreviewOn(0, mainDemo); // 配流和启流if (rc ! RC_OK) {CAMERA_LOGE(main test: PreviewOn() error demo exit);return -1;}ManuList(mainDemo, argc, argv); // 打印菜单到控制台return RC_OK; } 初始化CameraHost函数实现如下这里调用了HDI接口ICameraHost::Get()去获取demoCameraHost并对其设置回调函数。 RetCode OhosCameraDemo::InitSensors() {int rc 0;CAMERA_LOGD(demo test: InitSensors enter);if (demoCameraHost_ ! nullptr) {return RC_OK;} #ifdef CAMERA_BUILT_ON_OHOS_LITEdemoCameraHost_ OHOS::Camera::CameraHost::CreateCameraHost(); #elseconstexpr const char *DEMO_SERVICE_NAME camera_service;demoCameraHost_ ICameraHost::Get(DEMO_SERVICE_NAME, false); #endifif (demoCameraHost_ nullptr) {CAMERA_LOGE(demo test: ICameraHost::Get error);return RC_ERROR;}#ifdef CAMERA_BUILT_ON_OHOS_LITEhostCallback_ std::make_sharedDemoCameraHostCallback(); #elsehostCallback_ new DemoCameraHostCallback(); #endifrc demoCameraHost_-SetCallback(hostCallback_);if (rc ! HDI::Camera::V1_0::NO_ERROR) {CAMERA_LOGE(demo test: demoCameraHost_-SetCallback(hostCallback_) error);return RC_ERROR;}CAMERA_LOGD(demo test: InitSensors exit);return RC_OK; } 初始化CameraDevice函数实现如下这里调用了GetCameraIds(cameraIds_)GetCameraAbility(cameraId, ability_)OpenCamera(cameraIds_.front(), callback, demoCameraDevice_)等接口实现了demoCameraHost的获取。 RetCode OhosCameraDemo::InitCameraDevice() {int rc 0;CAMERA_LOGD(demo test: InitCameraDevice enter);if (demoCameraHost_ nullptr) {CAMERA_LOGE(demo test: InitCameraDevice demoCameraHost_ nullptr);return RC_ERROR;}(void)demoCameraHost_-GetCameraIds(cameraIds_);if (cameraIds_.empty()) {return RC_ERROR;}const std::string cameraId cameraIds_.front();demoCameraHost_-GetCameraAbility(cameraId, cameraAbility_);MetadataUtils::ConvertVecToMetadata(cameraAbility_, ability_);GetFaceDetectMode(ability_);GetFocalLength(ability_);GetAvailableFocusModes(ability_);GetAvailableExposureModes(ability_);GetExposureCompensationRange(ability_);GetExposureCompensationSteps(ability_);GetAvailableMeterModes(ability_);GetAvailableFlashModes(ability_);GetMirrorSupported(ability_);GetStreamBasicConfigurations(ability_);GetFpsRange(ability_);GetCameraPosition(ability_);GetCameraType(ability_);GetCameraConnectionType(ability_);GetFaceDetectMaxNum(ability_);#ifdef CAMERA_BUILT_ON_OHOS_LITEstd::shared_ptrCameraDeviceCallback callback std::make_sharedCameraDeviceCallback(); #elsesptrDemoCameraDeviceCallback callback new DemoCameraDeviceCallback(); #endifrc demoCameraHost_-OpenCamera(cameraIds_.front(), callback, demoCameraDevice_);if (rc ! HDI::Camera::V1_0::NO_ERROR || demoCameraDevice_ nullptr) {CAMERA_LOGE(demo test: InitCameraDevice OpenCamera failed);return RC_ERROR;}CAMERA_LOGD(demo test: InitCameraDevice exit);return RC_OK; } PreviewOn()接口包含配置流、开启预览流和启动Capture动作。该接口执行完成后Camera预览通路已经开始运转并开启了两路流一路流是preview另外一路流是capture或者video两路流中仅对preview流进行capture动作。 static RetCode PreviewOn(int mode, const std::shared_ptrOhosCameraDemo mainDemo) {RetCode rc RC_OK;CAMERA_LOGD(main test: PreviewOn enter);rc mainDemo-StartPreviewStream(); // 配置preview流if (rc ! RC_OK) {CAMERA_LOGE(main test: PreviewOn StartPreviewStream error);return RC_ERROR;}if (mode 0) {rc mainDemo-StartCaptureStream(); // 配置capture流if (rc ! RC_OK) {CAMERA_LOGE(main test: PreviewOn StartCaptureStream error);return RC_ERROR;}} else {rc mainDemo-StartVideoStream(); // 配置video流if (rc ! RC_OK) {CAMERA_LOGE(main test: PreviewOn StartVideoStream error);return RC_ERROR;}}rc mainDemo-CaptureON(STREAM_ID_PREVIEW, CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW);if (rc ! RC_OK) {CAMERA_LOGE(main test: PreviewOn mainDemo-CaptureON() preview error);return RC_ERROR;}CAMERA_LOGD(main test: PreviewOn exit);return RC_OK; } StartCaptureStream()、StartVideoStream()和StartPreviewStream()接口都会调用CreateStream()接口只是传入的参数不同。 CreateStream()方法调用HDI接口去配置和创建流首先调用HDI接口去获取StreamOperation对象然后创建一个StreamInfo。调用CreateStreams()和CommitStreams()实际创建流并配置流。 RetCode OhosCameraDemo::CreateStream(const int streamId, std::shared_ptrStreamCustomer streamCustomer,StreamIntent intent) {int rc 0;CAMERA_LOGD(demo test: CreateStream enter);GetStreamOpt(); // 获取StreamOperator对象if (streamOperator_ nullptr) {CAMERA_LOGE(demo test: CreateStream GetStreamOpt() is nullptr\n);return RC_ERROR;}StreamInfo streamInfo {0};SetStreamInfo(streamInfo, streamCustomer, streamId, intent); // 填充StreamInfo流if (streamInfo.bufferQueue_-producer_ nullptr) {CAMERA_LOGE(demo test: CreateStream CreateProducer(); is nullptr\n);return RC_ERROR;}std::vectorStreamInfo streamInfos;streamInfos.push_back(streamInfo);rc streamOperator_-CreateStreams(streamInfos); // 创建流if (rc ! HDI::Camera::V1_0::NO_ERROR) {CAMERA_LOGE(demo test: CreateStream CreateStreams error\n);return RC_ERROR;}rc streamOperator_-CommitStreams(NORMAL, cameraAbility_);if (rc ! HDI::Camera::V1_0::NO_ERROR) {CAMERA_LOGE(demo test: CreateStream CommitStreams error\n);std::vectorint streamIds;streamIds.push_back(streamId);streamOperator_-ReleaseStreams(streamIds);return RC_ERROR;}CAMERA_LOGD(demo test: CreateStream exit);return RC_OK; } CaptureON()接口调用streamOperator的Capture()方法获取Camera数据并轮转buffer拉起一个线程接收相应类型的数据。 RetCode OhosCameraDemo::CaptureON(const int streamId,const int captureId, CaptureMode mode) {CAMERA_LOGI(demo test: CaptureON enter streamId %{public}d and captureId %{public}d and mode %{public}d,streamId, captureId, mode);std::lock_guardstd::mutex l(metaDatalock_);if (mode CAPTURE_SNAPSHOT) {constexpr double latitude 27.987500; // dummy data: Qomolangma latitdeconstexpr double longitude 86.927500; // dummy data: Qomolangma longituudeconstexpr double altitude 8848.86; // dummy data: Qomolangma altitudeconstexpr size_t entryCapacity 100;constexpr size_t dataCapacity 2000;captureSetting_ std::make_sharedCameraSetting(entryCapacity, dataCapacity);captureQuality_ OHOS_CAMERA_JPEG_LEVEL_HIGH;captureOrientation_ OHOS_CAMERA_JPEG_ROTATION_270;mirrorSwitch_ OHOS_CAMERA_MIRROR_ON;gps_.push_back(latitude);gps_.push_back(longitude);gps_.push_back(altitude);captureSetting_-addEntry(OHOS_JPEG_QUALITY, static_castvoid*(captureQuality_),sizeof(captureQuality_));captureSetting_-addEntry(OHOS_JPEG_ORIENTATION, static_castvoid*(captureOrientation_),sizeof(captureOrientation_));captureSetting_-addEntry(OHOS_CONTROL_CAPTURE_MIRROR, static_castvoid*(mirrorSwitch_),sizeof(mirrorSwitch_));captureSetting_-addEntry(OHOS_JPEG_GPS_COORDINATES, gps_.data(), gps_.size());}std::vectoruint8_t setting;MetadataUtils::ConvertMetadataToVec(captureSetting_, setting);captureInfo_.streamIds_ {streamId};if (mode CAPTURE_SNAPSHOT) {captureInfo_.captureSetting_ setting;} else {captureInfo_.captureSetting_ cameraAbility_;}captureInfo_.enableShutterCallback_ false;int rc streamOperator_-Capture(captureId, captureInfo_, true); // 实际capture开始buffer轮转开始if (rc ! HDI::Camera::V1_0::NO_ERROR) {CAMERA_LOGE(demo test: CaptureStart Capture error\n);streamOperator_-ReleaseStreams(captureInfo_.streamIds_);return RC_ERROR;}if (mode CAPTURE_PREVIEW) {streamCustomerPreview_-ReceiveFrameOn(nullptr); // 创建预览线程接收传递上来的buffer} else if (mode CAPTURE_SNAPSHOT) {streamCustomerCapture_-ReceiveFrameOn([this](void* addr, const uint32_t size) { // 创建capture线程通过StoreImage回调接收传递上来的bufferStoreImage(addr, size);});} else if (mode CAPTURE_VIDEO) {OpenVideoFile();streamCustomerVideo_-ReceiveFrameOn([this](void* addr, const uint32_t size) { // 创建video线程通过StoreImage回调接收传递上来的bufferStoreVideo(addr, size);});}CAMERA_LOGD(demo test: CaptureON exit);return RC_OK; } ManuList()函数从控制台通过fgets()接口获取字符不同字符所对应demo支持的功能不同并打印出该demo所支持功能的菜单。 static void ManuList(const std::shared_ptrOhosCameraDemo mainDemo,const int argc, char** argv) {int idx, c;bool isAwb true;const char *shortOptions h:cwvaeqof:;c getopt_long(argc, argv, shortOptions, LONG_OPTIONS, idx);while (1) {switch (c) {case h:c PutMenuAndGetChr(); // 打印菜单break;case f:FlashLightTest(mainDemo); // 手电筒功能测试c PutMenuAndGetChr();break;case o:OfflineTest(mainDemo); // Offline功能测试c PutMenuAndGetChr();break;case c:CaptureTest(mainDemo); // Capture功能测试c PutMenuAndGetChr();break;case w: // AWB功能测试if (isAwb) {mainDemo-SetAwbMode(OHOS_CAMERA_AWB_MODE_INCANDESCENT);} else {mainDemo-SetAwbMode(OHOS_CAMERA_AWB_MODE_OFF);}isAwb !isAwb;c PutMenuAndGetChr();break;case a: // AE功能测试mainDemo-SetAeExpo();c PutMenuAndGetChr();break;case e: // Metadata测试mainDemo-SetMetadata();c PutMenuAndGetChr();break;case v: // VIDEO功能测试VideoTest(mainDemo);c PutMenuAndGetChr();break;case q: // 退出demoPreviewOff(mainDemo);mainDemo-QuitDemo();return;default:CAMERA_LOGE(main test: command error please retry input command);c PutMenuAndGetChr();break;}} } PutMenuAndGetChr()接口打印了demo程序的菜单并调用fgets()等待从控制台输入命令内容如下 static int PutMenuAndGetChr(void) {constexpr uint32_t inputCount 50;int c 0;char strs[inputCount];Usage(stdout);CAMERA_LOGD(pls input command(input -q exit this app)\n);fgets(strs, inputCount, stdin);for (int i 0; i inputCount; i) {if (strs[i] ! -) {c strs[i];break;}}return c; } 控制台输出菜单详情如下 Options:\n -h | --help Print this message\n -o | --offline stream offline test\n -c | --capture capture one picture\n -w | --set WB Set white balance Cloudy\n -v | --video capture Video of 10s\n -a | --Set AE Set Auto exposure\n -e | --Set Metadeta Set Metadata\n -f | --Set Flashlight Set flashlight ON 5s OFF\n -q | --quit stop preview and quit this app\n); 编译用例 在drivers/peripheral/camera/BUILD.gn文件中的deps中添加“init:ohos_camera_demo”示例代码如下 deps [vdi_base/common/buffer_manager:camera_buffer_manager,vdi_base/common/device_manager:camera_device_manager,vdi_base/common/hdi_impl:camera_host_service_1.0,vdi_base/common/pipeline_core:camera_pipeline_core,vdi_base/common/utils:camera_utils,test/common:ohos_camera_demo,] 以RK3568为例 执行全量编译命令./build.sh --product-name rk3568 --ccache生成可执行二进制文件ohos_camera_demo路径为out/rk3568/packages/phone/vendor/bin/。将可执行文件ohos_camera_demo导入开发板修改权限直接运行即可。 参考 HCS配置文件说明 针对Camera模块0penHarmony提供了默认的HCS配置。开发者若有特殊需求可自行修改相关的HCS配置文件。Camera模块HCS配置文件路径/vendor/hihope/rk3568/hdf_config/uhdf/camera其中 ./hdi_impl/camera_host_config.hcs 相机静态能力:包括镜头位置、镜头类型、连接类型、支持的曝光模式等需要根据产品的具体规格来配置 ./pipeline_core/config.hcs 主要是pipeline的连接方式pipeline配置中包含支持的pipeline类型每一种pipeline中包含的节点以及节点之间的连接关系 编译后在/drivers/periphera/camra/vdi_base/common/pipeline_core/pipeline_impl/src/strategy/config目录下生产congfig.c和congfig.h文件 ./pipeline_core/ipp_algo_config.hcs 算法配置文件 ./pipeline_core/params.hcs 场景、流类型名及其id定义pipeline内部是以流id区分流类型的所以此处需要添加定义 编译后在/drivers/periphera/camra/vdi_base/common/pipeline_core/pipeline_impl/src/strategy/config目录下生产params.c和params.h文件 Camera Dump使用指导 功能简介 Camera Dump功能为Camera相关功能的开发提供测试保障根据需要配置开关文件即可开启此功能。 在流程的不同阶段提供buffer Dump功能可帮助开发者快速定位图像问题点和数据清晰直观地判断图像数据在哪个处理节点中出现问题。对metadata的Dump可以判断metadata参数设置是否正确还能确定不同参数对图像画质的影响。 源码目录说明 /drivers/peripheral/camera/vdi_base/common/dump ├── include │ └── camera_dump.h #Dump头文件 └── src└── camera_dump.cpp #Dump核心源码 Dump配置文件说明 Dump配置文件为dump.config存放在开发设备 /data/local/tmp 目录中。 表1 Dump开关说明 开关取值描述适用场景输出数据格式enableDQBufDumptrue/false开启开关可以Dump v4l2_buffer.cpp文件中DequeueBuffer函数中的数据预览、拍照、录像板载相机YUV420 USB相机YUV422enableUVCNodeBufferDumptrue/false开启开关可以Dump uvc_node.cpp文件中YUV422To420函数转换前的数据预览、拍照、录像USB相机YUV422enableUVCNodeConvertedBufferDumptrue/false开启开关可以Dump uvc_node.cpp文件中YUV422To420函数转换后的数据预览、拍照、录像USB相机YUV420enableExifNodeConvertedBufferDumptrue/false开启开关可以Dump exif_node.cpp文件中DeliverBuffer函数中的数据拍照JPEGenableFaceNodeConvertedBufferDumptrue/false开启开关可以Dump face_node.cpp文件中DeliverBuffer函数中的数据无预留给后期使用无enableForkNodeConvertedBufferDumptrue/false开启开关可以Dump fork_node.cpp文件中DeliverBuffer函数中的数据预览、拍照、录像YUV422enableRKFaceNodeConvertedBufferDumptrue/false开启开关可以Dump rk_face_node.cpp文件中DeliverBuffer函数中的数据无预留给后期使用无enableRKExifNodeConvertedBufferDumptrue/false开启开关可以Dump rk_exif_node.cpp文件中DeliverBuffer函数中的数据拍照JPEGenableCodecNodeConvertedBufferDumptrue/false开启开关可以Dump codec_node.cpp文件中DeliverBuffer函数中的数据预览、拍照、录像JPEG、YUV420、RGBA8888enableRKCodecNodeConvertedBufferDumptrue/false开启开关可以Dump rk_codec_node.cpp文件中DeliverBuffer函数中的数据预览、拍照、录像JPEG、H264、RGBA8888enableSreamTunnelBufferDumptrue/false开启开关可以Dump stream_tunnel.cpp文件中PutBuffer函数中的数据预览、拍照、录像JPEG、H264、YUV420、RGBA8888enableMetadataDumptrue/false开启Dump metadata 数据开关预览、拍照、录像.meta 除了上述开关还可以配置Dump的采样间隔如表2所示。 表2 Dump采样间隔 Dump采样间隔取值描述previewIntervalint(大于等于1)Dump预览间隔默认1每帧都DumpvideoIntervalint(大于等于1)Dump录像间隔默认1每帧都Dump 配置示例 在本地计算机任意位置新建一个文件命名为dump.config根据以上开关将要Dump的位置对应的开关写入文件中值指定为true。 完整配置 enableDQBufDumptrue enableUVCNodeBufferDumpfalse enableUVCNodeConvertedBufferDumpfalse enableExifNodeConvertedBufferDumpfalse enableFaceNodeConvertedBufferDumpfalse enableForkNodeConvertedBufferDumpfalse enableRKFaceNodeConvertedBufferDumpfalse enableRKExifNodeConvertedBufferDumpfalse enableCodecNodeConvertedBufferDumpfalse enableRKCodecNodeConvertedBufferDumpfalse enableSreamTunnelBufferDumpfalseenableMetadataDumptruepreviewInterval3 videoInterval1 配置示例 例如要Dump DequeueBuffer和metadata的数据并且将Dump采样间隔设置为3可以按上面加粗的配置修改。 开启Dump功能 将配置文件发送到开发设备的目录 /data/local/tmp 中。 hdc file send dump.config /data/local/tmp 修改Dump目录权限。 hdc shell mount -o rw,remount /data hdc shell chmod 777 /data/ -R 打开Dump。 hdc shell hidumper -s 5100 -a -host camera_host -o -s 5100 获取id为5100的元能力的全部信息这里指Camera。-a -host camera_host -o 导出指定的系统元能力信息。详细的hidumper说明请参考HiDumper使用指导。 打开相机进行预览、拍照和录像等操作。 Dump结果 打开Dump后会在设备的 /data/local/tmp 目录中生成Dump调测数据文件将数据文件发送到本地电脑中后即可查看。 hdc file recv /data/local/tmp/xxxx.yuv ~/ 最后 有很多小伙伴不知道学习哪些鸿蒙开发技术不知道需要重点掌握哪些鸿蒙应用开发知识点而且学习时频繁踩坑最终浪费大量时间。所以有一份实用的鸿蒙HarmonyOS NEXT资料用来跟着学习是非常有必要的。  这份鸿蒙HarmonyOS NEXT资料包含了鸿蒙开发必掌握的核心知识要点内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等鸿蒙HarmonyOS NEXT技术知识点。 希望这一份鸿蒙学习资料能够给大家带来帮助有需要的小伙伴自行领取限时开源先到先得~无套路领取 获取这份完整版高清学习路线请点击→纯血版全套鸿蒙HarmonyOS学习资料 鸿蒙HarmonyOS NEXT最新学习路线 HarmonOS基础技能 HarmonOS就业必备技能  HarmonOS多媒体技术 鸿蒙NaPi组件进阶 HarmonOS高级技能 初识HarmonOS内核 实战就业级设备开发 有了路线图怎么能没有学习资料呢小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙OpenHarmony 学习手册共计1236页与鸿蒙OpenHarmony 开发入门教学视频内容包含ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。 获取以上完整版高清学习路线请点击→纯血版全套鸿蒙HarmonyOS学习资料 《鸿蒙 (OpenHarmony)开发入门教学视频》 《鸿蒙生态应用开发V2.0白皮书》 《鸿蒙 (OpenHarmony)开发基础到实战手册》 OpenHarmony北向、南向开发环境搭建 《鸿蒙开发基础》 ArkTS语言安装DevEco Studio运用你的第一个ArkTS应用ArkUI声明式UI开发.…… 《鸿蒙开发进阶》 Stage模型入门网络管理数据管理电话服务分布式应用开发通知与窗口管理多媒体技术安全技能任务管理WebGL国际化开发应用测试DFX面向未来设计鸿蒙系统移植和裁剪定制…… 《鸿蒙进阶实战》 ArkTS实践UIAbility应用网络案例…… 获取以上完整鸿蒙HarmonyOS学习资料请点击→纯血版全套鸿蒙HarmonyOS学习资料 总结 总的来说华为鸿蒙不再兼容安卓对中年程序员来说是一个挑战也是一个机会。只有积极应对变化不断学习和提升自己他们才能在这个变革的时代中立于不败之地。
http://www.pierceye.com/news/30899/

相关文章:

  • 关于网站建设与维护的参考文献如可做网站
  • 类似于凡科的网站怎么设置公司网址
  • 山东岩海建设资源有限公司网站做推广网站那里好
  • 如何做网站公众号推广58同城做网站怎么做
  • 主页网站建设全景网站怎么做
  • 河南省建设厅官方网站 吴浩计算机论文8000到10000字
  • 那个网站做二手车好深圳建设资源交易服务中心网站
  • 做部队网站技术网页开发后端软件
  • 网站首页的logo这么修改安徽做网站公司哪家好一点
  • 两学一做教育网站app手机网站设计
  • 网站备案最新备案号免费ppt模板下载网址不需要会员
  • 营销型企业网站有哪些类型贵州十大广告公司
  • 培训网站制作Wordpress订阅栏目
  • 怎么让百度收录自己的网站wordpress购物主题
  • 儿童摄影网站怎么做wordpress文章内容标签做关键词
  • 昆明移动端网站建设wordpress 朴素
  • 网站开发相关职业岗位免费建站系统有哪些
  • 表白网站制作器部署一个网站要做哪些工作
  • 在那些网站可以接兼职做外贸建站及推广
  • 男女做暖暖视频网站国外做名片网站
  • 好看的手机网站布局怎样免费制作网页
  • asp 网站 模板锡林郭勒盟建设厅官方网站
  • 自建站平台苏州公司网站建站
  • 网站运营方案wordpress用户搜索次数插件
  • 网站前端建设wordpress注册不发邮件
  • 淘宝客网站建设分类网站开发必用代码
  • 设计网站意味着什么如何替换网站上的动画
  • 商城网站建设一般需要多少钱大型局域网组建方案
  • 中国建设银行贷款官网站wordpress微信h5支付宝
  • 福建网站建设开发wordpress修改模块样式