UNPKG

homebridge-eufy-security

Version:
190 lines 8.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RecordingDelegate = void 0; const eufy_security_client_1 = require("eufy-security-client"); const ffmpeg_1 = require("../utils/ffmpeg"); const utils_1 = require("../utils/utils"); const MAX_RECORDING_MINUTES = 1; // should never be used const HKSVQuitReason = [ 'Normal', 'Not allowed', 'Busy', 'Cancelled', 'Unsupported', 'Unexpected Failure', 'Timeout', 'Bad data', 'Protocol error', 'Invalid Configuration', ]; class RecordingDelegate { platform; accessory; camera; cameraConfig; localLivestreamManager; configuration; forceStopTimeout; closeReason; handlingStreamingRequest = false; controller; session; constructor(platform, accessory, camera, cameraConfig, localLivestreamManager) { this.platform = platform; this.accessory = accessory; this.camera = camera; this.cameraConfig = cameraConfig; this.localLivestreamManager = localLivestreamManager; } setController(controller) { this.controller = controller; } isRecording() { return this.handlingStreamingRequest; } async *handleRecordingStreamRequest() { this.handlingStreamingRequest = true; utils_1.log.info(this.camera.getName(), 'requesting recording for HomeKit Secure Video.'); let pending = []; let filebuffer = Buffer.alloc(0); try { const audioEnabled = this.controller?.recordingManagement?.recordingManagementService.getCharacteristic(utils_1.CHAR.RecordingAudioActive).value; if (audioEnabled) { utils_1.log.debug('HKSV and plugin are set to record audio.'); } else { utils_1.log.debug('HKSV and plugin are set to omit audio recording.'); } const videoParams = await ffmpeg_1.FFmpegParameters.forVideoRecording(); const audioParams = await ffmpeg_1.FFmpegParameters.forAudioRecording(); const videoConfig = this.cameraConfig.videoConfig ?? {}; if (this.cameraConfig.videoConfig && this.cameraConfig.videoConfig.videoProcessor) { videoConfig.videoProcessor = this.cameraConfig.videoConfig.videoProcessor; } videoParams.setupForRecording(videoConfig, this.configuration); audioParams.setupForRecording(videoConfig, this.configuration); const rtsp = (0, utils_1.is_rtsp_ready)(this.camera, this.cameraConfig); if (rtsp) { const url = this.camera.getPropertyValue(eufy_security_client_1.PropertyName.DeviceRTSPStreamUrl); utils_1.log.debug(this.camera.getName(), 'RTSP URL: ' + url); videoParams.setInputSource(url); audioParams.setInputSource(url); } else { const streamData = await this.localLivestreamManager.getLocalLivestream().catch((error) => { throw error; }); await videoParams.setInputStream(streamData.videostream); await audioParams.setInputStream(streamData.audiostream); } const ffmpeg = new ffmpeg_1.FFmpeg(`[${this.camera.getName()}] [HSV Recording Process]`, audioEnabled ? [videoParams, audioParams] : videoParams); this.session = await ffmpeg.startFragmentedMP4Session(); let timer = this.cameraConfig.hsvRecordingDuration ?? MAX_RECORDING_MINUTES * 60; if (this.platform.config.CameraMaxLivestreamDuration < timer) { timer = this.platform.config.CameraMaxLivestreamDuration; } if (timer > 0) { this.forceStopTimeout = setTimeout(() => { utils_1.log.warn(this.camera.getName(), `The recording process has been running for ${timer} seconds and is now being forced closed!`); this.accessory .getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected) .updateValue(false); }, timer * 1000); } for await (const box of this.session.generator) { if (!this.handlingStreamingRequest) { utils_1.log.debug(this.camera.getName(), 'Recording was ended prematurely.'); break; } const { header, type, data } = box; pending.push(header, data); const motionDetected = this.accessory .getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected).value; if (type === 'moov' || type === 'mdat') { const fragment = Buffer.concat(pending); filebuffer = Buffer.concat([filebuffer, Buffer.concat(pending)]); pending = []; yield { data: fragment, isLast: !motionDetected, }; if (!motionDetected) { utils_1.log.debug(this.camera.getName(), 'Ending recording session due to motion stopped!'); break; } } } } catch (error) { if (!this.handlingStreamingRequest && this.closeReason && this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) { utils_1.log.debug(this.camera.getName(), 'Recording encountered an error but that is expected, as the recording was canceled beforehand. Error: ' + error); } else { utils_1.log.error(this.camera.getName(), 'Error while recording: ' + error); } } finally { if (this.closeReason && this.closeReason !== 0 /* HDSProtocolSpecificErrorReason.NORMAL */ && this.closeReason !== 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) { utils_1.log.warn(this.camera.getName(), `The recording process was aborted by HSV with reason "${HKSVQuitReason[this.closeReason]}"`); } if (this.closeReason && this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) { utils_1.log.debug(this.camera.getName(), 'The recording process was canceled by the HomeKit Controller."'); } if (filebuffer.length > 0) { utils_1.log.debug(this.camera.getName(), 'Recording completed (HSV). Send ' + filebuffer.length + ' bytes.'); } if (this.forceStopTimeout) { clearTimeout(this.forceStopTimeout); this.forceStopTimeout = undefined; } // check whether motion is still in progress const motionDetected = this.accessory .getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected).value; if (motionDetected) { this.accessory .getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected) .updateValue(false); } this.localLivestreamManager.stopLocalLiveStream(); } } updateRecordingActive(active) { utils_1.log.debug(`Recording: ${active}`, this.accessory.displayName); } updateRecordingConfiguration(configuration) { this.configuration = configuration; } closeRecordingStream(streamId, reason) { utils_1.log.info(this.camera.getName(), 'Closing recording process'); if (this.session) { utils_1.log.debug(this.camera.getName(), 'Stopping recording session.'); this.session.socket?.destroy(); this.session.process?.kill('SIGKILL'); this.session = undefined; } else { utils_1.log.warn('Recording session could not be closed gracefully.'); } if (this.forceStopTimeout) { clearTimeout(this.forceStopTimeout); this.forceStopTimeout = undefined; } // check whether motion is still in progress const motionDetected = this.accessory .getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected).value; if (motionDetected) { this.accessory .getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected) .updateValue(false); } this.closeReason = reason; this.handlingStreamingRequest = false; } acknowledgeStream(streamId) { utils_1.log.debug('end of recording acknowledged!'); this.closeRecordingStream(streamId, undefined); } } exports.RecordingDelegate = RecordingDelegate; //# sourceMappingURL=recordingDelegate.js.map