# huaweicloud-cloudPhoneAccess-web **Repository Path**: HuaweiCloudDeveloper/huaweicloud-cloudPhoneAccess-web ## Basic Information - **Project Name**: huaweicloud-cloudPhoneAccess-web - **Description**: 作为端云协同的端侧 SDK,用于在 web 浏览器 上访问并接入云端云手机,呈现云手机的画面、声音并操控云手机。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 7 - **Forks**: 0 - **Created**: 2022-12-20 - **Last Updated**: 2025-06-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### [华为云手机开源项目仓主入口](https://gitee.com/HuaweiCloudDeveloper/huaweicloud-cloud-phone) ## H5-SDK ### 1. 项目简介 华为云手机端云协同方案,致力于高效、便捷的接入并使用华为云手机,涵盖信令交互、接入鉴权、音视频流传输、解码渲染、触控采集等模块,使得客户构建云游戏、个人云手机等业务场景变的简单。客户在云手机内部署 CloudAppEngine,真机侧集成安卓和 H5 的 SDK,RemoteServer 实现相关 RestApi 接口,进一步结合业务场景需要来丰富相关模块,实现完善的接入方案。 ### 2. 组件交互 ![](res/组件交互.png) 1. SDK->RemoteServer: 客户端 SDK 向后端 server 请求一个空闲云机实例 2. RemoteServer->SDK: 后端 server 按需求找到一个空闲云机实例信息返回给客户端 SDK 3. SDK->cloudAppEngine: 客户端 SDK 通过云机实例信息向 cloudAppEngine 建链、认证,发心跳、控制信令、触控指令等 4. cloudAppEngine->SDK: cloudAppEngine 向客户端SDK发信令、发送音视频流等 5. cloudAppEngine->RemoteServer: cloudAppEngine 从后端 server 获取解密秘钥、上报事件等 6. RemoteServer->cloudAppEngine: 后端 server 响应 cloudAppEngine 的请求 ### 3. 工程构建 #### 3.1 目录介绍 ``` └── sdk    ├── src     │ ├── 3rd <三方依赖>     │ ├── common <公共>    │ ├── config <配置>    │ ├── worker <音视频解码处理等>    │ ├── AESGCMCrypto.js <加解密>    │ ├── StartScreenPrint.js <预览接口层> │ ├── AppScreenPrint.js <预览逻辑控制>    │ ├── AppController.js <逻辑控制>    │ ├── AudioPlayer.js <音频播放>    │ ├── AutoRotation.js <自动旋转>    │ ├── CanvasPlayer.js <视频渲染>    │ ├── CloudApp.js <接口层>    │ ├── DelayAnalysis.js <时延分析>    │ ├── FrameParser.js <帧解析>    │ ├── Fullscreen.js <全屏>    │ ├── Logger.js <日志>    │ ├── TouchHandler.js <触控>    │ └── Util.js <工具>    └── demo ├──demo.html 测试页面 └──demo_multiple.html 单页面多云机连接测试页面 ``` #### 3.2 工程运行 ##### 3.2.1 打包 在sdk目录操作 命令 | 用途 -- | -- npm install | 安装打包脚本依赖的包 npm run dev | 开发模式 npm run build | 生产模式 node package.js | 将产物输出为 tar.gz 包,放在 dist 目录下 ##### 3.2.2 开发测试 (1)修改 demo.html 中的接入地址等配置 (2)执行 npm run dev 命令 #### 3.3 产物下载 ```shell ## 可以直接下载本项目构建好的产物进行尝鲜使用 1、从链接https://gitee.com/HuaweiCloudDeveloper/huaweicloud-cloudPhoneAccess-web/releases/ 下载最新压缩包并解压 2、解压后使用文本编辑软件编辑demo.html,写入正确的"aes_key"修改后保存 3、使用浏览器打开demo.html,输入云手机的ip和端口(可在华为云控制台云手机详情页查看cph_h5_server的公网访问地址),点击“ENTER” 4、连接之后会跳转到新的页面,需要信任一下证书,点击信任后关闭新的页面,回到原来页面刷新一下就可以看到云手机画面 ``` #### 3.4 FAQ (1)Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID ``` wss 协议需要证书认证,可能是证书问题导致,解决办法:改为 https 协议,在浏览器中打开一次,并信任,如 websocker 链接为wss://123.123.123.123:234,则用浏览器打开https://123.123.123.123:234 ``` (2)window.crypto.subtle.importKey ``` https://javascript.develop-bugs.com/article/21525804/Chrome+packaged+app+UDP+sockets+not+working I use a local server to obtain the response. Everything works fine when using localhost. The Request and Response are properly sent and obtained from the server. But, when connected over IP, it shows an error “NotSupportedError: Only secure origins are allowed”. When I used Chrome canary, it said that importKey method is not recognized. So when I 'console'ed it with Chrome, the control did not go beyond the importKey method. What could possibly be the problem? Chrome restricts the usage of WebCryptographyApi to secure origins. It means 'https'. localhost is a special address enabled for development. Therefore, to use WebCrypto in a real environment you need to setup a SSL/TLS server ``` ### 4. 功能接口 #### 4.1 是否满足运行 - 接口介绍 CloudApp.isSupport(); - 功能描述 获取当前环境是否满足 SDK 运行。 #### 4.2 进入云手机 - 接口介绍 const cloudApp = new CloudApp(containerId, params); - 功能描述 进入云手机。 - 参数介绍 @param {string} containerId:渲染游戏视图的 DOM 元素 id,必选。暂时不支持修改或删除,可能导致JS获取不到DOM @param {object} params:启动相关配置,必选 | 参数 | 是否必选 | 参数类型 | 描述 | | ------------------ | -------- | -------- | ---------------------------------------- | | ip | 是 | String | 云手机IP地址 | | port | 是 | String | 云手机端口信息 | | session_id | 是 | String | 会话id | | ticket | 是 | String | 随机数 | | aes_key | 是 | String | 对称秘钥 | | auth_ts | 是 | String | 验签时间戳 | | background_timeout | 是 | String | 页面非活跃状态超时时长 (取值范围\[16,3600\],单位是s) | | available_playtime | 是 | String | 单次连接可用时长 (值为0时,无超时判断) | | touch_timeout | 否 | String | 无触控时长 (值为0时,无超时判断) | | user_id | 否 | String | 用户id | | auto_rotate | 否 | Boolean | 是否根据真机方向和应用方向自适应旋转画面 | | keepRatio | 否 | Boolean | 是否始终保持CLOUD_PHONE_RATIO中设置的宽高比 | | media_config | 否 | Object | 详见 4.9 设置音视频参数(方法) | - 调用示例 ```javascript let session_id = "123e7654e89b12d3a412345655440000"; let params = { ip: "117.3.111.4", port: "12345", session_id: sessionId, background_timeout: "60", available_playtime: "6000", ticket: "xxx", aes_key: "xxx", auth_ts: "123456789", media_config: { stream_width: 720, stream_height: 1280 } }; let cloudapp = new CloudApp("container", params); ``` #### 4.3 退出云手机(方法) - 接口介绍 cloudApp.exit(); - 功能描述 退出云手机。 #### 4.4 重连云手机(方法) - 接口介绍 cloudApp.reconnect(); - 功能描述 重新连接云手机。 #### 4.5 获取音量值(方法) - 接口介绍 cloudApp.getVolume(); - 功能描述 获取当前音量值。 #### 4.6 设置音量值(方法) - 接口介绍 cloudApp.setVolume(); - 功能描述 设置音量值。 #### 4.7 获取是否全屏(方法) - 接口介绍 cloudApp.isFullscreen(); - 功能描述 获取是否全屏状态,仅限PC浏览器。 #### 4.8 全屏/非全屏切换(方法) - 接口介绍 cloudApp.fullscreenToggle(fullscreenElementId); - 功能描述 全屏/非全屏切换,仅限PC浏览器。 - 参数介绍 @param {string} fullscreenElementId,全屏元素ID,必须是 containerId 或其对应节点的父级节点的 ID。选填项,若不提供则默认值为 containerId。 #### 4.9 设置音视频参数(方法) - 接口介绍 cloudApp.setMediaConfig(config); - 功能描述 设置音视频参数。 - 参数介绍 @param {config} config | 参数 | 参数类型 | 描述 | 约束 | | -------------- | -------- | ------ | ---------------------- | | bitrate | String | 码率 | 设置范围[100000, 10000000] | | stream_width | String | 虚拟宽 | 240到4096,且为8的倍数,与stream_height乘积不能大于云手机规格宽高乘积 | | stream_height | String | 虚拟高 | 240到4096,且为8的倍数,与stream_width乘积不能大于云手机规格宽高乘积 | #### 4.10 应用状态更新(事件) - 接口介绍(具体状态可见第7条附录) cloudApp.on("appStateChange", function(event, data){ // todo }); - 功能描述 业务可以根据接入状态自定义交互行为,状态全集可查看端云引擎对接文档 - 参数介绍 @param {object} event,触发的事件,event 对象包含 name。 @param {object} data,接入状态数据,data 对象包含 state、message 属性 #### 4.11 网络时延更新(事件) - 接口介绍 cloudApp.on("netStateChange", function(event, data){ // todo }); - 功能描述 网络时延实时更新。 - 参数介绍 @param {object} event,触发的事件,event 对象包含 name。 @param {object} data,网络信息数据。 | 属性 | 属性类型 | 描述 | | ------- | -------- | ------------------ | | delay | number | 网络时延,单位是ms | | bitrate | number | 码率,单位是kbps | #### 4.12 云机剪切板(事件) - 接口介绍 cloudApp.on("clipboardDataChange", function(event, data){ // todo }); - 功能描述 获取云手机剪切板数据。 - 参数介绍 @param {object} event,触发的事件,event 对象包含 name。 @param {string} data,云手机剪切板数据。 #### 4.13 发送剪切板数据(方法) - 接口介绍 cloudApp.sendClipboardData(data); - 功能描述 将端侧剪切板数据发送到云手机。 - 参数介绍 @param {string} data,端侧剪切板数据。 #### 4.13 云手机桌面功能(方法) - 接口介绍 cloudApp.home(); - 功能描述 进入云手机桌面。 #### 4.14 云手机回退功能(方法) - 接口介绍 cloudApp.back(); - 功能描述 应用退出、返回上一步。 #### 4.15 云手机多任务功能(方法) - 接口介绍 cloudApp.menu(); - 功能描述 进入云手机多任务页面。 #### 4.16 云手机设置默认相机功能(方法) - 接口介绍 cloudApp.setDefaultCamera(devId); - 功能描述 设置需要调用的相机,参数为相机的设备Id(deviceId)。 #### 4.17 云手机获取可用相机功能(方法) - 接口介绍 cloudApp.getCameras(); - 功能描述 返回所有可用的相机,可根据返回值设置默认相机。 #### 4.18 云手机恢复默认相机功能(方法) - 接口介绍 cloudApp.resetDefaultCamera(); - 功能描述 重置指定相机,调用默认相机。 ### 5. 开发指导 ### 5.1 截图功能对接 (1)本次截图功能的核心为两个js文件,StartScreenPrint.js 截图功能入口,AppScreenPrint.js截图功能逻辑层。 (2)前置工作1:调用IAM接口获取Token接口,拿到认证信息(https://support.huaweicloud.com/api-iam/iam_30_0001.html) 前置工作2:调用查询云手机列表接口,获取需要截图预览的云手机(https://support.huaweicloud.com/api-cph/cph_api_0503.html) 前置工作3:调用获取云手机连接信息接口(https://support.huaweicloud.com/api-cph/cph_api_0564.html) (3)在项目中引入 StartScreenPrint.js ```javascript const SrceenPrintApp = new StartScreenPrint( bgContainer, // 需要展示截图的DOM ip: access_ip, // 连接信息中的IP port: access_port, // 连接信息中的端口号 session_id: this.connectInfo.access_info.session_id, // 连接信息中的会话id background_timeout: '0', // 后台运行超时时间 available_playtime: '0', // 试玩时间 touch_timeout: 0, // 未触控超时时间 connect_uri: 'wss://' + [access_ip, access_port].join(':'), // wss地址 channelType: 'WebSocket', // 连接类型 ticket: this.connectInfo.access_info.ticket, // 连接信息中的ticket aes_key: this.connectInfo.phone_id, // 连接信息中的phone_id auth_ts: this.connectInfo.access_info.timestamp, // 连接信息中的timestamp _debug: true, auto_rotate: true, libPath: window.location.origin + '/lib/', // 配置库文件地址 microPhoneOutputType: 'OPUS', // PCM或OPUS keepRatio: false, // 保持宽高比 fullscreenId: this.fullscreenContainer, // 全屏DOM ID media_config: { stream_width: '320', stream_height: '640', }, ); ``` (4)核心事件回调 ```javascript SrceenPrintApp.on('phoneViewChange', (event, data) => { console.log(data); // 返回最新的截图内容 }); ``` (5) 注意事项:同一手机在同一客户端只能存在一个wss连接(wss:1.95.8.XXX:10001)。因此,截图预览时需要暂停出流,且推荐销毁对应的实例,出流情况下同理。 (6)截图预览模式下,相关状态依然通过appStateChange回调传递,可在文档结尾的CAE状态码表查看,并封装对应逻辑 !!!重要:本次对接全流程已在H5_E2E中实现,参考H5_E2E文档可以启动H5_E2E进行体验。H5_E2E已将以下内容包含,使用H5_SDK可以参考: 1.IAM获取token、获取云手机列表、获取连接信息接口的使用 2.初始化SrceenPrintApp实例 3.出流模式与截图模式切换 4.截图模式下的不同状态的展示 ### 5.2 H5_E2E H5_E2E是使用vue框架对H5_SDK进行二次开发的全新项目。调整了JS与DOM之间的交互逻辑,实现了JS与DOM的解耦,并且对于UI进行了整体升级,对于希望使用端到端式引用,减轻前端开发工作量的用户来说较为友好。 同时,H5_E2E不仅支持IAM直连登录,也保留了IP和PORT的登录方式,也对移动端样式进行了调整优化。 对于仅希望使用API,自行封装UI和交互的用户,可以继续使用H5_SDK,参考H5_E2E的写法实现业务逻辑 H5_E2E如何接入当前用户业务的几种选择: 1.vue框架可以参考该项目,平移逻辑内容,复用组件,调整打包配置 2.非vue框架,可以使用iframe引入打包之后的H5_E2E,并通过线程进行通信 3.非vue框架,可使用相关的兼容性插件实现在其他框架中使用H5_E2E的vue组件(谨慎使用) 对于从零开始的项目,可以考虑参考H5_E2E,或基于H5_E2E进行二次开发 ### 5.3 Https向http改造 1. 设置'_HTTP_MODE_'开关:true为http模式,false为https模式 2. 涉及文件demo.html、enter.html,webpack.dev.js(开发模式),webpack.config.js(生产模式) 3. CAE云侧需联系开发人员同步支持 ### 5.4 仅使用与码流相关UI,不需要拓展UI,如:清晰度切换,返回、多任务、退出云手机、网络延迟信息等 1. 全局搜索“extra-ui”类名,相关元素及其子元素都删除 2. 设置“_EXTRA_UI_”参数,true为使用拓展的UI,false为仅使用与码流相关UI,不需要拓展UI。涉及文件demo.html、webpack.dev.js(开发模式),webpack.config.js(生产模式) ### 6. FAQ - 部署后页面访问地址中需要携带目标html的完整名称。如:https://110.123.23.34:1001/enter.html - 出现AESGCMCrypto undefined的相关错误,是由于浏览器安全策略限制。需要使用HTTPS协议访问项目, 或使用localhost:XXXX、127.0.0.1:XXXX访问项目。 - 使用iframe嵌入项目,报SecurityError:The operation is insecure.相关错误。需要设置iframe标签的属性sandbox为allow-scripts。 - 使用iframe嵌入项目,报错Failed to read the 'localStorage' property from 'Window'.相关错误。需要给iframe标签添加allow-same-origin属性。 - 项目报错:Failed to execute 'importScripts' on 'WorkerGlobalScope': The URL '/xxx/xxx/lib/libffmpeg_264_265.js' is invalid。请检查demo.html中libPath(编解码库文件的地址)参数的值是否正确。libPath可以不用修改。如果用户需要修改,要写完整的目录路径,如:'https://110.123.23.34:1001/lib/'。验证目录地址配置是否正确,可在浏览器中输入'https://110.123.23.34:1001/lib/libffmpeg_264_265.js'访问,可成功访问文件内容,则配置正确。 - 部署后页面提示样式找不到。如:style is null | undefined - 请检查是否修改了demo.html中DOM元素的ID,当前ID与逻辑绑定。若要修改,请确保JS操作DOM时,对应的ID参数也一并进行了修改。 ### 7. license Apache License 2.0。 ### 附录 # CAE状态码 | 十进制消息码 | 消息内容 | 说明 | | ------------ | --------------------------- | ----------------------------- | | 256 | Connecting | Socket连接中 | | 512 | Connect success | Socket连接成功 | | 769 | Server unreachable | 服务器不可达 | | 770 | Resource in using | 资源正在使用中 | | 1024 | Verifying | 接入信息认证中 | | 1280 | Verify success | 接入信息认证通过 | | 1537 | Verify parameter missing | 缺少认证参数 | | 1538 | Verify parameter invalid | 认证参数非法 | | 1540 | Server inner error | 系统内部错误 | | 1541 | Server inner error | 系统内部错误 | | 1542 | Server inner error | 接入信息认证失败 | | 1543 | / | SESSION_ID校验失败 | | 2048 | Start success | Start指令执行成功 | | 2308 | Start parameter missing | Start指令缺少参数 | | 2560 | Connect lost | 连接丢失 | | 2816 | Reconnecting | 重新连接中 | | 3072 | Reconnect success | 重连成功 | | 3329 | Reconnect parameter invalid | 重连参数非法 | | 3331 | Server inner error | 系统内部错误 | | 3584 | Available time usedup | 可用时间已用完 | | 3840 | Notouch timeout | 未触屏时间超时 | | 4096 | Switch background timeout | 播流软件切换到后台超时 | | 4353 | Engine Start failed | 接入引擎开启失败 | | 4354 | / | 云手机不支持H265编码 | | 4608 | Switch backgroud | 播流软件切换到后台 | | 4865 | Server inner error | 播流软件切换后台失败 | | 5120 | Switch foregroud | 播流软件切换到前台 | | 5377 | / | 播流软件切换前台失败 | | 6400 | Back home | 退出播流软件 | | 8448 | Set media config success | 媒体配置设置成功 | | 8705 | Set media config error | 媒体配置设置失败 | | 8960 | / | CAE被抢占,当前客户端被迫下线 | | 65535 | Invalid Operation | 非法操作 |