Ver Fonte

feat: 完善canvas替换video

shuisheng há 2 anos atrás
pai
commit
0c46a3126d
6 ficheiros alterados com 107 adições e 131 exclusões
  1. 2 0
      package.json
  2. 13 4
      pnpm-lock.yaml
  3. 38 30
      src/hooks/use-play.ts
  4. 27 15
      src/hooks/use-pull.ts
  5. 12 46
      src/utils/index.ts
  6. 15 36
      src/views/pull/index.vue

+ 2 - 0
package.json

@@ -41,6 +41,8 @@
     "browser-tool": "^1.0.5",
     "flv.js": "^1.6.2",
     "js-cookie": "^3.0.5",
+    "m3u8-parser": "^6.2.0",
+    "mediainfo.js": "^0.1.9",
     "mediasoup-client": "^3.6.84",
     "msr": "^1.3.4",
     "naive-ui": "^2.34.3",

+ 13 - 4
pnpm-lock.yaml

@@ -32,6 +32,12 @@ dependencies:
   js-cookie:
     specifier: ^3.0.5
     version: 3.0.5
+  m3u8-parser:
+    specifier: ^6.2.0
+    version: 6.2.0
+  mediainfo.js:
+    specifier: ^0.1.9
+    version: 0.1.9
   mediasoup-client:
     specifier: ^3.6.84
     version: 3.6.84
@@ -3661,7 +3667,6 @@ packages:
       string-width: 4.2.3
       strip-ansi: 6.0.1
       wrap-ansi: 7.0.0
-    dev: true
 
   /clone-deep@4.0.1:
     resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
@@ -7035,6 +7040,13 @@ packages:
     resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
     engines: {node: '>= 0.6'}
 
+  /mediainfo.js@0.1.9:
+    resolution: {integrity: sha512-RxTWC5cvfl+TMDM4cG+G7VFIdYfg1X/FLNLujqRERtKMr48Hbmno94fS0A8VP3zANDRXJn88DPWAUFDbcgzIrQ==}
+    hasBin: true
+    dependencies:
+      yargs: 17.7.1
+    dev: false
+
   /mediasoup-client@3.6.84:
     resolution: {integrity: sha512-eWkL0/rBPr9Bd8+qz4EqeYUg3+NtHNyAielk2BxfRiOyyKhknb38RabIM3L7eh9mi8qupZ3O11W7OkLSx+XRfQ==}
     engines: {node: '>=14'}
@@ -10750,7 +10762,6 @@ packages:
   /y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     engines: {node: '>=10'}
-    dev: true
 
   /yallist@3.1.1:
     resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -10779,7 +10790,6 @@ packages:
   /yargs-parser@21.1.1:
     resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
     engines: {node: '>=12'}
-    dev: true
 
   /yargs@15.4.1:
     resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
@@ -10822,7 +10832,6 @@ packages:
       string-width: 4.2.3
       y18n: 5.0.8
       yargs-parser: 21.1.1
-    dev: true
 
   /yn@3.1.1:
     resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}

+ 38 - 30
src/hooks/use-play.ts

@@ -1,10 +1,12 @@
 import '@/assets/css/videojs.scss';
-import { useAppStore } from '@/store/app';
 import flvJs from 'flv.js';
+import * as m3u8Parser from 'm3u8-parser';
 import videoJs from 'video.js';
 import Player from 'video.js/dist/types/player';
 import { onMounted, onUnmounted, ref, watch } from 'vue';
 
+import { useAppStore } from '@/store/app';
+
 export * as flvJs from 'flv.js';
 
 // @ts-ignore
@@ -54,9 +56,10 @@ export function useFlvPlay() {
 
 export function useHlsPlay() {
   const hlsPlayer = ref<Player>();
-  const videoEl = ref<HTMLVideoElement>();
+  const hlsVideoEl = ref<HTMLVideoElement>();
   const appStore = useAppStore();
-
+  const parser = new m3u8Parser.Parser();
+  console.log(parser, '====');
   onMounted(() => {});
 
   onUnmounted(() => {
@@ -66,12 +69,13 @@ export function useHlsPlay() {
   function destroyHls() {
     if (hlsPlayer.value) {
       hlsPlayer.value.dispose();
-      videoEl.value?.remove();
+      hlsVideoEl.value?.remove();
     }
   }
+
   function setMuted(val) {
-    if (videoEl.value) {
-      videoEl.value.muted = val;
+    if (hlsVideoEl.value) {
+      hlsVideoEl.value.muted = val;
     }
     if (hlsPlayer.value) {
       hlsPlayer.value.muted(val);
@@ -85,17 +89,16 @@ export function useHlsPlay() {
     }
   );
 
-  function startHlsPlay(data: { hlsurl: string; videoEl: HTMLVideoElement }) {
+  function startHlsPlay(data: { hlsurl: string }) {
     destroyHls();
-    const newVideo = document.createElement('video');
-    newVideo.muted = true;
-    newVideo.playsInline = true;
-    newVideo.setAttribute('webkit-playsinline', 'true');
-    videoEl.value = newVideo;
-    data.videoEl.parentElement?.appendChild(newVideo);
-    return new Promise((resolve) => {
+    const videoEl = document.createElement('video');
+    videoEl.muted = true;
+    videoEl.playsInline = true;
+    videoEl.setAttribute('webkit-playsinline', 'true');
+    hlsVideoEl.value = videoEl;
+    return new Promise<{ width: number; height: number }>((resolve) => {
       hlsPlayer.value = videoJs(
-        newVideo,
+        videoEl,
         {
           sources: [
             {
@@ -103,29 +106,34 @@ export function useHlsPlay() {
               type: 'application/x-mpegURL',
             },
           ],
-          controls: false,
-          preload: 'auto',
         },
-        function onPlayerReady() {
-          console.log('Your player is ready!');
+        function () {
+          console.log('开始播放hls');
+          hlsPlayer.value?.play();
 
-          // @ts-ignore
-          this.play();
-          resolve('ok');
+          hlsPlayer.value?.on('play', () => {
+            console.log('play', hlsPlayer.value?.videoHeight()); // 获取到的是0!
+          });
 
-          setTimeout(() => {
-            newVideo.muted = false;
-            appStore.setMuted(false);
-          }, 0);
+          hlsPlayer.value?.on('playing', () => {
+            console.log('playing', hlsPlayer.value?.videoHeight()); // 获取到的是正确的!
+            setTimeout(() => {
+              videoEl.muted = false;
+              appStore.setMuted(false);
+            }, 0);
+            resolve({
+              width: hlsPlayer.value?.videoWidth() || 0,
+              height: hlsPlayer.value?.videoHeight() || 0,
+            });
+          });
 
-          // @ts-ignore
-          this.on('ended', function () {
-            console.log('Awww...over so soon?!');
+          hlsPlayer.value?.on('loadedmetadata', (e) => {
+            console.log('loadedmetadata', e);
           });
         }
       );
     });
   }
 
-  return { startHlsPlay, destroyHls };
+  return { hlsVideoEl, startHlsPlay, destroyHls };
 }

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

@@ -34,13 +34,11 @@ import { videoToCanvas } from '@/utils';
 
 export function usePull({
   localVideoRef,
-  remoteVideoRef,
   canvasRef,
   isSRS,
   isFlv,
 }: {
   localVideoRef: Ref<HTMLVideoElement[]>;
-  remoteVideoRef: Ref<HTMLVideoElement | undefined>;
   canvasRef: Ref<HTMLDivElement | undefined>;
   isSRS?: boolean;
   isFlv?: boolean;
@@ -48,7 +46,11 @@ export function usePull({
   const route = useRoute();
   const userStore = useUserStore();
   const networkStore = useNetworkStore();
-
+  const videoEl = document.createElement('video');
+  videoEl.muted = true;
+  videoEl.playsInline = true;
+  videoEl.setAttribute('webkit-playsinline', 'true');
+  const remoteVideoRef = ref(videoEl);
   const heartbeatTimer = ref();
   const roomId = ref(route.params.roomId as string);
   const roomName = ref('');
@@ -482,17 +484,22 @@ export function usePull({
             flvurl: flvurl.value,
             videoEl: remoteVideoRef.value!,
           });
-          videoToCanvas(
-            remoteVideoRef.value!,
-            canvasRef.value!,
-            flvPlayer.value?.mediaInfo.width,
-            flvPlayer.value?.mediaInfo.height
-          );
+          videoToCanvas({
+            videoEl: remoteVideoRef.value!,
+            targetEl: canvasRef.value!,
+            width: flvPlayer.value?.mediaInfo.width!,
+            height: flvPlayer.value?.mediaInfo.height!,
+          });
         } else if (route.query.liveType === liveTypeEnum.srsHlsPull) {
           if (!autoplayVal.value) return;
           await startHlsPlay({
             hlsurl: hlsurl.value,
+          });
+          videoToCanvas({
             videoEl: remoteVideoRef.value!,
+            targetEl: canvasRef.value!,
+            width: flvPlayer.value?.mediaInfo.width!,
+            height: flvPlayer.value?.mediaInfo.height!,
           });
           videoLoading.value = false;
         } else if (
@@ -503,7 +510,12 @@ export function usePull({
           if (judgeDevice().isIphone) {
             await startHlsPlay({
               hlsurl: flvurl.value,
+            });
+            videoToCanvas({
               videoEl: remoteVideoRef.value!,
+              targetEl: canvasRef.value!,
+              width: flvPlayer.value?.mediaInfo.width!,
+              height: flvPlayer.value?.mediaInfo.height!,
             });
             videoLoading.value = false;
           } else {
@@ -511,12 +523,12 @@ export function usePull({
               flvurl: flvurl.value,
               videoEl: remoteVideoRef.value!,
             });
-            videoToCanvas(
-              remoteVideoRef.value!,
-              canvasRef.value!,
-              flvPlayer.value?.mediaInfo.width,
-              flvPlayer.value?.mediaInfo.height
-            );
+            videoToCanvas({
+              videoEl: remoteVideoRef.value!,
+              targetEl: canvasRef.value!,
+              width: flvPlayer.value?.mediaInfo.width!,
+              height: flvPlayer.value?.mediaInfo.height!,
+            });
           }
         }
       }

+ 12 - 46
src/utils/index.ts

@@ -19,70 +19,36 @@ export const getRandomString = (length: number): string => {
   return res;
 };
 
-export function videoToCanvas(
-  videoElement: HTMLVideoElement,
-  targetEl: HTMLElement,
-  width: number,
-  height
-) {
-  if (!videoElement || !targetEl) {
+export function videoToCanvas(data: {
+  videoEl: HTMLVideoElement;
+  targetEl: HTMLElement;
+  width: number;
+  height: number;
+}) {
+  // const { videoEl, targetEl, width = 572, height = 322 } = data;
+  const { videoEl, targetEl, width, height } = data;
+  console.log(videoEl.videoWidth);
+  if (!videoEl || !targetEl) {
     return;
   }
   const canvas = document.createElement('canvas');
   canvas.width = width;
   canvas.height = height;
   const ctx = canvas.getContext('2d')!;
-  const newVideo = videoElement.cloneNode(false);
 
-  const requestAnimationFrame = window.requestAnimationFrame;
-  const cancelAnimationFrame = window.cancelAnimationFrame;
   let timer;
 
   function drawCanvas() {
-    ctx.drawImage(videoElement, 0, 0);
+    ctx.drawImage(videoEl, 0, 0, width, height);
     timer = requestAnimationFrame(drawCanvas);
   }
 
   function stopDrawing() {
     cancelAnimationFrame(timer);
   }
-
-  newVideo.addEventListener(
-    'play',
-    function () {
-      drawCanvas();
-    },
-    false
-  );
-  newVideo.addEventListener('pause', stopDrawing, false);
-  newVideo.addEventListener('ended', stopDrawing, false);
-
   targetEl.parentNode?.replaceChild(canvas, targetEl);
 
   drawCanvas();
 
-  // this.play = function () {
-  //   newVideo.play();
-  // };
-
-  // this.pause = function () {
-  //   newVideo.pause();
-  // };
-
-  // this.playPause = function () {
-  //   if (newVideo.paused) {
-  //     this.play();
-  //   } else {
-  //     this.pause();
-  //   }
-  // };
-
-  // this.change = function (src) {
-  //   if (!src) {
-  //     return;
-  //   }
-  //   newVideo.src = src;
-  // };
-
-  // this.drawFrame = drawCanvas;
+  return { drawCanvas, stopDrawing };
 }

+ 15 - 36
src/views/pull/index.vue

@@ -38,19 +38,6 @@
                 })`,
               }"
             ></div>
-            <!-- <video
-              id="remoteVideo"
-              ref="remoteVideoRef"
-              autoplay
-              webkit-playsinline="true"
-              playsinline
-              x-webkit-airplay="allow"
-              x5-video-player-type="h5"
-              x5-video-player-fullscreen="true"
-              x5-video-orientation="portraint"
-              :muted="appStore.muted"
-              @click="showControls = !showControls"
-            ></video> -->
             <div ref="canvasRef"></div>
             <div
               v-if="
@@ -220,7 +207,6 @@
             @click="sendDanmu"
           >
             发送
-            <span @click="aaa">222</span>
           </div>
         </div>
       </div>
@@ -244,14 +230,13 @@ import {
   IGoods,
   liveTypeEnum,
 } from '@/interface';
-import { useAppStore } from '@/store/app';
 import { useUserStore } from '@/store/user';
+import { videoToCanvas } from '@/utils';
 
 import RechargeCpt from './recharge/index.vue';
 
 const route = useRoute();
 const userStore = useUserStore();
-const appStore = useAppStore();
 
 const giftGoodsList = ref<IGoods[]>([]);
 const showControls = ref(false);
@@ -265,11 +250,6 @@ const danmuListRef = ref<HTMLDivElement>();
 const canvasRef = ref<HTMLDivElement>();
 const containerRef = ref<HTMLDivElement>();
 const localVideoRef = ref<HTMLVideoElement[]>([]);
-const videoEl = document.createElement('video');
-videoEl.muted = true;
-videoEl.playsInline = true;
-videoEl.setAttribute('webkit-playsinline', 'true');
-const remoteVideoRef = ref(videoEl);
 const {
   initPull,
   closeWs,
@@ -301,32 +281,29 @@ const {
   sidebarList,
 } = usePull({
   localVideoRef,
-  remoteVideoRef,
   canvasRef,
   isFlv: route.query.liveType === liveTypeEnum.srsFlvPull,
   isSRS: route.query.liveType === liveTypeEnum.srsWebrtcPull,
 });
 const showPlayBtn = ref(true);
 
-const { startHlsPlay } = useHlsPlay();
+const { hlsVideoEl, startHlsPlay } = useHlsPlay();
 
 watch(
   () => videoLoading.value,
-  (newVal) => {
-    if (newVal) return;
-    // remoteVideoRef.value.style.width = flvPlayer.value?.mediaInfo.width! + 'px';
-    // remoteVideoRef.value.style.height =
-    //   flvPlayer.value?.mediaInfo.height! + 'px';
-    // console.log(flvPlayer.value?.mediaInfo.width, 888);
-    // videoToCanvas(remoteVideoRef.value, canvasRef.value!);
-  }
+  (newVal) => {}
 );
 
 async function startPull() {
   showPlayBtn.value = false;
-  await startHlsPlay({
+  const res = await startHlsPlay({
     hlsurl: hlsurl.value,
-    videoEl: remoteVideoRef.value!,
+  });
+  videoToCanvas({
+    videoEl: hlsVideoEl.value!,
+    targetEl: canvasRef.value!,
+    width: res.width,
+    height: res.height,
   });
 }
 
@@ -380,9 +357,11 @@ watch(
 onMounted(() => {
   getGoodsList();
   if (
-    [liveTypeEnum.srsFlvPull, liveTypeEnum.srsWebrtcPull].includes(
-      route.query.liveType as liveTypeEnum
-    )
+    [
+      liveTypeEnum.srsHlsPull,
+      liveTypeEnum.srsFlvPull,
+      liveTypeEnum.srsWebrtcPull,
+    ].includes(route.query.liveType as liveTypeEnum)
   ) {
     showSidebar.value = false;
   }