Jelajahi Sumber

fix: 视频播放组件和平台的保持一致

louhangfei 1 tahun lalu
induk
melakukan
1c82ee389b
1 mengubah file dengan 51 tambahan dan 31 penghapusan
  1. 51 31
      src/components/LiveVideo/LiveVideo.vue

+ 51 - 31
src/components/LiveVideo/LiveVideo.vue

@@ -1,11 +1,10 @@
 <template>
   <div class="video-content-wrapper">
-    <div class="videoLoading" v-loading="!isVideoLoadingFailed" :element-loading-text="loadingText">
-    </div>
+    <div class="videoLoading" v-loading="!isVideoLoadingFailed"> </div>
 
     <div class="loadingError" v-if="isVideoLoadingFailed">视频加载失败</div>
 
-    <video v-loading="true" id="video" autoplay muted class="video-js video-content">
+    <video v-loading="true" ref="videoRef" autoplay muted class="video-js video-content">
       <source :src="props.url" />
     </video>
   </div>
@@ -14,12 +13,9 @@
 <script setup lang="ts">
   import { onMounted, onBeforeUnmount, watch, ref, computed } from 'vue';
   import mpegts from 'mpegts.js';
-
   const restartNum = ref(0);
 
-  const MAX_RESTART_NUM = 20;
   let isVideoLoadingFailed = ref(false);
-
   const props = defineProps<{
     url: string;
   }>();
@@ -27,17 +23,23 @@
   const emit = defineEmits(['timeUpdate']);
 
   let player: mpegts.Player | null;
+  let lastDecodedFrames = 0; // 10s前的解码帧数
+  let currentDecodedFrames = 0; // 当前解码帧数
+
+  const videoRef = ref<HTMLVideoElement | null>(null);
 
-  const loadingText = computed(() => {
-    if (restartNum.value === 0) return '视频加载中...';
-    return `正在尝试第${restartNum.value}次重连...`;
-  });
   const handleTimeUpdate = (event) => {
     emit('timeUpdate', event.target.currentTime);
   };
 
   const initPlay = () => {
-    const videoElement = document.getElementById('video') as HTMLMediaElement;
+    if (!props.url || !videoRef.value) {
+      return;
+    }
+
+    const videoElement = videoRef.value;
+    lastDecodedFrames = 0;
+    currentDecodedFrames = 0;
     player = mpegts.createPlayer(
       {
         type: 'flv',
@@ -56,37 +58,48 @@
     player.attachMediaElement(videoElement);
     player.load();
     player.on(mpegts.Events.MEDIA_INFO, () => {
-      // testStore.setVideoReady(true);
+      console.log('视频开始播放');
     });
     player.on(mpegts.Events.ERROR, (e) => {
-      // testStore.setVideoReady(true);
-      console.log('video error', e);
-      if (restartNum.value >= MAX_RESTART_NUM) {
-        isVideoLoadingFailed.value = true;
-        return;
-      }
-      if (player) {
-        player.pause();
-        player.unload();
-        player.detachMediaElement();
-        player.destroy();
-        player = null;
-        restartNum.value += 1;
-        initPlay();
-      }
+      console.log('视频加载错误', e);
+      // 当发生error时,这里会发生死循环,所以要注销掉。 interval方式中已经包含了此种错误的处理
+      // reloadPlayer();
+    });
+    player.on(mpegts.Events.RECOVERED_EARLY_EOF, () => {
+      console.log('视频播放结束');
+    });
+    player.on(mpegts.Events.STATISTICS_INFO, (e) => {
+      // console.log("视频播放信息", e.decodedFrames);
+      const frame = e.decodedFrames || 0;
+      currentDecodedFrames = frame;
     });
     // player.play();
     setTimeout(() => {
-      player?.play();
+      player?.play() as Promise<void>;
     }, 50);
   };
 
+  const interval = setInterval(() => {
+    if (currentDecodedFrames === lastDecodedFrames) {
+      console.log(
+        '视频播放卡顿,10s前解码帧数为 ' +
+          lastDecodedFrames +
+          ' ,当前解码帧数为 ' +
+          currentDecodedFrames,
+      );
+
+      reloadPlayer();
+    }
+    lastDecodedFrames = currentDecodedFrames;
+  }, 15000);
+
   const destroyPlayer = () => {
-    // testStore.setVideoReady(false);
     if (player) {
+      console.log('视频判断需要销毁');
       player.pause();
       player.unload();
       player.detachMediaElement();
+      console.log('视频播放器销毁');
       player.destroy();
       player = null;
     }
@@ -96,13 +109,19 @@
     initPlay();
   });
 
+  const reloadPlayer = () => {
+    destroyPlayer();
+    setTimeout(() => {
+      initPlay();
+    }, 100);
+  };
+
   //切换播放url
   watch(
     () => props.url,
     () => {
-      destroyPlayer();
       if (props.url) {
-        initPlay();
+        reloadPlayer();
       }
     },
     {
@@ -112,6 +131,7 @@
 
   onBeforeUnmount(() => {
     destroyPlayer();
+    clearInterval(interval);
   });
 </script>