diff --git a/src/index.ts b/src/index.ts index 643f57f..6e071f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { ChildProcess, spawn } from "child_process" +import { ChildProcess, spawn } from "child_process"; import { EventEmitter } from "events"; import { Writable } from "stream"; import { IOptions } from "./options"; @@ -7,88 +7,114 @@ const ef1 = "ff"; const ef2 = "d9"; export class Rtsp extends EventEmitter { - private _connecteionString: string; - private _childProcess: ChildProcess | undefined; - private _started: boolean; - private _buffer: Buffer; - private _options: IOptions; + private _connecteionString: string; + private _childProcess: ChildProcess | undefined; + private _started: boolean; + private _buffer: Buffer; + private _options: IOptions; + private _paused: boolean; - constructor(connectionString: string, options: IOptions) { - super(); - this._started = false; - this._connecteionString = connectionString; - this._childProcess = undefined; - this._buffer = Buffer.from(''); - this._options = options; + constructor(connectionString: string, options: IOptions) { + super(); + this._started = false; + this._connecteionString = connectionString; + this._childProcess = undefined; + this._buffer = Buffer.from(""); + this._options = options; + this._paused = false; + + this.onData = this.onData.bind(this); + } + + public get isStarted(): boolean { + return this._started; + } + + public get isPaused(): boolean { + return this._paused; + } + + public start(): void { + const argStrings = [ + // `-rtsp_transport tcp`, + `-i ${this._connecteionString}`, + `-r ${this._options.rate ?? 10}`, + this._options.image + ? `-f image2` + : `-codec:v ${this._options.codec ?? "libx264"}`, + `-update 1 -`, + ]; + const args = argStrings.join(" "); + this._childProcess = spawn("ffmpeg", args.split(/\s+/)); + + if (!this._childProcess) { + return; } - public get isStarted(): boolean { - return this._started; + this._childProcess.stdout?.on("data", this.onData); + this._childProcess.on("exit", this.onExit); + this._childProcess.on("error", this.onError); + + if (this._childProcess.stderr) { + this._childProcess.stderr.on("data", this.onStdErrorData); } + } - public start(): void { - const argStrings = [ - `-i ${this._connecteionString}`, - `-r ${this._options.rate ?? 10}`, - `-f image2`, - `-codec:v ${this._options.codec ?? 'libx264'}`, - `-update 1 -` - ]; - const args = argStrings.join(" "); - this._childProcess = spawn("ffmpeg", args.split(/\s+/)); + public close(): void { + this._childProcess && this._childProcess.kill("SIGKILL"); + this.emit("closed"); + } - if(!this._childProcess){ - return; - } - - this._childProcess.stdout?.on('data', this.onData) - this._childProcess.on('exit', this.onExit); - this._childProcess.on('error', this.onError); + public pause(): void { + this._paused = true; + } - if (this._childProcess.stderr) { - this._childProcess.stderr.on('data', this.onStdErrorData); - } + public resume(): void { + this._paused = false; + } + + public getStdin(): Writable | null { + return this._childProcess ? this._childProcess.stdin : null; + } + + private onError = (err: Error): void => { + this.emit("error", new Error("Failed to start stream: " + err.message)); + }; + + private onStdErrorData = (data: any): void => { + if (!this._started) { + this._started = true; } + let msg = ""; + data + .toString() + .split(/\n/) + .forEach((line: string) => { + msg += `${line}\n`; + }); - public close(): void { - this._childProcess && this._childProcess.kill('SIGKILL'); - this.emit('closed'); - } + this.emit("error", msg); + }; - public getStdin(): Writable | null { - return this._childProcess ? this._childProcess.stdin : null; + private onExit = (code: number, signal: NodeJS.Signals): void => { + const message = + "FFmpeg exited with code: " + code + " and signal: " + signal; + this.emit("closed", message); + }; + + private onData(data: Buffer): void { + if (!this._paused && data.length > 1) { + this._buffer = this._buffer + ? Buffer.concat([this._buffer, data]) + : (this._buffer = Buffer.from(data)); + //End of image + if ( + data[data.length - 2].toString(16) == ef1 && + data[data.length - 1].toString(16) == ef2 + ) { + this.emit("data", this._buffer); + this._buffer = Buffer.from(""); } - - private onError = (err: Error): void => { - this.emit('error', new Error('Failed to start stream: ' + err.message)); - } - - private onStdErrorData = (data: any): void => { - if (!this._started) { - this._started = true; - - } - let msg = ""; - data.toString().split(/\n/).forEach((line: string) => { - msg += `line\n`; - }); - - this.emit("error", msg); - } - - private onExit = (code: number, signal: NodeJS.Signals): void => { - const message = 'FFmpeg exited with code: ' + code + ' and signal: ' + signal; - this.emit('closed', message); - } - - private onData = (data: Buffer): void => { - if (data.length > 1) { - this._buffer = this._buffer ? Buffer.concat([this._buffer, data]) : this._buffer = Buffer.from(data); - //End of image - if(data[data.length - 2].toString(16) == ef1 && data[data.length -1].toString(16) == ef2){ - this.emit('data', this._buffer) - this._buffer = Buffer.from(''); - } - } } + } } diff --git a/src/options.ts b/src/options.ts index f17779f..49090db 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,6 +1,7 @@ export interface IOptions { - rate?: number, - quality?: number, - resolution?: string, - codec?: string, -} \ No newline at end of file + rate?: number; + quality?: number; + resolution?: string; + codec?: string; + image?: boolean; +} diff --git a/workspace.code-workspace b/workspace.code-workspace new file mode 100644 index 0000000..022c3e5 --- /dev/null +++ b/workspace.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +}