Browse Source

Signed-off-by: caner <5658514@qq.com>

caner 3 years ago
parent
commit
6eead13dcd

+ 0 - 0
Client.html/.gitignore → .gitignore


+ 0 - 101
Client.html/src/components/Battery.vue

@@ -1,101 +0,0 @@
-<template>
-  <div class="electric-panel" :class="bgClass">
-    <div class="panel">
-      <div class="remainder" :style="{ width:quantity+ '%' }" />
-    </div>
-  </div>
-</template>
-
-<script>
-/**
- * 电池电量Icon
- */
-export default {
-  name: "ElectricQuantity",
-  props:['quantity'],
-  computed: {
-    bgClass() {
-      if (this.quantity >= 40) {
-        return "success";
-      } else if (this.quantity >= 20) {
-        return "warning";
-      } else if (this.quantity >= 1) {
-        return "danger";
-      } else {
-        return "danger";
-      }
-    },
-  },
-};
-</script>
-
-<style scoped>
-.electric-panel {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-
-.panel {
-  box-sizing: border-box;
-  width: 32px;
-  height: 14px;
-  position: relative;
-  border: 2px solid #ccc;
-  padding: 1px;
-  border-radius: 3px;
-  margin-right: 5px;
-}
-
-.panel::before {
-  content: "";
-  border-radius: 0 1px 1px 0;
-  height: 6px;
-  background: #ccc;
-  width: 3px;
-  position: absolute;
-  top: 50%;
-  right: -4px;
-  transform: translateY(-50%);
-}
-
-.panel .remainder {
-  border-radius: 1px;
-  position: relative;
-  height: 100%;
-  width: 0%;
-  left: 0;
-  top: 0;
-  background: #fff;
-}
-
-.success .panel {
-  border-color: #40d7c1;
-}
-.success .panel:before {
-  background: #40d7c1;
-}
-.success .remainder {
-  background: #40d7c1;
-}
-
-.warning .panel {
-  border-color: #f90;
-}
-.warning .panel:before {
-  background: #f90;
-}
-.warning .remainder {
-  background: #f90;
-}
-
-.danger .panel {
-  border-color: #ed4014;
-}
-.danger .panel:before {
-  background: #ed4014;
-}
-.danger .remainder {
-  background: #ed4014;
-}
-</style>

+ 0 - 92
Client.html/src/components/Contrl.vue

@@ -1,92 +0,0 @@
-<template>
-  <div>
-    <div id="ctrl-left"></div>
-    <div id="ctrl-right"></div>
-  </div>
-</template>
-<script>
-import nipplejs from "nipplejs";
-const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
-export default {
-  data() {
-    return {
-      contrlR: null,
-      contrlL: null,
-      timer: null,
-      Option: {
-        left: "20%",
-        right: "20%",
-        bottom: "30%",
-        mode: "static",
-        lcolor: "red",
-        rcolor: "green",
-      },
-      contrlData: {
-          v0: 128,
-          v1: 128,
-          v2: 128,
-          v3: 128,
-      },
-    };
-  },
-  methods: {
-    //初始化控制器
-    initContrl() {
-      this.contrlL = nipplejs.create({
-        zone: document.getElementById("ctrl-left"),
-        mode: this.Option.mode,
-        position: {
-          left: this.Option.left,
-          bottom: this.Option.bottom,
-        },
-        color: this.Option.lcolor,
-      });
-      this.contrlR = nipplejs.create({
-        zone: document.getElementById("ctrl-right"),
-        mode: this.Option.mode,
-        position: {
-          right: this.Option.right,
-          bottom: this.Option.bottom,
-        },
-        color: this.Option.rcolor,
-      });
-      this.contrlL.on("move", this.contrlLeft);
-      this.contrlR.on("move", this.contrlRight);
-      this.contrlL.on("end", this.contrlLeft);
-      this.contrlR.on("end", this.contrlRight);
-      this.loop();
-    },
-    // 摇杆左
-    contrlLeft(a, b) {
-      const obj = b.vector || { x: 0, y: 0 };
-      this.contrlData.v2 = this.rotailContrl(obj.x);
-      this.contrlData.v3 = this.rotailContrl(obj.y);
-    },
-    // 摇杆右
-    contrlRight(a, b) {
-      const obj = b.vector || { x: 0, y: 0 };
-      this.contrlData.v0 = this.rotailContrl(obj.x);
-      this.contrlData.v1 = this.rotailContrl(obj.y);
-    },
-    // 转换比例
-    rotailContrl(v) {
-      return Math.floor(Math.abs(v * 128 - 128));
-    },
-    // 发送回调
-    async loop() {
-      await sleep(50);
-      this.$emit("callBack", this.contrlData);
-      this.timer = requestAnimationFrame(this.loop);
-    },
-    // 清楚控制器
-    clearContrl() {
-      if (this.timer) cancelAnimationFrame(this.timer);
-      if (this.contrlL) this.contrlL.destroy();
-      if (this.contrlR) this.contrlR.destroy();
-    },
-  },
-  destroyed() {
-    this.clearContrl();
-  },
-};
-</script>

+ 0 - 96
Client.html/src/components/Login.vue

@@ -1,96 +0,0 @@
-<template>
-  <div class="login">
-    <div>
-      <div class="logo">Caner 车控</div>
-      <input type="text" placeholder="房间" maxlength="20" v-model="roomID" />
-      <input type="text" placeholder="昵称" maxlength="20" v-model="name" />
-      <div class="err">{{ err || error }}</div>
-      <button :disabled="disabled" @click="login">加入</button>
-    </div>
-  </div>
-</template>
-<script>
-const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
-export default {
-  props: {
-    err: {
-      type: String,
-      default: () => {
-        return "";
-      },
-    },
-  },
-  data() {
-    return {
-      name: "",
-      roomID: "",
-      error: "",
-      disabled: false,
-    };
-  },
-  methods: {
-    async login() {
-      if (this.name && this.roomID) {
-        this.$emit("loginBack", { name: this.name, roomID: this.roomID });
-        this.disabled = true;
-        await sleep(5000);
-        this.disabled = false;
-      } else {
-        this.error = "请输入房间和用户名称";
-        this.disabled = false;
-      }
-    },
-  },
-};
-</script>
-<style scoped>
-.login {
-  width: 100vw;
-  height: 100vh;
-  font-size: 16px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-.logo {
-  width: 100px;
-  height: 50px;
-  margin: 0 auto;
-}
-input {
-  display: block;
-  border: 0;
-  border-bottom: solid 1px #ccc;
-  text-indent: 10px;
-  margin: 0 auto;
-  margin-bottom: 20px;
-  outline: none;
-  border-radius: 0;
-}
-button {
-  display: block;
-  width: 130px;
-  height: 30px;
-  background: #2d8cf0;
-  border: none;
-  color: white;
-  font-size: 17px;
-  font-weight: 500;
-  border-radius: 5px;
-  cursor: pointer;
-  margin: 0 auto;
-  margin-top: 20px;
-  line-height: 30px;
-}
-.err {
-  width: 175px;
-  margin: 0 auto;
-  font-size: 12px;
-  color: red;
-  text-align: left;
-  text-indent: 10px;
-}
-input:nth-child(3) {
-  margin-bottom: 5px;
-}
-</style>

+ 0 - 88
Client.html/src/components/Map.vue

@@ -1,88 +0,0 @@
-<template>
-  <div class="maps" @click="changeMap" :style="MapStyle">
-    <el-amap :zoom="Option.zoom" :center="Option.position">
-      <!-- 当前坐标 -->
-      <el-amap-circle-marker
-        :center="Option.position"
-        :radius="5"
-        fillColor="#3E86F1"
-        :strokeWeight="0"
-      />
-    </el-amap>
-  </div>
-</template>
-<script>
-import Vue from "vue";
-import VueAMap from "vue-amap";
-VueAMap.initAMapApiLoader({
-  key: "99f684b9a7773a40ca224894816f7c68",
-  plugin: [
-    "AMap.Autocomplete",
-    "AMap.Geolocation",
-    "Geocoder",
-    "AMap.OverView",
-  ],
-  v: "1.4.15",
-});
-Vue.use(VueAMap);
-export default {
-  props: {
-    Option: {
-      type: Object,
-      defualt: () => {
-        return {
-          position: [104.056608, 30.695942],
-          zoom: 5,
-        };
-      },
-    },
-  },
-  data () {
-    return {
-      num: 0,
-      MapStyle: {
-        top: "5px",
-        left: "5px",
-        width: "50px",
-        height: "50px",
-      },  
-    }
-  },
-  methods: {
-    // 切换地图
-    changeMap() {
-      this.num++;
-      if (this.num % 2) {
-        this.MapStyle = {
-          top: "20px",
-          left: "50%",
-          transform: "translateX(-50%)",
-          width: "400px",
-          height: "200px",
-          borderRadius: "10px",
-        };
-      } else {
-        this.MapStyle = {
-          top: "5px",
-          left: "5px",
-          width: "50px",
-          height: "50px",
-          borderRadius: "50%",
-        };
-      }
-    },
-  },
-};
-</script>
-<style scoped>
-.maps {
-  position: fixed;
-  overflow: hidden;
-  z-index: 99;  
-  top: 5px;
-  left: 5px;
-  width: 50px;
-  height: 50px;
-  border-radius: 50%;
-}
-</style>

+ 0 - 130
Client.html/src/components/Record.vue

@@ -1,130 +0,0 @@
-<template>
-  <div>
-    <div v-if="show" @click="audioCapture()">
-      <svg
-        viewBox="0 0 1024 1024"
-        version="1.1"
-        xmlns="http://www.w3.org/2000/svg"
-        width="20"
-        height="20"
-      >
-        <path
-          d="M801.728 364.8a32 32 0 0 0-32 32v91.392c0 129.28-115.648 234.432-257.728 234.432S254.272 617.408 254.272 488.192V393.216a32 32 0 0 0-64 0v94.976c0 157.888 133.248 286.208 300.672 296.448v99.392H357.632c-16.128 0-29.184 14.336-29.184 32.064 0 17.664 13.056 31.936 29.184 31.936h319.04c16.064 0 29.184-14.272 29.184-31.936 0-17.728-13.12-32.064-29.184-32.064H554.944v-101.376c156.992-19.776 278.784-143.488 278.784-294.464V396.8c0-17.728-14.272-32-32-32z"
-          fill="#40d7c1"
-        ></path>
-        <path
-          d="M517.12 678.656a199.104 199.104 0 0 0 198.912-198.848V268.736A199.168 199.168 0 0 0 517.12 69.888a199.04 199.04 0 0 0-198.784 198.848v211.072a199.04 199.04 0 0 0 198.784 198.848z m85.056-126.784a49.856 49.856 0 1 1 0-99.648 49.856 49.856 0 0 1 0 99.648zM382.336 268.736c0-74.368 60.48-134.848 134.784-134.848a135.04 135.04 0 0 1 134.912 134.848v28.48H382.336v-28.48z"
-          fill="#40d7c1"
-        ></path>
-      </svg>
-    </div>
-    <div class="loading" v-else>
-      <span></span>
-      <span></span>
-      <span></span>
-    </div>
-  </div>
-</template>
-<script>
-const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
-export default {
-  data() {
-    return {
-      chunks: [],
-      mediaRecorder: null,
-      show: true,
-    };
-  },
-  async mounted() {
-    try {
-      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
-      this.mediaRecorder = new MediaRecorder(stream);
-      // 事件监听
-      this.mediaRecorder.ondataavailable = (e) => {
-        this.chunks.push(e.data);
-      };
-      this.mediaRecorder.onstart = () => {
-        this.chunks = [];
-      };
-      this.mediaRecorder.onstop = () => {
-        const blob = new Blob(this.chunks, { type: "audio/webm;codecs=opus" });
-        this.$emit("callBack", blob);
-      };
-    } catch (error) {
-      this.show = false
-      alert("不支持音频输入");
-    }
-  },
-  methods: {
-    // 录音
-    async audioCapture() {
-      this.mediaRecorder.start();
-      this.show = false;
-      await sleep(1000 * 5);
-      this.mediaRecorder.stop();
-      this.show = true;
-    },
-    // blob2AudioBuffer
-    blob2audioBuffer(blob) {
-      const reader = new FileReader();
-      reader.onload = function () {
-        console.log(123, this.result);
-        const audioCtx = new AudioContext();
-        audioCtx.decodeAudioData(this.result, function (audioBuffer) {
-          // AudioBuffer
-          console.log(audioBuffer);
-        });
-      };
-      reader.readAsArrayBuffer(blob);
-    },
-  },
-};
-</script>
-<style scoped>
-.loading {
-  width: 20px;
-  height: 15px;
-  position: relative;
-}
-.loading span {
-  display: block;
-  bottom: 0px;
-  width: 4px;
-  height: 2px;
-  background: #40d7c1;
-  position: absolute;
-  animation: load 1.1s infinite ease-in-out;
-  border-radius: 3px;
-}
-
-.loading span:nth-child(2) {
-  left: 7px;
-  animation-delay: 0.2s;
-}
-.loading span:nth-child(3) {
-  left: 14px;
-  animation-delay: 0.4s;
-}
-@keyframes load {
-  0% {
-    height: 5px;
-    transform: translateY(0px);
-    background: #40d7c1;
-  }
-  25% {
-    height: 15px;
-    transform: translateY(5px);
-    background: #40d7c1;
-  }
-  50% {
-    height: 5px;
-    transform: translateY(0px);
-    background: #40d7c1;
-  }
-  100% {
-    height: 5px;
-    transform: translateY(0px);
-    background: #40d7c1;
-  }
-}
-</style>

+ 0 - 104
Client.html/src/components/Signal.vue

@@ -1,104 +0,0 @@
-<template>
-  <div class="signal-box">
-    <ul v-if="signalValue">
-      <li
-        :class="
-          signalValue == 1
-            ? 'signal-red'
-            : signalValue == 2
-            ? 'signal-yellow'
-            : signalValue == 3
-            ? 'signal-green'
-            : 'signal-default'
-        "
-      ></li>
-      <li
-        :class="
-          signalValue == 1
-            ? 'signal-default'
-            : signalValue == 2
-            ? 'signal-yellow'
-            : signalValue == 3
-            ? 'signal-green'
-            : 'signal-default'
-        "
-      ></li>
-      <li
-        :class="
-          signalValue == 1
-            ? 'signal-default'
-            : signalValue == 2
-            ? 'signal-default'
-            : signalValue == 3
-            ? 'signal-green'
-            : 'signal-default'
-        "
-      ></li>
-    </ul>
-    <span v-if="signalText !=1" :style="signalText > 100 ? 'color:red':'color:white'">{{signalText}}</span>
-  </div>
-</template>
- 
-<script>
-export default {
-  name: "SignalTower",
-  props: ['signalValue','signalText'],
-  computed: {},
-};
-</script>
- 
-<style scoped>
-.signal-box {
-  display: flex;
-  align-items: flex-start;
-  justify-content: center;
-}
-span{
-  font-size: 15px;
-  color: white;
-  margin-left: 3px;
-}
-ul {
-  height: 15px;
-  margin: 0;
-  padding: 0;
-  display: flex;
-  align-items: flex-end;
-}
-
-li {
-  width: 5px;
-  height: 6px;
-  border-radius: 10px;
-  list-style: none;
-  margin: 0 0.5px;
-}
-
-ul li:nth-child(1) {
-  height: 6px;
-}
-
-ul li:nth-child(2) {
-  height: 9px;
-}
-
-ul li:nth-child(3) {
-  height: 12px;
-}
-
-.signal-default {
-  background: rgba(0, 0, 0, 0.5);
-}
-
-.signal-red {
-  background-color: red;
-}
-
-.signal-yellow {
-  background-color: #e7d055;
-}
-
-.signal-green {
-  background-color: #40d7c1;
-}
-</style>

+ 0 - 133
Client.html/src/components/loading.vue

@@ -1,133 +0,0 @@
-<template>
-  <div class="loading">
-    <figure>
-      <div class="dot white"></div>
-      <div class="dot"></div>
-      <div class="dot"></div>
-      <div class="dot"></div>
-      <div class="dot"></div>
-    </figure>
-  </div>
-</template>
-
-<style scoped>
-.loading{
-    position: fixed;
-    width: 100%;
-    height: 100%;
-    top: 0;
-    left: 0;
-    z-index: 9999;
-    background: rgba(0, 0, 0, 0.8);
-}
-figure {
-  position: absolute;
-  margin: auto;
-  top: 0;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  width: 6.25em;
-  height: 6.25em;
-  animation: rotate 2.4s linear infinite;
-}
-.white {
-  top: 0;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  background: white;
-  animation: flash 2.4s linear infinite;
-  opacity: 0;
-}
-.dot {
-  position: absolute;
-  margin: auto;
-  width: 2.4em;
-  height: 2.4em;
-  border-radius: 100%;
-  transition: all 1s ease;
-}
-.dot:nth-child(2) {
-  top: 0;
-  bottom: 0;
-  left: 0;
-  background: #ff4444;
-  animation: dotsY 2.4s linear infinite;
-}
-.dot:nth-child(3) {
-  left: 0;
-  right: 0;
-  top: 0;
-  background: #ffbb33;
-  animation: dotsX 2.4s linear infinite;
-}
-.dot:nth-child(4) {
-  top: 0;
-  bottom: 0;
-  right: 0;
-  background: #99cc00;
-  animation: dotsY 2.4s linear infinite;
-}
-.dot:nth-child(5) {
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: #33b5e5;
-  animation: dotsX 2.4s linear infinite;
-}
-
-@keyframes rotate {
-  0% {
-    transform: rotate(0);
-  }
-  10% {
-    width: 6.25em;
-    height: 6.25em;
-  }
-  66% {
-    width: 2.4em;
-    height: 2.4em;
-  }
-  100% {
-    transform: rotate(360deg);
-    width: 6.25em;
-    height: 6.25em;
-  }
-}
-
-@keyframes dotsY {
-  66% {
-    opacity: 0.1;
-    width: 2.4em;
-  }
-  77% {
-    opacity: 1;
-    width: 0;
-  }
-}
-@keyframes dotsX {
-  66% {
-    opacity: 0.1;
-    height: 2.4em;
-  }
-  77% {
-    opacity: 1;
-    height: 0;
-  }
-}
-
-@keyframes flash {
-  33% {
-    opacity: 0;
-    border-radius: 0%;
-  }
-  55% {
-    opacity: 0.6;
-    border-radius: 100%;
-  }
-  66% {
-    opacity: 0;
-  }
-}
-</style>

+ 0 - 257
Client.html/src/page/index.vue

@@ -1,257 +0,0 @@
-<template>
-  <div class="box">
-    <div v-if="isLogin">
-      <!-- RTC -->
-      <video id="v2" autoplay playsinline></video>
-      <!-- 摇杆 -->
-      <Contrl class="ctrl" ref="Contrl" @callBack="contrlBack" />
-      <!-- 电量 -->
-      <Battery class="battery" :quantity="Battery" />
-      <!-- GPS -->
-      <Map :Option="GPS" />
-      <!-- 信号 -->
-      <Signal class="signal" :signalValue="Signal" :signalText="SignalText" />
-      <!-- 音频 -->
-      <Record class="audio" @callBack="AudioBack" />
-      <!-- loading -->
-      <Loading v-if="showLoading" />
-    </div>
-    <Login v-else :err="error" @loginBack="login" />
-  </div>
-</template>
-<script>
-const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
-import Contrl from "@/components/Contrl";
-import Battery from "@/components/Battery";
-import Map from "@/components/Map";
-import Signal from "@/components/Signal";
-import Login from "@/components/Login";
-import Loading from "@/components/loading";
-import webRTC from "@/utils/RTC";
-import Record from "@/components/Record";
-const { io } = require("socket.io-client");
-export default {
-  components: {
-    Battery,
-    Contrl,
-    Map,
-    Signal,
-    Login,
-    Loading,
-    Record,
-  },
-  data() {
-    return {
-      socket: null,
-      HOST: "wss://car.caner.top",
-      error: "",
-      isLogin: false,
-      showLoading: false,
-      GPS: {
-        position: [104.056608, 30.695942],
-        zoom: 18,
-      },
-      Signal: 0,
-      SignalText: 0,
-      Battery: 100,
-      remoteVideo: null
-    };
-  },
-  methods: {
-    // 初始化
-    intSoketRtc(host) {
-      // 初始化socket
-      this.socket = io(host, {
-        autoConnect: false,
-        transports: ["websocket"],
-      });
-      // 监听socket
-      this.socket.on("connect", () => {
-        this.connect();
-      });
-      this.socket.on("leaved", () => {
-        this.close("车端离开");
-      });
-      this.socket.on("joined", () => {
-        // 请求开始RTC
-        this.socket.emit("msg", {
-          type: "startRTC",
-        });
-      });
-      this.socket.on("msg", (data) => {
-        this.msg(data);
-      });
-      this.socket.on("connect_error", (err) => {
-        console.log("连接失败", err);
-        this.close(err);
-      });
-    },
-
-    // 连接
-    async connect() {
-      console.log("连接成功", this.socket.auth);
-      this.isLogin = true;
-      try {
-        // 初始化RTC
-        webRTC.init();
-        await sleep(100);
-        // 监听视频流
-        this.remoteVideo = document.getElementById("v2");
-        webRTC.ontrack((event) => {
-          this.remoteVideo.srcObject = event.streams[0];
-        });
-        // 监听ICE|发送
-        webRTC.onicecandidate((event) => {
-          if (event.candidate) {
-            this.socket.emit("msg", {
-              type: "candidate",
-              candidate: event.candidate,
-            });
-          }
-        });
-        // 监听ICE状态
-        webRTC.oniceconnectionstatechange(async () => {
-          // 失败
-          if (
-            webRTC.RTC.iceConnectionState === "failed" ||
-            webRTC.RTC.iceConnectionState === "disconnected" ||
-            webRTC.RTC.iceConnectionState === "closed"
-          ) {
-            this.close("ICE通信失败");
-          }
-          // ICE连接成功|初始化摇杆
-          if (webRTC.RTC.iceConnectionState === "connected") {
-            await sleep(1000 * 2);
-            this.$refs.Contrl.initContrl();
-            this.showLoading = false;
-          }
-        });
-        // RTC通信信号
-        webRTC.getReaport((data) => {
-          this.SignalText = data.size;
-        });
-      } catch (error) {
-        this.socket.disconnect();
-        this.isLogin = false
-        alert('不支持Webrtc')
-      }
-    },
-
-    // 通信
-    async msg(data) {
-      if (data.type === "offer") {
-        // 设置本地应答
-        webRTC.setRemoteDescription(data.offer);
-        // 返回应答
-        const answer = await webRTC.createAnswer();
-        webRTC.setLocalDescription(answer);
-        // 发送anwser
-        this.socket.emit("msg", {
-          type: "answer",
-          answer: answer,
-        });
-      } else if (data.type === "candidate") {
-        // 添加ICE
-        webRTC.addIceCandidate(data.candidate);
-      } else if (data.type === "power") {
-        // 电量
-        this.Battery = data.power;
-      } else if (data.type === "LTE") {
-        // 信号
-        this.Signal = data.LTE;
-      } else if (data.type === "GPS") {
-        // gps
-        this.GPS.position = data.GPS;
-      }
-    },
-
-    // 登录
-    login(data) {
-      // 设置登录头
-      if (this.socket) {
-        this.socket.auth = {
-          roomID: data.roomID,
-          name: data.name,
-        };
-        this.socket.connect();
-      } else {
-        this.error = "服务器连接失败";
-      }
-    },
-
-    // 摇杆
-    contrlBack(data) {
-      if (!this.socket && !this.socket.connected) return;
-      this.socket.emit("msg", {
-        type: "conctrl",
-        conctrl: data,
-      });
-    },
-
-    // 音频
-    AudioBack(data) {
-      if (!this.socket && !this.socket.connected) return;
-      console.log("回调", data);
-      this.socket.emit("msg", {
-        type: "Meadia",
-        Meadia: data,
-      });
-    },
-
-    // 清除
-    close(err) {
-      if (webRTC) webRTC.close();
-      if (this.remoteVideo) this.remoteVideo.srcObject = null;
-      if (this.$refs.Contrl) this.$refs.Contrl.clearContrl();
-      this.socket.disconnect();
-      this.isLogin = false;
-      this.showLoading = true;
-      this.error = err || "";
-    },
-  },
-  mounted() {
-    this.intSoketRtc(this.HOST);
-  },
-};
-</script>
-<style scoped>
-.box {
-  width: 100vw;
-  height: 100vh;
-  position: relative;
-  overflow: hidden;
-}
-.ctrl,
-.battery,
-.signal {
-  position: fixed;
-  overflow: hidden;
-  z-index: 99;
-}
-.ctrl {
-  width: 100vw;
-  height: 100vh;
-  top: 0;
-  left: 0;
-}
-.battery {
-  top: 5px;
-  right: 5px;
-}
-.signal {
-  right: 50px;
-  top: 3px;
-}
-video {
-  width: 100vw;
-  height: 100vh;
-  background: none;
-  object-fit: fill;
-}
-.audio {
-  position: fixed;
-  top: 3px;
-  right: 90px;
-  z-index: 99;
-}
-</style>

+ 4 - 0
README.md

@@ -0,0 +1,4 @@
+# 控制端
+```
+测试数数据通道
+```

+ 0 - 0
Client.html/package.json → package.json


+ 0 - 0
Client.html/public/favicon.ico → public/favicon.ico


+ 0 - 0
Client.html/public/index.html → public/index.html


+ 0 - 0
Client.html/src/App.vue → src/App.vue


+ 0 - 0
Client.html/src/main.js → src/main.js


+ 149 - 0
src/page/index.vue

@@ -0,0 +1,149 @@
+<template>
+  <div class="box">
+    <video id="v2" autoplay playsinline></video>
+  </div>
+</template>
+<script>
+import webRTC from "@/utils/RTC";
+const { io } = require("socket.io-client");
+export default {
+  data() {
+    return {
+      socket: null,
+      HOST: "wss://car.caner.top",
+      remoteVideo: null,
+    };
+  },
+  methods: {
+    // 初始化
+    intSoketRtc(host) {
+      // 初始化socket
+      this.socket = io(host, {
+        // autoConnect: false,
+        auth: {
+          roomID: "test",
+          name: "123",
+        },
+        transports: ["websocket"],
+      });
+      // 监听socket
+      this.socket.on("connect", () => {
+        this.connect();
+      });
+      this.socket.on("leaved", () => {
+        console.log("车端离开");
+      });
+      this.socket.on("joined", () => {
+        // 请求开始RTC
+        this.socket.emit("msg", {
+          type: "startRTC",
+        });
+      });
+      this.socket.on("msg", (data) => {
+        this.msg(data);
+      });
+      this.socket.on("connect_error", (err) => {
+        console.log("连接失败", err);
+      });
+    },
+
+    // 连接
+    async connect() {
+      console.log("连接成功", this.socket.auth);
+
+      try {
+        // 初始化RTC
+        webRTC.init();
+        // 数据通道
+        const channel = webRTC.RTC.createDataChannel("test");
+        channel.onopen = () => {
+          console.log("数据通道打开");
+        };
+        channel.onmessage = (event) => {
+          console.log("数据通道信息", event.data);
+        };
+
+        // 监听ICE|发送
+        webRTC.onicecandidate((event) => {
+          if (event.candidate) {
+            this.socket.emit("msg", {
+              type: "candidate",
+              candidate: event.candidate,
+            });
+          }
+        });
+
+        // 监听ICE状态
+        webRTC.oniceconnectionstatechange(async () => {
+          console.log("state: ", webRTC.RTC.iceConnectionState);
+        });
+      } catch (error) {
+        console.log(error);
+      }
+    },
+
+    // 通信
+    async msg(data) {
+      if (data.type === "offer") {
+        // 设置本地应答
+        webRTC.setRemoteDescription(data.offer);
+        // 返回应答
+        const answer = await webRTC.createAnswer();
+        webRTC.setLocalDescription(answer);
+        // 发送anwser
+        this.socket.emit("msg", {
+          type: "answer",
+          answer: answer,
+        });
+      } else if (data.type === "candidate") {
+        // 添加ICE
+        webRTC.addIceCandidate(data.candidate);
+      }
+    },
+  },
+  mounted() {
+    this.intSoketRtc(this.HOST);
+  },
+};
+</script>
+<style scoped>
+.box {
+  width: 100vw;
+  height: 100vh;
+  position: relative;
+  overflow: hidden;
+}
+.ctrl,
+.battery,
+.signal {
+  position: fixed;
+  overflow: hidden;
+  z-index: 99;
+}
+.ctrl {
+  width: 100vw;
+  height: 100vh;
+  top: 0;
+  left: 0;
+}
+.battery {
+  top: 5px;
+  right: 5px;
+}
+.signal {
+  right: 50px;
+  top: 3px;
+}
+video {
+  width: 100vw;
+  height: 100vh;
+  background: none;
+  object-fit: fill;
+}
+.audio {
+  position: fixed;
+  top: 3px;
+  right: 90px;
+  z-index: 99;
+}
+</style>

+ 0 - 0
Client.html/src/utils/RTC.js → src/utils/RTC.js


+ 0 - 0
Client.html/vue.config.js → vue.config.js