123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947 |
- <template>
- <div align="center" class="rtc-page cover-bg" :style="{height: pageData.height + 'px'}">
- <!-- video 容器 -->
- <div class="video-wrapper" id="videoRef" :style="{width: pageData.videoWidth + 'px', height: pageData.videoHeight + 'px'}"></div>
-
- <!-- 三menu键 -->
- <div id="foot-menu-wrap" :style="`height: ${pageData.footMenuHeight}px`">
- <div @click.stop="sendKey(187)"><van-icon name="wap-nav" size="24px"/></div>
- <div @click.stop="sendKey(3)"><van-icon name="wap-home-o" size="24px"/></div>
- <div @click.stop="sendKey(4)"><van-icon name="arrow-left" size="24px"/></div>
- </div>
- <!-- 悬浮按钮 -->
- <FloatBtn :width="pageData.width" :height="pageData.height" :latency="rtcNetwork.currentRoundTripTime" @onClick="levitatedSphereVisible = true"/>
- <!-- 左侧popup -->
- <LeftMenuPopup
- ref="leftMenuPopupRef"
- :engine="engine"
- :userCardId="activeCloud.userCardId"
- :levitatedSphereVisible.sync="levitatedSphereVisible"
- :latency="rtcNetwork.currentRoundTripTime"
- :groupList="groupList"
- :cloudList="cloudList"
- :mealTypeObj="mealTypeObj"
- :imgFun="imgFun"
- @funcHandle="funcHandle"
- @changeCloud="changeCloudHandle"
- @exit="exit"
- />
-
- <!-- 右侧popup -->
- <!-- <RightPopup ref="rightPopupRef" :engine="engine" :userCardId="activeCloud.userCardId" :levitatedSphereVisible.sync="levitatedSphereVisible" @shearplate="shearplate" @exit="exit"/> -->
- <!-- 输入并复制到粘贴板 -->
- <InputCopy ref="inputCopyRef" @openPasteboard="openPasteboard"/>
- <!-- 云机内部的粘贴板内容 -->
- <CloudPhoneClipboard ref="cloudPhoneClipboardRef"/>
- <!-- 超时无操作 -->
- <TimeoutNoOps ref="timeoutNoOpsRef" />
- <!-- 计时卡计时 | 计费规则 | 应用推荐 -->
- <TimeBalance ref="timeBalanceRef" :parametersData="parametersData" :userCardId="activeCloud.userCardId" :userCardType="parametersData.userCardType" @downline="$refs.rightPopupRef.downline()"/>
- </div>
- </template>
- <script>
- import meta from './config/meta.js';
- import request from './config/request.js';
- import logReport from './config/logReport.js';
- import publicMixin from './mixins/public.js';
- import * as uni from '../../static/static/js/uni.webview.1.5.2.js';
- import FloatBtn from './components/FloatBtn.vue';
- import RightPopup from './components/RightPopup.vue';
- import LeftMenuPopup from './components/LeftMenuPopup.vue';
- import InputCopy from './components/InputCopy.vue';
- import CloudPhoneClipboard from './components/CloudPhoneClipboard.vue';
- import TimeoutNoOps from './components/TimeoutNoOps.vue';
- import TimeBalance from './components/TimeBalance.vue';
- /**
- * @description: 判断当前页面运行环境
- * @return {Object} 返回当前页面运行环境
- */
- const isBrowserEnvironment = function() {
- // 判断是否在浏览器环境中
- const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined';
- // 判断是否在微信环境中
- const isWechat = /MicroMessenger/i.test(navigator.userAgent);
- // 判断是否在微信小程序的 web-view 中
- const isMiniProgramWebview = isWechat && /miniProgram/i.test(navigator.userAgent);
- // 判断是否是 iPhone 设备
- const isIPhone = /iPhone/i.test(navigator.userAgent);
- // 判断是否是顶级窗口(不是嵌套在 iframe 中)目前只有ios的浏览器中才会是顶级窗口
- const isTopWindow = window.parent === window.self;
- return {
- isBrowser, // 是否在浏览器环境中
- isWechat, // 是否在微信环境中
- isMiniProgramWebview, // 是否在微信小程序的 web-view 中
- isIPhone, // 是否是 iPhone 设备
- isTopWindow, // 是否是顶级窗口
- };
- }
- export default {
- auth: false,
- name: 'webRTC',
- layout: 'cloudPhone',
- components: {
- FloatBtn,
- RightPopup,
- InputCopy,
- CloudPhoneClipboard,
- TimeoutNoOps,
- TimeBalance,
- LeftMenuPopup,
- },
- head() {
- return {
- title: '云手机',
- meta: [ ...meta ],
- script: [
- {
- // ./ 路径指向nuxt.config.js同级目录的static文件夹
- src: './rtcEngine/config/js/SDK.min.js', // sdk 2.0文件
- type: 'text/javascript',
- async: false,
- defer: false,
- onload: '$_script_loadHandler()', // 加载成功回调created生命周期中定义的方法
- onerror: '$_script_errHandler()', // 加载失败回调created生命周期中定义的方法
- },
- // {
- // // ./ 路径指向nuxt.config.js同级目录的static文件夹
- // src: './static/js/uni.webview.1.5.2.js', // uniapp webview 1.5.2文件
- // type: 'text/javascript',
- // async: true,
- // defer: false,
- // onload: '$_script_uni_loadHandler()', // 加载成功回调created生命周期中定义的方法
- // }
- ]
- }
- },
- mixins: [publicMixin],
- data() {
- return {
- // 日志上报实例
- logReportObj: null,
- // SDK加载状态
- sdkLoadStatus: 'loading', // sdk 加载状态 [loading|success|error]
- // 页面数据
- pageData: {
- width: 0, // 页面宽度
- height: 0, // 页面高度
- footMenuHeight: 40, // 底部菜单高度
- videoWidth: 0, // 视频宽度
- videoHeight: 0, // 视频高度
- },
- // 是否支持webRTC
- isSupportRtc: !!(
- typeof RTCPeerConnection !== 'undefined' &&
- typeof RTCIceCandidate !== 'undefined' &&
- typeof RTCSessionDescription !== 'undefined'
- ),
- // url 问号后的参数
- parametersData: {
- /**
- * @description: 传递的参数
- * @param {String} record 数据id
- * @param {Number} userCardId 必传 云机的id
- * @param {String} mealType 云机套餐类型 eg: VIP、STARBALL...
- * @param {Number} sourceType 云机来源: 0:购买 1试用 2:免费激活码 3:免费活动抽奖 4:ar app注册 5:9.9元套餐年卡 ',
- * @param {Number} userCardType 必传 云机的类型 0 普通套餐 1、2、3:年卡、普通计时、自动续费普通计时
- * @param {Number} validTime 卡的有效期
- * @param {String} rm 卡所在的机房
- * @param {Number} isShowCountdown 是否显示倒计时 0:否 1:是
- * @param {Number} isShowRule 是否显示规则 0:否 1:是
- * @param {Number} timingStatus 卡的计时状态 0:停止计时 1:开始计时 2:待确认开始计时"
- * @param {String} isFirstConnect 是否是首次连接
- * @param {Number} authPhone 0自身购买的云手机 1获取得到的云手机
- * @param {String} username 用户名
- * @param {String} token 必传 token
- * @param {Number} isTips 必传 是否显示提示 0:否 1:是
- * @param {Number} isWeixin 必传 是否是微信小程序环境 0:否 1:是
- * @param {String} merchantSign 必传 商户标识
- */
- },
- // 当前使用的云机数据
- activeCloud: {},
- // 卡的连接信息
- connectData: {},
- // 云手机引擎 播放器实例
- engine: {},
- // webRtc网络分析数据
- rtcNetwork: {
- currentRoundTripTime: 0, // 当前往返时间(网络延迟)
- },
- doConnectDirectivesWs: null, // 云手机指令通道
- doConnectDirectivesIntervalerPing: null, // 业务通道定时标识 云手机指令通道心跳
- doConnectDirectivesRequestNum: 1, // 业务通道重连次数
- doConnectDirectivesRequestNumMax: 6, // 业务通道重连次数上限
- // 右侧popup显隐
- levitatedSphereVisible: false,
- }
- },
- // 页面初始化后触发
- async fetch() {
- // 获取页面传递参数
- this.parametersData = this.$route.query;
- // 获取用户所有云机列表
- await this.getCloudList();
- // 获取当前云机信息
- this.activeCloud = this.cloudList.find(item => item.userCardId === +this.parametersData.userCardId) ?? {};
- // 获取所有云机套餐信息
- await this.getMealIconInfo();
- // 获取云机分组
- await this.getCloudGroupId();
- if(this.sdkLoadStatus === 'success') {
- // 开始运行程序
- if(Object.keys(this.activeCloud).length) {
- this.start(this.activeCloud);
- }else {
- this.$toast('云手机不存在');
- }
- }else {
- console.log('SDK加载失败');
- this.$toast('加载失败');
- }
- },
- computed: {
- // 是否为微信浏览器环境
- isWeChatBrowser() {
- return this.$userAgent.isWx;
- },
- },
- created() {
- // 设置html标签的背景为黑色
- document.body.style.background = '#000';
- // 定义全局变量 用于监听sdk加载状态
- window.$_script_loadHandler = async ()=> {
- console.log('$_script_loadHandler: SDK加载成功');
- this.sdkLoadStatus = 'success';
- };
- window.$_script_errHandler = ()=> {
- console.log('SDK加载失败');
- this.sdkLoadStatus = 'error';
- }
- },
- mounted() {
- // 获取窗口尺寸
- this.getInitSize();
- window.onresize = () => {
- console.log('窗口尺寸变化');
- this.getInitSize()
- };
- // 初始化页面监听事件
- this.initListener();
- },
- // 页面销毁前触发
- beforeDestroy() {
- // 销毁页面监听事件
- this.destroyListener();
- },
- methods: {
- // 初始化页面监听事件
- initListener() {
- // 禁止双击缩放
- document.addEventListener('dblclick', this.preventDefault);
- // 添加监听 页面显示或隐藏 事件
- document.addEventListener('visibilitychange', this.visibilitychanged);
- },
- // 销毁页面监听事件
- destroyListener() {
- // 允许双击缩放
- document.removeEventListener('dblclick', this.preventDefault);
- // 移除监听 页面显示或隐藏 事件
- document.removeEventListener('visibilitychange', this.visibilitychanged);
- },
- // 阻止默认事件
- preventDefault(e) {
- e.preventDefault();
- },
- // 监听 页面显示或隐藏 执行的函数
- visibilitychanged() {
- // 获取当前环境
- const env = isBrowserEnvironment();
- // 获取当前页面的可见性状态
- const visibilityState = document.visibilityState;
- if (visibilityState === 'visible') {
- // 页面显示时的逻辑
- // 网页重载
- if (env.isBrowser && env.isTopWindow && env.isIPhone) {
- location.reload();
- }
- } else if (visibilityState === 'hidden') {
- // 页面隐藏时的逻辑
- // video.pause();
- } else if (visibilityState === 'prerender') {
- // 页面预渲染时的逻辑
- console.log('页面处于预渲染状态');
- } else if (visibilityState === 'unloaded') {
- // 页面即将卸载时的逻辑 移除监听
- document.removeEventListener('visibilitychange', this.visibilitychanged);
- }
- },
- // 获取初始化尺寸
- getInitSize() {
- // 获取窗口尺寸
- this.pageData.height = window.innerHeight;
- this.pageData.width = window.innerWidth;
- // 计算视频尺寸 webRTC需要做成16:9的画面
- let videoWidth = this.pageData.width;
- let videoHeight = this.pageData.height - this.pageData.footMenuHeight;
- // 计算当前视口的宽高比
- const currentRatio = videoWidth / videoHeight;
- console.log(`当前视口的宽高比: ${currentRatio}`);
- // 9:16 的目标比例
- const targetRatio = 9 / 16;
- // 判断当前视口的宽高比与目标比例的关系
- if (currentRatio > targetRatio) {
- // 当前视口的宽高比大于目标比例,说明宽度“过宽”,需要以高度为基准
- console.log("当前视口宽度过宽,应以高度为基准调整宽度");
- this.pageData.videoWidth = videoHeight * targetRatio;
- this.pageData.videoHeight = videoHeight;
- console.log(`1目标: 宽${this.pageData.videoWidth},高${this.pageData.videoHeight}`);
- } else {
- // 当前视口的宽高比小于目标比例,说明高度“过高”,需要以宽度为基准
- console.log("当前视口高度过高,应以宽度为基准调整高度");
- this.pageData.videoWidth = videoWidth;
- this.pageData.videoHeight = videoWidth / targetRatio;
- console.log(`2目标: 宽${this.pageData.videoWidth},高${this.pageData.videoHeight}`);
- }
- },
- // 获取卡的信息
- async getConnectData({isWeixin, merchantSign}) {
- let userCardId = this.activeCloud.userCardId;
- try {
- // 设置上报参数
- this.logReportObj.setParams({userCardId});
- let isWx = this.$userAgent.isWx;
- let clientType = (+isWeixin || isWx) ? 'wx' : undefined;
- // 设置上报参数
- this.logReportObj.setParams({userCardId});
- clientType && this.logReportObj.setParams({clientType});
- const res = await this.$axios.$post('/resources/user/cloud/connect', { userCardId }, {
- headers: {
- merchantSign,
- },
- });
- if (!res.success) {
- // 设置日志 推流状态为失败,和链接状态
- this.logReportObj.setParams({plugFowStatus: 2, linkWay: this.logReportObj.RESPONSE_CODE[res.status] || 0, linkEndTime: this.logReportObj.formatDate(new Date())});
- // 日志上报
- this.logReportObj.collectLog(
- `接口获取数据失败:
- url: /api/resources/user/cloud/connect
- method: post
- 参数: ${JSON.stringify({ userCardId })}
- 响应: ${JSON.stringify(res)}`
- );
- // res.status状态码枚举值 0: 正常
- const statusEnum = {
- // 5200:RBD资源挂载中
- 5200: '网络异常,请稍后重试',
- // 入使用排队9.9,年卡
- 5220: '云手机正在一键修复中',
- 5203: '正在排队中,请稍等',
- // 9.9年卡连接异常,重新进入排队
- 5204: '云机异常,正在为你重新分配云机',
- 5228: '卡的网络状态为差',
- 5229: '接口返回链接信息缺失',
- };
- // NOTE 这里可设置重连机制, 重连次数上限6次, 每次重连间隔3秒, 暂不做重连
- // 提示错误信息
- this.$toast(statusEnum[res.status] || '网络异常,请稍后重试');
- return Promise.reject(new Error(statusEnum[res.status] || '网络异常,请稍后重试'));
- }
- return res.data;
- }catch (error) {
- // 设置上报参数
- this.logReportObj.setParams({linkWay: 4, plugFowStatus: 2, linkEndTime: this.logReportObj.formatDate(new Date())});
- // 日志上报
- this.logReportObj.collectLog(
- `接口获取数据失败:
- url: /api/resources/user/cloud/connect
- method: post
- 参数: ${JSON.stringify({ userCardId })}
- 响应: ${JSON.stringify(error)}`
- );
- console.log('error connectAxios:', error);
- return Promise.reject(error);
- }
- },
- // 判断卡的连接方式
- async judgeConnectType(cardData) {
- try {
- // 设置上报参数
- this.logReportObj.setParams({videoType: cardData.videoCode.toLowerCase(), resourceId: cardData.resourceId});
- // 不支持webRTC跳转到指定的页面进行拉流
- if (!cardData.isWebrtc) {
- // 关闭日志上报
- this.logReportObj?.destroy();
- // 跳转指定页面
- location.replace(`${location.origin}/h5/webRtcYJ/WXtrialInterface.html${location.search}`)
- return Promise.reject();
- }
- // 是否支持webRTC
- if (!this.isSupportRtc) {
- // 设置日志 推流状态为失败
- this.logReportObj.setParams({plugFowStatus: 2, linkEndTime: this.logReportObj.formatDate(new Date())});
- // 日志上报
- this.logReportObj.collectLog(`${+this.parametersData.isWeixin ? '微信小程序' : ''}当前版本暂不支持使用`);
- this.$dialog.alert({
- title: '提示',
- message: '当前环境不支持使用,可下载谷歌浏览器或客户端进行使用',
- confirmButtonText: '确定',
- confirmButtonColor: '#3cc51f',
- callback: (_, done) => {
- done();
- this.exit();
- }
- })
- return Promise.reject(new Error('当前浏览器不支持webRTC'));
- }
- // webRtc连接,需获取连接中转地址
- if (cardData.webrtcNetworkAnalysis) {
- // 如果有网络分析的请求地址, 则请求,否则失败
- const { data: webrtcNetworkAnalysisReq }= await request.get(cardData.webrtcNetworkAnalysis); // 这个接口单独使用axios请求, 因为返回的数据跟封装的数据结构不一统一,是其他平台的接口,所以单独请求
- if (webrtcNetworkAnalysisReq !== null && webrtcNetworkAnalysisReq.success && webrtcNetworkAnalysisReq.data) {
- // 保存获取的连接地址到connect的请求的响应中, 方便后面使用
- cardData.webrtcNetwork = webrtcNetworkAnalysisReq.data;
- // 设置上报参数
- this.logReportObj.setParams({transferServerIp: webrtcNetworkAnalysisReq.data});
- return cardData;
- }else{
- // 设置上报参数
- this.logReportObj.setParams({linkWay: 4, plugFowStatus: 2, linkEndTime: this.logReportObj.formatDate(new Date())});
- // 日志上报
- this.logReportObj.collectLog(
- `webRtc连接,获取中转地址失败:
- url: ${cardData.webrtcNetworkAnalysis}
- method: get
- 参数: 无
- 响应: ${JSON.stringify(webrtcNetworkAnalysisReq)}`
- );
- // 弹窗并退出
- this.$dialog.alert({
- title: '提示',
- message: '访问失败,请稍后重试',
- confirmButtonText: '确定',
- confirmButtonColor: '#3cc51f',
- beforeClose: (action, done) => {
- done()
- this.exit();
- }
- })
-
- return Promise.reject(new Error('网络分析请求失败'));
- }
- }else{
- // 设置上报参数
- this.logReportObj.setParams({linkWay: 4, plugFowStatus: 2});
- // 日志上报
- this.logReportObj.collectLog(
- `webRtc连接,获取请求中转地址为空:
- url: /api/resources/user/cloud/connect
- method: post
- 参数: ${JSON.stringify({ userCardId: this.activeCloud.userCardId })}
- 响应: ${JSON.stringify(res)}`
- );
- return Promise.reject(new Error('网络分析请求地址不存在'));
- }
- } catch (error) {
- console.log('判断卡的连接方式', error);
- return Promise.reject(error);
- }
- },
- // 初始化webRTC及相关配置
- initWebRtc() {
- try {
- // 获取挂载的容器元素
- const videoRef = document.getElementById("videoRef");
- // 解构connectData中的数据
- const { sn: topic, cardToken: authToken, localIp, internetHttps, internetHttp, webrtcNetwork, webrtcTransferCmnet, webrtcTransferTelecom, webrtcTransferUnicom, videoCode } = this.connectData;
- // 判断长连接的协议方式
- const isWss = location.protocol === 'https:';
-
- // 生成连接地址
- const url = `${isWss ? 'wss://' : 'ws://'}${isWss ? internetHttps : internetHttp}/nats`;
- let quality = localStorage.getItem('definitionValue') ?? '自动';
- // 统一使用三网解析地址
- const ICEServerUrl = [
- { "CMNET": webrtcNetwork }, // 移动
- { 'CHINANET-GD': webrtcNetwork }, // 电信
- { 'UNICOM-GD': webrtcNetwork }, // 联通
- ];
- // 配置连接参数
- const connection = {
- mount: videoRef,
- displaySize: { // 视频在页面显示的尺寸 必填
- width: this.pageData.videoWidth,
- height: this.pageData.videoHeight,
- },
- topic, // SN号 必填
- url, //信令服务地址 必填
- ICEServerUrl,
- forwardServerAddress: '', // 转发服务器地址
- ip: localIp, // 实例ip
- controlToken: '', // 控制token
- width: 720, // 推流视频宽度 必填
- height: 1080, // 推流视频高度 必填
- cardWidth: 0, // 云机系统分辨率 宽 必填
- cardHeight: 0, // 云机系统分辨率 高 必填
- cardDensity: 0, // 云机系统显示 密度 必填
- authToken, //拉流鉴权 token 必填
- quality, // 画质(码率) 超清 | 高清 | 标清 | 流畅
- fps: 30, //必填
- videoCodec: videoCode, // 视频编码格式 必填
- videoCodecMethod: true, // 硬编true | 软编false
- isMuted: false, // 是否静音
- isAllowedOpenCamera: true, // 是否允许打开摄像头
- sendFollow: true, // 是否允许主控转发文本到实例
- callback: (event)=> {console.log('webRTC回调', event);}
- };
- // 设置日志参数 推流质量
- this.logReportObj.setParams({imageQuality: quality});
- // 获取SDK类
- const MediaSdk = window.rtc_sdk.MediaSdk;
- // 初始化 SDK
- this.engine = new MediaSdk();
- console.log('RtcEngineConfig==', connection)
- // 初始化 SDK 并传入连接参数
- this.engine.RtcEngine(connection);
- // 监听回调方法
- this.eventCallbackFunction();
- } catch (error) {
- console.log('webRTC初始化失败', error);
- }
- },
- // webRTC状态回调监听回调方法
- eventCallbackFunction() {
- const engine = this.engine;
- // 连接成功
- engine.on('CONNECT_SUCCESS', (r) => {
- console.log("webrtc连接成功====★★★★★", r);
- if (r.code === 1005) { // 1005: 拉流鉴权成功
- // 清除loading
- this.$toast.clear();
- // 设置日志 推流状态为成功
- let now = new Date();
- this.logReportObj.setParams({plugFowStatus: 1, linkWay: 1, timeConsuming: now.getTime() - this.logReportObj.timeStartTime, linkEndTime: this.logReportObj.formatDate(now)});
- // 日志上报
- this.logReportObj.collectLog(`拉流成功`);
- // 初始化业务指令通道
- this.initControlChannel();
-
- // 查询超过指定触碰时间是否提示关闭弹窗
- this.$refs.timeoutNoOpsRef.pushflowPopup();
- // 获取云机剩余时长
- this.$refs.timeBalanceRef.getResidueTime();
- }
- });
- // 连接关闭
- engine.on('CONNECT_CLOSE', (r) => {
- console.log("webrtc关闭====★★★★★", r);
- });
-
- // 网络连接统计信息监听
- engine.on('NETWORK_STATS', (r) => {
- console.log("webrtc网络连接统计信息监听====★★★★★", r);
- this.rtcNetwork = r;
- });
- // 连接异常
- engine.on('CONNECT_ERROR', (r) => {
- console.log("webrtc异常状态====★★★★★", r);
- // 异常状态枚举值
- const statusEnum = {
- 10011: '连接超时',
- 10012: '连接失败',
- 1002: 'rtc连接失败',
- 1003: 'rtc异常断开',
- 1004: 'rtc连接失败',
- 1006: '鉴权失败',
- };
- // 设置日志 推流状态为失败
- this.logReportObj.setParams({plugFowStatus: 2, linkWay: 0, linkEndTime: this.logReportObj.formatDate(new Date())});
- // 日志上报
- this.logReportObj.collectLog( `${statusEnum[r.code] || '连接异常'}: 消息: ${JSON.stringify(r)}` );
- this.$dialog.alert({
- title: '提示',
- message: '链接超时',
- confirmButtonText: '确定',
- confirmButtonColor: '#3cc51f',
- beforeClose: (action, done) => {
- done()
- this.exit()
- }
- })
- });
- // 显示区域大小发生改变 响应
- engine.on('RECEIVE_RESOLUTION', (r) => {
- // 分辨率大小发生改变,响应
- console.log("分辨率大小发生改变,响应 => RECEIVE_RESOLUTION", r);
- });
- },
- // 业务指令通道初始化
- initControlChannel() {
- try {
- // 初始化业务指令通道
- let { internetHttps, internetHttp, localIp, cardToken } = this.connectData;
- const isWss = location.protocol === 'https:';
- let cUrl = `${isWss ? 'wss' : 'ws'}://${isWss ? internetHttps : internetHttp}/businessChannel?cardIp=${localIp}&token=${cardToken}&type=directives`;
- this.doConnectDirectivesWs = new WebSocket(cUrl);
- this.doConnectDirectivesWs.binaryType = 'arraybuffer';
- // 清除定时器
- if (this.doConnectDirectivesIntervalerPing) {
- clearInterval(this.doConnectDirectivesIntervalerPing);
- }
- // 链接成功
- this.doConnectDirectivesWs.onopen = (e) => {
- // 日志上报
- this.logReportObj.collectLog( `消息: 业务通道连接成功` );
- // 重置重连次数
- this.doConnectDirectivesRequestNum = 1;
- // 设置定时器 每3秒发送一次心跳
- this.doConnectDirectivesIntervalerPing = setInterval(() => {
- if (this.doConnectDirectivesWs.readyState === 1) {
- this.sendWsCommand({ type: 'ping' });
- } else {
- clearInterval(this.doConnectDirectivesIntervalerPing);
- }
- }, 3000);
- // 输入法: 本地输入法 1 云机输入法 2
- this.sendWsCommand({ type: 'InputMethod', data: { type: 2 } });
- }
- // 接受到的消息
- this.doConnectDirectivesWs.onmessage = res => {
- const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
- switch (result.type) {
- case 'reProduceText':
- this.$native.clipboard.writeText(result.data.text);
- break
- case 'downAdnInstallRep':
- this.$toast(result.data.msg)
- break
- // 接受到这个消息就自动退出云机
- case 'exitPhone':
- this.exit();
- break
- }
- }
- // 链接报错的回调
- this.doConnectDirectivesWs.onerror = res => {
- // 设置日志
- this.logReportObj.setParams({plugFowStatus: 1, linkWay: 0, linkEndTime: this.logReportObj.formatDate(new Date())});
- // 日志上报
- this.logReportObj.collectLog(
- `业务指令通道报错:
- url: ${res.target.url}
- type: ${res.type}
- 消息: ${JSON.stringify(res)}`
- );
- if (this.doConnectDirectivesRequestNum > this.doConnectDirectivesRequestNumMax) {
- return this.exit();
- }else{
- // 重连次数加1
- ++this.doConnectDirectivesRequestNum;
- // 重连
- this.initControlChannel();
- }
- }
- } catch (error) {
- console.log('initControlChannel error', error);
- }
- },
- /**
- * popup 功能按钮点击事件
- * @param {Object} val 传递的参数
- * @description 传递的参数格式: { code: 'hideKey', label: '隐藏虚拟键', icon: 'icon-hide' }
- */
- funcHandle({code}) {
- console.log('funcHandle code:', code);
- switch (code) {
- case 'hideKey':
- // 隐藏虚拟按键
- document.getElementById('foot-menu-wrap').style.display = 'none';
- this.pageData.footMenuHeight = 0; // 设置底部菜单高度为0
- this.getInitSize(); // 重新计算视频尺寸
- this.changeVideoStyle(); // 重新设置视频尺寸
- break;
- case 'showKey':
- // 显示虚拟按键
- document.getElementById('foot-menu-wrap').style.display = 'flex';
- this.pageData.footMenuHeight = 40; // 设置底部菜单高度为40px
- this.getInitSize(); // 重新计算视频尺寸
- this.changeVideoStyle(); // 重新设置视频尺寸
- break
- case 'shearplate':
- // 粘贴板按钮
- this.shearplate();
- break;
- case 'shake':
- // 摇一摇
- if(this.sendWsCommand({ type: 'shakeit' })) {
- this.$toast('操作成功,如有异常请联系客服');
- } else {
- this.$toast('操作失败,请联系客服');
- }
- break;
- case 'blow':
- // 吹一吹
- if(this.sendWsCommand({ type: 'blow' })) {
- this.$toast('操作成功,如有异常请联系客服');
- } else {
- this.$toast('操作失败,请联系客服');
- }
- break;
- }
- },
- // 切换云机
- async changeCloudHandle(cloudData) {
- try {
- // 保存当前云机数据
- this.activeCloud = cloudData;
- // 重置相关数据
- // 关闭日志上报
- this.logReportObj?.destroy();
- // 关闭webRTC
- this.engine?.disconnect && this.engine?.disconnect();
- // 关闭业务指令通道
- this.doConnectDirectivesWs?.close();
- // 重置重连次数
- this.doConnectDirectivesRequestNum = 1;
- // 重置 end
- if(this.sdkLoadStatus === 'success') {
- // 开始运行程序
- this.start(cloudData);
- }else {
- console.log('SDK加载失败');
- }
- } catch (error) {
- console.log('changeCloud error', error);
- }
- },
- // 重新设置视频尺寸
- changeVideoStyle() {
- this.$nextTick(() => {
- // 获取video元素
- const video = document.getElementById("videoRef").getElementsByTagName('video')[0];
- video.style.width = this.pageData.videoWidth + 'px';
- video.style.height = this.pageData.videoHeight + 'px';
- });
- },
- // 三键
- sendKey (keyCode) {
- try {
- this.engine?.sendKey(keyCode);
- // 重置超时无操作定时器
- this.$refs.timeoutNoOpsRef?.noOperationSetTimeout();
- } catch (error) {
- console.log('sendKey error', error);
- }
- },
- // popup粘贴板按钮点击事件
- shearplate(){
- // 调用InputCopy组件的pasteText方法读取粘贴板
- this.$refs.inputCopyRef.pasteText();
- },
- // 打开去取云机内的粘贴板内容
- openPasteboard(text){
- this.$refs.cloudPhoneClipboardRef.init(text);
- },
- // 退出功能
- exit() {
- // 关闭日志上报
- this.logReportObj?.destroy();
- // 关闭webRTC
- this.engine?.disconnect && this.engine?.disconnect();
- // 关闭业务指令通道
- this.doConnectDirectivesWs?.close();
- // 获取当前环境
- const env = isBrowserEnvironment();
- // ios环境下 直接使用浏览器api返回上一页
- if(env.isBrowser && !env.isMiniProgramWebview && env.isIPhone && env.isTopWindow) {
- // 上面的方法执行未生效就走这里
- if (window.history.length > 1) {
- return window.history.back();
- }
- }
- uni && uni.reLaunch({ url: '/pages/index/index' });
- },
- // 发送业务指令通道消息
- sendWsCommand(data) {
- if(this.doConnectDirectivesWs.readyState === 1) {
- this.doConnectDirectivesWs.send(JSON.stringify(data));
- return true;
- }
- return false;
- },
- // 初始化日志上报实例
- initLogReport() {
- // 初始化日志上报实例
- this.logReportObj = new logReport({ request: this.$axios });
- uni.getEnv((res) => {
- // 设置上报参数
- this.logReportObj.setParams({ clientType: Object.keys(res)[0] });
- })
- },
- // 开始运行程序
- async start(activeCloud) {
- console.log('start activeCloud', activeCloud);
- try {
- this.$toast.loading({
- className: 'rtc-loading',
- duration: 0, // 持续展示 toast
- message: `设备(${activeCloud.userCardId})正在获取...`,
- });
- // 初始化日志上报
- this.initLogReport();
- // 调用接口获取卡连接数据
- const cardData = await this.getConnectData(this.parametersData);
-
- // 判断卡的连接方式
- const connectData = await this.judgeConnectType(cardData);
- // 保存卡连接信息
- this.connectData = connectData;
- this.initWebRtc();
- } catch (error) {
- console.log('start error', error);
- }
- },
- }
- }
- </script>
- <style lang="scss" scoped>
- html{
- background-color: #000;
- }
- // 动态生成 从 0 到 100px 的样式
- @for $i from 0 through 100 {
- .mb-#{$i} {
- margin-bottom: #{$i}px;
- }
- .mt-#{$i} {
- margin-top: #{$i}px;
- }
- .ml-#{$i} {
- margin-left: #{$i}px;
- }
- .mr-#{$i} {
- margin-right: #{$i}px;
- }
- }
- $-radeus-12: 12px;
- $-bg-yellow: rgb(255, 253, 241);
- .rtc-page{
- position: relative;
- font-size: 14px;
-
- .video-wrapper{
- position: relative;
- }
- }
- .cover-bg{
- background-color: #000;
- }
- #foot-menu-wrap{
- border-width: 0px;
- position: absolute;
- left: 0px;
- bottom: 0px;
- width: 100%;
- // height: 40px; // 三大功能键高度,通过vue动态添加
- background: inherit;
- background-color: rgba(0, 12, 23, 1);
- border: none;
- border-radius: 0px;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- box-shadow: none;
- z-index: 1;
- display: flex;
- justify-content: space-evenly;
- align-items: center;
- color: #fff;
- }
- //
- .rtc-page >>> .van-toast.rtc-loading{
- white-space: nowrap;
- }
- </style>
|