詳解Vue實(shí)現(xiàn)直播功能
最近公司剛好在做直播,那么今天就記錄一下遇到的坑,公司服務(wù)器用的亞馬遜aws,所以直接看官方的api就可以了,aws官方地址aws直播api
先看下具體的實(shí)現(xiàn)后的效果圖把
按照網(wǎng)上成熟的方法,使用的是video.js,然后aws做了一層封裝,那么我們直接拿來(lái)使用把,這里使用vue版本的vue-video-player
先安裝下相關(guān)的包
npm install vue-video-player --save
在main.js引入vue-video-player
// 第一個(gè)是videoJs的樣式,后一個(gè)是vue-video-player的樣式,因?yàn)榭紤]到我其他業(yè)務(wù)組件可能也會(huì)用到視頻播放,所以就放在了main.js內(nèi) require('video.js/dist/video-js.css') require('vue-video-player/src/custom-theme.css') /*導(dǎo)入視頻播放組件*/ import VideoPlayer from 'vue-video-player' Vue.use(VideoPlayer)
導(dǎo)入組件,修改配置參數(shù)
<video-player class="video-player vjs-custom-skin" ref="videoPlayer" :options="playerOptions" @play="onPlayerPlay($event)" @pause="onPlayerPause($event)" @statechanged="playerStateChanged($event)" ></video-player>
修改參數(shù),添加src
playerOptions: { playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度 autoplay: false, //如果true,瀏覽器準(zhǔn)備好時(shí)開(kāi)始回放。 controls: true, //控制條 preload: "auto", //視頻預(yù)加載 muted: true, //默認(rèn)情況下將會(huì)消除任何音頻。 loop: false, //導(dǎo)致視頻一結(jié)束就重新開(kāi)始。 language: "zh-CN", aspectRatio: "16:9", // 將播放器置于流暢模式,并在計(jì)算播放器的動(dòng)態(tài)大小時(shí)使用該值。值應(yīng)該代表一個(gè)比例 - 用冒號(hào)分隔的兩個(gè)數(shù)字(例如"16:9"或"4:3") fluid: true, // 當(dāng)true時(shí),Video.js player將擁有流體大小。換句話說(shuō),它將按比例縮放以適應(yīng)其容器。 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: "此視頻暫無(wú)法播放,請(qǐng)稍后再試", //允許覆蓋Video.js無(wú)法播放媒體源時(shí)顯示的默認(rèn)信息。 controlBar: { timeDivider: true, // 當(dāng)前時(shí)間和持續(xù)時(shí)間的分隔符 durationDisplay: true, // 顯示持續(xù)時(shí)間 remainingTimeDisplay: false, // 是否顯示剩余時(shí)間功能 fullscreenToggle: true // 是否顯示全屏按鈕 } },
注意要先測(cè)試直播源可以成功播放才可以,否則就會(huì)報(bào)下這些錯(cuò)誤
那么我們先按照官方的搭建一個(gè)本地的直播源測(cè)試吧
先搭建html界面,注意要引入相關(guān)的js庫(kù)和文件,我這里用hbuilder,因?yàn)橛玫谋容^順手,而且預(yù)覽模式類(lèi)似于開(kāi)了一個(gè)端口,通過(guò)get方式的方法,返回了一個(gè)本地服務(wù),而不是直接本地雙擊打開(kāi)html文件,訪問(wèn)靜態(tài)文件哦~~~~
<!doctype html> <html lang="en"> <head> <link 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>
通過(guò)端口訪問(wèn),
后來(lái)發(fā)現(xiàn)通過(guò)本地靜態(tài)文件,也可以實(shí)現(xiàn)在線直播源播放
ps:如果不想自己搭建本機(jī)服務(wù)測(cè)試,也可以直接使用awd提供的在線測(cè)試
https://replit.com/@changdong0524/amazon-ivs-player-web-sample#samples/common/form-control.ts,但是要自己注冊(cè)賬號(hào)哦
大概就是下面這樣子哦
大家自己去摸索一下就會(huì)了,修改input.value為直播源地址,然后在右邊shell控制臺(tái)啟動(dòng)就可以了
npm install && npm run start
效果如下,是一模一樣的
load這里的地址換成你自己的直播源m3u8格式就好了,我這里是已經(jīng)搭建好的在線直播源
直播源沒(méi)問(wèn)題后,接下來(lái)就直接繼續(xù)寫(xiě)項(xiàng)目代碼
<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: { // 播放回調(diào) onPlayerPlay(player) { console.log('player play!', player) }, // 暫?;卣{(diào) onPlayerPause(player) { console.log('player pause!', player) }, // 視頻播完回調(diào) onPlayerEnded($event) { console.log(player) }, // DOM元素上的readyState更改導(dǎo)致播放停止 onPlayerWaiting($event) { console.log(player) }, // 已開(kāi)始播放回調(diào) onPlayerPlaying($event) { console.log(player) }, // 當(dāng)播放器在當(dāng)前播放位置下載數(shù)據(jù)時(shí)觸發(fā) onPlayerLoadeddata($event) { console.log(player) }, // 當(dāng)前播放位置發(fā)生變化時(shí)觸發(fā)。 onPlayerTimeupdate($event) { console.log(player) }, //媒體的readyState為HAVE_FUTURE_DATA或更高 onPlayerCanplay(player) { // console.log('player Canplay!', player) }, //媒體的readyState為HAVE_ENOUGH_DATA或更高。這意味著可以在不緩沖的情況下播放整個(gè)媒體文件。 onPlayerCanplaythrough(player) { // console.log('player Canplaythrough!', player) }, //播放狀態(tài)改變回調(diào) playerStateChanged(playerCurrentState) { console.log('player current update state', playerCurrentState) }, //將偵聽(tīng)器綁定到組件的就緒狀態(tài)。與事件監(jiān)聽(tīng)器的不同之處在于,如果ready事件已經(jīng)發(fā)生,它將立即觸發(fā)該函數(shù)。。 playerReadied(player) { console.log('example player 1 readied', player); } }, } </script>
定義相關(guān)的監(jiān)聽(tīng)函數(shù),可以根據(jù)自己需要加上,常用的有下面幾個(gè)
onPlayerPlay(player) { console.log("onPlayerPlay", player); }, onPlayerPause(player) { console.log("onPlayerPause", player); }, playerStateChanged(player) { console.log("playerStateChanged", player); },
然后啟動(dòng)服務(wù)
npm run start
發(fā)現(xiàn)報(bào)錯(cuò),無(wú)法找到相關(guān)的視頻,于是發(fā)現(xiàn)缺少相關(guān)的庫(kù),還得加上aws的庫(kù)才可以
在整個(gè)項(xiàng)目的index.html中加入下面的庫(kù)支持文件
<link 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>
最后完整效果就出來(lái)了
注意事項(xiàng):
video-player標(biāo)簽的class必須設(shè)置成“video-player vjs-custom-skin”,你引入的樣式才能起作用。 增加hls的支持。支持流媒體m3u8g等等格式播放。
增加hls.js支持,故此要安裝依賴(lài),如下:
npm install --save videojs-contrib-hls
這里提供下aws的官方倉(cāng)庫(kù)啊,需要自取哦
https://github.com/aws-samples
補(bǔ)充一下:如果直接在頁(yè)面中實(shí)現(xiàn)的話,可能無(wú)法直接播放,會(huì)報(bào)錯(cuò)無(wú)法播放視頻,我猜測(cè)可能有2個(gè)原因,見(jiàn)截圖
1:異步獲取后臺(tái)返回的拉流地址的時(shí)候,需要一定的時(shí)間,這個(gè)時(shí)間直播組件已經(jīng)初始化完畢,但是還沒(méi)有獲取到直播源地址,所以會(huì)報(bào)錯(cuò)找不到直播地址
2:直播組件也有自己一整套完整的生命周期,我們可以檢測(cè)不同的生命周期,然后把直播源地址,在請(qǐng)求完畢后放入合適的時(shí)間,直播組件會(huì)一直請(qǐng)求這個(gè)直播地址,從而實(shí)現(xiàn)在線直播
這里我為了偷懶,暫時(shí)沒(méi)有那么多時(shí)間去研究一下,等空了會(huì)去仔細(xì)研究一下,我是把他抽離出一個(gè)單組的子組件,通過(guò)props來(lái)實(shí)現(xiàn)地址的傳遞
效果一樣一樣的,也可以方便其他組件調(diào)用
最后為了方便管理,雙手奉上最后的全部代碼
start
1:main.js
// 第一個(gè)是videoJs的樣式,后一個(gè)是vue-video-player的樣式,因?yàn)榭紤]到我其他業(yè)務(wù)組件可能也會(huì)用到視頻播放,所以就放在了main.js內(nèi) require('video.js/dist/video-js.css') require('vue-video-player/src/custom-theme.css') /*導(dǎo)入視頻播放組件*/ 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,瀏覽器準(zhǔn)備好時(shí)開(kāi)始回放。 controls: true, //控制條 preload: "auto", //視頻預(yù)加載 muted: false, //默認(rèn)情況下將會(huì)消除任何音頻。 loop: false, //導(dǎo)致視頻一結(jié)束就重新開(kāi)始。 language: "zh-CN", aspectRatio: "16:9", // 將播放器置于流暢模式,并在計(jì)算播放器的動(dòng)態(tài)大小時(shí)使用該值。值應(yīng)該代表一個(gè)比例 - 用冒號(hào)分隔的兩個(gè)數(shù)字(例如"16:9"或"4:3") fluid: true, // 當(dāng)true時(shí),Video.js player將擁有流體大小。換句話說(shuō),它將按比例縮放以適應(yīng)其容器。 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: "此視頻暫無(wú)法播放,請(qǐng)稍后再試", //允許覆蓋Video.js無(wú)法播放媒體源時(shí)顯示的默認(rèn)信息。 controlBar: { timeDivider: true, // 當(dāng)前時(shí)間和持續(xù)時(shí)間的分隔符 durationDisplay: true, // 顯示持續(xù)時(shí)間 remainingTimeDisplay: false, // 是否顯示剩余時(shí)間功能 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="直播標(biāo)題"> <template slot-scope="scope"> <span>{{ scope.row.title }}</span> </template> </el-table-column> <el-table-column align="center" label="賬號(hào)"> <template slot-scope="scope"> <span>{{ scope.row.name }}</span> </template> </el-table-column> <el-table-column align="center" label="直播開(kāi)始時(shí)間"> <template slot-scope="scope"> <span>{{ scope.row.liveStart | timestampFormat }}</span> </template> </el-table-column> <el-table-column align="center" label="觀看人數(shù)"> <template slot-scope="scope"> <span>{{ scope.row.watchNumber }}</span> </template> </el-table-column> <el-table-column align="center" label="評(píng)論數(shù)"> <template slot-scope="scope"> <span>{{ scope.row.reserveNumber }}</span> </template> </el-table-column> <el-table-column align="center" label="購(gòu)票金額(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">分類(lèi)</span> <span class="playContent">{{typeName}}</span> </li> <li class="playItem"> <span class="playTitle">預(yù)售類(lèi)型</span> <span class="playContent">{{formData.preSaleType == 1 ? "預(yù)售" :"非預(yù)售"}}</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>預(yù)售信息</p> <ul class="leftInfo"> <li class="playItem"> <span class="playTitle">預(yù)售時(shí)段</span> <span class="playContent"> {{formData.preSaleStart}} <span style="color:#333;margin:0 5px">-</span> {{formData.preSaleEnd}} </span> </li> <li class="playItem"> <span class="playTitle">成型人數(shù)</span> <span class="playContent">{{formData.formingNum ? formData.formingNum : 0}}</span> </li> <li class="playItem"> <span class="playTitle">成型狀態(tài)</span> <span class="playContent" >{{formData.reserveNumber > formData.reserveNumber ? "已成型":"未成型" }}</span> </li> </ul> <p>非預(yù)售信息</p> <ul class="leftInfo"> <li class="playItem"> <span class="playTitle">售票開(kāi)始時(shí)間</span> <span class="playContent">{{formData.ticketingStart}}</span> </li> </ul> <p>票價(jià)</p> <ul class="leftInfo"> <li class="playItem"> <span class="playTitle">預(yù)售定金</span> <span class="playContent">{{formData.preSaleDeposit ? formData.preSaleDeposit : 0}}</span> </li> <li class="playItem"> <span class="playTitle">預(yù)售尾款</span> <span class="playContent">{{formData.preSaleBalance ? formData.preSaleBalance : 0}}</span> </li> <li class="playItem"> <span class="playTitle">全款價(jià)格</span> <span class="playContent">{{formData.fullPayment ? formData.fullPayment : 0}}</span> </li> <li class="playItem"> <span class="playTitle">回放價(jià)格</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">暫無(wú)視頻</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">暫無(wú)視頻</span> </span> </li> <li class="playItem"> <span class="playTitle">分享海報(bào)</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 標(biāo)簽。</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: "", //預(yù)售定金價(jià)格 preSaleBalance: "", //預(yù)售尾款價(jià)格 fullPayment: "", //全款價(jià)格 playbackPrice: "", //回放價(jià)格 preSale: [], //預(yù)售時(shí)間 preSaleStart: "", preSaleEnd: "", liveStart: "", //直播開(kāi)始時(shí)間 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; }); }, //彈框打開(kāi)看視頻 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; //沒(méi)有分類(lèi)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 + '' //投票時(shí)段 // 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" // ); //賽事結(jié)束時(shí)段 // this.formData.liveStart = new Date(result.liveStart); //this.formData.registration.push(start) //this.formData.registration.push(end) //手動(dòng)賦值 // this.$set(this.formData, "preSale", [preSaleStart, preSaleEnd]); //this.$set(this.formData, "vote", [voteStart, voteEnd]); //日期格式化 //預(yù)售 時(shí)間段 this.formData.preSaleStart = result.preSaleStart ? timestampFormat(result.preSaleStart) : ""; this.formData.preSaleEnd = result.preSaleEnd ? timestampFormat(result.preSaleEnd) : ""; //非預(yù)售 開(kāi)始售票時(shí)間 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 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
加油~~~~
到此這篇關(guān)于Vue實(shí)現(xiàn)直播功能的文章就介紹到這了,更多相關(guān)Vue實(shí)現(xiàn)直播內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue綁定class和綁定內(nèi)聯(lián)樣式的實(shí)現(xiàn)方法
本文主要介紹了Vue綁定class和綁定內(nèi)聯(lián)樣式的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11VSCode開(kāi)發(fā)UNI-APP 配置教程及插件
uni-app 是一個(gè)使用 Vue.js 開(kāi)發(fā)所有前端應(yīng)用的框架,今天通過(guò)本文給大家分享VSCode開(kāi)發(fā)UNI-APP 配置教程及插件推薦與注意事項(xiàng),感興趣的朋友一起看看吧2021-08-08關(guān)于ElementPlus中的表單驗(yàn)證規(guī)則詳解
這篇文章主要介紹了關(guān)于ElementPlus中的表單驗(yàn)證,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06vue?select組件綁定的值為數(shù)字類(lèi)型遇到的問(wèn)題
這篇文章主要介紹了vue?select組件綁定的值為數(shù)字類(lèi)型遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09Vue 使用formData方式向后臺(tái)發(fā)送數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了Vue 使用formData方式向后臺(tái)發(fā)送數(shù)據(jù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04關(guān)于Vue的?Vuex的4個(gè)輔助函數(shù)
這篇文章主要介紹了關(guān)于Vue的?Vuex的4個(gè)輔助函數(shù),輔助函數(shù)的好處就是幫助我們簡(jiǎn)化了獲取store中state、getter、mutation和action,下面我們一起來(lái)看看文章具體的舉例說(shuō)明吧,需要的小伙伴也可以參考一下2021-12-12解決VUE mounted 鉤子函數(shù)執(zhí)行時(shí) img 未加載導(dǎo)致頁(yè)面布局的問(wèn)題
這篇文章主要介紹了解決VUE mounted 鉤子函數(shù)執(zhí)行時(shí) img 未加載導(dǎo)致頁(yè)面布局的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07