websocket搭建简单的H264实时视频流播放
为了方便引入了vuejs,跟element-ui,
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>hs-demo</title> <!-- 引入样式 --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <style> .el-main { display: flex; width: 100%; flex-wrap: wrap; justify-content: space-between; } .video-box { width: 400px; height: 500px; border-radius: 4px; border: 1px solid gray; margin-bottom: 20px; } .video-main { width: 100%; } .video-info { padding: 5px; } .el-input {margin-bottom: 5px} .video-btn .el-button { margin-right: 5px; } </style></head><body><div id="app"> <el-button style="margin: 20px 0 0 20px;" @click="addPlayer">Add+</el-button> <el-main> <div v-for="(item, index) in videoList" class="video-box"> <div class="video-main"> <video width="100%" height="330px" controls autoplay :id="item.el"></video> </div> <div class="video-info"> <el-input placeholder="输入视频地址" v-model="item.assetURL"> <template slot="prepend">视频地址</template> </el-input> <div class="video-btn"> <el-button v-show="!item.isPlaying" :loading="item.videoLoading" @click="wsInit(item)">play</el-button> <el-button v-show="item.isPlaying" @click="wsDestroy(item)">destory</el-button> <el-button @click="wsRemove(index)">remove</el-button> </div> </div> </div> </el-main></div><script src="./vue.js"></script><script src="https://unpkg.com/element-ui/lib/index.js"></script><script> new Vue({ el: '#app', data: { videoList: [], loading: null, }, mounted() { this.addPlayer(); }, methods: { addPlayer() { let obj = { el: `video${Math.random().toString().slice(2,9)}`, // 播放器dom assetURL: 'ws://10.116.64.179:20185/real/sub/2fa2f2d8cc6be4d02f92994a5933674f/4325031985_581.mp4', seq: 1, mimeCodec: null, mediaSource: null, sourceBuffer: null, isPlaying: false, isReady: true, queue: [], ws: null, videoLoading: false } this.videoList.push(obj); }, wsInit(item) { item.videoLoading = true; this.loading = this.$loading({ target: '.video-main', lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }); item.ws = new WebSocket(item.assetURL); item.ws.binaryType = 'arraybuffer'; item.ws.onopen = () => { item.ws.send(JSON.stringify({method:'open', seq: item.seq})) }; item.ws.onerror = function () { }; item.ws.onmessage = (ev) => { let {data} = ev; if (data instanceof ArrayBuffer) { item.queue.push(data); if (!item.isPlaying) { this.initPlayer(item); } else { this.feed(item); } } else { let mes = JSON.parse(ev.data) if (mes.method === 'open') { item.mimeCodec = mes.mime; item.ws.send(JSON.stringify({method:'play', seq: item.seq})) } } }; item.ws.onclose = function () { console.log(item.el); }; }, initPlayer(item) { if ('MediaSource' in window && MediaSource.isTypeSupported(item.mimeCodec)) { item.isPlaying = true; item.mediaSource = new MediaSource(); item.video = document.getElementById(`${item.el}`); item.video.src = URL.createObjectURL(item.mediaSource); item.mediaSource.addEventListener('sourceopen', this.sourceOpen.bind(null, item)); } else { console.error('Unsupported MIME type or codec: ', item.mimeCodec); } }, sourceOpen(item) { // 存在了sourceBuffer 不再重复生成 item.sourceBuffer = item.mediaSource.addSourceBuffer(item.mimeCodec); item.sourceBuffer.mode = 'sequence'; item.sourceBuffer.addEventListener('updateend', () => { if (!item.isPlaying) { item.video.play(); } // 判断如果队列里有数据则继续放入sourceBuffer item.isReady = true; }); this.feed(item); }, feed(item) { if (item.isReady && item.queue.length) { item.videoLoading = false; this.loading.close(); item.isReady = false; item.sourceBuffer.appendBuffer(item.queue.shift()); } }, wsDestroy(item) { item.isPlaying = false; item.ws.send(JSON.stringify({method:'close', seq: item.seq})); item.ws.close(); this.wsReset(item); }, wsReset(item) { Object.assign(item, { mimeCodec: null, mediaSource: null, sourceBuffer: null, isPlaying: false, isReady: true, queue: [], ws: null, videoLoading: false }) }, wsRemove(index) { this.videoList.splice(index, 1) } } })</script></body></html>连接websocket跟获取视频流的地址采用的同一个,也就是说每个视频流播放都会重新实例化一个websocket,原理就是利用了mideaSource流式加载视频资源,c++通过websocket不断的推二进制数据流过来,然后喂到video里面,注意的是,sourceBuffer在上一个chunk updateend之前不能加入新的chunk,否则会报错。
原文链接: https://www.cnblogs.com/hsdying/p/14207844.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/206732
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!