gemercheung il y a 2 ans
Parent
commit
c691453192

+ 2 - 2
.env.eur

@@ -5,8 +5,8 @@ VITE_APP_CDN_URL=https://4dkk.4dage.com/v4/www/
 # sdk文件地址
 VITE_APP_SDK_DIR=https://testeurs3.4dkankan.com/v4/www/sdk
 # socket地址
-VITE_APP_SOCKET_URL=wss://ws.gemer.xyz
-# VITE_APP_SOCKET_URL=wss://testeurws.4dkankan.com
+# VITE_APP_SOCKET_URL=wss://ws.gemer.xyz
+VITE_APP_SOCKET_URL=wss://testeurws.4dkankan.com
 # VITE_APP_SOCKET_URL=wss://221.4.210.172:16666
 # 静态资源目录
 VITE_APP_STATIC_DIR=viewer

+ 1 - 1
package.json

@@ -4,7 +4,7 @@
   "version": "0.0.0",
   "scripts": {
     "dev": "vite",
-    "dev-eur": "vite --mode eur",
+    "dev-eur": "vite --mode eur --port=6100",
     "build-eur-test": "vue-tsc  --noEmit && vite build --mode eur",
     "build-eur-prod": "vue-tsc  --noEmit && vite build --mode eur.prod",
     "build": "vue-tsc  --noEmit && vite build --mode production",

+ 5 - 0
src/app.scss

@@ -169,3 +169,8 @@
     left: 0;
   }
 }
+// .remote,
+// .local {
+//   position: relative;
+//   z-index: 10000;
+// }

+ 2 - 1
src/components/basic/icon/images/index.ts

@@ -19,7 +19,7 @@ import scene from './scene.png';
 import show from './show@2x.png';
 import music_off from './music_off.png';
 import music_on from './music_on.png';
-
+import video_no from './video_no.png';
 const icons = {
   arrow,
   arrows,
@@ -42,5 +42,6 @@ const icons = {
   show,
   music_off,
   music_on,
+  video_no,
 };
 export { icons };

BIN
src/components/basic/icon/images/video_no.png


+ 97 - 0
src/components/chatRoom/camera copy.vue.bk

@@ -0,0 +1,97 @@
+<template>
+  <div class="avatar-video" ref="refVideo"> </div>
+</template>
+<script lang="ts" setup>
+  import { onMounted, onUnmounted, ref, unref, watch } from 'vue';
+  import { computed } from 'vue';
+  import { useRtcSdk } from '/@/hooks/useTRTC';
+  import { useRtcStore } from '/@/store/modules/rtc';
+
+  const rtcStore = useRtcStore();
+  //   const destination = computed(() => (rtcStore.isLeader ? '.local' : '.remote'));
+  const isPublished = computed(() => rtcStore.isPublished);
+  const remoteStreams = computed(() => rtcStore.remoteStreams);
+  const refVideo = ref(null);
+  const initHostCam = ref(false);
+  const initCustomCam = ref(false);
+
+  const handleCapVideoElement = (event) => {
+    const { localStream } = useRtcSdk();
+    const steamId = localStream.getId();
+    initHostCam.value = true;
+    if (event.type === 'video' && event.state === 'PLAYING') {
+      const localVideo = document.getElementById(`video_${steamId}`);
+      const avatarVideo = document.querySelector('.avatar-video');
+      avatarVideo && localVideo && avatarVideo.append(localVideo);
+      console.log('localVideo', `video_${steamId}`, localVideo);
+    }
+  };
+  const handleCapVideoRemoteElement = () => {
+    const remoteStream = unref(remoteStreams)[0];
+    const steamId = remoteStream.getId();
+    initCustomCam.value = true;
+    const remoteVideo = document.getElementById(`video_${steamId}`);
+    const avatarVideo = document.querySelector('.avatar-video');
+    const preVideo = document.querySelector('.avatar-video video');
+    preVideo && avatarVideo?.removeChild(preVideo);
+    avatarVideo && remoteVideo && avatarVideo.append(remoteVideo);
+    console.log('remoteVideo', `video_${steamId}`, remoteVideo);
+  };
+  onUnmounted(() => {
+    const { localStream } = useRtcSdk();
+    localStream && localStream.off('player-state-changed', handleCapVideoElement);
+  });
+
+  onMounted(() => {
+    watch(
+      () => isPublished,
+      (val) => {
+        if (unref(val) && !unref(initHostCam)) {
+          if (rtcStore.isLeader) {
+            const { localStream } = useRtcSdk();
+            const hasVideo = localStream.hasVideo();
+            if (hasVideo) {
+              localStream.on('player-state-changed', handleCapVideoElement);
+            }
+          } else {
+            console.log('gemer', remoteStreams);
+          }
+        }
+      },
+      {
+        deep: true,
+      },
+    );
+    watch(
+      () => remoteStreams,
+      (val) => {
+        if (unref(val)?.length) {
+          if (!rtcStore.isLeader) {
+            const remoteStreams = unref(val)[0];
+            const hasVideo = remoteStreams.hasVideo();
+            if (hasVideo) {
+              setTimeout(() => {
+                handleCapVideoRemoteElement();
+              }, 2000);
+            }
+          }
+        }
+      },
+      {
+        deep: true,
+      },
+    );
+  });
+</script>
+<style scoped lang="scss">
+  .avatar-video {
+    width: 1.94rem;
+    height: 1.94rem;
+    position: fixed;
+    top: 0.69rem;
+    left: 0.53rem;
+    overflow: hidden;
+    z-index: 10000;
+    border-radius: 50%;
+  }
+</style>

+ 34 - 0
src/components/chatRoom/camera.vue

@@ -0,0 +1,34 @@
+<template>
+  <div class="avatar-video">
+    <teleport :to="destination" v-if="isPublished">
+      <div>video</div>
+    </teleport>
+  </div>
+</template>
+<script lang="ts" setup>
+  //   import { ref } from 'vue';
+  import { computed } from 'vue';
+  //   import { useRtcSdk } from '/@/hooks/useTRTC';
+  import { useRtcStore } from '/@/store/modules/rtc';
+
+  const rtcStore = useRtcStore();
+  const destination = computed(() => (rtcStore.isLeader ? '.local' : '.remote'));
+  const isPublished = computed(() => rtcStore.isPublished);
+  //   const remoteStreams = computed(() => rtcStore.remoteStreams);
+  //   const refVideo = ref(null);
+  //   const initHostCam = ref(false);
+  //   const initCustomCam = ref(false);
+</script>
+<style lang="scss">
+  .local,
+  .remote {
+    width: 1.94rem;
+    height: 1.94rem;
+    position: fixed;
+    top: 0.69rem;
+    left: 0.53rem;
+    overflow: hidden;
+    z-index: 10000;
+    border-radius: 50%;
+  }
+</style>

+ 4 - 0
src/components/chatRoom/chatroom.scss

@@ -602,3 +602,7 @@
   opacity: 0.5;
   pointer-events: none;
 }
+// #local{
+//   position: relative;
+//   z-index: 10000;
+// }

+ 14 - 12
src/components/chatRoom/controls/actions.ts

@@ -382,17 +382,19 @@ const handleRoomValidTime = (data: RoomValidTimeType) => {
   appStore.setRoomValidTime(data.ttl);
   if (rtcStore.isLeader) {
     const seconds = data.ttl;
-    const time = dayjs.duration(seconds, 'seconds').format('mm:ss');
-    console.log('time', time);
-    const { t } = useI18n();
-    const msg = String(t('base.roomTimeOut')).replace(new RegExp('%time%', 'g'), time);
-    const chat: ChatContentType = {
-      role: 'leader',
-      mode: '',
-      Nickname: t('base.tips'),
-      UserId: rtcStore.userId,
-      text: msg,
-    };
-    rtcStore.addToChatList(chat);
+    if (seconds > 0) {
+      const time = dayjs.duration(seconds, 'seconds').format('mm:ss');
+      console.log('time', time);
+      const { t } = useI18n();
+      const msg = String(t('base.roomTimeOut')).replace(new RegExp('%time%', 'g'), time);
+      const chat: ChatContentType = {
+        role: 'leader',
+        mode: '',
+        Nickname: t('base.tips'),
+        UserId: rtcStore.userId,
+        text: msg,
+      };
+      rtcStore.addToChatList(chat);
+    }
   }
 };

+ 5 - 5
src/components/chatRoom/controls/join.ts

@@ -71,6 +71,11 @@ export function handleJoin(data: any) {
       socket.emit('action', {
         type: 'ask-currentscene',
       });
+      socket.emit('action', {
+        type: 'users-muted',
+        muted: true,
+        userId: rtcStore.userId,
+      });
     } else {
       const { currentScene } = useRoom();
       console.log('currentScene', unref(currentScene));
@@ -78,11 +83,6 @@ export function handleJoin(data: any) {
         type: 'changeScene',
         data: unref(currentScene),
       });
-      socket.emit('action', {
-        type: 'users-muted',
-        muted: true,
-        userId: rtcStore.userId,
-      });
     }
   }, 1500);
 }

+ 17 - 10
src/components/chatRoom/index.vue

@@ -7,7 +7,10 @@
       <div class="members"></div>
       <span>{{ members.length }}{{ t('base.view') }}</span>
     </div>
-
+    <!-- 视频头像 start -->
+    <Camera />
+    <!-- 视频头像 end -->
+    <!-- {{ selfRoomStatus }} -->
     <div class="room_valid" v-if="isPanoramaMode && roomCounter > 0 && isNativeLeader">
       <span>{{ dayjs.duration(roomCounter, 'seconds').format('mm:ss') }}</span>
     </div>
@@ -145,6 +148,7 @@
   import chat from './chat.vue';
   import memberList from './memberList.vue';
   import SceneList from './sceneList.vue';
+  import Camera from './camera.vue';
   import { useRoom, SceneItemType } from '/@/hooks/useRoom';
   import consola from 'consola';
   import CloseDialog from './dialog/close.vue';
@@ -183,7 +187,7 @@
 
   const members = computed(() => rtcStore.memberList);
   const showScenes = ref(false);
-  const audioMuted = computed(() => rtcStore.audioMuted);
+
   const remoteStreams = computed(() => rtcStore.remoteStreams);
   const currentSession = computed(() => rtcStore.currentSession);
   const isPanoramaMode = computed(() => appStore.mode === 'panorama');
@@ -193,6 +197,9 @@
   const showCreateNameDialog = ref(false);
   const roomTTL = computed(() => appStore.ttl);
 
+  const selfRoomStatus = computed(() => rtcStore.getSelfRoomStatus());
+  const audioMuted = computed(() => unref(selfRoomStatus)?.IsMuted);
+
   let roomCounter = ref(-1);
   let roomCount: NodeJS.Timeout;
   const { t } = useI18n();
@@ -410,28 +417,29 @@
     const { localStream } = useRtcSdk();
     const { socket } = useSocket();
     console.log('localStream', localStream);
-    localStream.resume();
-    rtcStore.unmute();
-    localStream.unmuteAudio();
+
     console.log('开启MIC');
     socket.emit('action', {
       type: 'users-muted',
       muted: false,
       userId: rtcStore.userId,
     });
+    localStream.resume();
+    rtcStore.unmute();
+    localStream.unmuteAudio();
   };
   const handleMute = () => {
     const { localStream } = useRtcSdk();
     const { socket } = useSocket();
-    console.log('localStream', localStream);
-    console.log('关闭MIC');
-    rtcStore.mute();
-    localStream.muteAudio();
     socket.emit('action', {
       type: 'users-muted',
       muted: true,
       userId: rtcStore.userId,
     });
+    console.log('localStream', localStream);
+    console.log('关闭MIC');
+    rtcStore.mute();
+    localStream.muteAudio();
   };
 
   const handleCloseRoom = async () => {
@@ -466,7 +474,6 @@
     });
     changeScene(unref(scene));
   };
-  // const handleFullRoom = () => {};
 </script>
 
 <style scoped lang="scss">

+ 0 - 1
src/components/chatRoom/memberList.vue

@@ -37,7 +37,6 @@
               <div
                 class="micBtn"
                 :class="i.IsMuted ? 'mute_one_mic_off' : 'mute_one_mic_on'"
-                v-if="i?.Role == 'leader'"
                 @click="isNativeLeader && userCanSpeak(i)"
               ></div>
               <!-- mic -->

+ 40 - 94
src/hooks/useTRTC.ts

@@ -12,11 +12,11 @@ const invitedRemoteStreams = ref<RemoteStream[]>([]);
 const muteAudioLeader = ref(false);
 const muteVideoLeader = ref(false);
 
-export const updateDevice = async () => {
+export const checkDevice = async () => {
   try {
-    console.log('updateDevice');
     const rtcStore = useRtcStore();
     const microphoneItems = await TRTC.getMicrophones();
+    console.log('microphoneItems', microphoneItems);
     microphoneItems.forEach((item) => {
       item['value'] = item.deviceId;
     });
@@ -25,12 +25,30 @@ export const updateDevice = async () => {
     } else {
       rtcStore.setAudioDeviceId('');
     }
-  } catch (error) {}
+
+    const camerasItems = await TRTC.getCameras();
+
+    camerasItems.forEach((item) => {
+      item['value'] = item.deviceId;
+    });
+    if (camerasItems?.length) {
+      rtcStore.setVideoDeviceId(camerasItems[0].deviceId || camerasItems[0].groupId);
+    } else {
+      rtcStore.setVideoDeviceId('');
+    }
+  } catch (error) {
+    console.log('error', error);
+  }
 };
 
-const checkoutIsExistAudioInput = async () => {
+const checkSystemRequirements = async () => {
   const result = await TRTC.checkSystemRequirements();
   console.log('result', result);
+  const isSmallStreamSupported = await TRTC.isSmallStreamSupported();
+  console.log('isSmallStreamSupported', isSmallStreamSupported);
+  // if (!isSmallStreamSupported) {
+  //   alert('You browser does not support opening small streams.');
+  // }
 };
 
 // interface UseRtcSdkType {
@@ -40,18 +58,27 @@ const checkoutIsExistAudioInput = async () => {
 async function createLocalStream() {
   try {
     const rtcStore = useRtcStore();
+    const enableVideo = rtcStore.isLeader && rtcStore.videoDeviceId.length > 0;
     localStream = TRTC.createStream({
       userId: rtcStore.userId,
       audio: true,
-      video: false,
+      video: enableVideo,
       microphoneId: rtcStore.audioDeviceId,
     });
-
+    //开大小流
+    await localClient.enableSmallStream();
+    localClient.setSmallStreamProfile({
+      width: 160,
+      height: 90,
+      bitrate: 100,
+      frameRate: 15,
+    });
+    //
     await localStream.initialize();
 
-    if (rtcStore.audioMuted) {
-      localStream.muteAudio();
-    }
+    // if (rtcStore.audioMuted) {
+    //   localStream.muteAudio();
+    // }
   } catch (error) {
     console.log(error, 'createStream');
   }
@@ -60,14 +87,9 @@ async function createLocalStream() {
 async function createRTCSocket(): Promise<void> {
   try {
     const rtcStore = useRtcStore();
-    await checkoutIsExistAudioInput();
-    await updateDevice();
-    console.log('rtcStore', rtcStore);
-    // const microphoneItems = await TRTC.getMicrophones();
-
-    // if (microphoneItems?.length) {
-
-    // }
+    await checkSystemRequirements();
+    await checkDevice();
+    console.log('createRTCSocket', rtcStore.videoDeviceId);
     await handleJoin();
   } catch (error) {
     consola.error({
@@ -129,19 +151,6 @@ async function handleJoin() {
           localStream.resume();
         },
       );
-      // Dialog.confirm({
-      //   showCloseIcon: false,
-      //   okText: '确定',
-      //   content:
-      //     "<span style='font-size: 16px; line-height: 1.5;'>在用户与网页产生交互(例如点击、触摸页面等)之前,网页将被禁止播放带有声音的媒体。点击恢复播放<span/>",
-      //   title: '隐私条款:',
-      //   single: true,
-      //   func: (state) => {
-      //     if (state == 'ok') {
-      //       localStream.resume();
-      //     }
-      //   },
-      // });
     }
   });
 }
@@ -371,71 +380,8 @@ async function handleStreamSubscribed(event) {
         tag: 'rtc:audio',
       });
       await remoteStream.play(remoteId);
-    } catch (error) {
-      // remoteStream.resume();
-      // rtcStore.showBaseDialog(
-      //   {
-      //     title: t('base.tips'),
-      //     desc: t('base.audioPermission'),
-      //     okTxt: t('base.confirm'),
-      //     closeTxt: t('base.cancel'),
-      //   },
-      //   () => {
-      //     consola.info({
-      //       message: 'resume',
-      //       tag: 'rtc:audio',
-      //     });
-      //     remoteStream.resume();
-      //   },
-      // );
-      // Dialog.confirm({
-      //   showCloseIcon: false,
-      //   okText: '确定',
-      //   content:
-      //     "<span style='font-size: 16px; line-height: 1.5;'>继续访问该页面需要获取您摄像头及麦克风的权限。<span/>",
-      //   title: '温馨提示:',
-      //   single: true,
-      //   func: (state) => {
-      //     if (state == 'ok') {
-      //       remoteStream.resume();
-      //     }
-      //   },
-      // });
-    }
+    } catch (error) {}
   }, 200);
-  // console.info(
-  //     remoteStream.userId_,
-  //     rtcStore,
-  //     "handleStreamSubscribedhandleStreamSubscribed.value"
-  // );
-
-  // if (
-  //     !invitedRemoteStreams.value.some(
-  //         (item) => item.userId_ == remoteStream.userId_
-  //     )
-  // ) {
-  //     debugger
-  //     invitedRemoteStreams.value.push(remoteStream);
-  // }
-
-  // console.log(invitedRemoteStreams.value, "invitedRemoteStreams.value");
-
-  // await nextTick();
-  // setTimeout(() => {
-  //     console.log(remoteStream.userId_, "remoteStream.getId()");
-  //     remoteStream
-  //         .play(remoteStream.userId_)
-  //         .then(() => {
-  //             console.log(`RemoteStream play success`, 88888888888888888888);
-  //         })
-  //         .catch((error) => {
-  //             console.log(`RemoteStream play failed:  error: ${error.message_}`);
-  //         });
-  // }, 100);
-
-  // const remoteStream = event.stream;
-  // const userId = remoteStream.getUserId();
-  // console.log(`RemoteStream subscribed: [${userId}]`);
 }
 
 function handleStreamRemoved(event) {

+ 1 - 1
src/locales/lang/en/base.ts

@@ -37,5 +37,5 @@ export default {
   baseError1: 'The scene is calculating, please try again later',
   baseError2: 'Abnormal studio, please contact the host.',
   roomTimeOut:
-    '房间有效时长为%time%分钟,%time%分钟后将自动结束此次带看,请及时关注上方的剩余时长,谢谢!',
+    'the valid duration of the studio is %time% minutes; after %time% minutes, the Livestream will automatically end; please pay attention to the remaining time above. Thank you.',
 };

+ 6 - 3
src/store/modules/rtc.ts

@@ -29,7 +29,7 @@ interface RtcState {
   mode: string;
   chatList: ChatContentType[];
   memberList: UserInfoType[];
-  audioMuted: boolean;
+  // audioMuted: boolean;
   inputStatus: boolean;
   currentSession: Nullable<UserInfoType>;
   baseDialog: BaseDialog;
@@ -118,7 +118,7 @@ export const useRtcStore = defineStore({
     mode: '',
     chatList: [],
     memberList: [],
-    audioMuted: true,
+    // audioMuted: true,
     inputStatus: true,
     currentSession: null,
     baseDialog: defaultBaseDialog,
@@ -163,6 +163,9 @@ export const useRtcStore = defineStore({
         return this.remoteStreams.some((item) => item.getUserId() === userId);
       };
     },
+    getSelfRoomStatus() {
+      return () => this.memberList.find((item) => item.UserId === this.userId);
+    },
   },
   actions: {
     showBaseDialog(dialog: BaseDialog, callback?: () => void): void {
@@ -282,7 +285,7 @@ export const useRtcStore = defineStore({
       this.setMute(true);
     },
     setMute(mute: boolean) {
-      this.audioMuted = mute;
+      // this.audioMuted = mute;
       this.updateMemberDatabyId(this.userId, {
         IsMuted: mute,
       });