Browse Source

分解webrtc页面功能移植

t_finder 3 weeks ago
parent
commit
38da291e59

+ 1 - 1
assets/style/main.scss

@@ -61,7 +61,7 @@ body {
 }
 
 .van-overlay {
-	background-color: rgba(0, 0, 0, .9) !important;
+	background-color: rgba(0, 0, 0, .9);
 }
 
 [v-cloak] {

+ 23 - 15
pages/rtcEngine/components/CloudPhoneClipboard.vue

@@ -3,13 +3,13 @@
     <template v-if="pasteVersionList.length">
       <div class="tc paste-version-title pre">
         <span class="pab">粘贴版</span>
-        <span class="pab paste-version-clear" @click="deletePasteVersion"> 清空 </span>
+        <span class="pab paste-version-clear" @click="deletePasteVersion()"> 清空 </span>
       </div>
       <div class="paste-version-list">
           <div v-for="(item,index) in pasteVersionList" :key="index"
               :class="[{'mb-1': index!==pasteVersionList.length - 1}]">
             <van-swipe-cell>
-              <div :class="`copy-value-${index}, ellipsis`" @click="copyPasteVersiontext">
+              <div :class="`copy-value-${index}, ellipsis`" @click="copyPasteVersiontext(item.content)">
                 {{item.content}}
               </div>
               <template #right>
@@ -35,6 +35,12 @@
 import Qs from 'qs';
 
 export default {
+  props: {
+    doConnectDirectivesWs: {
+      type: Function,
+      default: () => {}
+    }
+  },
   name: 'CloudPhoneClipboard',
   data() {
     return {
@@ -127,19 +133,14 @@ export default {
       }
     },
     // 复制粘贴某条数据
-    copyPasteVersiontext(e) {
-      clickCopyText(e, (event) => {
-        // TODO: 复制成功后发送消息给云机
-        // this.doConnectDirectivesWs.send(JSON.stringify({
-        //   type: 'cutting',
-        //   data: {
-        //     str: event.text,
-        //   },
-        // }))
-        // this.$toast('复制成功')
-      }, () => {
-        Toast('复制失败')
-      })
+    async copyPasteVersiontext(text) {
+      try {
+        await this.$native.clipboard.writeText(text);
+        this.$toast('复制成功')
+      }catch (error) {
+        console.log('error', error);
+        this.$toast('复制失败')
+      }
     },
   }
 }
@@ -261,7 +262,14 @@ export default {
     .paste-version-empty > div > div {
       margin-top: 9px;
       font-size: 15px;
+      color: #fff;
     }
   }
+
+  .flex-center-all{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
 }
 </style>

+ 30 - 6
pages/rtcEngine/components/RightPopup.vue

@@ -45,6 +45,10 @@ export default {
       type: Object,
       default: () => ({})
     },
+    userCardId: {
+      type: String,
+      default: ''
+    },
     // popup是否显示
     levitatedSphereVisible: {
       type: Boolean,
@@ -162,8 +166,7 @@ export default {
     },
     // 音量控制 24: 音量+ 25: 音量-
     volumeControl(value) {
-      this.engine.sendKey && this.engine.sendKey(value)
-      // this.$refs.rtcMediaPlayer && (this.$refs.rtcMediaPlayer.muted = false)
+      this.engine.sendKey && this.engine.sendKey(value);
     },
     // 退出相关按钮操作
     exitFun(key) {
@@ -179,7 +182,7 @@ export default {
               beforeClose: (action, done) => {
                   if (action === 'cancel') done()
                   if (action === 'confirm') {
-                      // this.downline(done)
+                      this.downline(done)
                   }
               }
           })
@@ -193,12 +196,33 @@ export default {
         case 'signout':
           // 关闭popup
           this.$emit('update:levitatedSphereVisible', false);
+          this.$emit('exit');
           break;
       }
     },
-    close() {
-      
-    }
+    // 退出并下机
+    async downline(fun = () => {}) {
+      try {
+        const res = await this.$axios.get('/resources/yearMember/downline?userCardId=${this.userCardId}`');
+        if(!res.status){
+          fun(true);
+          // 判断是否是顶级窗口(不是嵌套在 iframe 中)目前只有ios的浏览器中才会是顶级窗口
+          const isTopWindow = window.parent === window.self;
+          if(isTopWindow){
+            // 通信给h5项目告知是退出并下机
+            parent.postMessage({ type: 'exit' }, '*');
+            uni.postMessage({ data: { type: 'exit' }});
+          }
+
+          this.$emit('exit');
+          return
+        }
+        fun(false)
+        this.$toast(res.msg);
+      } catch (error) {
+        console.log(error);
+      }
+    },
   }
 }
 </script>

+ 186 - 104
pages/rtcEngine/rtc.vue

@@ -5,22 +5,22 @@
     
     <!-- 三menu键 -->
     <div id="foot-menu-wrap" :style="`height: ${footMenuWrapHeight}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 @click.stop="()=>{sendKey(187); noOperationSetTimeout}"><van-icon name="wap-nav" size="24px"/></div>
+      <div @click.stop="()=>{sendKey(3); noOperationSetTimeout}"><van-icon name="wap-home-o" size="24px"/></div>
+      <div @click.stop="()=>{sendKey(4); noOperationSetTimeout}"><van-icon name="arrow-left" size="24px"/></div>
     </div>
 
     <!-- 悬浮按钮 -->
     <FloatBtn :width="pageData.width" :height="pageData.height" @onClick="levitatedSphereVisible = true"/>
 
     <!-- 右侧popup -->
-    <RightPopup :engine="engine" :levitatedSphereVisible.sync="levitatedSphereVisible" @shearplate="shearplate"/>
+    <RightPopup :engine="engine" :userCardId="this.parametersData.userCardId" :levitatedSphereVisible.sync="levitatedSphereVisible" @shearplate="shearplate" @exit="exit"/>
 
     <!-- 输入并复制到粘贴板 -->
     <InputCopy ref="inputCopyRef" @openPasteboard="openPasteboard"/>
 
     <!-- 云机内部的粘贴板内容 -->
-    <CloudPhoneClipboard ref="cloudPhoneClipboardRef"/>
+    <CloudPhoneClipboard ref="cloudPhoneClipboardRef" :doConnectDirectivesWs="doConnectDirectivesWs"/>
   </div>
 </template>
 
@@ -119,6 +119,11 @@ export default {
       connectData: {},
       // 云手机引擎 播放器实例
       engine: null,
+      doConnectDirectivesWs: null, // 云手机指令通道
+      doConnectDirectivesIntervalerPing: null, // 业务通道定时标识 云手机指令通道心跳
+      doConnectDirectivesRequestNum: 1, // 业务通道重连次数
+      doConnectDirectivesRequestNumMax: 6, // 业务通道重连次数上限
+
       // 右侧popup显隐
       levitatedSphereVisible: false,
     }
@@ -131,14 +136,6 @@ export default {
     // http://192.168.211.37:3000/h5/rtcEngine/rtc/?record=902481&userCardId=902481&mealType=sq&sourceType=0&userCardType=0&validTime=10334&rm=tencent-ap-tianjin-1&authPhone=none&username=0EsH01666175530SZX&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyYW5kb20iOiI0MTg5MyIsImNsaWVudCI6IjciLCJ1c2VyVHlwZSI6IjIiLCJtZXJjaGFudFNpZ24iOiJTWlgiLCJleHAiOjE3NDczODQ2MTgsInVzZXJuYW1lIjoiMEVzSDAxNjY2MTc1NTMwU1pYIn0.fTXawDu4SyEj4mIGr61ZXt0RSXzt3JztPq8-rFe-Zes&isTips=1&isWeixin=0&merchantSign=SZX
     // 获取页面传递参数
     this.parametersData = this.$route.query;
-    // 调用接口获取卡连接数据
-    const cardData = await this.getConnectData(this.parametersData);
-    
-    // 判断卡的连接方式
-    const connectData = await this.judgeConnectType(cardData);
-
-    // 保存卡连接信息
-    this.connectData = connectData;
   },
   computed: {
     // 是否为微信浏览器环境
@@ -161,9 +158,20 @@ export default {
     document.body.style.background = '#000';
 
     // 定义全局变量 用于监听sdk加载状态
-    window.$_script_loadHandler = ()=> {
-      console.log('SDK加载成功');
+    window.$_script_loadHandler = async ()=> {
+      console.log('$_script_loadHandler: SDK加载成功');
       this.sdkLoadStatus = 'success';
+
+      // 调用接口获取卡连接数据
+      const cardData = await this.getConnectData(this.parametersData);
+      
+      // 判断卡的连接方式
+      const connectData = await this.judgeConnectType(cardData);
+
+      // 保存卡连接信息
+      this.connectData = connectData;
+
+      this.initWebRtc();
     };
     window.$_script_errHandler = ()=> {
       console.log('SDK加载失败');
@@ -179,22 +187,6 @@ export default {
     };
     // 初始化页面监听事件
     this.initListener();
-
-    // 定时器 监听sdk加载状态, 加载成功和失败则停止定时器, 加载中则继续监听
-    let timer = setInterval(() => {
-      console.log('SDK加载状态:', this.sdkLoadStatus);
-      if (this.sdkLoadStatus === 'success' || window?.rtc_sdk?.MediaSdk) {
-        this.sdkLoadStatus = 'success';
-        console.log('SDK加载成功');
-        clearInterval(timer);
-        // 初始化webRTC
-        this.initWebRtc();
-      }
-      if (this.sdkLoadStatus === 'error') {
-        console.log('SDK加载失败');
-        clearInterval(timer);
-      }
-    })
   },
   // 页面销毁前触发
   beforeDestroy() {
@@ -283,7 +275,7 @@ export default {
     // 获取卡的信息
     async getConnectData(params) {
       try {
-        const res = await this.$axios.$post('/resources/user/cloud/connect', { userCardId: params. userCardId}, {
+        const res = await this.$axios.$post('/resources/user/cloud/connect', { userCardId: params.userCardId}, {
           headers: {
             merchantSign: params.merchantSign,
           },
@@ -330,8 +322,9 @@ export default {
             message: '当前环境不支持使用,可下载谷歌浏览器或客户端进行使用',
             confirmButtonText: '确定',
             confirmButtonColor: '#3cc51f',
-            callback: () => {
-              // TODO 关闭页面
+            callback: (_, done) => {
+              done();
+              this.exit();
             }
           })
           return Promise.reject(new Error('当前浏览器不支持webRTC'));
@@ -361,71 +354,67 @@ export default {
     },
     // 初始化webRTC及相关配置
     initWebRtc() {
-      // 检查连接卡信息是否存在,不存在则循环等待,直到存在为止
-      if (!Object.keys(this.connectData).length) {
-        setTimeout(() => {
-          this.initWebRtc();
-        }, 50);
-        return;
+      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`;
+
+        // 统一使用三网解析地址
+        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)=> {}
+        };
+
+        // 获取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);
       }
-
-      // 获取挂载的容器元素
-      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`;
-
-      // 统一使用三网解析地址
-      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)=> {}
-      };
-
-      // 获取SDK类
-      const MediaSdk = window.rtc_sdk.MediaSdk;
-      // 初始化 SDK
-      this.engine = new MediaSdk();
-      console.log('RtcEngineConfig==', connection)
-      // 初始化 SDK 并传入连接参数
-      this.engine.RtcEngine(connection);
-
-      // 监听回调方法
-      this.eventCallbackFunction();
     },
     // webRTC状态回调监听回调方法
     eventCallbackFunction() {
@@ -453,6 +442,70 @@ export default {
         console.log("webrtc异常状态====★★★★★", 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.doConnectDirectivesIntervalerPing = setInterval(() => {
+            if (this.doConnectDirectivesWs.readyState === 1) {
+              this.doConnectDirectivesWs.send(JSON.stringify({ type: 'ping' }));
+            } else {
+              clearInterval(this.doConnectDirectivesIntervalerPing);
+            }
+          }, 3000);
+          this.doConnectDirectivesWs.send(JSON.stringify({ type: 'getVsStatus' }))
+          this.doConnectDirectivesWs.send(JSON.stringify({ type: 'bitRate', data: { bitRate: 1243000 } }))
+          // // 设置日志参数 推流质量
+          // logReportObj.setParams({ imageQuality: 1243000 });
+
+          this.doConnectDirectivesWs.send(JSON.stringify({ type: 'InputMethod', data: { type: 2 } }))
+          this.doConnectDirectivesWs.send(JSON.stringify({ type: 'getPhoneSize' }))
+        }
+
+        // 接受到的消息
+        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(text);
+              break
+            case 'downAdnInstallRep':
+              this.$toast(result.data.msg)
+              break
+            // 接受到这个消息就自动退出云机
+            case 'exitPhone':
+              this.exit();
+              break
+          }
+        }
+
+        // 链接报错的回调
+        this.doConnectDirectivesWs.onerror = res => {
+          if (this.doConnectDirectivesRequestNum > this.doConnectDirectivesRequestNumMax) {
+            return this.exit();
+          }else{
+            // 重连次数加1
+            ++this.doConnectDirectivesRequestNum;
+            // 重连
+            this.initControlChannel();
+          }
+        }
+      } catch (error) {
+        console.log('initControlChannel error', error);
+      }
+    },
     // 三键
     sendKey (keyCode) {
       try {
@@ -470,7 +523,40 @@ export default {
     // 打开去取云机内的粘贴板内容
     openPasteboard(text){
       this.$refs.cloudPhoneClipboardRef.init(text);
-    }
+    },
+    // 超过指定触碰时间是否提示关闭链接
+    async pushflowPopup() {
+      try {
+        const res = await this.$axios.get('/public/v5/pushflow/popup');
+        if (res.success) {
+          this.isFiringNoOperationSetTimeout = res.data;
+          this.noOperationSetTimeout();
+        }
+      } catch (error) {
+        console.log(error);
+      }
+    },
+    // 退出功能
+    exit() {
+      // 关闭日志上报
+      // logReportObj.destroy();
+      // 关闭webRTC
+      this.engine.disconnect && this.engine.disconnect();
+      // 关闭业务指令通道
+      this.doConnectDirectivesWs && 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' });
+    },
   }
 }
 </script>
@@ -518,7 +604,7 @@ $-bg-yellow: rgb(255, 253, 241);
   left: 0px;
   bottom: 0px;
   width: 100%;
-  // height: 40px; // 通过vue动态添加
+  // height: 40px; // 三大功能键高度,通过vue动态添加
   background: inherit;
   background-color: rgba(0, 12, 23, 1);
   border: none;
@@ -531,9 +617,5 @@ $-bg-yellow: rgb(255, 253, 241);
   justify-content: space-evenly;
   align-items: center;
   color: #fff;
-
-  // .foot-menu{
-  //   width: 40px;
-  // }
 }
 </style>