Files
scrypted/plugins/sip/src/rtp-utils.ts
slyoldfox e8ee21e567 btcino 0.0.7 / sip 0.0.6 (#644)
* * Fix an issues in SIP.js where the ACK and BYE replies didn't go to the correct uri

* * Implemented outgoing SIP MESSAGE sending
* Adding voice mail check
* Adding a lock for a bticino doorbell

* Cleanup dependencies, code in sip, bticino plugins

* Cleanup dependencies, code in sip, bticino plugins

* Clear stale devices from our map and clear the voicemail check

* Do not require register() for a SIP call

* Narrow down the event matching to deletes of devices

* Use releaseDevice to clean up stale entries

* Fix uuid version

* Attempt to make two way audio work

* Attempt to make two way audio work - fine tuning

* Enable incoming doorbell events

* SipCall was never a "sip call" but more like a manager
SipSession was more the "sip call"

* * Rename sip registered session to persistent sip manager
* Allow handling of call pickup in homekit (hopefully!)

* * use the consoles from the camera object

* * use the consoles from the camera object

* * Fix the retry timer

* * Added webhook url

* * parse record route correctly

* * Add gruu and use a custom fork of sip.js which supports keepAlive SIP clients (and dropped Websocket)
* use cross-env in package.json

* Added webhook urls for faster handling of events

* Added videoclips

* plugins/sip 0.0.6

* plugins/bticino 0.0.7
2023-03-20 07:19:08 -07:00

127 lines
3.1 KiB
TypeScript

// by @dgrief from @homebridge/camera-utils
import { SrtpOptions } from '@homebridge/camera-utils'
import dgram from 'dgram'
const stun = require('stun')
const stunMagicCookie = 0x2112a442 // https://tools.ietf.org/html/rfc5389#section-6
export interface RtpStreamOptions extends SrtpOptions {
port: number
rtcpPort: number
}
export interface RtpOptions {
audio: RtpStreamOptions
video: RtpStreamOptions
}
export interface RtpStreamDescription extends RtpStreamOptions {
ssrc?: number
iceUFrag?: string
icePwd?: string
}
export interface RtpDescription {
address: string
audio: RtpStreamDescription
video: RtpStreamDescription
sdp: string
}
export function isRtpMessagePayloadType(payloadType: number) {
return payloadType > 90 || payloadType === 0
}
export function getPayloadType(message: Buffer) {
return message.readUInt8(1) & 0x7f
}
export function getSequenceNumber(message: Buffer) {
return message.readUInt16BE(2)
}
export function isStunMessage(message: Buffer) {
return message.length > 8 && message.readInt32BE(4) === stunMagicCookie
}
export function sendStunBindingRequest({
rtpDescription,
rtpSplitter,
rtcpSplitter,
localUfrag,
type,
}: {
rtpSplitter: dgram.Socket
rtcpSplitter: dgram.Socket
rtpDescription: RtpDescription
localUfrag?: string
type: 'audio' | 'video'
}) {
const remoteDescription = rtpDescription[type];
if( remoteDescription.port == 0 )
return;
const message = stun.createMessage(1),
{ address } = rtpDescription,
{ iceUFrag, icePwd, port, rtcpPort } = remoteDescription
if (iceUFrag && icePwd && localUfrag) {
// Full ICE supported. Send as formal stun request
message.addUsername(iceUFrag + ':' + localUfrag)
message.addMessageIntegrity(icePwd)
stun
.request(`${address}:${port}`, {
socket: rtpSplitter,
message,
})
.then(() => console.debug(`${type} stun complete`))
.catch((e: Error) => {
console.error(`${type} stun error`)
console.error(e)
})
} else {
// ICE not supported. Fire and forget the stun request for RTP and RTCP
const encodedMessage = stun.encode(message)
try {
rtpSplitter.send(encodedMessage, port, address)
} catch (e) {
console.error(e)
}
try {
rtcpSplitter.send(encodedMessage, rtcpPort, address)
} catch (e) {
console.error(e)
}
}
}
export function createStunResponder(rtpSplitter: dgram.Socket) {
return rtpSplitter.on('message', (message, info) => {
if (!isStunMessage(message)) {
return null
}
try {
const decodedMessage = stun.decode(message),
response = stun.createMessage(
stun.constants.STUN_BINDING_RESPONSE,
decodedMessage.transactionId
)
response.addXorAddress(info.address, info.port)
try {
rtpSplitter.send(stun.encode(response), info.port, info.address)
} catch (e) {
console.error(e)
}
} catch (e) {
console.debug('Failed to Decode STUN Message')
console.debug(message.toString('hex'))
console.debug(e)
}
return null
})
}