OpenHarmony 分布式相机(下)

时间:2023-02-22 15:15:21

作者:徐金生

前面两篇,已经介绍了分布式相机应用开发的整个流程,有兴趣的可以回顾下:

这篇我们主要讲讲分布式相机开发过程中遇到的一些问题和思考,由于本地人目前主要是北向入手,所以只从应用开发的角度总结目前遇到的问题,如有一些低级错误,还希望各位老师不吝赐教。

分布式相机问题一览

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

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

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

MainAbility.ts


const TAG: string = '[DistributedCamera]'
let permissionList: Array<string> = [
    "ohos.permission.MEDIA_LOCATION",
    "ohos.permission.READ_MEDIA",
    "ohos.permission.WRITE_MEDIA",
    "ohos.permission.CAMERA",
    "ohos.permission.MICROPHONE",
    "ohos.permission.DISTRIBUTED_DATASYNC"
]


export default class MainAbility extends Ability {
    async onCreate(want, launchParam) {
        console.info(`${TAG} onCreate`)
        globalThis.cameraAbilityContext = this.context
        await globalThis.cameraAbilityContext.requestPermissionsFromUser(permissionList)
    }
  }

index.ets

// ...
// 截取部分主要代码

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

// ...

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

OpenHarmony 分布式相机(下)

日志告诉我们,page的生命周期已启动到onShow,并且页面布局也完成了加载,XComponent 组件回调 onLoad() ,但是由于还未授权,导致无法初始化相机,此时即便授权成功,也不会再进行初始化,导致相机无法启动,无预览视图。

知道原因后,我们可以有多种方式解决,重点就是在授权完成后,需要再次触发初始化相机,让相机启动才可以正常预览。

我的处理方式:

1、在index.ets页面中处理授权 2、定义是否已授权的标识,用于判断是否可以初始化相机 3、定义是否已经初始化相机标识,防止对此初始化 4、在page页面初始化函数aboutToAppear()中请求权限,并在权限申请结果中添加初始化相机操作 5、XComponent 组件回调 onLoad() 初始化相机操作不变

index.ets


  private isInitCamera: boolean = false // 是否已初始化相机
  private isPermissions: boolean = false // 是否完成授权

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

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

解析: 从现象看,预览画面卡在息屏前的状态,需要退出应用后,重启应用才能正常预览。从日志上看没有查看到具体的原因,只是camera_host的数据量日志消失。 猜想:相机在系统息屏后强制关闭,需要重新加载相机才能正常预览,实现方式如下: 1、在page的onPageShow()回调函数中重新初始化相机 2、在page的onPageHide()函数中释放相机资源,减少系统资源不必要的消耗。

index.ets


  async onPageShow() {
    console.info(`${TAG} onPageShow`)
    await this.initCamera()
  }
  onPageHide() {
    console.info(`${TAG} onPageHide`)
    this.isSwitchDeviceing = false
    this.isInitCamera = false
    this.mCameraService.releaseCamera()
  }

结论: 实践验证此方法有效解决息屏后点亮返回相机无法预览的问题。

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

解析: 两台设备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

原因: 在创建PhotoOutput时需要传递支持的拍照配置信息Profile,这里的Profile可以通过CmeraManager.getSupportedOutputCapability()返回的相机输出能力CameraOutputCapability对象获取,但远程相机设备拍照输出能力列表返回空,但通过查看本地相机拍照输出能力可知DAYU200设备支持的Profile信息:


photoProfile {"format":2000,"size":{"width":1280,"height":960}}

通过此将photoProfile作为远程相机设备构建拍照输出流的入参场景拍照输出流,并把此添加到拍照会话管理中,但是界面出现不支持此相机配置,最终关闭了相机,导致黑屏。

解决方案: 根据此问题,目前只能根据场景判断是否需要添加拍照输出流到会话管理,对于本地相机则可以添加拍照输出流,执行拍照业务,远程相机则不添加拍照输出流,这也就不能执行拍照业务,希望社区有解决方案。

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

解析: 此问题存在的原因可能有多种,这里我说下我遇到的情况, 1、分布式连接被断开,但是因为底层机制,设备之间下线需要在一段时间内才能上报(预计5分钟),所以在应用层看到可以连接的远端设备,其实已经下线了,这时当然不能切换到远程相机。

2、与问题3中描述的相同,因为添加了一个无法支持的拍照配置信息导致相机被关闭。

解决方案 1、等待线下通知,再重新连接设备,或者等待设备自动完成重连,简单粗暴就是重启设备。 2、待社区反馈

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

解析: 如题描述,目前可能存在堵塞UI线程的可能,需要将一些耗时的操作移动到子线程,比如预览、拍照保存图片等

目前正在修改优化,关于ets的异步线程worker可以查看之前写的一篇关于:OpenHarmony stage worker 多线程

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

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

7、no permission for function call

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

discoverFail data= {"subscribeId":26386,"reason":-20007,"errInfo":"no permission for function call."}

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

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

继续探索...

感谢

如果您能看到最后,还希望您能动动手指点个赞,一个人能走多远关键在于与谁同行,我用跨越山海的一路相伴,希望得到您的点赞。

本文作者:NL_AIDC_XJS

想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com/#bkwz​