UNPKG

homebridge-camera-ffmpeg

Version:

Homebridge Plugin Providing FFmpeg-based Camera Support

126 lines 5.08 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FfmpegProcess = void 0; const child_process_1 = require("child_process"); const os_1 = __importDefault(require("os")); const readline_1 = __importDefault(require("readline")); class FfmpegProcess { constructor(cameraName, sessionId, videoProcessor, ffmpegArgs, log, debug = false, delegate, callback) { log.debug('Stream command: ' + videoProcessor + ' ' + ffmpegArgs, cameraName, debug); let started = false; const startTime = Date.now(); this.process = (0, child_process_1.spawn)(videoProcessor, ffmpegArgs.split(/\s+/), { env: process.env }); this.stdin = this.process.stdin; this.process.stdout.on('data', (data) => { const progress = this.parseProgress(data); if (progress) { if (!started && progress.frame > 0) { started = true; const runtime = (Date.now() - startTime) / 1000; const message = 'Getting the first frames took ' + runtime + ' seconds.'; if (runtime < 5) { log.debug(message, cameraName, debug); } else if (runtime < 22) { log.warn(message, cameraName); } else { log.error(message, cameraName); } } } }); const stderr = readline_1.default.createInterface({ input: this.process.stderr, terminal: false }); stderr.on('line', (line) => { if (callback) { callback(); callback = undefined; } if (debug && line.match(/\[(panic|fatal|error)\]/)) { log.error(line, cameraName); } else if (debug) { log.debug(line, cameraName, true); } }); this.process.on('error', (error) => { log.error('FFmpeg process creation failed: ' + error.message, cameraName); if (callback) { callback(new Error('FFmpeg process creation failed')); } delegate.stopStream(sessionId); }); this.process.on('exit', (code, signal) => { if (this.killTimeout) { clearTimeout(this.killTimeout); } const message = 'FFmpeg exited with code: ' + code + ' and signal: ' + signal; if (this.killTimeout && code === 0) { log.debug(message + ' (Expected)', cameraName, debug); } else if (code == null || code === 255) { if (this.process.killed) { log.debug(message + ' (Forced)', cameraName, debug); } else { log.error(message + ' (Unexpected)', cameraName); } } else { log.error(message + ' (Error)', cameraName); delegate.stopStream(sessionId); if (!started && callback) { callback(new Error(message)); } else { delegate.controller.forceStopStreamingSession(sessionId); } } }); } parseProgress(data) { const input = data.toString(); if (input.indexOf('frame=') == 0) { try { const progress = new Map(); input.split(/\r?\n/).forEach((line) => { const split = line.split('=', 2); progress.set(split[0], split[1]); }); return { frame: parseInt(progress.get('frame')), fps: parseFloat(progress.get('fps')), stream_q: parseFloat(progress.get('stream_0_0_q')), bitrate: parseFloat(progress.get('bitrate')), total_size: parseInt(progress.get('total_size')), out_time_us: parseInt(progress.get('out_time_us')), out_time: progress.get('out_time').trim(), dup_frames: parseInt(progress.get('dup_frames')), drop_frames: parseInt(progress.get('drop_frames')), speed: parseFloat(progress.get('speed')), progress: progress.get('progress').trim() }; } catch (_a) { return undefined; } } else { return undefined; } } stop() { this.process.stdin.write('q' + os_1.default.EOL); this.killTimeout = setTimeout(() => { this.process.kill('SIGKILL'); }, 2 * 1000); } } exports.FfmpegProcess = FfmpegProcess; //# sourceMappingURL=ffmpeg.js.map