flv+hls播放器封装,支持flv和m3u8俩种数据流

可以播放flv流
可以播放m3u8流
可以截屏并且自动下载到本地
<template>
  <div
    v-if="isWho === 'Nodata'"
    style="
      height: 300px;
      line-height: 300px;
      text-align: center;
      font-size: 20px;
      color: red;
      background-color: #000000;
      color: #fff;
    "
  >
    播放地址错误</div
  >
  <div class="video-container" v-if="isWho === 'flv'">
    <video
      :id="`videoElement${index}`"
      crossorigin="Anonymous"
      class="centeredVideo"
      controls
      autoplay
      muted
      loop
      preload="auto"
    ></video>
    <div class="btn-wrap" @click="save" v-if="isScreenCapture"> 截屏 </div>
  </div>
  <div
    v-if="isWho === 'm3u8'"
    class="video-play video-container"
    :class="{ 'video-bg-wrap': !`videoElement${props.index}` }"
  >
    <div
      style="
        font-size: 20px;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      "
      v-if="showloading"
    >
      <a-button
        :loading="showloading"
        type="link"
        style="border: 0px solid transparent; color: #cccccc"
      >
        加载中...
      </a-button></div
    >
    <video
      v-show="!error"
      :id="`videoElement${index}`"
      autoplay
      muted
      @play="handleFullScreen"
    ></video>
    <div class="btn-wrap" @click="save" v-if="isScreenCapture && !showloading"> 截屏 </div>
    <div class="wrap_screen" @click="launchFullscreen" v-if="isScreenCapture && !showloading">
      全屏
    </div>
  </div>
</template>

<script setup>
  import flvjs from 'flv.js';
  import { ref, nextTick, onMounted, defineProps, watch, onUnmounted } from 'vue';
  import { message } from 'ant-design-vue';

  const props = defineProps({
    url: {
      type: String,
      default: '',
    },
    index: {
      type: Number,
      default: 0,
    },
    downFileName: {
      type: String,
      default: '截图',
    },
    isScreenCapture: {
      type: Boolean,
      default: true,
    },
  });
  const hls = ref(null);
  const error = ref(false);
  let showloading = ref(true); //加载中预留控制
  console.log(props.url);
  let isWho = ref('flv');
  if (props.url.indexOf('.m3u8') !== -1) {
    // 此处不是m3u8数据流
    isWho.value = 'm3u8';
  } else if (props.url.indexOf('.flv') !== -1) {
    // 此处不是m3u8数据流
    isWho.value = 'flv';
  } else {
    // 此处不是数据流
    isWho.value = 'Nodata';
  }
  if (isWho.value === 'flv') {
    const flv_load = () => {
      var videoElement = document.getElementById(`videoElement${props.index}`);
      if (flvjs.isSupported()) {
        var flvPlayer = flvjs.createPlayer(
          {
            type: 'flv', //媒体类型 mp4/flv
            url: props.url, //flv格式媒体URL
            isLive: true, //数据源是否为直播流
            hasAudio: false, //数据源是否包含有音频
            hasVideo: true, //数据源是否包含有视频
            enableStashBuffer: true, //是否启用缓存区
          },
          {
            enableWorker: false, // 是否启用分离的线程进行转换
            enableStashBuffer: false, //关闭IO隐藏缓冲区
            autoCleanupSourceBuffer: true, //自动清除缓存
          },
        );
        flvPlayer?.attachMediaElement(videoElement); //将播放实例注册到节点
        flvPlayer?.load(); //加载数据流
        flvPlayer?.play().catch(() => {
          flvPlayer?.play();
        }); //播放数据流
      }
    };
    onMounted(() => {
      nextTick(() => {
        flv_load();
      });
    });
  }
  if (isWho.value === 'm3u8') {
    watch(
      props,
      () => {
        setTimeout(() => {
          playVideo(`videoElement${props.index}`, props.url);
        }, 1000);
      },
      {
        immediate: true,
      },
    );
    onMounted(() => {
      playVideo(`videoElement${props.index}`, props.url);
      setTimeout(() => {
        showloading.value = false;
      }, 6000);
    });
    const playVideo = (id, url) => {
      if (id && url) {
        // showloading.value = true;
        error.value = false;
        let video = document.getElementById(id);
        hls.value = new Hls();
        hls.value.attachMedia(video);
        hls.value.on(Hls.Events.MEDIA_ATTACHED, () => {
          hls.value.loadSource(url);
        });
        hls.value.on(Hls.Events.MANIFEST_PARSED, () => {
          // showloading.value = false;
        });
        hls.value.on(Hls.Events.ERROR, (event, data) => {
          console.log('MANIFEST_ERROR', id, url, event, data);
          if (data.fatal) {
            // showloading.value = false;
            this.error = true;
            hls.value.stopLoad();
            switch (data.type) {
              case Hls.ErrorTypes.NETWORK_ERROR:
                //
                break;
              case Hls.ErrorTypes.MEDIA_ERROR:
                //
                break;
              default:
                hls.value.destroy();
                break;
            }
          }
        });
      }
    };

    onUnmounted(() => {
      if (hls.value) {
        hls.value?.detachMedia();
        hls.value?.destroy();
      }
      hls.value = null;
    });
  }

  // -------------------------------截屏并且下载----------------------------------------
  let mySrc = '';
  const htmlToImage = () => {
    var videoElement = document.getElementById(`videoElement${props.index}`);
    var canvas = document.createElement('canvas');
    canvas.width = videoElement.clientWidth;
    canvas.height = videoElement.clientHeight;
    if (!canvas.getContext) {
      message.error('您的浏览器暂不支持canvas');
      return false;
    } else {
      var context = canvas.getContext('2d');
      var video = document.getElementById(`videoElement${props.index}`);
      context.drawImage(video, 0, 0, canvas.width, canvas.height);
      return (mySrc = canvas.toDataURL('image/png'));
    }
  };
  const save = () => {
    htmlToImage();
    nextTick(() => {
      var triggerDownload = document.createElement('a');
      triggerDownload.setAttribute('href', mySrc);
      triggerDownload.setAttribute('download', `${props.downFileName}.png`);
      triggerDownload.click();
    });
  };
  const myE = ref();
  const handleFullScreen = (e) => {
    myE.value = e.target;
  };
  const launchFullscreen = () => {
    let element = myE.value;
    //此方法不可以在異步任務中執行,否則火狐無法全屏
    if (element.requestFullscreen) {
      element.requestFullscreen();
    } else if (element.mozRequestFullScreen) {
      element.mozRequestFullScreen();
    } else if (element.msRequestFullscreen) {
      element.msRequestFullscreen();
    } else if (element.oRequestFullscreen) {
      element.oRequestFullscreen();
    } else if (element.webkitRequestFullscreen) {
      element.webkitRequestFullScreen();
    } else {
      var docHtml = document.documentElement;
      var docBody = document.body;
      var videobox = element;
      var cssText = 'width:100%;height:100%;overflow:hidden;';
      docHtml.style.cssText = cssText;
      docBody.style.cssText = cssText;
      videobox.style.cssText = cssText + ';' + 'margin:0px;padding:0px;';
      document.IsFullScreen = true;
    }
  };
</script>

<style scoped lang="less">
  .video-play {
    width: 100%;
    height: 100%;
    position: relative;
    video {
      object-fit: fill;
      height: 100%;
      width: 100%;
    }
  }
  .video-container {
    position: relative;
    .centeredVideo {
      width: 100%;
    }
    .btn-wrap,
    .wrap_screen {
      width: 40px;
      height: 40px;
      text-align: center;
      line-height: 40px;
      border-radius: 50%;
      // border: 1px solid #cccccc;
      font-size: 14px;
      position: absolute;
      bottom: 80px;
      right: 10px;
      color: #fff;
      font-weight: 700;
      cursor: pointer;
      opacity: 0;
      transition: opacity 0.5s ease;
    }
    .wrap_screen {
      bottom: 30px;
    }
  }
  .video-container:hover {
    .btn-wrap {
      opacity: 1;
      background-color: #666666;
    }
    .wrap_screen {
      opacity: 1;
      background-color: #666666;
    }
  }
</style>

使用

//引入播放器
import VideoFlv from './flvAndM3u8Video.vue';
//使用
//url:视频流
//downFileName:指定截屏后,自动下载到本地的文件名的名称
//index:注意,此处类型为number类型,不可重复!(id的唯一性,不可重复),最好使用时间戳或者BigInt类型
<VideoFlv :url="XXXXXX" :downFileName="XXXXXX" :index="XXXXXX" />
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值