UNPKG

krtp

Version:

Node implementation of rdp protocol. RFC 3550

207 lines (170 loc) 5.17 kB
/* * +=============================================== * | Author: Parham Alvani (parham.alvani@gmail.com) * | * | Creation Date: 01-06-2017 * | * | File Name: session.ts * +=============================================== */ import { randomBytes } from "crypto"; import * as dgram from "dgram"; import { EventEmitter } from "events"; import { Observable, fromEvent } from "rxjs"; import { Packet } from "./Packet"; import { ControlSR } from "./Control"; import { Readable, Writable } from "stream"; export declare interface Session { on( event: "message", listener: (msg: Packet, rinfo: dgram.RemoteInfo) => void ): this; on(event: "close", listener: () => void): this; on(event: string, listener: Function): this; } export class ReadRTPStream extends Readable { private onMessage: (msg: Packet) => void; constructor(private session: Session) { super(); this.session.on("close", () => this.push(null)); this.onMessage = (msg: Packet) => { if (!this.push(msg.payload)) { this.session.removeListener("message", this.onMessage); } }; } _read(_size?: number) { if (this.session.listeners("message").indexOf(this.onMessage) === -1) { this.session.on("message", this.onMessage); } } } export class WriteRTPStream extends Writable { constructor(private session: Session, private destination: string) { super(); } _write(chunk: Buffer, _encoding: string, callback: (err?: Error) => void) { this.session.send(chunk, this.destination).then( () => callback(), (err) => callback(err) ); } _destroy(err: Error, callback: (err: Error) => void) { this.session.close(); callback(err); } } /** * RTP session: an association among a set of participants * communicating with RTP. */ export class Session extends EventEmitter { /* * The SSRC field identifies * the synchronization source */ public readonly ssrc: number; public get sequenceNumber(): number { return this._sequenceNumber; } public get packetCount(): number { return this._packetCount; } public get octetCount(): number { return this._octetCount; } private timestamp: number; /* * The sequence number increments by one for each * RTP data packet sent, and may be used by the receiver to detect * packet loss and to restore packet sequence. */ private _sequenceNumber: number; // the total number of RTP data packets private _packetCount: number; // the total number of payload octets private _octetCount: number; // socket for session's data communication private socket: dgram.Socket; // socket for session's control communication private controlSocket: dgram.Socket; /** * creates a RTP session with RTCP. please note that the port + 1 is used for rtcp communication. * @param port - RTP port * @param packetType - RTP packet type: This field identifies the format of the RTP * payload and determines its interpretation by the application. */ constructor(private port: number, private packetType: number = 95) { super(); this.timestamp = (Date.now() / 1000) | 0; this._sequenceNumber = randomBytes(2).readUInt16BE(0); this.ssrc = randomBytes(4).readUInt32BE(0); this._packetCount = 0; this._octetCount = 0; this.socket = dgram.createSocket("udp4"); this.socket.on("message", (msg: Buffer, rinfo: dgram.RemoteInfo) => { const packet: Packet = Packet.deserialize(msg); this.emit("message", packet, rinfo); }); this.socket.bind(this.port, "0.0.0.0"); this.controlSocket = dgram.createSocket("udp4"); this.controlSocket.bind(this.port + 1); } public sendSR( address: string = "127.0.0.1", timestamp: number = ((Date.now() / 1000) | 0) - this.timestamp ): Promise<void> { const packet = new ControlSR( this._packetCount, this._octetCount, this.ssrc, timestamp ); return new Promise<void>((resolve, reject) => { this.controlSocket.send( packet.serialize(), this.port + 1, address, (err) => { if (err) { return reject(err); } return resolve(); } ); }); } public send( payload: Buffer, address: string = "127.0.0.1", timestamp: number = ((Date.now() / 1000) | 0) - this.timestamp ): Promise<void> { const packet = new Packet( payload, this._sequenceNumber, this.ssrc, timestamp, this.packetType ); this._sequenceNumber = (this._sequenceNumber + 1) % (1 << 16); this._packetCount += 1; this._octetCount += payload.length; return new Promise<void>((resolve, reject) => { this.socket.send(packet.serialize(), this.port, address, (err) => { if (err) { return reject(err); } return resolve(); }); }); } public close(): void { this.socket.close(() => { this.emit("close"); }); this.controlSocket.close(); } public get message$(): Observable<Packet> { return fromEvent(this, "message", (msg) => msg); } }