当前位置: 首页 > article >正文

浏览器播放 WebRTC 视频流

源码(vue)

<template><video ref="videoElement" class="video" autoplay muted playsinline></video>
</template><script setup lang="ts">import { onBeforeUnmount, onMounted, ref } from 'vue'import { JSWebrtc } from '@/utils/jswebrtc.min.js'const videoElement = ref<HTMLVideoElement | null>(null)let player: JSWebrtc.Player | null = nullonMounted(() => {if (!videoElement.value) returnplayer = new JSWebrtc.Player('webrtc://192.168.20.222/live/34020000001320000002', {video: videoElement.value,autoplay: true,onPlay: (obj: any) => {console.log('start play', obj)},onError: (error: Error) => {console.error('Playback error:', error)}})})onBeforeUnmount(() => {player?.destroy()player = null})
</script>

jswebrtc.min.js

export var JSWebrtc = {Player: null,VideoElement: null,CreateVideoElements: function () {let elements = document.querySelectorAll('.jswebrtc')for (let i = 0; i < elements.length; i++) {new JSWebrtc.VideoElement(elements[i])}},FillQuery: function (query_string, obj) {obj.user_query = {}if (query_string.length == 0) returnif (query_string.indexOf('?') >= 0) query_string = query_string.split('?')[1]let queries = query_string.split('&')for (let i = 0; i < queries.length; i++) {let query = queries[i].split('=')obj[query[0]] = query[1]obj.user_query[query[0]] = query[1]}if (obj.domain) obj.vhost = obj.domain},ParseUrl: function (rtmp_url) {let a = document.createElement('a')a.href = rtmp_url.replace('rtmp://', 'http://').replace('webrtc://', 'http://').replace('rtc://', 'http://')let vhost = a.hostnamelet app = a.pathname.substr(1, a.pathname.lastIndexOf('/') - 1)let stream = a.pathname.substr(a.pathname.lastIndexOf('/') + 1)app = app.replace('...vhost...', '?vhost=')if (app.indexOf('?') >= 0) {let params = app.substr(app.indexOf('?'))app = app.substr(0, app.indexOf('?'))if (params.indexOf('vhost=') > 0) {vhost = params.substr(params.indexOf('vhost=') + 'vhost='.length)if (vhost.indexOf('&') > 0) {vhost = vhost.substr(0, vhost.indexOf('&'))}}}if (a.hostname == vhost) {let re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/if (re.test(a.hostname)) vhost = '__defaultVhost__'}let schema = 'rtmp'if (rtmp_url.indexOf('://') > 0) schema = rtmp_url.substr(0, rtmp_url.indexOf('://'))let port = a.portif (!port) {if (schema === 'http') {port = 80} else if (schema === 'https') {port = 443} else if (schema === 'rtmp') {port = 1935} else if (schema === 'webrtc' || schema === 'rtc') {port = 1985}}let ret = {url: rtmp_url,schema: schema,server: a.hostname,port: port,vhost: vhost,app: app,stream: stream}JSWebrtc.FillQuery(a.search, ret)return ret},HttpPost: function (url, data) {return new Promise(function (resolve, reject) {let xhr = new XMLHttpRequest()xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {let respone = JSON.parse(xhr.responseText)xhr.onreadystatechange = new Function()xhr = nullresolve(respone)}}xhr.open('POST', url, true)xhr.timeout = 5e3xhr.responseType = 'text'xhr.setRequestHeader('Content-Type', 'application/json')xhr.send(data)})}
}
if (document.readyState === 'complete') {JSWebrtc.CreateVideoElements()
} else {document.addEventListener('DOMContentLoaded', JSWebrtc.CreateVideoElements)
}
JSWebrtc.VideoElement = (function () {'use strict'let VideoElement = function (element) {let url = element.dataset.urlif (!url) {throw 'VideoElement has no `data-url` attribute'}let addStyles = function (element, styles) {for (let name in styles) {element.style[name] = styles[name]}}this.container = elementaddStyles(this.container, {display: 'inline-block',position: 'relative',minWidth: '80px',minHeight: '80px'})this.video = document.createElement('video')this.video.width = 960this.video.height = 540addStyles(this.video, { display: 'block', width: '100%' })this.container.appendChild(this.video)this.playButton = document.createElement('div')this.playButton.innerHTML = VideoElement.PLAY_BUTTONaddStyles(this.playButton, {zIndex: 2,position: 'absolute',top: '0',bottom: '0',left: '0',right: '0',maxWidth: '75px',maxHeight: '75px',margin: 'auto',opacity: '0.7',cursor: 'pointer'})this.container.appendChild(this.playButton)let options = { video: this.video }for (let option in element.dataset) {try {options[option] = JSON.parse(element.dataset[option])} catch (err) {options[option] = element.dataset[option]}}this.player = new JSWebrtc.Player(url, options)element.playerInstance = this.playerif (options.poster && !options.autoplay) {options.decodeFirstFrame = falsethis.poster = new Image()this.poster.src = options.posterthis.poster.addEventListener('load', this.posterLoaded)addStyles(this.poster, {display: 'block',zIndex: 1,position: 'absolute',top: 0,left: 0,bottom: 0,right: 0})this.container.appendChild(this.poster)}if (!this.player.options.streaming) {this.container.addEventListener('click', this.onClick.bind(this))}if (options.autoplay) {this.playButton.style.display = 'none'}if (this.player.audioOut && !this.player.audioOut.unlocked) {let unlockAudioElement = this.containerif (options.autoplay) {this.unmuteButton = document.createElement('div')this.unmuteButton.innerHTML = VideoElement.UNMUTE_BUTTONaddStyles(this.unmuteButton, {zIndex: 2,position: 'absolute',bottom: '10px',right: '20px',width: '75px',height: '75px',margin: 'auto',opacity: '0.7',cursor: 'pointer'})this.container.appendChild(this.unmuteButton)unlockAudioElement = this.unmuteButton}this.unlockAudioBound = this.onUnlockAudio.bind(this, unlockAudioElement)unlockAudioElement.addEventListener('touchstart', this.unlockAudioBound, false)unlockAudioElement.addEventListener('click', this.unlockAudioBound, true)}}VideoElement.prototype.onUnlockAudio = function (element, ev) {if (this.unmuteButton) {ev.preventDefault()ev.stopPropagation()}this.player.audioOut.unlock(function () {if (this.unmuteButton) {this.unmuteButton.style.display = 'none'}element.removeEventListener('touchstart', this.unlockAudioBound)element.removeEventListener('click', this.unlockAudioBound)}.bind(this))}VideoElement.prototype.onClick = function (ev) {if (this.player.isPlaying) {this.player.pause()this.playButton.style.display = 'block'} else {this.player.play()this.playButton.style.display = 'none'if (this.poster) {this.poster.style.display = 'none'}}}VideoElement.PLAY_BUTTON ='<svg style="max-width: 75px; max-height: 75px;" ' +'viewBox="0 0 200 200" alt="Play video">' +'<circle cx="100" cy="100" r="90" fill="none" ' +'stroke-width="15" stroke="#fff"/>' +'<polygon points="70, 55 70, 145 145, 100" fill="#fff"/>' +'</svg>'VideoElement.UNMUTE_BUTTON ='<svg style="max-width: 75px; max-height: 75px;" viewBox="0 0 75 75">' +'<polygon class="audio-speaker" stroke="none" fill="#fff" ' +'points="39,13 22,28 6,28 6,47 21,47 39,62 39,13"/>' +'<g stroke="#fff" stroke-width="5">' +'<path d="M 49,50 69,26"/>' +'<path d="M 69,50 49,26"/>' +'</g>' +'</svg>'return VideoElement
})()
JSWebrtc.Player = (function () {'use strict'let Player = function (url, options) {this.options = options || {}if (!url.match(/^webrtc?:\/\//)) {throw 'JSWebrtc just work with webrtc'}if (!this.options.video) {throw 'VideoElement is null'}this.urlParams = JSWebrtc.ParseUrl(url)this.pc = nullthis.autoplay = !!options.autoplay || falsethis.paused = trueif (this.autoplay) this.options.video.muted = truethis.startLoading()}Player.prototype.startLoading = function () {let _self = thisif (_self.pc) {_self.pc.close()}_self.pc = new RTCPeerConnection(null)_self.pc.ontrack = function (event) {_self.options.video['srcObject'] = event.streams[0]}_self.pc.addTransceiver('audio', { direction: 'recvonly' })_self.pc.addTransceiver('video', { direction: 'recvonly' })_self.pc.createOffer().then(function (offer) {return _self.pc.setLocalDescription(offer).then(function () {return offer})}).then(function (offer) {return new Promise(function (resolve, reject) {let port = _self.urlParams.port || 1985let api = _self.urlParams.user_query.play || '/rtc/v1/play/'if (api.lastIndexOf('/') != api.length - 1) {api += '/'}let url = 'http://' + _self.urlParams.server + ':' + port + apifor (let key in _self.urlParams.user_query) {if (key != 'api' && key != 'play') {url += '&' + key + '=' + _self.urlParams.user_query[key]}}let data = {api: url,streamurl: _self.urlParams.url,clientip: null,sdp: offer.sdp,tid: Number(parseInt(new Date().getTime() * Math.random() * 100)).toString(16).slice(0, 7)}//   console.log('offer:1111111111111 ' + JSON.stringify(data))JSWebrtc.HttpPost(url, JSON.stringify(data)).then(function (res) {// console.log('answer: ' + JSON.stringify(res))resolve(res.sdp)},function (rej) {reject(rej)})})}).then(function (answer) {return _self.pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: answer }))}).catch(function (reason) {throw reason})if (this.autoplay) {this.play()}}Player.prototype.play = function (ev) {if (this.animationId) {return}this.animationId = requestAnimationFrame(this.update.bind(this))this.paused = false}Player.prototype.pause = function (ev) {if (this.paused) {return}cancelAnimationFrame(this.animationId)this.animationId = nullthis.isPlaying = falsethis.paused = truethis.options.video.pause()if (this.options.onPause) {this.options.onPause(this)}}Player.prototype.stop = function (ev) {this.pause()}Player.prototype.destroy = function () {this.pause()this.pc && this.pc.close() && this.pc.destroy()this.audioOut && this.audioOut.destroy()}Player.prototype.update = function () {this.animationId = requestAnimationFrame(this.update.bind(this))if (this.options.video.readyState < 4) {return}if (!this.isPlaying) {this.isPlaying = truethis.options.video.play()if (this.options.onPlay) {this.options.onPlay(this)}}}return Player
})()
http://www.lryc.cn/news/2383497.html

相关文章:

  • 从零开始:使用 PyTorch 构建深度学习网络
  • 分类算法 Kmeans、KNN、Meanshift 实战
  • 【razor】回环结构导致的控制信令错位:例如发送端收到 SR的问题
  • 网络安全之身份验证绕过漏洞
  • MySQL 主从复制搭建全流程:基于 Docker 与 Harbor 仓库
  • vscode打开vue + element项目
  • Django框架的前端部分使用Ajax请求一
  • cmd如何从C盘默认路径切换到D盘某指定目录
  • 693SJBH基于.NET的题库管理系统
  • [Vue]跨组件传值
  • 每日Prompt:实物与手绘涂鸦创意广告
  • 学习笔记:黑马程序员JavaWeb开发教程(2025.4.8)
  • vue3 在线播放语音 mp3
  • Ubuntu部署私有Gitlab
  • genicamtl_lmi_gocator_objectmodel3d
  • [LevelDB]LevelDB版本管理的黑魔法-为什么能在不锁表的情况下管理数据?
  • bisheng系列(二)- 本地部署(前后端)
  • 【网络编程】十二、两万字详解 IP协议
  • Linux探秘:驾驭开源,解锁高效能——基础指令
  • WebSocket解决方案的一些细节阐述
  • 大数据量下Redis分片的5种策略
  • muduo库TcpServer模块详解
  • Java 代码生成工具:如何快速构建项目骨架?
  • Nginx核心服务
  • 第22天-Python ttkbootstrap 界面美化指南
  • Kubernetes控制平面组件:Kubelet详解(七):容器网络接口 CNI
  • web应用技术第6次课---Apifox的使用
  • Flutter与Kotlin Multiplatform(KMP)深度对比及鸿蒙生态适配解析
  • Predict Podcast Listening Time-(回归+特征工程+xgb)
  • Redis队列与Pub/Sub方案全解析:原理、对比与实战性能测试