shuisheng hace 2 años
padre
commit
028698a8f3

+ 2 - 13
README.md

@@ -178,20 +178,9 @@ npm run build
 
 [https://live.hsslive.cn/sponsors](https://live.hsslive.cn/sponsors)
 
-## 交流
+## 微信群
 
-如果你对该项目感兴趣或有想法,欢迎进群或添加我的微信:
-
-<div>
-  <img
-    src="https://resource.hsslive.cn/image/c582d500f460939a97882ce503f8b6b3.png" 
-    style="height:300px"
-    />
-  <img
-    src="https://resource.hsslive.cn/image/57c5b5598736e6e4f7e406ae503120f8.webp" 
-    style="height:300px"
-    />
-</div>
+详情:[https://live.hsslive.cn/about/group](https://live.hsslive.cn/about/group)
 
 ## 环境配置
 

+ 56 - 11
src/components/VideoControls/index.vue

@@ -16,14 +16,29 @@
     </div>
 
     <div class="right">
+      <div class="line">
+        <span class="txt">线路</span>
+        <div class="list">
+          <div
+            class="iten"
+            :class="{ active: appStore.liveLine === item }"
+            v-for="item in LiveLineEnum"
+            :key="item"
+            @click="changeLiveLine(item)"
+          >
+            {{ item }}
+          </div>
+        </div>
+      </div>
       <div class="speed">
         <span class="txt">倍速</span>
-        <div class="list">
+        <div
+          class="list"
+          @click="handleTip"
+        >
           <div class="iten">2.0x</div>
           <div class="iten">1.5x</div>
-          <div class="iten">1.25x</div>
-          <div class="iten">1.0x</div>
-          <div class="iten">0.75x</div>
+          <div class="iten active">1.0x</div>
           <div class="iten">0.5x</div>
         </div>
       </div>
@@ -34,9 +49,31 @@
 <script lang="ts" setup>
 import { VolumeHighOutline, VolumeMuteOutline } from '@vicons/ionicons5';
 
+import { LiveLineEnum, LiveRoomTypeEnum } from '@/interface';
 import { useAppStore } from '@/store/app';
 
 const appStore = useAppStore();
+
+function handleTip() {
+  window.$message.info('敬请期待~');
+}
+function changeLiveLine(item) {
+  if (
+    item === LiveLineEnum.rtc &&
+    appStore.liveRoomInfo?.type !== LiveRoomTypeEnum.user_wertc
+  ) {
+    window.$message.info('不支持该线路!');
+    return;
+  }
+  if (
+    appStore.liveRoomInfo?.type === LiveRoomTypeEnum.user_wertc &&
+    item !== LiveLineEnum.rtc
+  ) {
+    window.$message.info('不支持该线路!');
+    return;
+  }
+  appStore.setLiveLine(item);
+}
 </script>
 
 <style lang="scss" scoped>
@@ -44,30 +81,35 @@ const appStore = useAppStore();
   position: absolute;
   bottom: 0;
   left: 0;
+  z-index: 1;
   display: flex;
   align-items: center;
+  justify-content: space-between;
   box-sizing: border-box;
   padding: 0 20px 0 10px;
   width: 100%;
-  z-index: 1;
   height: 40px;
-  user-select: none;
   background-image: linear-gradient(
     -180deg,
     rgba(0, 0, 0, 0),
-    rgba(0, 0, 0, 0.6)
+    rgba(0, 0, 0, 0.7)
   );
   color: white;
   text-align: initial;
-  justify-content: space-between;
+
+  user-select: none;
   .item {
     cursor: pointer;
   }
   .left {
   }
   .right {
-    .speed {
+    display: flex;
+    align-items: center;
+    .speed,
+    .line {
       position: relative;
+      margin-left: 15px;
       &:hover {
         .list {
           display: block;
@@ -78,14 +120,17 @@ const appStore = useAppStore();
       }
       .list {
         position: absolute;
-        left: 50%;
         top: 0;
-        text-align: center;
+        left: 50%;
         display: none;
         background-color: rgba($color: #000000, $alpha: 0.5);
+        text-align: center;
         transform: translate(-50%, -100%);
         .iten {
           padding: 6px 10px;
+          &.active {
+            color: $theme-color-gold;
+          }
           &:not(:last-child) {
             margin-bottom: 4px;
           }

+ 2 - 11
src/hooks/modal/index.vue

@@ -89,17 +89,8 @@ export default defineComponent({
         font-size: 14px;
       }
       &.next {
-        background: #fceabb; /* fallback for old browsers */
-        background: -webkit-linear-gradient(
-          to left,
-          #f8b500,
-          #fceabb
-        ); /* Chrome 10-25, Safari 5.1-6 */
-        background: linear-gradient(
-          to left,
-          #f8b500,
-          #fceabb
-        ); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
+        background-color: #fbab7e;
+        background-image: linear-gradient(62deg, #fbab7e 0%, #f7ce68 100%);
         color: white;
         font-weight: 700;
         font-size: 16px;

+ 46 - 15
src/hooks/use-pull.ts

@@ -6,7 +6,9 @@ import { useSrsWs } from '@/hooks/use-srs-ws';
 import {
   DanmuMsgTypeEnum,
   IDanmu,
+  ILiveRoom,
   IMessage,
+  LiveLineEnum,
   LiveRoomTypeEnum,
 } from '@/interface';
 import { WsMsgTypeEnum } from '@/network/webSocket';
@@ -39,7 +41,6 @@ export function usePull() {
     mySocketId,
     initSrsWs,
     roomLiving,
-    liveRoomInfo,
     anchorInfo,
     liveUserList,
     damuList,
@@ -77,6 +78,7 @@ export function usePull() {
     console.log('handleHlsPlay', url);
     handleStopDrawing();
     videoLoading.value = true;
+    appStore.setLiveLine(LiveLineEnum.hls);
     startHlsPlay({
       hlsurl: url || hlsurl.value,
     });
@@ -97,17 +99,31 @@ export function usePull() {
     console.log('handleFlvPlay');
     handleStopDrawing();
     videoLoading.value = true;
+    appStore.setLiveLine(LiveLineEnum.flv);
     startFlvPlay({
       flvurl: flvurl.value,
     });
   }
 
-  async function handlePlay() {
+  function handlePlay(data: ILiveRoom) {
     console.warn('handlePlay');
-    if (liveRoomInfo.value?.type !== LiveRoomTypeEnum.user_wertc) {
-      if (!autoplayVal.value) return;
-      // await handleFlvPlay();
-      await handleHlsPlay();
+    roomLiving.value = true;
+    appStore.setLiveRoomInfo(data);
+    flvurl.value = data.flv_url!;
+    hlsurl.value = data.hls_url!;
+    switch (data.type) {
+      case LiveRoomTypeEnum.user_srs:
+        handleHlsPlay(data.hls_url);
+        break;
+      case LiveRoomTypeEnum.user_obs:
+        handleHlsPlay(data.hls_url);
+        break;
+      case LiveRoomTypeEnum.system:
+        handleHlsPlay(data.hls_url);
+        break;
+      case LiveRoomTypeEnum.user_wertc:
+        appStore.setLiveLine(LiveLineEnum.rtc);
+        break;
     }
   }
 
@@ -115,10 +131,8 @@ export function usePull() {
     () => roomLiving.value,
     (val) => {
       if (val) {
-        if (liveRoomInfo.value?.type !== LiveRoomTypeEnum.user_wertc) {
-          flvurl.value = liveRoomInfo.value?.flv_url!;
-          hlsurl.value = liveRoomInfo.value?.hls_url!;
-          handlePlay();
+        if (appStore.liveRoomInfo?.type !== LiveRoomTypeEnum.user_wertc) {
+          handlePlay(appStore.liveRoomInfo!);
         }
       } else {
         closeRtc();
@@ -127,6 +141,25 @@ export function usePull() {
     }
   );
 
+  watch(
+    () => appStore.liveLine,
+    (newVal) => {
+      console.log('liveLine变了', newVal);
+      if (!roomLiving.value) {
+        return;
+      }
+      switch (newVal) {
+        case LiveLineEnum.flv:
+          handleFlvPlay();
+          break;
+        case LiveLineEnum.hls:
+          handleHlsPlay();
+          break;
+        case LiveLineEnum.rtc:
+          break;
+      }
+    }
+  );
   watch(
     () => appStore.muted,
     (newVal) => {
@@ -140,7 +173,7 @@ export function usePull() {
   watch(
     () => networkStore.rtcMap,
     (newVal) => {
-      if (liveRoomInfo.value?.type === LiveRoomTypeEnum.user_wertc) {
+      if (appStore.liveRoomInfo?.type === LiveRoomTypeEnum.user_wertc) {
         newVal.forEach((item) => {
           videoLoading.value = false;
           const { canvas } = videoToCanvas({
@@ -164,7 +197,7 @@ export function usePull() {
         console.log('localStream变了');
         console.log('音频轨:', val?.getAudioTracks());
         console.log('视频轨:', val?.getVideoTracks());
-        if (liveRoomInfo.value?.type === LiveRoomTypeEnum.user_wertc) {
+        if (appStore.liveRoomInfo?.type === LiveRoomTypeEnum.user_wertc) {
           videoElArr.value.forEach((dom) => {
             dom.remove();
           });
@@ -299,6 +332,7 @@ export function usePull() {
   }
 
   return {
+    handlePlay,
     handleStopDrawing,
     initPull,
     closeWs,
@@ -307,8 +341,6 @@ export function usePull() {
     keydownDanmu,
     sendDanmu,
     addVideo,
-    handleHlsPlay,
-    handleFlvPlay,
     remoteVideo,
     roomLiving,
     autoplayVal,
@@ -317,7 +349,6 @@ export function usePull() {
     liveUserList,
     sidebarList,
     danmuStr,
-    liveRoomInfo,
     anchorInfo,
   };
 }

+ 1 - 1
src/hooks/use-push.ts

@@ -50,7 +50,7 @@ export function usePush() {
   watch(
     () => appStore.allTrack,
     (newTrack, oldTrack) => {
-      console.log('appStore.allTrack变了', newTrack, oldTrack);
+      console.log('appStore.allTrack变了');
       const mixedStream = new MediaStream();
       newTrack.forEach((item) => {
         if (item.track) {

+ 13 - 8
src/hooks/use-srs-ws.ts

@@ -6,7 +6,6 @@ import { WEBSOCKET_URL } from '@/constant';
 import {
   DanmuMsgTypeEnum,
   IDanmu,
-  ILiveRoom,
   ILiveUser,
   IUser,
   LiveRoomTypeEnum,
@@ -52,8 +51,8 @@ export const useSrsWs = () => {
   const isPull = ref(false);
   const roomLiving = ref(false);
   const isAnchor = ref(false);
-  const liveRoomInfo = ref<ILiveRoom>();
   const anchorInfo = ref<IUser>();
+  const anchorSocketId = ref('');
   const canvasVideoStream = ref<MediaStream>();
   const lastCoverImg = ref('');
   const currentMaxBitrate = ref(maxBitrate.value[2].value);
@@ -274,10 +273,14 @@ export const useSrsWs = () => {
     });
 
     // 主播正在直播
-    ws.socketIo.on(WsMsgTypeEnum.roomLiving, (data: WsRoomLivingType) => {
-      prettierReceiveWsMsg(WsMsgTypeEnum.roomLiving, data);
-      roomLiving.value = true;
-    });
+    ws.socketIo.on(
+      WsMsgTypeEnum.roomLiving,
+      (data: WsRoomLivingType['data']) => {
+        prettierReceiveWsMsg(WsMsgTypeEnum.roomLiving, data);
+        roomLiving.value = true;
+        anchorSocketId.value = data.anchor_socket_id;
+      }
+    );
 
     // 主播不在直播
     ws.socketIo.on(WsMsgTypeEnum.roomNoLive, (data) => {
@@ -318,7 +321,7 @@ export const useSrsWs = () => {
         id: data.socket_id,
         userInfo: data.user_info,
       });
-      liveRoomInfo.value = data.live_room;
+      appStore.setLiveRoomInfo(data.live_room);
       anchorInfo.value = data.anchor_info;
       ws.send<WsGetLiveUserType['data']>({
         msgType: WsMsgTypeEnum.getLiveUser,
@@ -401,6 +404,9 @@ export const useSrsWs = () => {
     // 用户离开房间完成
     ws.socketIo.on(WsMsgTypeEnum.leaved, (data: WsLeavedType['data']) => {
       prettierReceiveWsMsg(WsMsgTypeEnum.leaved, data);
+      if (anchorSocketId.value === data.socket_id) {
+        roomLiving.value = false;
+      }
       networkStore.rtcMap
         .get(`${roomId.value}___${data.socket_id as string}`)
         ?.close();
@@ -452,7 +458,6 @@ export const useSrsWs = () => {
     canvasVideoStream,
     lastCoverImg,
     roomLiving,
-    liveRoomInfo,
     anchorInfo,
     liveUserList,
     damuList,

+ 1 - 0
src/interface-ws.ts

@@ -27,6 +27,7 @@ export type WSGetRoomAllUserType = IWsFormat<{
 
 export type WsRoomLivingType = IWsFormat<{
   live_room: ILiveRoom;
+  anchor_socket_id: string;
 }>;
 
 export type WsGetLiveUserType = IWsFormat<{

+ 9 - 11
src/interface.ts

@@ -1,5 +1,11 @@
 // 这里放项目里面的类型
 
+export enum LiveLineEnum {
+  rtc = 'rtc',
+  hls = 'hls',
+  flv = 'flv',
+}
+
 export enum PayStatusEnum {
   error = 'error',
   WAIT_BUYER_PAY = 'WAIT_BUYER_PAY',
@@ -177,20 +183,12 @@ export interface IUserLiveRoom {
   deleted_at?: string;
 }
 
-export enum LiveTypeEnum {
-  srsFlvPull = 'srsFlvPull',
-  srsHlsPull = 'srsHlsPull',
-
-  srsPush = 'srsPush',
-  webrtcPush = 'webrtcPush',
-}
-
 /** 直播间类型 */
 export enum LiveRoomTypeEnum {
   system, // 系统直播
-  user_wertc, // 主播使用webrtc直播(用户只能看webrtc直播)
-  user_srs, // 主播使用srs直播(用户可以看webrtc或flv直播)
-  user_obs, // 主播使用obs/ffmpeg直播(用户只能看flv直播)
+  user_wertc, // 主播使用webrtc直播
+  user_srs, // 主播使用srs直播
+  user_obs, // 主播使用obs/ffmpeg直播
 }
 
 export interface BilldHtmlWebpackPluginLog {

+ 4 - 0
src/network/webRTC.ts

@@ -112,6 +112,7 @@ export class WebRTCClass {
           mediaName: '',
           streamid: stream.id,
           trackid: track.id,
+          scaleInfo: {},
         });
       }
     });
@@ -127,6 +128,7 @@ export class WebRTCClass {
           mediaName: '',
           streamid: stream.id,
           trackid: track.id,
+          scaleInfo: {},
         });
       }
     });
@@ -142,6 +144,7 @@ export class WebRTCClass {
           mediaName: '',
           streamid: stream.id,
           trackid: track.id,
+          scaleInfo: {},
         });
       }
     });
@@ -157,6 +160,7 @@ export class WebRTCClass {
           mediaName: '',
           streamid: stream.id,
           trackid: track.id,
+          scaleInfo: {},
         });
       }
     });

+ 12 - 2
src/store/app/index.ts

@@ -1,6 +1,6 @@
 import { defineStore } from 'pinia';
 
-import { MediaTypeEnum } from '@/interface';
+import { ILiveRoom, LiveLineEnum, MediaTypeEnum } from '@/interface';
 import { mobileRouterName } from '@/router';
 
 export type AppRootState = {
@@ -27,8 +27,10 @@ export type AppRootState = {
     timeInfo?: { color: string };
     stopwatchInfo?: { color: string };
     rect?: { top: number; left: number };
-    scaleInfo?: { scaleX: number; scaleY: number };
+    scaleInfo: Record<number, { scaleX: number; scaleY: number }>;
   }[];
+  liveLine: LiveLineEnum;
+  liveRoomInfo?: ILiveRoom;
 };
 
 export const useAppStore = defineStore('app', {
@@ -42,9 +44,17 @@ export const useAppStore = defineStore('app', {
         { routeName: mobileRouterName.h5Profile, name: '我的' },
       ],
       allTrack: [],
+      liveLine: LiveLineEnum.hls,
+      liveRoomInfo: undefined,
     };
   },
   actions: {
+    setLiveRoomInfo(res: AppRootState['liveRoomInfo']) {
+      this.liveRoomInfo = res;
+    },
+    setLiveLine(res: AppRootState['liveLine']) {
+      this.liveLine = res;
+    },
     setMuted(res: AppRootState['muted']) {
       this.muted = res;
     },

+ 5 - 3
src/utils/index.ts

@@ -227,10 +227,11 @@ export function videoToCanvas(data: { videoEl: HTMLVideoElement }) {
   let timer;
   let w = videoEl.videoWidth;
   let h = videoEl.videoHeight;
-  videoEl.addEventListener('resize', () => {
+  function handleResize() {
     w = videoEl.videoWidth;
     h = videoEl.videoHeight;
-  });
+  }
+  videoEl.addEventListener('resize', handleResize);
   function drawCanvas() {
     canvas.width = w;
     canvas.height = h;
@@ -239,10 +240,11 @@ export function videoToCanvas(data: { videoEl: HTMLVideoElement }) {
   }
 
   function stopDrawing() {
+    videoEl.removeEventListener('resize', handleResize);
     cancelAnimationFrame(timer);
   }
 
   drawCanvas();
 
-  return { drawCanvas, stopDrawing, canvas, videoEl };
+  return { drawCanvas, stopDrawing, canvas };
 }

+ 22 - 28
src/views/area/index.vue

@@ -1,16 +1,14 @@
 <template>
-  <div class="area-list">
-    <a
+  <div class="tab-list">
+    <div
       v-for="(item, index) in areaList"
       :key="index"
-      class="item"
-      :class="{
-        active: router.currentRoute.value.name === routerName.ad,
-      }"
+      class="tab"
+      :class="{ active: router.currentRoute.value.params.id === item.id + '' }"
       @click.prevent="changeArea(item)"
     >
       {{ item.name }}
-    </a>
+    </div>
   </div>
   <router-view></router-view>
 </template>
@@ -45,39 +43,35 @@ onMounted(() => {
 </script>
 
 <style lang="scss" scoped>
-.area-list {
+.tab-list {
   display: flex;
   align-items: center;
-  padding: 10px 30px;
-  height: 30px;
-  .item {
+  margin-bottom: 20px;
+  height: 40px;
+  font-size: 14px;
+  padding: 0 30px;
+
+  user-select: none;
+  .tab {
     position: relative;
-    display: flex;
-    align-items: center;
     margin-right: 20px;
-    height: 100%;
-    color: black;
-    text-decoration: none;
-    font-size: 14px;
     cursor: pointer;
-
     &.active {
+      color: $theme-color-gold;
+      font-size: 16px;
+
       &::after {
         position: absolute;
-        top: calc(50% - 8px);
-        right: -5px;
-        width: 5px;
-        height: 5px;
-        border-radius: 50%;
+        bottom: -6px;
+        left: 50%;
+        width: 20px;
+        height: 2px;
+        border-radius: 10px;
         background-color: $theme-color-gold;
         content: '';
-        transition: all 0.1s ease;
-        transform: translateY(-100%);
+        transform: translateX(-50%);
       }
     }
-    &:hover {
-      color: $theme-color-gold;
-    }
   }
 }
 </style>

+ 7 - 3
src/views/h5/index.vue

@@ -64,7 +64,7 @@
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref } from 'vue';
+import { onMounted, onUnmounted, ref } from 'vue';
 
 import { fetchAreaLiveRoomList } from '@/api/area';
 import { IArea, IAreaLiveRoom } from '@/interface';
@@ -77,7 +77,7 @@ const swiperList = ref([
   { id: 2, txt: '广告位2', bg: '#C850C0', url: '' },
   { id: 3, txt: '广告位3', bg: '#4158D0', url: '' },
 ]);
-const timer = ref();
+const swiperTimer = ref();
 const currentSwiper = ref(swiperList.value[0]);
 
 async function getLiveRoomList() {
@@ -115,7 +115,7 @@ function goRoom(item: IAreaLiveRoom) {
 onMounted(() => {
   getLiveRoomList();
   let num = 0;
-  timer.value = setInterval(() => {
+  swiperTimer.value = setInterval(() => {
     num += 1;
     if (num > swiperList.value.length - 1) {
       num = 0;
@@ -123,6 +123,10 @@ onMounted(() => {
     currentSwiper.value = swiperList.value[num];
   }, 3000);
 });
+
+onUnmounted(() => {
+  clearInterval(swiperTimer.value);
+});
 </script>
 
 <style lang="scss" scoped>

+ 2 - 3
src/views/h5/room/index.vue

@@ -127,10 +127,10 @@ const videoWrapHeight = ref(0);
 const remoteVideoRef = ref<HTMLDivElement>();
 
 const {
+  handlePlay,
   initPull,
   keydownDanmu,
   sendDanmu,
-  handleHlsPlay,
   autoplayVal,
   videoLoading,
   damuList,
@@ -174,7 +174,6 @@ async function getLiveRoomInfo() {
     if (res.code === 200) {
       if (res.data.type === LiveRoomTypeEnum.user_wertc) {
         autoplayVal.value = true;
-        roomLiveType.value = LiveRoomTypeEnum.user_wertc;
         showPlayBtn.value = false;
       } else {
         showPlayBtn.value = true;
@@ -191,7 +190,7 @@ async function getLiveRoomInfo() {
 function startPull() {
   appStore.setMuted(false);
   showPlayBtn.value = false;
-  handleHlsPlay(liveRoomInfo.value?.hls_url);
+  handlePlay();
 }
 onUnmounted(() => {
   closeWs();

+ 7 - 71
src/views/home/index.vue

@@ -42,44 +42,10 @@
               }"
             >
               <div
-                v-if="
-                  currentLiveRoom.live_room?.type ===
-                  LiveRoomTypeEnum.user_wertc
-                "
                 class="btn webrtc"
-                @click="joinRtcRoom()"
+                @click="joinRoom({ roomId: currentLiveRoom.live_room?.id! })"
               >
-                进入直播(webrtc)
-              </div>
-              <div
-                v-if="
-                  currentLiveRoom.live_room?.type !==
-                  LiveRoomTypeEnum.user_wertc
-                "
-                class="btn flv"
-                @click="
-                  joinRoom({
-                    isFlv: true,
-                    roomId: currentLiveRoom.live_room_id!,
-                  })
-                "
-              >
-                进入直播(flv)
-              </div>
-              <div
-                v-if="
-                  currentLiveRoom.live_room?.type !==
-                  LiveRoomTypeEnum.user_wertc
-                "
-                class="btn hls"
-                @click="
-                  joinRoom({
-                    isFlv: false,
-                    roomId: currentLiveRoom.live_room_id!,
-                  })
-                "
-              >
-                进入直播(hls)
+                进入直播
               </div>
             </div>
           </template>
@@ -144,7 +110,6 @@
             class="live-room"
             @click="
               joinRoom({
-                isFlv: false,
                 roomId: iten.live_room?.id!,
               })
             "
@@ -189,7 +154,7 @@ import { useRouter } from 'vue-router';
 import { fetchLiveList } from '@/api/live';
 import { sliderList } from '@/constant';
 import { usePull } from '@/hooks/use-pull';
-import { ILive, LiveRoomTypeEnum, LiveTypeEnum } from '@/interface';
+import { ILive } from '@/interface';
 import { routerName } from '@/router';
 
 const router = useRouter();
@@ -202,8 +167,7 @@ const currentLiveRoom = ref<ILive>();
 const interactionList = ref(sliderList);
 const remoteVideoRef = ref<HTMLDivElement>();
 
-const { handleHlsPlay, videoLoading, remoteVideo, handleStopDrawing } =
-  usePull();
+const { handlePlay, videoLoading, remoteVideo, handleStopDrawing } = usePull();
 
 watch(
   () => remoteVideo.value,
@@ -225,15 +189,7 @@ function changeLiveRoom(item: ILive) {
   canvasRef.value?.childNodes?.forEach((item) => {
     item.remove();
   });
-  if (
-    [
-      LiveRoomTypeEnum.user_srs,
-      LiveRoomTypeEnum.user_obs,
-      LiveRoomTypeEnum.system,
-    ].includes(item.live_room?.type!)
-  ) {
-    handleHlsPlay(item.live_room?.hls_url!);
-  }
+  handlePlay(item.live_room!);
 }
 
 async function getLiveRoomList() {
@@ -247,15 +203,7 @@ async function getLiveRoomList() {
       otherLiveRoomList.value = res.data.rows.slice(topNums.value);
       if (res.data.total) {
         currentLiveRoom.value = topLiveRoomList.value[0];
-        if (
-          [
-            LiveRoomTypeEnum.user_srs,
-            LiveRoomTypeEnum.user_obs,
-            LiveRoomTypeEnum.system,
-          ].includes(currentLiveRoom.value?.live_room?.type!)
-        ) {
-          handleHlsPlay(currentLiveRoom.value.live_room?.hls_url!);
-        }
+        handlePlay(currentLiveRoom.value.live_room!);
       }
     }
   } catch (error) {
@@ -267,22 +215,10 @@ onMounted(() => {
   getLiveRoomList();
 });
 
-function joinRtcRoom() {
-  router.push({
-    name: routerName.pull,
-    params: {
-      roomId: currentLiveRoom.value?.live_room_id,
-    },
-  });
-}
-
-function joinRoom(data: { roomId: number; isFlv: boolean }) {
+function joinRoom(data: { roomId: number }) {
   router.push({
     name: routerName.pull,
     params: { roomId: data.roomId },
-    query: {
-      liveType: data.isFlv ? LiveTypeEnum.srsFlvPull : LiveTypeEnum.srsHlsPull,
-    },
   });
 }
 </script>

+ 2 - 5
src/views/pull/index.vue

@@ -15,7 +15,7 @@
           <div class="detail">
             <div class="top">{{ anchorInfo?.username }}</div>
             <div class="bottom">
-              <span>{{ liveRoomInfo?.name }}</span>
+              <span>{{ appStore.liveRoomInfo?.name }}</span>
               <span v-if="NODE_ENV === 'development'">
                 socketId:{{ mySocketId }}
               </span>
@@ -43,7 +43,7 @@
             class="cover"
             :style="{
               backgroundImage: `url(${
-                liveRoomInfo?.cover_img || anchorInfo?.avatar
+                appStore.liveRoomInfo?.cover_img || anchorInfo?.avatar
               })`,
             }"
           ></div>
@@ -182,7 +182,6 @@
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, ref, watch } from 'vue';
-import { useRoute } from 'vue-router';
 
 import { fetchGoodsList } from '@/api/goods';
 import { loginTip } from '@/hooks/use-login';
@@ -194,7 +193,6 @@ import { NODE_ENV } from 'script/constant';
 
 import RechargeCpt from './recharge/index.vue';
 
-const route = useRoute();
 const userStore = useUserStore();
 const appStore = useAppStore();
 const giftGoodsList = ref<IGoods[]>([]);
@@ -219,7 +217,6 @@ const {
   damuList,
   liveUserList,
   danmuStr,
-  liveRoomInfo,
   anchorInfo,
 } = usePull();
 

+ 4 - 5
src/views/push/index.vue

@@ -1,21 +1,20 @@
 <template>
   <div>
-    <SrsCpt v-if="route.query.liveType === LiveTypeEnum.srsPush"></SrsCpt>
-    <RtcCpt
-      v-else-if="route.query.liveType === LiveTypeEnum.webrtcPush"
-    ></RtcCpt>
+    <SrsCpt v-if="liveType === LiveRoomTypeEnum.user_srs"></SrsCpt>
+    <RtcCpt v-else-if="liveType === LiveRoomTypeEnum.user_wertc"></RtcCpt>
   </div>
 </template>
 
 <script lang="ts" setup>
 import { useRoute } from 'vue-router';
 
-import { LiveTypeEnum } from '@/interface';
+import { LiveRoomTypeEnum } from '@/interface';
 
 import RtcCpt from './rtc/index.vue';
 import SrsCpt from './srs/index.vue';
 
 const route = useRoute();
+const liveType = Number(route.query.liveType);
 </script>
 
 <style lang="scss" scoped></style>

+ 92 - 18
src/views/push/rtc/index.vue

@@ -674,14 +674,56 @@ function initCanvas() {
   changeCanvasStyle();
 }
 
+/**
+ * 1: {scaleX: 1, scaleY: 1}
+ * 2: {scaleX: 0.5, scaleY: 0.5}
+ * 3: {scaleX: 0.3333333333333333, scaleY: 0.3333333333333333}
+ * 4: {scaleX: 0.25, scaleY: 0.25}
+ */
+
+/**
+ * 二倍屏即1px里面有2个像素;三倍屏1px里面有3个像素,以此类推
+ * 一个图片,宽高都是100px
+ * 一倍屏展示:100px等于100个像素,一比一展示
+ * 二倍屏展示:100px等于100个像素,二比一展示,即在二倍屏的100px看起来会比一倍屏的100px小一倍
+ * 如果需要在一杯和二倍屏幕的时候看的大小都一样:
+ * 1,在二倍屏的时候,需要将100px放大一倍,即200px;
+ * 2,在一倍屏的时候,需要将100px缩小一百,即50px;
+ */
 function handleScaling({ canvasDom, id }) {
   canvasDom.on('scaling', () => {
     appStore.allTrack.forEach((item) => {
       if (id === item.id) {
-        item.scaleInfo = {
+        item.scaleInfo[window.devicePixelRatio] = {
           scaleX: canvasDom.scaleX || 1,
           scaleY: canvasDom.scaleY || 1,
         };
+        Object.keys(item.scaleInfo).forEach((iten) => {
+          if (window.devicePixelRatio !== Number(iten)) {
+            if (window.devicePixelRatio > Number(iten)) {
+              item.scaleInfo[iten] = {
+                scaleX:
+                  item.scaleInfo[window.devicePixelRatio].scaleX *
+                  window.devicePixelRatio,
+                scaleY:
+                  item.scaleInfo[window.devicePixelRatio].scaleY *
+                  window.devicePixelRatio,
+              };
+            } else {
+              if (window.devicePixelRatio === 1) {
+                item.scaleInfo[iten] = {
+                  scaleX: item.scaleInfo[1].scaleX / Number(iten),
+                  scaleY: item.scaleInfo[1].scaleY / Number(iten),
+                };
+              } else {
+                item.scaleInfo[iten] = {
+                  scaleX: item.scaleInfo[1].scaleX * Number(iten),
+                  scaleY: item.scaleInfo[1].scaleY * Number(iten),
+                };
+              }
+            }
+          }
+        });
       }
     });
     resourceCacheStore.setList(appStore.allTrack);
@@ -762,7 +804,7 @@ async function handleCache() {
             handleMoving({ canvasDom, id: item.id });
             handleScaling({ canvasDom, id: item.id });
             canvasDom.scale(
-              (item.scaleInfo?.scaleX || 1) / window.devicePixelRatio
+              item.scaleInfo[window.devicePixelRatio].scaleX || 1
             );
             fabricCanvas.value!.add(canvasDom);
             obj.videoEl = videoEl;
@@ -813,9 +855,7 @@ async function handleCache() {
           );
           handleMoving({ canvasDom, id: obj.id });
           handleScaling({ canvasDom, id: obj.id });
-          canvasDom.scale(
-            (item.scaleInfo?.scaleX || 1) / window.devicePixelRatio
-          );
+          canvasDom.scale(item.scaleInfo[window.devicePixelRatio].scaleX || 1);
           fabricCanvas.value.add(canvasDom);
           obj.canvasDom = canvasDom;
         }
@@ -841,7 +881,7 @@ async function handleCache() {
         handleMoving({ canvasDom, id: obj.id });
         handleScaling({ canvasDom, id: obj.id });
         // fabric.Text类型不能除以分辨率
-        canvasDom.scale(item.scaleInfo?.scaleX || 1);
+        canvasDom.scale(item.scaleInfo[window.devicePixelRatio].scaleX);
         fabricCanvas.value.add(canvasDom);
         obj.canvasDom = canvasDom;
       }
@@ -860,7 +900,7 @@ async function handleCache() {
         handleMoving({ canvasDom, id: obj.id });
         handleScaling({ canvasDom, id: obj.id });
         // fabric.Text类型不能除以分辨率
-        canvasDom.scale(item.scaleInfo?.scaleX || 1);
+        canvasDom.scale(item.scaleInfo[window.devicePixelRatio].scaleX);
         fabricCanvas.value.add(canvasDom);
         obj.canvasDom = canvasDom;
       }
@@ -879,7 +919,7 @@ async function handleCache() {
         handleMoving({ canvasDom, id: obj.id });
         handleScaling({ canvasDom, id: obj.id });
         // fabric.Text类型不能除以分辨率
-        canvasDom.scale(item.scaleInfo?.scaleX || 1);
+        canvasDom.scale(item.scaleInfo[window.devicePixelRatio].scaleX);
         fabricCanvas.value.add(canvasDom);
         obj.canvasDom = canvasDom;
       }
@@ -897,6 +937,23 @@ function selectMediaOk(val: MediaTypeEnum) {
   currentMediaType.value = val;
 }
 
+function setScaleInfo({ track, canvasDom }) {
+  [1, 2, 3, 4].forEach((devicePixelRatio) => {
+    track.scaleInfo[devicePixelRatio] = {
+      scaleX: 1 / devicePixelRatio,
+      scaleY: 1 / devicePixelRatio,
+    };
+  });
+  if (window.devicePixelRatio !== 1) {
+    const ratio = 1 / window.devicePixelRatio;
+    canvasDom.scale(ratio);
+    track.scaleInfo[window.devicePixelRatio] = {
+      scaleX: ratio,
+      scaleY: ratio,
+    };
+  }
+}
+
 async function addMediaOk(val: {
   type: MediaTypeEnum;
   deviceId: string;
@@ -932,13 +989,14 @@ async function addMediaOk(val: {
       streamid: event.id,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
 
     const { canvasDom, videoEl, scale } = await autoCreateVideo({
       stream: event,
       id: videoTrack.id,
     });
-    videoTrack.scaleInfo = { scaleX: scale, scaleY: scale };
+    setScaleInfo({ canvasDom, track: videoTrack });
     videoTrack.videoEl = videoEl;
     // @ts-ignore
     videoTrack.canvasDom = canvasDom;
@@ -958,6 +1016,7 @@ async function addMediaOk(val: {
         streamid: event.id,
         hidden: true,
         muted: false,
+        scaleInfo: {},
       };
       const res = [...appStore.allTrack, videoTrack, audioTrack];
       appStore.setAllTrack(res);
@@ -995,12 +1054,13 @@ async function addMediaOk(val: {
       streamid: event.id,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
     const { canvasDom, videoEl, scale } = await autoCreateVideo({
       stream: event,
       id: videoTrack.id,
     });
-    videoTrack.scaleInfo = { scaleX: scale, scaleY: scale };
+    setScaleInfo({ canvasDom, track: videoTrack });
     videoTrack.videoEl = videoEl;
     // @ts-ignore
     videoTrack.canvasDom = canvasDom;
@@ -1028,6 +1088,7 @@ async function addMediaOk(val: {
       streamid: event.id,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
     const res = [...appStore.allTrack, audioTrack];
     appStore.setAllTrack(res);
@@ -1050,6 +1111,7 @@ async function addMediaOk(val: {
       streamid: undefined,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
     if (fabricCanvas.value) {
       console.log('val.txtInfo?.txt ', val.txtInfo?.txt);
@@ -1063,7 +1125,16 @@ async function addMediaOk(val: {
       handleMoving({ canvasDom, id: txtTrack.id });
       handleScaling({ canvasDom, id: txtTrack.id });
       txtTrack.txtInfo = val.txtInfo;
-      txtTrack.scaleInfo = { scaleX: 1, scaleY: 1 };
+      if (window.devicePixelRatio !== 1) {
+        const ratio = 1 / window.devicePixelRatio;
+        canvasDom.scale(ratio);
+        txtTrack.scaleInfo[window.devicePixelRatio] = {
+          scaleX: ratio,
+          scaleY: ratio,
+        };
+      } else {
+        txtTrack.scaleInfo[window.devicePixelRatio] = { scaleX: 1, scaleY: 1 };
+      }
       // @ts-ignore
       txtTrack.canvasDom = canvasDom;
       fabricCanvas.value.add(canvasDom);
@@ -1091,6 +1162,7 @@ async function addMediaOk(val: {
       streamid: undefined,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
     if (fabricCanvas.value) {
       const canvasDom = markRaw(
@@ -1100,12 +1172,11 @@ async function addMediaOk(val: {
           fill: val.timeInfo?.color,
         })
       );
+      setScaleInfo({ canvasDom, track: timeTrack });
       timeCanvasDom.value.push(canvasDom);
       handleMoving({ canvasDom, id: timeTrack.id });
       handleScaling({ canvasDom, id: timeTrack.id });
       timeTrack.timeInfo = val.timeInfo;
-      timeTrack.scaleInfo = { scaleX: 1, scaleY: 1 };
-
       // @ts-ignore
       timeTrack.canvasDom = canvasDom;
       fabricCanvas.value.add(canvasDom);
@@ -1133,6 +1204,7 @@ async function addMediaOk(val: {
       streamid: undefined,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
     if (fabricCanvas.value) {
       const canvasDom = markRaw(
@@ -1143,6 +1215,7 @@ async function addMediaOk(val: {
           // editable: true,
         })
       );
+      setScaleInfo({ canvasDom, track: stopwatchTrack });
       stopwatchCanvasDom.value.push(canvasDom);
       handleMoving({ canvasDom, id: stopwatchTrack.id });
       handleScaling({ canvasDom, id: stopwatchTrack.id });
@@ -1174,6 +1247,7 @@ async function addMediaOk(val: {
       streamid: undefined,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
 
     if (fabricCanvas.value) {
@@ -1207,13 +1281,12 @@ async function addMediaOk(val: {
           height: imgEl.height,
         })
       );
+      const ratio = handleScale({ width: imgEl.width, height: imgEl.height });
+      setScaleInfo({ canvasDom, track: imgTrack });
       handleMoving({ canvasDom, id: imgTrack.id });
       handleScaling({ canvasDom, id: imgTrack.id });
-      const ratio = handleScale({ width: imgEl.width, height: imgEl.height });
       // @ts-ignore
       imgTrack.canvasDom = canvasDom;
-      imgTrack.scaleInfo = { scaleX: ratio, scaleY: ratio };
-      canvasDom.scale(ratio / window.devicePixelRatio);
       fabricCanvas.value.add(canvasDom);
     }
 
@@ -1239,6 +1312,7 @@ async function addMediaOk(val: {
       streamid: undefined,
       hidden: false,
       muted: false,
+      scaleInfo: {},
     };
     if (fabricCanvas.value) {
       if (!val.mediaInfo) return;
@@ -1261,7 +1335,7 @@ async function addMediaOk(val: {
         stream,
         id: mediaVideoTrack.id,
       });
-      mediaVideoTrack.scaleInfo = { scaleX: scale, scaleY: scale };
+      setScaleInfo({ canvasDom, track: mediaVideoTrack });
       mediaVideoTrack.videoEl = videoEl;
       // @ts-ignore
       mediaVideoTrack.canvasDom = canvasDom;
@@ -1280,6 +1354,7 @@ async function addMediaOk(val: {
           streamid: stream.id,
           hidden: true,
           muted: false,
+          scaleInfo: {},
         };
         // @ts-ignore
         const res = [...appStore.allTrack, audioTrack];
@@ -1302,7 +1377,6 @@ async function addMediaOk(val: {
 
     console.log('获取视频成功', fabricCanvas.value);
   }
-
   // canvasVideoStream.value = pushCanvasRef.value!.captureStream();
 }
 

+ 37 - 11
src/views/push/srs/index.vue

@@ -838,8 +838,12 @@ async function handleCache() {
         );
         handleMoving({ canvasDom, id: obj.id });
         handleScaling({ canvasDom, id: obj.id });
-        // fabric.Text类型不能除以分辨率
-        canvasDom.scale(item.scaleInfo?.scaleX || 1);
+        if (window.devicePixelRatio !== 1) {
+          // fabric.Text类型不能除以分辨率
+          canvasDom.scale(
+            (item.scaleInfo?.scaleX || 1) / window.devicePixelRatio
+          );
+        }
         fabricCanvas.value.add(canvasDom);
         obj.canvasDom = canvasDom;
       }
@@ -857,8 +861,12 @@ async function handleCache() {
         timeCanvasDom.value.push(canvasDom);
         handleMoving({ canvasDom, id: obj.id });
         handleScaling({ canvasDom, id: obj.id });
-        // fabric.Text类型不能除以分辨率
-        canvasDom.scale(item.scaleInfo?.scaleX || 1);
+        if (window.devicePixelRatio !== 1) {
+          // fabric.Text类型不能除以分辨率
+          canvasDom.scale(
+            (item.scaleInfo?.scaleX || 1) / window.devicePixelRatio
+          );
+        }
         fabricCanvas.value.add(canvasDom);
         obj.canvasDom = canvasDom;
       }
@@ -876,8 +884,11 @@ async function handleCache() {
         stopwatchCanvasDom.value.push(canvasDom);
         handleMoving({ canvasDom, id: obj.id });
         handleScaling({ canvasDom, id: obj.id });
-        // fabric.Text类型不能除以分辨率
-        canvasDom.scale(item.scaleInfo?.scaleX || 1);
+        if (window.devicePixelRatio !== 1) {
+          // fabric.Text类型不能除以分辨率
+          canvasDom.scale(item.scaleInfo?.scaleX || 1);
+          console.log(item.scaleInfo?.scaleX, '4423341');
+        }
         fabricCanvas.value.add(canvasDom);
         obj.canvasDom = canvasDom;
       }
@@ -1061,7 +1072,12 @@ async function addMediaOk(val: {
       handleMoving({ canvasDom, id: txtTrack.id });
       handleScaling({ canvasDom, id: txtTrack.id });
       txtTrack.txtInfo = val.txtInfo;
-      txtTrack.scaleInfo = { scaleX: 1, scaleY: 1 };
+      let ratio = 1;
+      if (window.devicePixelRatio !== 1) {
+        ratio = 1 / window.devicePixelRatio;
+        txtTrack.scaleInfo = { scaleX: 1, scaleY: 1 };
+      }
+      canvasDom.scale(ratio);
       // @ts-ignore
       txtTrack.canvasDom = canvasDom;
       fabricCanvas.value.add(canvasDom);
@@ -1098,12 +1114,16 @@ async function addMediaOk(val: {
           fill: val.timeInfo?.color,
         })
       );
-      timeCanvasDom.value.push(canvasDom);
       handleMoving({ canvasDom, id: timeTrack.id });
       handleScaling({ canvasDom, id: timeTrack.id });
       timeTrack.timeInfo = val.timeInfo;
-      timeTrack.scaleInfo = { scaleX: 1, scaleY: 1 };
-
+      let ratio = 1;
+      if (window.devicePixelRatio !== 1) {
+        ratio = 1 / window.devicePixelRatio;
+        timeTrack.scaleInfo = { scaleX: 1, scaleY: 1 };
+      }
+      canvasDom.scale(ratio);
+      timeCanvasDom.value.push(canvasDom);
       // @ts-ignore
       timeTrack.canvasDom = canvasDom;
       fabricCanvas.value.add(canvasDom);
@@ -1141,10 +1161,16 @@ async function addMediaOk(val: {
           // editable: true,
         })
       );
-      stopwatchCanvasDom.value.push(canvasDom);
       handleMoving({ canvasDom, id: stopwatchTrack.id });
       handleScaling({ canvasDom, id: stopwatchTrack.id });
       stopwatchTrack.stopwatchInfo = val.stopwatchInfo;
+      let ratio = 1;
+      if (window.devicePixelRatio !== 1) {
+        ratio = 1 / window.devicePixelRatio;
+        stopwatchTrack.scaleInfo = { scaleX: 1, scaleY: 1 };
+      }
+      canvasDom.scale(ratio);
+      stopwatchCanvasDom.value.push(canvasDom);
       // @ts-ignore
       stopwatchTrack.canvasDom = canvasDom;
       fabricCanvas.value.add(canvasDom);

+ 1 - 1
src/views/shop/index.vue

@@ -135,7 +135,7 @@ function startPay(item: IGoods) {
 
 <style lang="scss" scoped>
 .shop-wrap {
-  padding: 10px 30px;
+  padding: 0 30px;
   .tab-list {
     display: flex;
     align-items: center;