Selaa lähdekoodia

feat: 阶段提交

shuisheng 2 vuotta sitten
vanhempi
sitoutus
d2b7fa3aa6
10 muutettua tiedostoa jossa 246 lisäystä ja 122 poistoa
  1. 3 1
      package.json
  2. 44 0
      src/App copy.vue
  3. 41 5
      src/App.vue
  4. 1 0
      src/main.ts
  5. 119 0
      src/network/webRtc.ts
  6. 2 12
      src/router/index.ts
  7. 0 30
      src/views/about/about.vue
  8. 0 52
      src/views/home/home.vue
  9. 36 0
      src/views/home/index.vue
  10. 0 22
      src/views/login/login.vue

+ 3 - 1
package.json

@@ -34,11 +34,13 @@
     "billd-html-webpack-plugin": "^1.0.0",
     "billd-scss": "^0.0.6",
     "billd-utils": "^0.0.9",
+    "flv.js": "^1.6.2",
     "pinia": "^2.0.11",
     "socket.io-client": "^4.6.1",
     "vue": "^3.2.31",
     "vue-demi": "^0.13.11",
-    "vue-router": "^4.0.13"
+    "vue-router": "^4.0.13",
+    "webrtc-adapter": "^8.2.2"
   },
   "devDependencies": {
     "@babel/core": "^7.14.0",

+ 44 - 0
src/App copy.vue

@@ -0,0 +1,44 @@
+<template>
+  <div>
+    <video
+      id="videoElement"
+      ref="videoRef"
+      style="width: 100vw"
+      :muted="muted"
+      controls
+    ></video>
+
+    <div class="btn">btn</div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, ref } from 'vue';
+
+import { WebSocketClass, wsConnectStatus } from '@/network/websocket';
+
+const muted = ref(true);
+const videoRef = ref<HTMLVideoElement>();
+
+onMounted(() => {
+  // if (flvJs.isSupported() && videoRef.value) {
+  //   const flvPlayer = flvJs.createPlayer({
+  //     type: 'flv',
+  //     url: 'http://localhost:8000/live/fddm_2.flv',
+  //   });
+  //   flvPlayer.attachMediaElement(videoRef.value);
+  //   flvPlayer.load();
+  //   try {
+  //     flvPlayer.play();
+  //   } catch (error) {
+  //     console.log(error);
+  //   }
+  // }
+  const instance = new WebSocketClass({ url: 'ws://localhost:4300' });
+  instance.wsInstance?.on(wsConnectStatus.connect, () => {
+    console.log('连接websocket成功!');
+  });
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 41 - 5
src/App.vue

@@ -1,23 +1,59 @@
 <template>
   <div>
-    <div class="btn">btn</div>
+    <router-view></router-view>
+    <video
+      id="localVideo"
+      ref="localVideoRef"
+      autoplay
+      playsinline
+      :muted="muted"
+    ></video>
+    <div>
+      <button @click="startAction">start</button>
+    </div>
   </div>
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 
+import { WebRTCClass } from '@/network/webRtc';
 import { WebSocketClass, wsConnectStatus } from '@/network/websocket';
 
-let _instance;
+const muted = ref(true);
+const localVideoRef = ref<HTMLVideoElement>();
+
+let stream: MediaStream;
 
 onMounted(() => {
-  const instance = new WebSocketClass({ url: 'ws://localhost:3300' });
-  console.log(instance, 11);
+  const instance = new WebSocketClass({ url: 'ws://localhost:4300' });
   instance.wsInstance?.on(wsConnectStatus.connect, () => {
     console.log('连接websocket成功!');
+    handleWebRtc();
   });
 });
+
+async function handleWebRtc() {
+  const webrtc = new WebRTCClass();
+  console.log(webrtc);
+  const offer = await webrtc.createOffer();
+  console.log(offer);
+}
+
+// Handles start button action: creates local MediaStream.
+function startAction() {
+  navigator.mediaDevices
+    .getUserMedia({ video: true, audio: true })
+    .then((event) => {
+      console.log('getUserMedia成功', event);
+      stream = event;
+      // if (!localVideoRef.value) return;
+      // localVideoRef.value.srcObject = event;
+    })
+    .catch((err) => {
+      console.log('getUserMedia失败', err);
+    });
+}
 </script>
 
 <style lang="scss" scoped></style>

+ 1 - 0
src/main.ts

@@ -1,3 +1,4 @@
+import 'webrtc-adapter';
 import './main.scss';
 import './showBilldVersion';
 

+ 119 - 0
src/network/webRtc.ts

@@ -0,0 +1,119 @@
+export class WebRTCClass {
+  peerConnection: RTCPeerConnection | null = null;
+  dataChannel: RTCDataChannel | null = null;
+
+  constructor() {
+    this.createPeerConnection();
+  }
+
+  // 创建offer
+  createOffer = async () => {
+    if (!this.peerConnection) return;
+    try {
+      const description = await this.peerConnection.createOffer();
+      console.warn('createOffer成功');
+      await this.peerConnection.setLocalDescription(description);
+      console.warn('setLocalDescription成功', description);
+      return description;
+    } catch (error) {
+      console.error('创建offer失败', error);
+    }
+  };
+
+  // 设置远端描述
+  setRemoteDescription = async (description: any) => {
+    if (!this.peerConnection) return;
+    try {
+      await this.peerConnection.setRemoteDescription(
+        new RTCSessionDescription(description)
+      );
+      console.warn('设置远端描述成功');
+    } catch (error) {
+      console.error('设置远端描述错误', error);
+    }
+  };
+
+  // 创建连接
+  createConnect = () => {
+    if (!this.peerConnection) return;
+    console.warn('createConnect');
+    this.peerConnection.addEventListener('icecandidate', (event) => {
+      console.log('icecandidate:', event);
+    });
+
+    this.peerConnection.addEventListener('addstream', (event: any) => {
+      console.log('addstream', event.stream);
+    });
+
+    // 已经有视频或者声音通道
+    this.peerConnection.addEventListener('ontrack', (event: any) => {
+      console.log('ontrack', event.stream.id);
+    });
+
+    // 有视频或者声音通道
+    this.peerConnection.addEventListener('addtrack', (event: any) => {
+      console.log('addtrack', event.stream.id);
+    });
+
+    // connectionstatechange
+    this.peerConnection.addEventListener(
+      'connectionstatechange',
+      (event: any) => {
+        console.log('connectionstatechange', event);
+        const connectionState = event.currentTarget.connectionState;
+        const iceConnectionState = event.currentTarget.iceConnectionState;
+        console.log(
+          // eslint-disable-next-line
+          `connectionState:${connectionState}, iceConnectionState:${iceConnectionState}`
+        );
+        if (connectionState === 'connected') {
+          console.warn('connectionState:connected');
+        }
+        if (connectionState === 'failed') {
+          // 失败
+          console.error('connectionState:failed', event);
+        }
+        if (iceConnectionState === 'disconnected') {
+          // 已断开,请重新连接
+          console.error('iceConnectionState:disconnected', event);
+        }
+      }
+    );
+  };
+
+  // 创建对等连接
+  createPeerConnection() {
+    if (!window.RTCPeerConnection) {
+      console.error('当前环境不支持RTCPeerConnection!');
+      alert('当前环境不支持RTCPeerConnection!');
+      return;
+    }
+    if (!this.peerConnection) {
+      this.peerConnection = new RTCPeerConnection();
+      this.dataChannel =
+        this.peerConnection.createDataChannel('MessageChannel');
+
+      this.dataChannel.onopen = (event) => {
+        console.warn('dataChannel---onopen', event);
+      };
+      this.dataChannel.onerror = (event) => {
+        console.warn('dataChannel---onerror', event);
+      };
+      this.dataChannel.onmessage = (event) => {
+        console.log('dataChannel---onmessage', event);
+      };
+      this.peerConnection.addTransceiver('video', { direction: 'recvonly' });
+      this.peerConnection.addTransceiver('audio', { direction: 'recvonly' });
+      this.createConnect();
+    }
+  }
+
+  // 手动关闭webrtc连接
+  close() {
+    console.warn(`${new Date().toLocaleString()},手动关闭webrtc连接`);
+    this.peerConnection?.close();
+    this.dataChannel?.close();
+    this.peerConnection = null;
+    this.dataChannel = null;
+  }
+}

+ 2 - 12
src/router/index.ts

@@ -6,19 +6,9 @@ import type { RouteRecordRaw } from 'vue-router';
 // 默认路由
 export const defaultRoutes: RouteRecordRaw[] = [
   {
-    // name: '',
+    name: 'home',
     path: '/',
-    component: () => import('@/views/home/home.vue'),
-  },
-  {
-    name: 'login',
-    path: '/login',
-    component: () => import('@/views/login/login.vue'),
-  },
-  {
-    name: 'about',
-    path: '/about',
-    component: () => import('@/views/about/about.vue'),
+    component: () => import('@/views/home/index.vue'),
   },
 ];
 const router = createRouter({

+ 0 - 30
src/views/about/about.vue

@@ -1,30 +0,0 @@
-<template>
-  <div class="about-wrap">
-    <h1>about页面</h1>
-    <p class="myfont">MIUI 13 采用全新系统字体 MiSans</p>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
-  components: {},
-  setup() {},
-});
-</script>
-
-<style lang="scss" scoped>
-@font-face {
-  font-family: 'MiSans';
-  src: url('@/assets/font/MiSans-Normal.ttf');
-}
-
-.about-wrap {
-  padding: 20px;
-  background-color: bisque;
-  .myfont {
-    font-family: 'MiSans';
-  }
-}
-</style>

+ 0 - 52
src/views/home/home.vue

@@ -1,52 +0,0 @@
-<template>
-  <div class="home-wrap">
-    <h1>home页面</h1>
-    <div>pinia的user: {{ userDetail }}</div>
-    <div>pinia的counter: {{ counter }}</div>
-    <button @click="handlecounter">设置counter</button>
-    <button @click="handleInfo(1)">模拟异步请求成功</button>
-    <button @click="handleInfo(2)">模拟异步请求失败</button>
-    <CardCpt></CardCpt>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent, toRef, ref } from 'vue';
-
-import CardCpt from '@/components/Card/index.vue';
-import { useAppStore } from '@/store/app';
-import { useUserStore } from '@/store/user';
-
-export default defineComponent({
-  components: { CardCpt },
-  setup() {
-    const userStore = useUserStore();
-    const appStore = useAppStore();
-    const userInfo = ref(userStore);
-    const userDetail = toRef(userStore, 'detail');
-    const counter = toRef(appStore, 'counter');
-    const handlecounter = () => {
-      appStore.setCounter((counter.value += 1));
-    };
-    const handleInfo = (num) => {
-      userStore.setDetail(num).then(
-        (res) => {
-          console.log(res);
-        },
-        (err) => {
-          console.log(err);
-        }
-      );
-    };
-
-    return { userInfo, userDetail, counter, handlecounter, handleInfo };
-  },
-});
-</script>
-
-<style lang="scss" scoped>
-.home-wrap {
-  padding: 20px;
-  background-color: skyblue;
-}
-</style>

+ 36 - 0
src/views/home/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div class="home-wrap">
+    房间号:<input
+      type="text"
+      placeholder="输入房间号"
+    />
+    <button
+      class="join-btn"
+      @click="join"
+    >
+      进入
+    </button>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const homeId = ref<number>();
+
+function join() {
+  if (!homeId.value) {
+    console.error('房间号不能为空!');
+    return;
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.home-wrap {
+  padding: 10px;
+  .join-btn {
+    margin-left: 10px;
+  }
+}
+</style>

+ 0 - 22
src/views/login/login.vue

@@ -1,22 +0,0 @@
-<template>
-  <div class="login-wrap">
-    <h1>login页面</h1>
-    <p>欢迎登录!</p>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
-  components: {},
-  setup() {},
-});
-</script>
-
-<style lang="scss" scoped>
-.login-wrap {
-  padding: 20px;
-  background-color: aqua;
-}
-</style>