390 lines
17 KiB
JavaScript
390 lines
17 KiB
JavaScript
|
|
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()
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|