浏览代码

fix: 兼容safari

shuisheng 2 年之前
父节点
当前提交
2cf0d241e9
共有 7 个文件被更改,包括 67 次插入44 次删除
  1. 7 1
      .eslintrc.js
  2. 19 6
      src/hooks/use-play.ts
  3. 15 17
      src/hooks/use-pull.ts
  4. 3 2
      src/hooks/use-push.ts
  5. 7 4
      src/hooks/use-ws.ts
  6. 8 9
      src/utils/index.ts
  7. 8 5
      src/views/home/index.vue

+ 7 - 1
.eslintrc.js

@@ -158,7 +158,13 @@ module.exports = {
     'import/no-named-as-default': 0, // https://github.com/import-js/eslint-plugin-import/blob/v2.26.0/docs/rules/no-named-as-default.md
 
     // @typescript-eslint插件
-    '@typescript-eslint/restrict-template-expressions': 2, // 强制模板文字表达式为string类型。即const a = {};console.log(`${a}`);会报错
+    '@typescript-eslint/restrict-template-expressions': [
+      'error',
+      {
+        allowBoolean: true,
+        allowNumber: true,
+      },
+    ], // 强制模板文字表达式为string类型。即const a = {};console.log(`${a}`);会报错
     '@typescript-eslint/no-unused-vars': 2,
     '@typescript-eslint/no-floating-promises': 0, // 要求适当处理类似 Promise 的语句。即将await或者return Promise,或者对promise进行.then或者.catch
     '@typescript-eslint/no-explicit-any': 0, // 不允许定义any类型。即let a: any;会报错

+ 19 - 6
src/hooks/use-play.ts

@@ -23,10 +23,9 @@ export function useFlvPlay() {
 
   function destroyFlv() {
     if (flvPlayer.value) {
-      console.log(flvPlayer.value.destroy);
       flvPlayer.value.destroy();
     }
-    // flvVideoEl.value?.remove();
+    flvVideoEl.value?.remove();
   }
 
   watch(
@@ -44,17 +43,28 @@ export function useFlvPlay() {
       flvVideoEl.value.muted = val;
     }
   }
+  function flvPlayTest() {
+    flvPlayer.value?.play();
+  }
 
   function startFlvPlay(data: { flvurl: string }) {
     destroyFlv();
     return new Promise<{ width: number; height: number }>((resolve) => {
-      if (mpegts.getFeatureList().mseLivePlayback) {
+      if (mpegts.getFeatureList().mseLivePlayback && mpegts.isSupported()) {
         flvPlayer.value = mpegts.createPlayer({
           type: 'flv', // could also be mpegts, m2ts, flv
           isLive: true,
           url: data.flvurl,
         });
         const videoEl = createVideo({ muted: true, autoplay: true });
+        videoEl.style.width = `1px`;
+        videoEl.style.height = `1px`;
+        videoEl.style.position = 'fixed';
+        videoEl.style.bottom = '0';
+        videoEl.style.right = '0';
+        videoEl.style.opacity = '0';
+        videoEl.style.pointerEvents = 'none';
+        document.body.appendChild(videoEl);
         flvVideoEl.value = videoEl;
         flvVideoEl.value.addEventListener('play', () => {
           console.log('flv-play');
@@ -71,7 +81,7 @@ export function useFlvPlay() {
         flvPlayer.value.attachMediaElement(flvVideoEl.value);
         flvPlayer.value.load();
         try {
-          console.log('开始播放flv');
+          console.log(`开始播放flv,muted:${appStore.muted}`);
           flvPlayer.value.play();
         } catch (err) {
           console.error('flv播放失败');
@@ -83,7 +93,7 @@ export function useFlvPlay() {
     });
   }
 
-  return { flvPlayer, flvVideoEl, startFlvPlay, destroyFlv };
+  return { flvPlayer, flvVideoEl, startFlvPlay, destroyFlv, flvPlayTest };
 }
 
 export function useHlsPlay() {
@@ -138,7 +148,7 @@ export function useHlsPlay() {
           ],
         },
         function () {
-          console.log('开始播放hls');
+          console.log(`开始播放hls,muted:${appStore.muted}`);
           hlsPlayer.value?.play();
           hlsPlayer.value?.on('play', () => {
             console.log('hls-play');
@@ -167,6 +177,9 @@ export function useHlsPlay() {
           });
         }
       );
+      hlsPlayer.value.on('error', (e) => {
+        console.error('hlsPlayer错误回调', e);
+      });
     });
   }
 

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

@@ -45,7 +45,7 @@ export function usePull({
     roomLiveing,
     liveRoomInfo,
     roomNoLive,
-    heartbeatTimer,
+    loopHeartbeatTimer,
     localStream,
     liveUserList,
     damuList,
@@ -64,22 +64,22 @@ export function usePull({
   const stopDrawingArr = ref<any[]>([]);
 
   onUnmounted(() => {
-    stopOldDrawing();
+    clearInterval(loopHeartbeatTimer.value);
+    handleStopDrawing();
   });
 
-  function stopOldDrawing() {
+  function handleStopDrawing() {
     stopDrawingArr.value.forEach((cb) => cb());
     stopDrawingArr.value = [];
   }
 
   async function handleHlsPlay() {
     console.log('handleHlsPlay');
+    handleStopDrawing();
     videoLoading.value = true;
-    stopOldDrawing();
     const { width, height } = await startHlsPlay({ hlsurl: hlsurl.value });
     const { canvas, stopDrawing } = videoToCanvas({
       videoEl: hlsVideoEl.value!,
-      targetEl: canvasRef.value!,
       size: { width, height },
     });
     stopDrawingArr.value.push(stopDrawing);
@@ -89,33 +89,30 @@ export function usePull({
 
   async function handleFlvPlay() {
     console.log('handleFlvPlay');
-    stopOldDrawing();
-    await startFlvPlay({ flvurl: flvurl.value });
+    handleStopDrawing();
+    const { width, height } = await startFlvPlay({
+      flvurl: flvurl.value,
+    });
     const initCanvas = videoToCanvas({
       videoEl: flvVideoEl.value!,
-      targetEl: canvasRef.value!,
+      size: {
+        width,
+        height,
+      },
     });
     stopDrawingArr.value.push(initCanvas.stopDrawing);
     canvasRef.value!.appendChild(initCanvas.canvas);
     flvPlayer.value!.on(mpegts.Events.MEDIA_INFO, () => {
-      stopOldDrawing();
-      const newCanvas = videoToCanvas({
-        videoEl: flvVideoEl.value!,
-        targetEl: canvasRef.value!,
-      });
-      initCanvas.canvas = newCanvas.canvas;
-      stopDrawingArr.value.push(newCanvas.stopDrawing);
+      initCanvas.videoEl = flvVideoEl.value!;
     });
     videoLoading.value = false;
   }
 
   async function handlePlay() {
     if (roomLiveType.value === liveTypeEnum.srsFlvPull) {
-      console.log('srsFlvPull', autoplayVal.value);
       if (!autoplayVal.value) return;
       await handleFlvPlay();
     } else if (roomLiveType.value === liveTypeEnum.srsHlsPull) {
-      console.log('srsHlsPull', autoplayVal.value);
       if (!autoplayVal.value) return;
       await handleHlsPlay();
     }
@@ -273,6 +270,7 @@ export function usePull({
   }
 
   function sendDanmu() {
+    startFlvPlay({ flvurl: flvurl.value });
     if (!danmuStr.value.trim().length) {
       window.$message.warning('请输入弹幕内容!');
       return;

+ 3 - 2
src/hooks/use-push.ts

@@ -73,7 +73,7 @@ export function usePush({
     initWs,
     canvasVideoStream,
     lastCoverImg,
-    heartbeatTimer,
+    loopHeartbeatTimer,
     localStream,
     liveUserList,
     damuList,
@@ -136,6 +136,7 @@ export function usePush({
   });
 
   onUnmounted(() => {
+    clearInterval(loopHeartbeatTimer.value);
     closeWs();
     closeRtc();
   });
@@ -223,7 +224,7 @@ export function usePush({
   function endLive() {
     isLiving.value = false;
     localStream.value = undefined;
-    clearInterval(heartbeatTimer.value);
+    clearInterval(loopHeartbeatTimer.value);
     const instance = networkStore.wsMap.get(roomId.value);
     if (instance) {
       instance.send({

+ 7 - 4
src/hooks/use-ws.ts

@@ -1,5 +1,5 @@
 import { getRandomString } from 'billd-utils';
-import { reactive, ref, watch } from 'vue';
+import { onUnmounted, reactive, ref, watch } from 'vue';
 
 import { fetchRtcV1Play, fetchRtcV1Publish } from '@/api/srs';
 import { WEBSOCKET_URL } from '@/constant';
@@ -34,7 +34,7 @@ export const useWs = () => {
   const appStore = useAppStore();
   const userStore = useUserStore();
   const networkStore = useNetworkStore();
-  const heartbeatTimer = ref();
+  const loopHeartbeatTimer = ref();
   const liveUserList = ref<ILiveUser[]>([]);
   const roomId = ref('');
   const roomName = ref('');
@@ -180,6 +180,9 @@ export const useWs = () => {
     },
     { deep: true }
   );
+  onUnmounted(() => {
+    clearInterval(loopHeartbeatTimer.value);
+  });
 
   watch(
     () => currentResolutionRatio.value,
@@ -368,7 +371,7 @@ export const useWs = () => {
   }
 
   function handleHeartbeat(liveId: number) {
-    heartbeatTimer.value = setInterval(() => {
+    loopHeartbeatTimer.value = setInterval(() => {
       const instance = networkStore.wsMap.get(roomId.value);
       if (!instance) return;
       const heartbeatData: IHeartbeat['data'] = {
@@ -880,7 +883,7 @@ export const useWs = () => {
     roomLiveing,
     liveRoomInfo,
     roomNoLive,
-    heartbeatTimer,
+    loopHeartbeatTimer,
     localStream,
     liveUserList,
     damuList,

+ 8 - 9
src/utils/index.ts

@@ -60,26 +60,26 @@ export const createVideo = ({ muted = true, autoplay = true }) => {
 
 export function videoToCanvas(data: {
   videoEl: HTMLVideoElement;
-  targetEl: Element;
   size?: { width: number; height: number };
 }) {
-  const { videoEl, targetEl } = data;
-  if (!videoEl || !targetEl) {
-    throw new Error('videoEl或targetEl为空');
+  const { videoEl } = data;
+  if (!videoEl) {
+    throw new Error('videoEl不能为空!');
   }
   const canvas = document.createElement('canvas');
 
   const ctx = canvas.getContext('2d')!;
 
   let timer;
-
   function drawCanvas() {
     if (data.size) {
       const { width, height } = data.size;
       canvas.width = width;
       canvas.height = height;
       ctx.drawImage(videoEl, 0, 0, width, height);
+      // console.log('有size', width, height, performance.now());
     } else {
+      // WARN safari没有captureStream方法
       const videoTrack = videoEl
         // @ts-ignore
         .captureStream()
@@ -89,6 +89,7 @@ export function videoToCanvas(data: {
         canvas.width = width!;
         canvas.height = height!;
         ctx.drawImage(videoEl, 0, 0, width!, height!);
+        // console.log('没有size', width, height, performance.now());
       }
     }
     timer = requestAnimationFrame(drawCanvas);
@@ -97,11 +98,9 @@ export function videoToCanvas(data: {
   function stopDrawing() {
     cancelAnimationFrame(timer);
   }
-  // targetEl.appendChild(canvas);
-  // document.body.appendChild(videoEl);
-  // targetEl.parentNode?.replaceChild(canvas, targetEl);
+  document.body.appendChild(videoEl);
 
   drawCanvas();
 
-  return { drawCanvas, stopDrawing, canvas };
+  return { drawCanvas, stopDrawing, canvas, videoEl };
 }

+ 8 - 5
src/views/home/index.vue

@@ -185,15 +185,20 @@ const stopDrawingArr = ref<any[]>([]);
 const { hlsVideoEl, startHlsPlay, destroyHls } = useHlsPlay();
 
 onUnmounted(() => {
-  stopDrawingArr.value.forEach((cb) => cb());
+  handleStopDrawing();
 });
 
+function handleStopDrawing() {
+  stopDrawingArr.value.forEach((cb) => cb());
+  stopDrawingArr.value = [];
+}
+
 async function handleHlsPlay(hlsurl: string) {
+  handleStopDrawing();
   await startHlsPlay({ hlsurl });
   const { width, height } = await startHlsPlay({ hlsurl });
   const { canvas, stopDrawing } = videoToCanvas({
     videoEl: hlsVideoEl.value!,
-    targetEl: canvasRef.value!,
     size: { width, height },
   });
   stopDrawingArr.value.push(stopDrawing);
@@ -213,8 +218,6 @@ function changeLiveRoom(item: ILive) {
   ) {
     destroyHls();
     handleHlsPlay(item.live_room.hls_url!);
-  } else {
-    destroyHls();
   }
 }
 
@@ -281,7 +284,7 @@ function goRoom(item: ILiveRoom) {
     name: routerName.pull,
     params: { roomId: item.id },
     query: {
-      liveType: liveTypeEnum.srsFlvPull,
+      liveType: liveTypeEnum.srsHlsPull,
     },
   });
 }