我们正常会在MainAbility.ts的onCreate()函数加载的时候执行申请授权,在index.ets页面中,当 XComponent 组件 onLoad() 回调后执行初始化相机操作。
想了解更多关于开源的内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com
前面两篇,已经介绍了分布式相机应用开发的整个流程,有兴趣的可以回顾下:
这篇我们主要讲讲分布式相机开发过程中遇到的一些问题和思考,由于本地人目前主要是北向入手,所以只从应用开发的角度总结目前遇到的问题,如有一些低级错误,还希望各位老师不吝赐教。
分布式相机问题一览
对于开发过程中所遇到的一些坑,前面两篇多少有简单的提到一些,这里做一次规整,也算是一次回顾。
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()请求权限后,但未手动授权时,查看相关日志:
日志告诉我们,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,即为系统应用。
想了解更多关于开源的内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com