您当前的位置:首页 > 网站建设 > javascript
| php | asp | css | H5 | javascript | Mysql | Dreamweaver | Delphi | 网站维护 | 帝国cms | React | 考试系统 | ajax | jQuery | 小程序 |

详解Vue实现直播功能

51自学网 2022-02-21 13:39:39
  javascript

最近公司刚好在做直播,那么今天就记录一下遇到的坑,公司服务器用的亚马逊aws,所以直接看官方的api就可以了,aws官方地址aws直播api
先看下具体的实现后的效果图把

在这里插入图片描述

按照网上成熟的方法,使用的是video.js,然后aws做了一层封装,那么我们直接拿来使用把,这里使用vue版本的vue-video-player

先安装下相关的包

npm install vue-video-player --save

在main.js引入vue-video-player

// 第一个是videoJs的样式,后一个是vue-video-player的样式,因为考虑到我其他业务组件可能也会用到视频播放,所以就放在了main.js内require('video.js/dist/video-js.css')require('vue-video-player/src/custom-theme.css')/*导入视频播放组件*/import VideoPlayer from 'vue-video-player'Vue.use(VideoPlayer)

导入组件,修改配置参数

          <video-player            class="video-player vjs-custom-skin"            ref="videoPlayer"            :options="playerOptions"            @play="onPlayerPlay($event)"            @pause="onPlayerPause($event)"            @statechanged="playerStateChanged($event)"          ></video-player>

修改参数,添加src

     playerOptions: {       playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度       autoplay: false, //如果true,浏览器准备好时开始回放。       controls: true, //控制条       preload: "auto", //视频预加载       muted: true, //默认情况下将会消除任何音频。       loop: false, //导致视频一结束就重新开始。       language: "zh-CN",       aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")       fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。       sources: [         {           withCredentials: false,           type: "application/x-mpegURL",           //src: this.liveSrc           src:             "https://50f5175980ea.us-east-1.playback.live-video.net/api/video/v1/us-east-1.003054160756.channel.bSt8OCsmBtFq.m3u8"         }       ],       poster: this.image, //你的封面地址       //width: 200 || document.documentElement.clientWidth,       notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。       controlBar: {         timeDivider: true, // 当前时间和持续时间的分隔符         durationDisplay: true, // 显示持续时间         remainingTimeDisplay: false, // 是否显示剩余时间功能         fullscreenToggle: true // 是否显示全屏按钮       }     },

注意要先测试直播源可以成功播放才可以,否则就会报下这些错误

在这里插入图片描述

那么我们先按照官方的搭建一个本地的直播源测试吧

先搭建html界面,注意要引入相关的js库和文件,我这里用hbuilder,因为用的比较顺手,而且预览模式类似于开了一个端口,通过get方式的方法,返回了一个本地服务,而不是直接本地双击打开html文件,访问静态文件哦~~~~

<!doctype html><html lang="en"><head>    <link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.11.4/video-js.css" rel="stylesheet">    <script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.11.4/video.min.js"></script>    <script src="https://player.live-video.net/1.4.0/amazon-ivs-videojs-tech.min.js"></script></head><body>    <div class="video-container">        <video id="amazon-ivs-videojs" class="video-js vjs-4-3 vjs-big-play-centered" controls autoplay playsinline></video>    </div>    <style>        body {            margin: 0;        }        .video-container {            width: 640px;            height: 480px;            margin: 15px;        }    </style>    <script>        (function play() {            // Get playback URL from Amazon IVS API            //var PLAYBACK_URL = 'https://50f5175980ea.us-east-1.playback.live-video.net/api/video/v1/us-east-1.003054160756.channel.bSt8OCsmBtFq.m3u8';            var PLAYBACK_URL = 'https://50f5175980ea.us-east-1.playback.live-video.net/api/video/v1/us-east-1.003054160756.channel.bSt8OCsmBtFq.m3u8'            // Register Amazon IVS as playback technology for Video.js            registerIVSTech(videojs);            // Initialize player            var player = videojs('amazon-ivs-videojs', {               techOrder: ["AmazonIVS"]            }, () => {               console.log('Player is ready to use!');               // Play stream               player.src(PLAYBACK_URL);            });        })();    </script></body></html>

通过端口访问,

在这里插入图片描述

后来发现通过本地静态文件,也可以实现在线直播源播放

在这里插入图片描述

ps:如果不想自己搭建本机服务测试,也可以直接使用awd提供的在线测试
https://replit.com/@changdong0524/amazon-ivs-player-web-sample#samples/common/form-control.ts,但是要自己注册账号哦
大概就是下面这样子哦

在这里插入图片描述

大家自己去摸索一下就会了,修改input.value为直播源地址,然后在右边shell控制台启动就可以了

npm  install && npm  run  start

效果如下,是一模一样的

在这里插入图片描述

load这里的地址换成你自己的直播源m3u8格式就好了,我这里是已经搭建好的在线直播源

在这里插入图片描述

直播源没问题后,接下来就直接继续写项目代码

  <template>    <div class='demo'>      <video-player class="video-player vjs-custom-skin"         ref="videoPlayer"         :playsinline="true"         :options="playerOptions"        @play="onPlayerPlay($event)"         @pause="onPlayerPause($event)"        @ended="onPlayerEnded($event)"        @waiting="onPlayerWaiting($event)"        @playing="onPlayerPlaying($event)"        @loadeddata="onPlayerLoadeddata($event)"        @timeupdate="onPlayerTimeupdate($event)"        @canplay="onPlayerCanplay($event)"        @canplaythrough="onPlayerCanplaythrough($event)"        @statechanged="playerStateChanged($event)"        @ready="playerReadied"      >      </video-player>    </div>  </template>  <script>    export default {      methods: {        // 播放回调        onPlayerPlay(player) {          console.log('player play!', player)        },        // 暂停回调        onPlayerPause(player) {          console.log('player pause!', player)        },        // 视频播完回调        onPlayerEnded($event) {          console.log(player)        },        // DOM元素上的readyState更改导致播放停止        onPlayerWaiting($event) {          console.log(player)        },        // 已开始播放回调        onPlayerPlaying($event) {          console.log(player)        },        // 当播放器在当前播放位置下载数据时触发        onPlayerLoadeddata($event) {          console.log(player)        },        // 当前播放位置发生变化时触发。        onPlayerTimeupdate($event) {          console.log(player)        },        //媒体的readyState为HAVE_FUTURE_DATA或更高        onPlayerCanplay(player) {          // console.log('player Canplay!', player)        },        //媒体的readyState为HAVE_ENOUGH_DATA或更高。这意味着可以在不缓冲的情况下播放整个媒体文件。        onPlayerCanplaythrough(player) {          // console.log('player Canplaythrough!', player)        },        //播放状态改变回调        playerStateChanged(playerCurrentState) {          console.log('player current update state', playerCurrentState)        },        //将侦听器绑定到组件的就绪状态。与事件监听器的不同之处在于,如果ready事件已经发生,它将立即触发该函数。。        playerReadied(player) {          console.log('example player 1 readied', player);        }      },    } </script>

定义相关的监听函数,可以根据自己需要加上,常用的有下面几个

    onPlayerPlay(player) {      console.log("onPlayerPlay", player);    },    onPlayerPause(player) {      console.log("onPlayerPause", player);    },    playerStateChanged(player) {      console.log("playerStateChanged", player);    },

然后启动服务

npm  run start

发现报错,无法找到相关的视频,于是发现缺少相关的库,还得加上aws的库才可以
在整个项目的index.html中加入下面的库支持文件

  <link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.11.4/video-js.css" rel="stylesheet">  <script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.11.4/video.min.js"></script>  <script src="https://player.live-video.net/1.4.0/amazon-ivs-videojs-tech.min.js"></script>

在这里插入图片描述

最后完整效果就出来了

在这里插入图片描述

注意事项:
video-player标签的class必须设置成“video-player vjs-custom-skin”,你引入的样式才能起作用。 增加hls的支持。支持流媒体m3u8g等等格式播放。

增加hls.js支持,故此要安装依赖,如下:

npm install --save videojs-contrib-hls

这里提供下aws的官方仓库啊,需要自取哦
https://github.com/aws-samples

补充一下:如果直接在页面中实现的话,可能无法直接播放,会报错无法播放视频,我猜测可能有2个原因,见截图

在这里插入图片描述

1:异步获取后台返回的拉流地址的时候,需要一定的时间,这个时间直播组件已经初始化完毕,但是还没有获取到直播源地址,所以会报错找不到直播地址
2:直播组件也有自己一整套完整的生命周期,我们可以检测不同的生命周期,然后把直播源地址,在请求完毕后放入合适的时间,直播组件会一直请求这个直播地址,从而实现在线直播
这里我为了偷懒,暂时没有那么多时间去研究一下,等空了会去仔细研究一下,我是把他抽离出一个单组的子组件,通过props来实现地址的传递

在这里插入图片描述
在这里插入图片描述

效果一样一样的,也可以方便其他组件调用

在这里插入图片描述

最后为了方便管理,双手奉上最后的全部代码
start
1:main.js

// 第一个是videoJs的样式,后一个是vue-video-player的样式,因为考虑到我其他业务组件可能也会用到视频播放,所以就放在了main.js内require('video.js/dist/video-js.css')require('vue-video-player/src/custom-theme.css')/*导入视频播放组件*/import VideoPlayer from 'vue-video-player'Vue.use(VideoPlayer)

2:videoPlayer.vue

<template>  <video-player    class="video-player vjs-custom-skin"    ref="videoPlayer"    :options="playerOptions"    @play="onPlayerPlay($event)"    @pause="onPlayerPause($event)"    @statechanged="playerStateChanged($event)"  ></video-player></template><script >//import { registerIVSTech } from "amazon-ivs-player";export default {  name: "",  props: ["src", "image"],  data() {    return {      // liveSrc:      //   "https://50f5175980ea.us-east-1.playback.live-video.net/api/video/v1/us-east-1.003054160756.channel.bSt8OCsmBtFq.m3u8",      playerOptions: {        playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度        autoplay: false, //如果true,浏览器准备好时开始回放。        controls: true, //控制条        preload: "auto", //视频预加载        muted: false, //默认情况下将会消除任何音频。        loop: false, //导致视频一结束就重新开始。        language: "zh-CN",        aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")        fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。        sources: [          {            withCredentials: false,            type: "application/x-mpegURL",            src: this.src            //  "https://50f5175980ea.us-east-1.playback.live-video.net/api/video/v1/us-east-1.003054160756.channel.bSt8OCsmBtFq.m3u8"          }        ],        poster: this.image, //你的封面地址        //width: 200 || document.documentElement.clientWidth,        notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。        controlBar: {          timeDivider: true, // 当前时间和持续时间的分隔符          durationDisplay: true, // 显示持续时间          remainingTimeDisplay: false, // 是否显示剩余时间功能          fullscreenToggle: true // 是否显示全屏按钮        }      }    };  },  // livePlays() {  //   this.playerOptions.sources[0].src = this.liveSrc;  //   var obj = {};  //   obj.withCredentials = false;  //   obj.type = "application/x-mpegURL";  //   obj.src = this.pullUrl;  //   this.playerOptions.sources.append(obj);  // },  computed: {    player() {      return this.$refs.videoPlayer.player;    }  },  computed: {    player() {      return this.$refs.videoPlayer.player;    }  },  methods: {    onPlayerPlay(player) {      console.log("onPlayerPlay", player);    },    onPlayerPause(player) {      console.log("onPlayerPause", player);    },    playerStateChanged(player) {      console.log("playerStateChanged", player);    }  }};</script>

3:detail.vue 父组件

<template>  <d2-container>    <div>      <div class="webTitle">直播管理 > 大型直播 > 详情</div>      <el-table :data="list" border stripe>        <el-table-column align="center" label="直播ID">          <template slot-scope="scope">            <span>{{ scope.row.id }}</span>          </template>        </el-table-column>        <el-table-column align="center" label="直播标题">          <template slot-scope="scope">            <span>{{ scope.row.title }}</span>          </template>        </el-table-column>        <el-table-column align="center" label="账号">          <template slot-scope="scope">            <span>{{ scope.row.name }}</span>          </template>        </el-table-column>        <el-table-column align="center" label="直播开始时间">          <template slot-scope="scope">            <span>{{ scope.row.liveStart | timestampFormat }}</span>          </template>        </el-table-column>        <el-table-column align="center" label="观看人数">          <template slot-scope="scope">            <span>{{ scope.row.watchNumber }}</span>          </template>        </el-table-column>        <el-table-column align="center" label="评论数">          <template slot-scope="scope">            <span>{{ scope.row.reserveNumber }}</span>          </template>        </el-table-column>        <el-table-column align="center" label="购票金额(GP)">          <template slot-scope="scope">            <span>{{scope.row.preSaleType == 1 ? scope.row.preSaleBalance*1 + scope.row.preSaleDeposit *1+ scope.row.fullPayment*1 : scope.row.fullPayment}}</span>          </template>        </el-table-column>        <el-table-column align="center" label="礼物金额">          <template slot-scope="scope">            <span>{{ scope.row.reserveNumber }}</span>          </template>        </el-table-column>      </el-table>      <div class="playWrap">        <div class="livePicture">          <vueVideoPlayers :src="src" :image="image" />        </div>        <div class="liveCommet"></div>      </div>      <div class="playWrap">        <div class="playLeft">          <p>基本信息</p>          <ul class="leftInfo">            <li class="playItem">              <span class="playTitle">分类</span>              <span class="playContent">{{typeName}}</span>            </li>            <li class="playItem">              <span class="playTitle">预售类型</span>              <span class="playContent">{{formData.preSaleType == 1 ? "预售" :"非预售"}}</span>            </li>            <li class="playItem">              <span class="playTitle">是否录播</span>              <span class="playContent">{{formData.isRecordedBroadcast ==1 ? "录播" : "不录播"}}</span>            </li>            <li class="playItem">              <span class="playTitle">演员列表</span>              <span class="playContent">{{formData.actor}}</span>            </li>            <li class="playItem">              <span class="playTitle">直播介绍</span>              <span class="playContent">{{formData.liveIntroduce}}</span>            </li>          </ul>          <p>预售信息</p>          <ul class="leftInfo">            <li class="playItem">              <span class="playTitle">预售时段</span>              <span class="playContent">                {{formData.preSaleStart}}                <span style="color:#333;margin:0 5px">-</span>                {{formData.preSaleEnd}}              </span>            </li>            <li class="playItem">              <span class="playTitle">成型人数</span>              <span class="playContent">{{formData.formingNum ? formData.formingNum : 0}}</span>            </li>            <li class="playItem">              <span class="playTitle">成型状态</span>              <span                class="playContent"              >{{formData.reserveNumber > formData.reserveNumber ? "已成型":"未成型" }}</span>            </li>          </ul>          <p>非预售信息</p>          <ul class="leftInfo">            <li class="playItem">              <span class="playTitle">售票开始时间</span>              <span class="playContent">{{formData.ticketingStart}}</span>            </li>          </ul>          <p>票价</p>          <ul class="leftInfo">            <li class="playItem">              <span class="playTitle">预售定金</span>              <span class="playContent">{{formData.preSaleDeposit ? formData.preSaleDeposit : 0}}</span>            </li>            <li class="playItem">              <span class="playTitle">预售尾款</span>              <span class="playContent">{{formData.preSaleBalance ? formData.preSaleBalance : 0}}</span>            </li>            <li class="playItem">              <span class="playTitle">全款价格</span>              <span class="playContent">{{formData.fullPayment ? formData.fullPayment : 0}}</span>            </li>            <li class="playItem">              <span class="playTitle">回放价格</span>              <span class="playContent">{{formData.playbackPrice ? formData.playbackPrice : 0}}</span>            </li>          </ul>        </div>        <div class="playRight">          <p>图像资料</p>          <ul class="leftInfo">            <li class="playItem">              <span class="playTitle">宣传视频</span>              <span class="playContent">                <img                  v-if="formData.propagandaVideoUrl"                  :src="videoPng"                  class="playImage"                  @click="showVideo(formData.propagandaVideoUrl,true)"                />                <span v-else style="color:#cfcfcf">暂无视频</span>              </span>            </li>            <li class="playItem">              <span class="playTitle">回访视频</span>              <span class="playContent">                <img                  v-if="formData.recordedBroadcastUrl"                  :src="videoPng"                  class="playImage"                  @click="showVideo(formData.recordedBroadcastUrl,false)"                />                <span v-else style="color:#cfcfcf">暂无视频</span>              </span>            </li>            <li class="playItem">              <span class="playTitle">分享海报</span>              <span class="playContent">                <el-image                  class="matchImg"                  :src="formData.shareImage"                  :preview-src-list="[formData.shareImage]"                />              </span>            </li>            <li class="playItem">              <span class="playTitle">封面图片</span>              <span class="playContent">                <el-image                  class="matchImg"                  v-for="(item,index)  in  JSON.parse(formData.coverImage)"                  :src="item"                  :key="index"                  :preview-src-list="[item]"                />              </span>            </li>          </ul>          <!-- <p>图像资料</p>          <ul class="leftInfo"></ul>-->        </div>      </div>    </div>    <el-button @click="backPage">返回</el-button>    <el-dialog title="查看" :visible.sync="videoVisible" width="850px">      <div v-if="video">        <video :src="tempSrc" controls="controls" width="800" height="600">您的浏览器不支持 video 标签。</video>      </div>      <div v-else>        <vueVideoPlayers :src="tempSrc" :image="image" />      </div>    </el-dialog>  </d2-container></template><script >import { getLiveDetail, getLiveSellDetail } from "@/api/3d/liveApi";import videoPng from "@/assets/img/video.jpg";import { timestampFormat } from "@/common/filters";//import { registerIVSTech } from "amazon-ivs-player";import vueVideoPlayers from "./videoPlayer";export default {  name: "",  data() {    return {      src: "", //直播源视频      image: "",      videoPng: videoPng,      video: true,      videoVisible: false,      // videoSrc: "",   //宣传视频      // recordedBroadcastUrl:'',   //回放视频      tempSrc: "",      list: [],      id: "",      typeName: "",      pullUrl: "",      formData: {        id: "",        pullUrl: "",        pushUrl: "",        title: "",        liveIntroduce: "",        actor: "",        typeId: "",        isRecordedBroadcast: 2,        coverImage: "",        propagandaVideoUrl: "",        formingNum: "",        preSaleDeposit: "", //预售定金价格        preSaleBalance: "", //预售尾款价格        fullPayment: "", //全款价格        playbackPrice: "", //回放价格        preSale: [], //预售时间        preSaleStart: "",        preSaleEnd: "",        liveStart: "", //直播开始时间        isSpeak: 1,        priority: "",        shareImage: ""      }    };  },  created() {    this.getLiveSell();    this.getData();  },  mounted() {},  components: {    vueVideoPlayers  },  methods: {    backPage() {      this.$router.push("/liveMange/largeBrand");    },    //售票情况    getLiveSell() {      var id = this.$route.params.id;      getLiveSellDetail(id).then(res => {        const result = res.data;      });    },    //弹框打开看视频    showVideo(playSrc, mark) {      this.videoVisible = true;      this.video = mark;      this.tempSrc = playSrc;    },    getData() {      var id = this.$route.params.id;      this.id = id;      //var  localMatchTypeId=localStorage.getItem('matchTypeId')      //var  localPriority = localStorage.getItem('priority')      // var data = { id, page: 1, limit: 10 };      getLiveDetail(id).then(res => {        const result = res.data;        //没有分类ID取本地        // if(!result.matchTypeId){        //   result.matchTypeId = localMatchTypeId        // }        // if(!result.priority){        //   result.priority = localPriority        // }        this.formData = result;        let { pullUrl, pushUrl, coverImage } = result;        this.src = pullUrl;        this.image = JSON.parse(coverImage)[0];        const {          id,          title,          liveStart,          ticketingStart,          playbackPrice,          preSaleDeposit,          preSaleBalance,          fullPayment        } = result;        const objData = {          id,          title,          name: "admin",          liveStart,          watchNumber: localStorage.getItem("watchNumber") | 0,          reserveNumber: localStorage.getItem("reserveNumber") | 0,          preSaleDeposit,          preSaleBalance,          fullPayment,          ticketingStart,          playbackPrice        };        this.list.push(objData);        // this.formData.registrationStart=result.registrationStart + ''        // this.formData.registrationEnd = result.registrationEnd + ''        // this.formData.voteStart = result.voteStart + ''        // this.formData.voteEnd = result.voteEnd + ''        //投票时段        // var preSaleStart = moment(parseInt(result.preSaleStart)).format(        //   "YYYY-MM-DD hh:mm:ss:SSS"        // );        // var preSaleEnd = moment(parseInt(result.preSaleEnd)).format(        //   "YYYY-MM-DD hh:mm:ss:SSS"        // );        //赛事结束时段        // this.formData.liveStart = new Date(result.liveStart);        //this.formData.registration.push(start)        //this.formData.registration.push(end)        //手动赋值        // this.$set(this.formData, "preSale", [preSaleStart, preSaleEnd]);        //this.$set(this.formData, "vote", [voteStart, voteEnd]);        //日期格式化        //预售  时间段        this.formData.preSaleStart = result.preSaleStart          ? timestampFormat(result.preSaleStart)          : "";        this.formData.preSaleEnd = result.preSaleEnd          ? timestampFormat(result.preSaleEnd)          : "";        //非预售 开始售票时间        this.formData.ticketingStart = result.ticketingStart          ? timestampFormat(result.ticketingStart)          : "";        this.typeName = localStorage.getItem("typeName") || "";      });    }  }};</script><style  scoped>.playWrap {  display: flex;  background: #fff;  margin-top: 20px;}.leftInfo {  list-style: none;  border: 1px solid #cfcfcf;}.playLeft {  width: 48%;  /* border: 1px  solid #f5f5f5; */}.playRight {  width: 48%;  margin-left: 2%;}.playItem {  display: flex;  align-items: center;  padding: 10px 0;  border-bottom: 1px solid #cfcfcf;}.playItem:last-child {  border-bottom: none;}.playContent {  margin-left: 20px;  color: #999;}.matchImg {  width: 80px;  height: 80px;  margin-right: 10px;}.playImage {  width: 80px;  height: 80px;}.playWrap {  display: flex;}.livePicture {  width: 40%;  height: 400px;}</style>

3:index.html记得加入如下代码

 <link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.11.4/video-js.css" rel="stylesheet">  <script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.11.4/video.min.js"></script>  <script src="https://player.live-video.net/1.4.0/amazon-ivs-videojs-tech.min.js"></script>

end
加油~~~~

到此这篇关于Vue实现直播功能的文章就介绍到这了,更多相关Vue实现直播内容请搜索51zixue.net以前的文章或继续浏览下面的相关文章希望大家以后多多支持51zixue.net!


下载地址:
浅谈Vue单页面做SEO的四种方案
关于js typeof 与 instanceof 判断数据类型区别及开发使用
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。