class Webrtc { /******* * @name: * @description: * @param {object} InitialData 以下数据的对象 * @param {string} element 标签dom * @param {boolean} debug 是否打印日志 * @param {string} zlmsdpUrl 流地址 * @param {boolean} simulcast 无线电和电视同步播放 * @param {boolean} useCamera 是否使用摄像头 * @param {boolean} audioEnable 音频数据 * @param {boolean} videoEnable 视频数据 * @param {boolean} recvOnly true是拉流 false是推流 * @param {object} resolution { w: 3840, h: 2160 } 分辨率 * @param {boolean} usedatachannel //数据通道 * @param {*} player webrtc实例 * @return {*} * @author: */ /* let InitialData = { element:element, debug:debug, zlmsdpUrl:zlmsdpUrl, simulcast:simulcast, useCamera:useCamera, audioEnable:audioEnable, videoEnable:videoEnable, recvOnly:recvOnly, resolution:resolution, usedatachannel:usedatachannel, } */ constructor(InitialData) { this.element = InitialData.element this.debug = InitialData.debug this.zlmsdpUrl = InitialData.zlmsdpUrl this.simulcast = InitialData.simulcast this.useCamera = InitialData.useCamera this.audioEnable = InitialData.audioEnable this.videoEnable = InitialData.videoEnable this.recvOnly = InitialData.recvOnly this.resolution = InitialData.resolution this.usedatachannel = InitialData.usedatachannel this.player = null this.eventHandlers = {} } // 检测摄像头分辨率 detection() { return new Promise((resolve, reject) => { navigator.mediaDevices.enumerateDevices().then(devices => { devices = devices.filter(d => d.kind === 'videoinput') let creamData = devices[0].getCapabilities() console.log(creamData, 'creamData') let resolution = {} resolution.w = creamData.width.max resolution.h = creamData.height.max resolve(resolution) // this.resolution = resolution }).catch((err) => { console.log(err) reject(err) // this.resolution = resolution }) }) } // 拉流 start_play() { return new Promise((resolve, reject) => { let this_ = this this.push_url = null this.pull_url = null let ZLMRTCClient = ZLMRTCClient_({}) if (this.useCamera) { this.detection().then((resolution) => { this.player = new ZLMRTCClient.Endpoint({ element: this.element, debug: this.debug, zlmsdpUrl: this.zlmsdpUrl, simulcast: this.simulcast, useCamera: this.useCamera, audioEnable: this.audioEnable, videoEnable: this.videoEnable, recvOnly: this.recvOnly, resolution: resolution, usedatachannel: this.usedatachannel, }) this.player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, function (e) { // ICE 协商出错 console.log('ICE 协商出错') reject() }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) { //获取到了远端流,可以播放 console.log('播放成功', e.streams, e, this_.zlmsdpUrl) this_.pull_url = e.streams[0] // resolve(e.streams[0]) }) this.player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, function (e) { // offer anwser 交换失败 console.log('offer anwser 交换失败', e) this_.stop_play(reject, 'offer anwser 交换失败') }) // WEBRTC_IS_H265 this.player.on(ZLMRTCClient.Events.WEBRTC_IS_H265, function (e) { // offer anwser 交换失败 console.log('H265视频格式', e) this_.stop_play(reject, '右上角设置打开插件模式以支持H265') }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, function (s) { // 获取到了本地流 console.log('获取到了本地流') this_.push_url = s // resolve(s) }) this.player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, function (s) { // 获取本地流失败 console.log('获取本地流失败', s) this_.stop_play(reject, '获取本地流失败') }) /** *new:表示刚创建一个新的 WebRTC 对象。 connecting:表示正在建立连接。 connected:表示已经成功建立连接。 disconnected:表示连接已经中断。 failed:表示连接失败。 closed:表示连接已经关闭。 have-local-offer:表示本地已经创建了一个 offer。 have-remote-offer:表示远程端已经创建了一个 offer。 stable:表示连接已经稳定。 have-local-pranswer:表示本地已经创建了一个 pranswer。 have-remote-pranswer:表示远程端已经创建了一个 pranswer。 * */ this.player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, function (state) { // RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState console.log('当前状态==>', state) if (state == 'connected') { if (this_.push_url) { resolve(this_.push_url) } else if (this_.pull_url) { resolve(this_.pull_url) } } else { this_.triggerEvent(state) } }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN, function (event) { console.log('rtc datachannel 打开 :', event) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG, function (event) { console.log('rtc datachannel 消息 :', event.data) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR, function (event) { console.log('rtc datachannel 错误 :', event) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE, function (event) { console.log('rtc datachannel 关闭 :', event) }) }).catch(() => { this.player = new ZLMRTCClient.Endpoint({ element: this.element, debug: this.debug, zlmsdpUrl: this.zlmsdpUrl, simulcast: this.simulcast, useCamera: this.useCamera, audioEnable: this.audioEnable, videoEnable: this.videoEnable, recvOnly: this.recvOnly, resolution: this.resolution, usedatachannel: this.usedatachannel, }) this.player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, function (e) { // ICE 协商出错 console.log('ICE 协商出错') reject() }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) { //获取到了远端流,可以播放 console.log('播放成功', e.streams, e, this_.zlmsdpUrl) this_.pull_url = e.streams[0] // resolve(e.streams[0]) }) this.player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, function (e) { // offer anwser 交换失败 console.log('offer anwser 交换失败', e) this_.stop_play(reject, 'offer anwser 交换失败') }) // WEBRTC_IS_H265 this.player.on(ZLMRTCClient.Events.WEBRTC_IS_H265, function (e) { // offer anwser 交换失败 console.log('H265视频格式', e) this_.stop_play(reject, '右上角设置打开插件模式以支持H265') }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, function (s) { // 获取到了本地流 console.log('获取到了本地流') this_.push_url = s // resolve(s) }) this.player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, function (s) { // 获取本地流失败 console.log('获取本地流失败', s) this_.stop_play(reject, '获取本地流失败') }) /** *new:表示刚创建一个新的 WebRTC 对象。 connecting:表示正在建立连接。 connected:表示已经成功建立连接。 disconnected:表示连接已经中断。 failed:表示连接失败。 closed:表示连接已经关闭。 have-local-offer:表示本地已经创建了一个 offer。 have-remote-offer:表示远程端已经创建了一个 offer。 stable:表示连接已经稳定。 have-local-pranswer:表示本地已经创建了一个 pranswer。 have-remote-pranswer:表示远程端已经创建了一个 pranswer。 * */ this.player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, function (state) { // RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState console.log('当前状态==>', state) if (state == 'connected') { if (this_.push_url) { resolve(this_.push_url) } else if (this_.pull_url) { resolve(this_.pull_url) } } else { this_.triggerEvent(state) } }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN, function (event) { console.log('rtc datachannel 打开 :', event) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG, function (event) { console.log('rtc datachannel 消息 :', event.data) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR, function (event) { console.log('rtc datachannel 错误 :', event) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE, function (event) { console.log('rtc datachannel 关闭 :', event) }) }) } else { this.player = new ZLMRTCClient.Endpoint({ element: this.element, debug: this.debug, zlmsdpUrl: this.zlmsdpUrl, simulcast: this.simulcast, useCamera: this.useCamera, audioEnable: this.audioEnable, videoEnable: this.videoEnable, recvOnly: this.recvOnly, resolution: this.resolution, usedatachannel: this.usedatachannel, }) this.player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, function (e) { // ICE 协商出错 console.log('ICE 协商出错') reject() }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) { //获取到了远端流,可以播放 console.log('播放成功', e.streams, e, this_.zlmsdpUrl) this_.pull_url = e.streams[0] // resolve(e.streams[0]) }) this.player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, function (e) { // offer anwser 交换失败 console.log('offer anwser 交换失败', e) this_.stop_play(reject, 'offer anwser 交换失败') }) // WEBRTC_IS_H265 this.player.on(ZLMRTCClient.Events.WEBRTC_IS_H265, function (e) { // offer anwser 交换失败 console.log('H265视频格式', e) this_.stop_play(reject, '右上角设置打开插件模式以支持H265') }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, function (s) { // 获取到了本地流 console.log('获取到了本地流') this_.push_url = s // resolve(s) }) this.player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, function (s) { // 获取本地流失败 console.log('获取本地流失败', s) this_.stop_play(reject, '获取本地流失败') }) /** *new:表示刚创建一个新的 WebRTC 对象。 connecting:表示正在建立连接。 connected:表示已经成功建立连接。 disconnected:表示连接已经中断。 failed:表示连接失败。 closed:表示连接已经关闭。 have-local-offer:表示本地已经创建了一个 offer。 have-remote-offer:表示远程端已经创建了一个 offer。 stable:表示连接已经稳定。 have-local-pranswer:表示本地已经创建了一个 pranswer。 have-remote-pranswer:表示远程端已经创建了一个 pranswer。 * */ this.player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, function (state) { // RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState console.log('当前状态==>', state) if (state == 'connected') { this_.player.pc.onjitterBuffer() if (this_.push_url) { resolve(this_.push_url) } else if (this_.pull_url) { resolve(this_.pull_url) } } else { this_.triggerEvent(state) } }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN, function (event) { console.log('rtc datachannel 打开 :', event) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG, function (event) { console.log('rtc datachannel 消息 :', event.data) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR, function (event) { console.log('rtc datachannel 错误 :', event) }) this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE, function (event) { console.log('rtc datachannel 关闭 :', event) }) } }) } // 拉流失败的回调 stop_play(reject, msg) { if (this.player) { this.player.close() this.player = null } reject && reject(msg) } // 关闭拉流 close_play(remote1) { return new Promise((resolve, reject) => { console.log('关闭拉流或者推流') if (this.player) { this.player.close() this.player = null if (remote1) { remote1.srcObject = null remote1.load() } } resolve() }) } on(eventName, eventHandler) { if (!this.eventHandlers[eventName]) { this.eventHandlers[eventName] = [] } this.eventHandlers[eventName].push(eventHandler) } triggerEvent(eventName) { const handlers = this.eventHandlers[eventName] console.log(handlers) if (handlers) { handlers.forEach((handler) => { handler() }) } } }