Files
ring-mqtt/devices/stream/live.js
2025-03-28 22:49:25 -04:00

116 lines
3.7 KiB
JavaScript

import { Worker } from 'worker_threads'
import dgram from 'dgram'
export class LiveStream {
constructor(device) {
this.altVideoData = false
this.mqttCamera = device
this.rtpSocket = false
this.rtcpSocket = false
this.session = false
this.status = 'inactive'
this.worker = new Worker('./devices/stream/live-worker.js', {
workerData: {
doorbotId: this.mqttCamera.device.id,
deviceName: this.mqttCamera.deviceData.name
}
})
this.worker.on('message', (message) => {
if (message.type === 'state') {
switch (message.data) {
case 'active':
this.status = 'active'
this.session = true
this.altVideoData = message.altVideoData
this.bindAltVideoPorts()
break
case 'inactive':
this.clearSession('inactive')
break
case 'failed':
this.clearSession('failed')
break
}
this.mqttCamera.publishStreamState()
} else {
switch (message.type) {
case 'log_info':
this.mqttCamera.debug(message.data, 'wrtc')
break
case 'log_error':
this.mqttCamera.debug(message.data, 'wrtc-error')
break
}
}
})
}
clearSession(status) {
this.altVideoData = false
this.session = false
this.status = status
this.unbindAltVideoPorts()
}
bindAltVideoPorts() {
if (this.altVideoData) {
this.rtpSocket = dgram.createSocket('udp4')
this.rtcpSocket = dgram.createSocket('udp4')
this.rtpSocket.bind(this.altVideoData.port)
this.rtcpSocket.bind(this.altVideoData.port + 1)
}
}
unbindAltVideoPorts() {
if (this.rtpSocket) {
this.rtpSocket.close()
}
if (this.rtcpSocket) {
this.rtcpSocket.close()
}
this.rtpSocket = false
this.rtcpSocket = false
}
async start(rtspPublishUrl) {
this.session = true
const streamData = {
rtspPublishUrl,
ticket: null
}
try {
this.mqttCamera.debug('Acquiring a live stream WebRTC signaling session ticket')
const response = await this.mqttCamera.device.restClient.request({
method: 'POST',
url: 'https://app.ring.com/api/v1/clap/ticket/request/signalsocket'
})
streamData.ticket = response.ticket
} catch(error) {
if (error?.response?.statusCode === 403) {
this.mqttCamera.debug('Camera returned 403 when starting a live stream. This usually indicates that live streaming is blocked by Modes settings.')
} else {
this.mqttCamera.debug(error)
}
}
if (streamData.ticket) {
this.mqttCamera.debug('Live stream WebRTC signaling session ticket acquired, starting live stream worker')
this.worker.postMessage({ command: 'start', streamData })
} else {
this.mqttCamera.debug('Live stream failed to initialize WebRTC signaling session')
this.clearSession('failed')
this.mqttCamera.publishStreamState()
}
}
async stop() {
if (this.session) {
this.worker.postMessage({ command: 'stop' })
}
this.clearSession('inactive')
}
}