missing files

This commit is contained in:
Koushik Dutta
2021-09-14 02:11:33 -07:00
parent d9728d1d47
commit 89d0c57ec7
2 changed files with 181 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
import sdk from "@scrypted/sdk";
import { listenZeroCluster } from "@scrypted/common/src/listen-cluster";
import { FFMpegInput, Intercom, ScryptedDevice } from "@scrypted/sdk";
import { createSocket, Socket, SocketType } from "dgram";
import { createServer, Server } from "net";
import child_process from "child_process";
import { ffmpegLogInitialOutput } from "@scrypted/common/src/ffmpeg-helper";
import { FFMpegRebroadcastSession, startRebroadcastSession } from "@scrypted/common/src/ffmpeg-rebroadcast";
const { mediaManager } = sdk;
async function pickPort(socketType: SocketType) {
// const socket = createSocket(socketType);
// return await new Promise(resolve => socket.bind(0, () => {
// const { port } = socket.address();
// socket.close(() => resolve(port));
// }));
return Math.round(Math.abs(Math.random()) * 40000 + 10000);
}
export class IntercomSession {
sdpReturnAudio: string;
sdpServer: Server;
session: FFMpegRebroadcastSession;
port: number;
heartbeatTimer: NodeJS.Timeout;
constructor(public device: ScryptedDevice & Intercom, public socketType: SocketType, public address: string, public srtp: Buffer) {
}
async start(): Promise<FFMpegRebroadcastSession> {
const sdpIpVersion = this.socketType === "udp6" ? "IP6 " : "IP4";
this.port = await pickPort(this.socketType);
// Session description protocol message that FFmpeg will share with HomeKit.
// SDP messages tell the other side of the connection what we're expecting to receive.
//
// Parameters are:
// v protocol version - always 0.
// o originator and session identifier.
// s session description.
// c connection information.
// t timestamps for the start and end of the session.
// m media type - audio, adhering to RTP/AVP, payload type 110.
// b bandwidth information - application specific, 24k.
// a=rtpmap payload type 110 corresponds to an MP4 stream.
// a=fmtp for payload type 110, use these format parameters.
// a=crypto crypto suite to use for this session.
this.sdpReturnAudio = [
"v=0",
"o=- 0 0 IN " + sdpIpVersion + " 127.0.0.1",
"s=" + this.device.name + " Audio Talkback",
"c=IN " + sdpIpVersion + " " + this.address,
"t=0 0",
"m=audio " + this.port + " RTP/AVP 110",
"b=AS:24",
"a=rtpmap:110 MPEG4-GENERIC/16000/1",
"a=fmtp:110 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=F8F0212C00BC00",
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:" + this.srtp.toString("base64")
].join("\n");
this.sdpServer = createServer(socket => {
this.sdpServer.close();
socket.write(this.sdpReturnAudio);
socket.end();
});
const sdpPort = await listenZeroCluster(this.sdpServer);
console.log('sdp port', sdpPort);
const ffmpegInput: FFMpegInput = {
inputArguments: [
"-f", "sdp",
"-acodec", "libfdk_aac",
"-ac", '1',
"-i", `tcp://127.0.0.1:${sdpPort}`,
]
};
this.session = await startRebroadcastSession(ffmpegInput, {
vcodec: ['-vn'],
acodec: ['-acodec', 'libfdk_aac', '-ac', '1'],
outputFormat: 'adts',
});
return this.session;
}
// Send a regular heartbeat to FFmpeg to ensure the pipe remains open and the process alive.
heartbeat(socket: Socket, heartbeat: Buffer): void {
// Clear the old heartbeat timer.
clearTimeout(this.heartbeatTimer);
// Send a heartbeat to FFmpeg every few seconds to keep things open. FFmpeg has a five-second timeout
// in reading input, and we want to be comfortably within the margin for error to ensure the process
// continues to run.
this.heartbeatTimer = setTimeout(() => {
socket.send(heartbeat, this.port);
this.heartbeat(socket, heartbeat);
}, 3.5 * 1000);
}
destroy() {
this.sdpServer?.close();
this.session?.kill();
}
}

View File

@@ -0,0 +1,72 @@
/* Copyright(C) 2017-2021, HJD (https://github.com/hjdhjd). All rights reserved.
*
* protect-rtp.ts: RTP-related utilities to slice and dice RTP streams.
*
* This module is heavily inspired by the homebridge and homebridge-camera-ffmpeg source code and
* borrows heavily from both. Thank you for your contributions to the HomeKit world.
*/
import { Socket } from "dgram";
import { EventEmitter } from "stream";
// How often, in seconds, should we heartbeat FFmpeg in two-way audio sessions. This should be less than 5 seconds, which is
// FFmpeg's input timeout interval.
export const PROTECT_TWOWAY_HEARTBEAT_INTERVAL = 3.5;
/*
* Here's the problem this class solves: FFmpeg doesn't support multiplexing RTP and RTCP data on a single UDP port (RFC 5761).
* If it did, we wouldn't need this workaround for HomeKit compatibility, which does multiplex RTP and RTCP over a single UDP port.
*
* This class inspects all packets coming in from inputPort and demultiplexes RTP and RTCP traffic to rtpPort and rtcpPort, respectively.
*
* Credit to @dgreif and @brandawg93 who graciously shared their code as a starting point, and their collaboration
* in answering the questions needed to bring all this together. A special thank you to @Sunoo for the many hours of
* discussion and brainstorming on this and other topics.
*/
export class RtpDemuxer extends EventEmitter {
private heartbeatTimer!: NodeJS.Timeout;
private heartbeatMsg!: Buffer;
// Create an instance of RtpDemuxer.
constructor(public deviceName: string, public console: Console, public socket: Socket) {
super();
// Catch errors when they happen on our demuxer.
this.socket.on("error", (error) => {
this.console.error("%s: RtpDemuxer Error: %s", this.deviceName, error);
this.socket.close();
});
// Split the message into RTP and RTCP packets.
this.socket.on("message", (msg) => {
// Send RTP packets to the RTP port.
if (this.isRtpMessage(msg)) {
this.emit('rtp', msg);
} else {
this.emit('rtcp', msg);
}
});
this.console.log("%s: Creating an RtpDemuxer instance - inbound port: %s, RTCP port: %s, RTP port: %s.",
this.deviceName);
}
// Close the socket and cleanup.
public close(): void {
this.console.log("%s: Closing the RtpDemuxer instance on port %s.", this.deviceName);
clearTimeout(this.heartbeatTimer);
this.socket.close();
}
// Retrieve the payload information from a packet to discern what the packet payload is.
private getPayloadType(message: Buffer): number {
return message.readUInt8(1) & 0x7f;
}
// Return whether or not a packet is RTP (or not).
private isRtpMessage(message: Buffer): boolean {
const payloadType = this.getPayloadType(message);
return (payloadType > 90) || (payloadType === 0);
}
}