0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙分布式相机“踩坑”分享

OpenHarmony技术社区 来源:OST开源开发者 2023-03-08 14:19 次阅读

接上一篇 OpenHarmony 分布式相机(上),今天我们来说下如何实现分布式相机。

实现分布式相机其实很简单,正如官方介绍的一样,当被控端相机被连接成功后,可以像使用本地设备一样使用远程相机。

我们先看下效果:

066055e4-b9cf-11ed-bfe3-dac502259ad0.jpg

上一篇已经完整的介绍了如何开发一个本地相机,对于分布式相机我们需要完成以下几个步骤。

前置条件:

两台带摄像头的设备

建议使用相同版本的 OH 系统,本案例使用 OpenHarmony 3.2 beta5

连接在同一个网络

开发步骤:

引入设备管理(@ohos.distributedHardware.deviceManager)

通过 deviceManager 发现周边设备

通过 pin 码完成设备认证

获取和展示可信设备

在可信设备直接选择切换不同设备的摄像头

在主控端查看被控端的摄像头图像

以上描述的功能在应用开发时可以使用一张草图来表示,草图中切换设备->弹窗显示设备列表的过程,草图如下:

0679e5a4-b9cf-11ed-bfe3-dac502259ad0.png

代码

①RemoteDeviceModel.ts

说明:远程设备业务处理类,包括获取可信设备列表、获取周边设备列表、监听设备状态(上线、下线、状态变化)、监听设备连接失败、设备授信认证、卸载设备状态监听等。

代码如下:

importdeviceManagerfrom'@ohos.distributedHardware.deviceManager'
importLoggerfrom'./util/Logger'
constTAG:string='RemoteDeviceModel'
letsubscribeId:number=-1
exportclassRemoteDeviceModel{
privatedeviceList:Array=[]
privatediscoverList:Array=[]
privatecallback:()=>void
privateauthCallback:()=>void
privatedeviceManager:deviceManager.DeviceManager
constructor(){
}
publicregisterDeviceListCallback(bundleName:string,callback){
if(typeof(this.deviceManager)!=='undefined'){
this.registerDeviceListCallbackImplement(callback)
return
}
Logger.info(TAG,`deviceManager.createDeviceManagerbegin`)
try{
deviceManager.createDeviceManager(bundleName,(error,value)=>{
if(error){
Logger.info(TAG,`createDeviceManagerfailed.`)
return
}
this.deviceManager=value
this.registerDeviceListCallbackImplement(callback)
Logger.info(TAG,`createDeviceManagercallbackreturned,error=${error},value=${value}`)
})
}catch(err){
Logger.error(TAG,`createDeviceManagerfailed,codeis${err.code},messageis${err.message}`)
}
Logger.info(TAG,`deviceManager.createDeviceManagerend`)
}
privatedeviceStateChangeActionOffline(device){
if(this.deviceList.length<= 0) {
            this.callback()
            return
        }
        for (let j = 0; j < this.deviceList.length; j++) {
            if (this.deviceList[j ].deviceId === device.deviceId) {
                this.deviceList[j] = device
                break
            }
        }
        Logger.info(TAG, `offline, device list= ${JSON.stringify(this.deviceList)}`)
        this.callback()
    }
    private registerDeviceListCallbackImplement(callback) {
        Logger.info(TAG, `registerDeviceListCallback`)
        this.callback = callback
        if (this.deviceManager === undefined) {
            Logger.info(TAG, `deviceManager has not initialized`)
            this.callback()
            return
        }
        Logger.info(TAG, `getTrustedDeviceListSync begin`)
        try {
            let list = this.deviceManager.getTrustedDeviceListSync()
            Logger.info(TAG, `getTrustedDeviceListSync end, deviceList= ${JSON.stringify(list)}`)
            if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
                this.deviceList = list
            }
        } catch (err) {
            Logger.error(`getTrustedDeviceListSync failed, code is ${err.code}, message is ${err.message}`)
        }
        this.callback()
        Logger.info(TAG, `callback finished`)
        this.deviceManager.on('deviceStateChange', (data) =>{
if(data===null){
return
}
Logger.info(TAG,`deviceStateChangedata=${JSON.stringify(data)}`)
switch(data.action){
casedeviceManager.DeviceStateChangeAction.READY:
this.discoverList=[]
this.deviceList.push(data.device)
try{
letlist=this.deviceManager.getTrustedDeviceListSync()
if(typeof(list)!=='undefined'&&typeof(list.length)!=='undefined'){
this.deviceList=list
}
this.callback()
}catch(err){
Logger.error(TAG,`getTrustedDeviceListSyncfailed,codeis${err.code},messageis${err.message}`)
}
break
casedeviceManager.DeviceStateChangeAction.OFFLINE:
casedeviceManager.DeviceStateChangeAction.CHANGE:
this.deviceStateChangeActionOffline(data.device)
break
default:
break
}
})
this.deviceManager.on('deviceFound',(data)=>{
if(data===null){
return
}
Logger.info(TAG,`deviceFounddata=${JSON.stringify(data)}`)
this.deviceFound(data)
})
this.deviceManager.on('discoverFail',(data)=>{
Logger.info(TAG,`discoverFaildata=${JSON.stringify(data)}`)
})
this.deviceManager.on('serviceDie',()=>{
Logger.info(TAG,`serviceDie`)
})
this.startDeviceDiscovery()
}
privatedeviceFound(data){
for(vari=0;i< this.discoverList.length; i++) {
            if (this.discoverList[i].deviceId === data.device.deviceId) {
                Logger.info(TAG, `device founded ignored`)
                return
            }
        }
        this.discoverList[this.discoverList.length] = data.device
        Logger.info(TAG, `deviceFound self.discoverList= ${this.discoverList}`)
        this.callback()
    }
    private startDeviceDiscovery() {
        if (subscribeId >=0){
Logger.info(TAG,`startedDeviceDiscovery`)
return
}
subscribeId=Math.floor(65536*Math.random())
letinfo={
subscribeId:subscribeId,
mode:deviceManager.DiscoverMode.DISCOVER_MODE_ACTIVE,
medium:deviceManager.ExchangeMedium.COAP,
freq:deviceManager.ExchangeFreq.HIGH,
isSameAccount:false,
isWakeRemote:true,
capability:deviceManager.SubscribeCap.SUBSCRIBE_CAPABILITY_DDMP
}
Logger.info(TAG,`startDeviceDiscovery${subscribeId}`)
try{
// todo 多次启动发现周边设备有什么影响吗?
this.deviceManager.startDeviceDiscovery(info)
}catch(err){
Logger.error(TAG,`startDeviceDiscoveryfailed,codeis${err.code},messageis${err.message}`)
}
}
publicunregisterDeviceListCallback(){
Logger.info(TAG,`stopDeviceDiscovery$subscribeId}`)
this.deviceList=[]
this.discoverList=[]
try{
this.deviceManager.stopDeviceDiscovery(subscribeId)
}catch(err){
Logger.error(TAG,`stopDeviceDiscoveryfailed,codeis${err.code},messageis${err.message}`)
}
this.deviceManager.off('deviceStateChange')
this.deviceManager.off('deviceFound')
this.deviceManager.off('discoverFail')
this.deviceManager.off('serviceDie')
}
publicauthenticateDevice(device,extraInfo,callBack){
Logger.info(TAG,`authenticateDevice${JSON.stringify(device)}`)
for(leti=0;i< this.discoverList.length; i++) {
            if (this.discoverList[i].deviceId !== device.deviceId) {
                continue
            }
            let authParam = {
                'authType': 1,
                'appIcon': '',
                'appThumbnail': '',
                'extraInfo': extraInfo
            }
            try {
                this.deviceManager.authenticateDevice(device, authParam, (err, data) =>{
if(err){
Logger.error(TAG,`authenticateDeviceerror:${JSON.stringify(err)}`)
this.authCallback=null
return
}
Logger.info(TAG,`authenticateDevicesucceed:${JSON.stringify(data)}`)
this.authCallback=callBack
})
}catch(err){
Logger.error(TAG,`authenticateDevicefailed,codeis${err.code},messageis${err.message}`)
}
}
}
/**
*已认证设备列表
*/
publicgetDeviceList():Array{
returnthis.deviceList
}
/**
*发现设备列表
*/
publicgetDiscoverList():Array{
returnthis.discoverList
}
}

getDeviceList() :获取已认证的设备列表;getDiscoverList:发现周边设备的列表。

②DeviceDialog.ets

说明:通过 RemoteDeviceModel.getDiscoverList() 和通过 RemoteDeviceModel.getDeviceList() 获取到所有周边设备列表,用户通过点击"切换设备"按钮弹窗显示所有设备列表信息

importdeviceManagerfrom'@ohos.distributedHardware.deviceManager';
constTAG='DeviceDialog'
//分布式设备选择弹窗
@CustomDialog
exportstructDeviceDialog{
privatecontroller?:CustomDialogController//弹窗控制器
@LinkdeviceList:Array//设备列表
@LinkselectIndex:number//选中的标签
build(){
Column(){
List(){
ForEach(this.deviceList,(item:deviceManager.DeviceInfo,index)=>{
ListItem(){
Row(){
Text(item.deviceName)
.fontSize(22)
.width(350)
.fontColor(Color.Black)
Image(index===this.selectIndex?$r('app.media.checked'):$r('app.media.uncheck'))
.width(35)
.objectFit(ImageFit.Contain)
}
.height(55)
.onClick(()=>{
console.info(`${TAG}selectdevice${item.deviceId}`)
if(index===this.selectIndex){
console.info(`${TAG}devicenotchange`)
}else{
this.selectIndex=index
}
this.controller.close()
})
}
},item=>item.deviceName)
}
.width('100%')
.height(150)
Button(){
Text($r('app.string.cancel'))
.width('100%')
.height(45)
.fontSize(18)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
}.onClick(()=>{
this.controller.close()
})
.backgroundColor('#ed3c13')
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.border({
color:Color.White,
radius:20
})
}
}
③打开设备列表弹窗

说明:在 index.ets 页面中,点击“切换设备”按钮即可以开启设备列表弹窗,通过 @Watch(‘selectedIndexChange’) 监听用户选择的设备标签,在 devices 中获取到具体的 DeviceInfo 对象。

代码如下:

@State@Watch('selectedIndexChange')selectIndex:number=0
//设备列表
@Statedevices:Array=[]
//设备选择弹窗
privatedialogController:CustomDialogController=newCustomDialogController({
builder:DeviceDialog({
deviceList:$devices,
selectIndex:$selectIndex,
}),
autoCancel:true,
alignment:DialogAlignment.Center
})


showDialog(){
console.info(`${TAG}RegisterDeviceListCallbackbegin`)
distributed.registerDeviceListCallback(BUNDLE_NAME,()=>{
console.info(`${TAG}RegisterDeviceListCallbackcallbackentered`)
this.devices=[]
//添加本地设备
this.devices.push({
deviceId:Constant.LOCAL_DEVICE_ID,
deviceName:Constant.LOCAL_DEVICE_NAME,
deviceType:0,
networkId:'',
range:1//发现设备的距离
})
letdiscoverList=distributed.getDiscoverList()
letdeviceList=distributed.getDeviceList()
letdiscoveredDeviceSize=discoverList.length
letdeviceSize=deviceList.length
console.info(`${TAG}discoveredDeviceSize:${discoveredDeviceSize}deviceSize:${deviceSize}`)
letdeviceTemp=discoveredDeviceSize>0?discoverList:deviceList
for(letindex=0;index< deviceTemp.length; index++) {
        this.devices.push(deviceTemp[index])
      }
    })
    this.dialogController.open()
    console.info(`${TAG} RegisterDeviceListCallback end`)
  }


async selectedIndexChange() {
    console.info(`${TAG} select device index ${this.selectIndex}`)
    let discoverList: Array=distributed.getDiscoverList()
if(discoverList.length<= 0) {
      this.mCurDeviceID = this.devices[this.selectIndex].deviceId
      await this.switchDevice()
      this.devices = []
      return
    }
    let selectDeviceName = this.devices[this.selectIndex].deviceName
    let extraInfo = {
      'targetPkgName': BUNDLE_NAME,
      'appName': APP_NAME,
      'appDescription': APP_NAME,
      'business': '0'
    }
    distributed.authenticateDevice(this.devices[this.selectIndex], extraInfo, async () =>{
//获取到相关的设备ID,启动远程应用
for(varindex=0;index< distributed.getDeviceList().length; index++) {
        let deviceName = distributed.getDeviceList()[index].deviceName
        if (deviceName === selectDeviceName) {
          this.mCurDeviceID = distributed.getDeviceList()[index].deviceId
          await this.switchDevice()
        }
      }
    })
    this.devices = []
  }

④重新加载相机

说明:根据用户选择的设备标签获取到当前用户需要切换的相机设备对象,重新加载相机,重新加载需要释放原有的相机资源,然后重新构建 createCameraInput、createPreviewOutput、createSession。 可能你注意到这里好像没有执行 createPhotoOutput,这是因为在实践过程中发现,添加了一个当前设备所支持的拍照配置到会话管理(CaptureSession.addOutput())时,系统会返回当前拍照配置流不支持,并关闭相机,导致相机预览黑屏,所以这里没有添加。

issues:远程相机拍照失败 not found in supported streams

https://gitee.com/openharmony/distributedhardware_distributed_camera/issues/I6E5ZX
mCameraService:这个是相机管理类,代码可以查看上一篇:OpenHarmony 分布式相机(上)中查看。

代码如下:

/**
*切换摄像头
*同一台设备上切换不同摄像头
*/
asyncswitchCamera(){
console.info(`${TAG}switchCamera`)
letcameraList=this.mCameraService.getDeviceCameras(this.mCurDeviceID)
if(cameraList&&cameraList.length>1){
letcameraCount:number=cameraList.length
console.info(`${TAG}cameralist${cameraCount}}`)
if(this.mCurCameraIndex< cameraCount - 1) {
        this.mCurCameraIndex += 1
      } else {
        this.mCurCameraIndex = 0
      }
      await this.reloadCamera()
    } else {
      this.showToast($r('app.string.only_one_camera_hint'))
    }
  }

  /**
   * 重新加载摄像头
   */
  async reloadCamera() {
    // 显示切换loading
    this.isSwitchDeviceing = true
    // 先关闭当前摄像机,再切换新的摄像机
    await this.mCameraService.releaseCamera()
    await this.startPreview()
  }


private async startPreview() {
    console.info(`${TAG} startPreview`)
    await this.mCameraService.createCameraInput(this.mCurCameraIndex, this.mCurDeviceID)
    await this.mCameraService.createPreviewOutput(this.surfaceId, this.previewImpl)
    if (this.mCurDeviceID === Constant.LOCAL_DEVICE_ID) {
      // fixme xjs 如果是远程相机,则不支持拍照,添加拍照输出流会导致相机黑屏
      await this.mCameraService.createPhotoOutput(this.functionBackImpl)
    }
    await this.mCameraService.createSession(this.surfaceId)
  }

⑤加载过度动画

说明:在相机切换中会需要释放原相机的资源,在重启新相机,在通过软总线通道同步远程相机的预览数据。 这里需要一些时间,根据目前测试,在网络稳定状态下,切换时间 3~5s,网络不稳定状态下,切换最长需要 13s,当然有时候会出现无法切换成功,这种情况可能是远程设备已经下线,无法再获取到数据。

代码如下:

@StateisSwitchDeviceing:boolean=false//是否正在切换相机

if(this.isSwitchDeviceing){
Column(){
Image($r('app.media.load_switch_camera'))
.width(400)
.height(306)
.objectFit(ImageFit.Fill)
Text($r('app.string.switch_camera'))
.width('100%')
.height(50)
.fontSize(16)
.fontColor(Color.White)
.align(Alignment.Center)
}
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.onClick(()=>{
})
}
至此,分布式相机的整体流程就已实现完成。下面我们介绍下分布式相机开发中所遇到的问题。

分布式相机问题一览

对于开发过程中所遇到的一些坑,前面多少有简单的提到一些,这里做一次规整,也算是一次回顾。

①首次授权成功无法显示相机预览

解析:我们正常会在 MainAbility.ts 的 onCreate() 函数加载的时候执行申请授权,在 index.ets 页面中,当 XComponent 组件 onLoad() 回调后执行初始化相机操作,代码如下:

MainAbility.ts:

constTAG:string='[DistributedCamera]'
letpermissionList:Array=[
"ohos.permission.MEDIA_LOCATION",
"ohos.permission.READ_MEDIA",
"ohos.permission.WRITE_MEDIA",
"ohos.permission.CAMERA",
"ohos.permission.MICROPHONE",
"ohos.permission.DISTRIBUTED_DATASYNC"
]


exportdefaultclassMainAbilityextendsAbility{
asynconCreate(want,launchParam){
console.info(`${TAG}onCreate`)
globalThis.cameraAbilityContext=this.context
awaitglobalThis.cameraAbilityContext.requestPermissionsFromUser(permissionList)
}
}
index.ets:
//...
//截取部分主要代码

Column(){
XComponent({
id:'componentId',
type:'surface',
controller:this.XComponentController
}).onLoad(async()=>{
console.info(`${TAG}XComponentonLoadiscalled`)
this.XComponentController.setXComponentSurfaceSize({
surfaceWidth:Resolution.DEFAULT_WIDTH,
surfaceHeight:Resolution.DEFAULT_HEIGHT
})
this.surfaceId=this.XComponentController.getXComponentSurfaceId()
console.info(`${TAG}surfaceId:${this.surfaceId}`)
awaitthis.initCamera()
}).height('100%')
.width('100%')
}
.width('100%')
.height('75%')
.margin({
bottom:20
})

//...

应用启动后,调用了 requestPermissionsFromUser() 请求权限后,但未手动授权时,查看相关日志:

0694b794-b9cf-11ed-bfe3-dac502259ad0.png

日志告诉我们,page 的生命周期已启动到 onShow,并且页面布局也完成了加载,XComponent 组件回调 onLoad()。 但是由于还未授权,导致无法初始化相机,此时即便授权成功,也不会再进行初始化,导致相机无法启动,无预览视图。 知道原因后,我们可以有多种方式解决,重点就是在授权完成后,需要再次触发初始化相机,让相机启动才可以正常预览。

我的处理方式:

在 index.ets 页面中处理授权

定义是否已授权的标识,用于判断是否可以初始化相机

定义是否已经初始化相机标识,防止对此初始化

在 page 页面初始化函数 aboutToAppear() 中请求权限,并在权限申请结果中添加初始化相机操作

XComponent 组件回调 onLoad() 初始化相机操作不变

index.ets:

privateisInitCamera:boolean=false//是否已初始化相机
privateisPermissions:boolean=false//是否完成授权

asyncaboutToAppear(){
console.info(`${TAG}aboutToAppear`)
globalThis.cameraAbilityContext.requestPermissionsFromUser(permissionList).then(async(data)=>{
console.info(`${TAG}datapermissions:${JSON.stringify(data.permissions)}`)
console.info(`${TAG}dataauthResult:${JSON.stringify(data.authResults)}`)
//判断授权是否完成
letresultCount:number=0
for(letresultofdata.authResults){
if(result===0){
resultCount+=1
}
}
if(resultCount===permissionList.length){
this.isPermissions=true
}
awaitthis.initCamera()
//获取缩略图
this.mCameraService.getThumbnail(this.functionBackImpl)
})
}

相机应用未关闭,系统息屏后重新点亮,重新返回相机应用,无预览输出流返回

解析:从现象看,预览画面卡在息屏前的状态,需要退出应用后,重启应用才能正常预览。从日志上看没有查看到具体的原因,只是 camera_host 的数据量日志消失。

猜想:相机在系统息屏后强制关闭,需要重新加载相机才能正常预览,实现方式如下:

在 page 的 onPageShow() 回调函数中重新初始化相机。

在 page 的 onPageHide() 函数中释放相机资源,减少系统资源不必要的消耗。

index.ets:

asynconPageShow(){
console.info(`${TAG}onPageShow`)
awaitthis.initCamera()
}
onPageHide(){
console.info(`${TAG}onPageHide`)
this.isSwitchDeviceing=false
this.isInitCamera=false
this.mCameraService.releaseCamera()
}
结论:实践验证此方法有效解决息屏后点亮返回相机无法预览的问题。

③加载远程相机,在会话管理中添加拍照输出流,无法拍照,预览黑屏

解析:两台设备 pin 码认证通过,连接成功,在主控端选择一台被控端设备时,加载相机,流程与加载本地相机相同。

流程如下:

createCameraInput()
createPreviewOutput()
createPhotoOutput()
createSession()

*createSession.beginConfig()
*createSession.addInput(CameraInput)
*createSession.addOutput(PreviewOutput)
*createSession.addOutput(PhotoOutput)
*createSession.commitConfig()
*createSession.start()

经过排查,发现日志中返回异常 not found in supported streams,详情可以查看关联issues。

https://gitee.com/openharmony/distributedhardware_distributed_camera/issues/I6E5ZX
原因:在创建 PhotoOutput 时需要传递支持的拍照配置信息 Profile,这里的 Profile 可以通过CmeraManager.getSupportedOutputCapability()返回的相机输出能力 CameraOutputCapability 对象获取,但远程相机设备拍照输出能力列表返回空。

但通过查看本地相机拍照输出能力可知 DAYU200 设备支持的 Profile 信息:

photoProfile{"format":2000,"size":{"width":1280,"height":960}}
通过此将 photoProfile 作为远程相机设备构建拍照输出流的入参场景拍照输出流,并把此添加到拍照会话管理中,但是界面出现不支持此相机配置,最终关闭了相机,导致黑屏。 解决方案:根据此问题,目前只能根据场景判断是否需要添加拍照输出流到会话管理,对于本地相机则可以添加拍照输出流,执行拍照业务,远程相机则不添加拍照输出流,这也就不能执行拍照业务,希望社区有解决方案。

④切换不同设备上的相机,相机预览输出流出现异常,无法显示远程相机的画面

解析:此问题存在的原因可能有多种,这里我说下我遇到的情况。 (1)分布式连接被断开,但是因为底层机制,设备之间下线需要在一段时间内才能上报(预计 5 分钟),所以在应用层看到可以连接的远端设备,其实已经下线了,这时当然不能切换到远程相机。 (2)与问题 3 中描述的相同,因为添加了一个无法支持的拍照配置信息导致相机被关闭。

解决方案:

等待线下通知,再重新连接设备,或者等待设备自动完成重连,简单粗暴就是重启设备。

待社区反馈。

⑤相机业务在主线程执行,需要将业务移动到子线程,防止 UI 线程堵塞

解析:如题描述,目前可能存在堵塞 UI 线程的可能,需要将一些耗时的操作移动到子线程,比如预览、拍照保存图片等。 目前正在修改优化,关于 ets 的异步线程 worker 可以查看之前写的一篇关于:OpenHarmony stage worker 多线程。

⑥远程相机预览数据传输存在 500ms 的延迟

解析:在 wifi 环境下,被控端相机将预览数据通过软总线传输到主控端显示,有 500ms 左右的延迟,此问题待排查,具体是那个环境出现的延迟。

⑦no permission for function call

解析:用户动态授予:允许不同设备间的数据(ohos.permission.DISTRIBUTED_DATASYNC) 交换权限后,DeviceManager.startDeviceDiscovery() 启动发现周边设备总会出现异常。

日志中提示:

discoverFaildata={"subscribeId":26386,"reason":-20007,"errInfo":"nopermissionforfunctioncall."}

原因:非系统应用无法使用 DeviceManager,详细可查看:issues。

https://gitee.com/openharmony/distributedhardware_device_manager/issues/I6BYK4
解决方案:系统应用和普通应用是通过签名来区分,那只要通过修改签名 UnsgnedReleasedProfileTemplate.json 文件中的 app-feature 值为 ohos_system_app,即为系统应用。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 摄像头
    +关注

    关注

    59

    文章

    4836

    浏览量

    95597
  • 相机
    +关注

    关注

    4

    文章

    1350

    浏览量

    53580
  • 设备
    +关注

    关注

    2

    文章

    4502

    浏览量

    70598
  • 鸿蒙
    +关注

    关注

    57

    文章

    2339

    浏览量

    42804
  • OpenHarmony
    +关注

    关注

    25

    文章

    3713

    浏览量

    16252

原文标题:鸿蒙分布式相机“踩坑”分享

文章出处:【微信号:gh_834c4b3d87fe,微信公众号:OpenHarmony技术社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    HarmonyOS鸿蒙操作系统之什么是“基于微内核的全场景分布式操作系统”?

    HarmonyOS鸿蒙操作系统之什么是“基于微内核的全场景分布式操作系统”?即使作为理工科的人咋一眼看上去似乎也不太懂这是什么,就像区块链这个概念刚出来一样,普通人都是一脸懵B(当然现在我对这个也是
    发表于 09-23 17:06

    鸿蒙分布式任务调度

    鸿蒙分布式任务调度,实现跨设备FA拉起
    发表于 06-12 17:28

    鸿蒙分布式任务调度——数据传递

    鸿蒙分布式任务调度之数据传递
    发表于 06-12 17:29

    HarmonyOS教程一分布式语音照相机

    文件系统和AI语音识别功能开发了一款分布式语音照相机,这款相机能将拍摄的照片实时共享到同一分布式网络下的不同设备中,这样大家都可以看到拍摄的照片,就不需要一窝蜂的跑到一台手机前,不满意
    发表于 09-10 09:53

    Linux学习过程过的与如何解决

    Linux记录记录Linux学习过程过的与如何解决1解决方法:F10进入BIOS使能
    发表于 11-04 08:44

    分布式系统硬件资源池原理和接入实践

    提供更好的服务体验。 图 3 鸿蒙硬件资源池支持各类消费者场景 2.2 开发者场景 对于开发者来说,由于分布式硬件资源池将跨设备硬件调用的复杂度都封装在了系统底层,跨设备硬件复用本地硬件的 API
    发表于 12-06 10:02

    什么是鸿蒙分布式游戏?为什么要做分布式游戏?

    鸿蒙”(Harmony)无疑是近期以来最为热点的话题之一,而在技术层面上,“分布式”又是鸿蒙最核心的关键点之一,无论应用还是游戏都与之息息相关。
    的头像 发表于 01-30 09:49 4615次阅读

    华为鸿蒙系统之分布式游戏详解

    鸿蒙”(Harmony)无疑是近期以来最为热点的话题之一,而在技术层面上,“分布式”又是鸿蒙最核心的关键点之一,无论应用还是游戏都与之息息相关。
    的头像 发表于 01-30 10:42 7179次阅读

    鸿蒙分布式怎么理解

    ”,带来设备内和设备间高吞吐、低时延、高可靠的流畅连接体验。   1. 介绍 鸿蒙分布式软总线致力于实现近场设备间统一的分布式通信能力,提供不区分链路的设备发现和传输接口,具备快速发现并连接设备,高效分发任务和传输数据。作为多终
    的头像 发表于 07-08 14:47 4496次阅读

    鸿蒙系统底层架构 鸿蒙系统分布式架构

    鸿蒙系统是一款面向未来、面向全场景的分布式操作系统,鸿蒙系统开创性地提出了基于同一套系统能力、适配多种终端形态的分布式理念,在这个理念加持下,可支持多种终端设备。
    的头像 发表于 07-08 14:35 1.3w次阅读

    分享一个有趣的鸿蒙分布式小游戏

       今天给大家分享一个有趣的鸿蒙分布式小游戏:你画我猜。       开发心得(如有错误还请大佬及时指正):   分布式流转: 一个 APP 应用在设备之间互相拉起迁移,只在一个终端上运行
    的头像 发表于 11-01 14:29 2541次阅读
    分享一个有趣的<b class='flag-5'>鸿蒙</b><b class='flag-5'>分布式</b>小游戏

    嵌入Linux记录

    Linux记录记录Linux学习过程过的与如何解决1解决方法:F10进入BIOS使能
    发表于 11-01 17:21 10次下载
    嵌入<b class='flag-5'>式</b>Linux<b class='flag-5'>踩</b><b class='flag-5'>坑</b>记录

    鸿蒙版JS如何实现分布式仿抖音应用

       之前大家看过了 Java 版的《 HarmonyOS 分布式之仿抖音应用 》,现在讲讲 JS 如何实现分布式仿抖音应用,通过 JS 方式开发视频播放,分布式设备迁移,评论,通过 Java
    的头像 发表于 11-15 09:44 2361次阅读

    OpenHarmony技术论坛:分布式相机分布式图库功能

    《OpenHarmony Tech Day·技术日》 技术论坛 新增分布式相机分布式图库功能 相比OpenHarmony 3.0版本,OpenHarmony 3.1版本的相机框架新增
    的头像 发表于 04-25 15:06 1819次阅读
    OpenHarmony技术论坛:<b class='flag-5'>分布式</b><b class='flag-5'>相机</b>和<b class='flag-5'>分布式</b>图库功能

    鸿蒙OS 分布式任务调度

    鸿蒙OS 分布式任务调度概述 在 HarmonyO S中,分布式任务调度平台对搭载 HarmonyOS 的多设备构筑的“超级虚拟终端”提供统一的组件管理能力,为应用定义统一的能力基线、接口
    的头像 发表于 01-29 16:50 485次阅读