diff --git a/plugins/prebuffer-mixin/package-lock.json b/plugins/prebuffer-mixin/package-lock.json index 546d186d1..ab616b08d 100644 --- a/plugins/prebuffer-mixin/package-lock.json +++ b/plugins/prebuffer-mixin/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.152", + "version": "0.1.153", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.152", + "version": "0.1.153", "license": "Apache-2.0", "dependencies": { "@scrypted/common": "file:../../common", diff --git a/plugins/prebuffer-mixin/package.json b/plugins/prebuffer-mixin/package.json index 5818e1e7e..1df7f0b41 100644 --- a/plugins/prebuffer-mixin/package.json +++ b/plugins/prebuffer-mixin/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.152", + "version": "0.1.153", "description": "Rebroadcast and Prebuffer for VideoCameras.", "author": "Scrypted", "license": "Apache-2.0", diff --git a/plugins/prebuffer-mixin/src/main.ts b/plugins/prebuffer-mixin/src/main.ts index 97da445e2..b0f38f913 100644 --- a/plugins/prebuffer-mixin/src/main.ts +++ b/plugins/prebuffer-mixin/src/main.ts @@ -15,9 +15,6 @@ const { mediaManager, log, systemManager, deviceManager } = sdk; const defaultPrebufferDuration = 10000; const PREBUFFER_DURATION_MS = 'prebufferDuration'; const SEND_KEYFRAME = 'sendKeyframe'; -const AUDIO_CONFIGURATION_KEY_PREFIX = 'audioConfiguration-'; -const FFMPEG_INPUT_ARGUMENTS_KEY_PREFIX = 'ffmpegInputArguments-'; -const REBROADCAST_MODE_KEY_PREFIX = 'rebroadcastMode-'; const DEFAULT_AUDIO = 'Default'; const AAC_AUDIO = 'AAC or No Audio'; const AAC_AUDIO_DESCRIPTION = `${AAC_AUDIO} (Copy)`; @@ -72,15 +69,17 @@ class PrebufferSession { inactivityTimeout: NodeJS.Timeout; audioConfigurationKey: string; ffmpegInputArgumentsKey: string; + lastDetectedAudioCodecKey: string; rebroadcastModeKey: string; constructor(public mixin: PrebufferMixin, public streamName: string, public streamId: string, public stopInactive: boolean) { this.storage = mixin.storage; this.console = mixin.console; this.mixinDevice = mixin.mixinDevice; - this.audioConfigurationKey = AUDIO_CONFIGURATION_KEY_PREFIX + this.streamId; - this.ffmpegInputArgumentsKey = FFMPEG_INPUT_ARGUMENTS_KEY_PREFIX + this.streamId; - this.rebroadcastModeKey = REBROADCAST_MODE_KEY_PREFIX + this.streamId; + this.audioConfigurationKey = 'audioConfiguration-' + this.streamId; + this.ffmpegInputArgumentsKey = 'ffmpegInputArguments-' + this.streamId; + this.rebroadcastModeKey = 'rebroadcastMode-' + this.streamId; + this.lastDetectedAudioCodecKey = 'lastDetectedAudioCodec-' + this.streamId; } clearPrebuffers() { @@ -246,7 +245,7 @@ class PrebufferSession { const { isUsingDefaultAudioConfig, aacAudio, compatibleAudio, reencodeAudio } = this.getAudioConfig(); - let detectedAudioCodec = this.storage.getItem('lastDetectedAudioCodec') || undefined; + let detectedAudioCodec = this.storage.getItem(this.lastDetectedAudioCodecKey) || undefined; if (detectedAudioCodec === 'null') detectedAudioCodec = null; @@ -403,7 +402,7 @@ class PrebufferSession { // before launching the parser session, clear out the last detected codec. // an erroneous cached codec could cause ffmpeg to fail to start. - this.storage.removeItem('lastDetectedAudioCodec'); + this.storage.removeItem(this.lastDetectedAudioCodecKey); const session = await startParserSession(ffmpegInput, rbo); @@ -418,7 +417,7 @@ class PrebufferSession { } // set/update the detected codec, set it to null if no audio was found. - this.storage.setItem('lastDetectedAudioCodec', session.inputAudioCodec || 'null'); + this.storage.setItem(this.lastDetectedAudioCodecKey, session.inputAudioCodec || 'null'); if (session.inputVideoCodec !== 'h264') { this.console.error(`Video codec is not h264. If there are errors, try changing your camera's encoder output.`); diff --git a/plugins/ring/fs/black.jpg b/plugins/ring/fs/black.jpg new file mode 100644 index 000000000..7fe1c6d7d Binary files /dev/null and b/plugins/ring/fs/black.jpg differ diff --git a/plugins/ring/src/main.ts b/plugins/ring/src/main.ts index e59229bab..4780bc083 100644 --- a/plugins/ring/src/main.ts +++ b/plugins/ring/src/main.ts @@ -7,9 +7,12 @@ import { encodeSrtpOptions, RtpSplitter } from '@homebridge/camera-utils' import child_process, { ChildProcess } from 'child_process'; import { createRTCPeerConnectionSource } from '../../../common/src/wrtc-ffmpeg-source'; import { generateUuid } from './ring-client-api'; +import fs from 'fs'; +import { clientApi } from '@koush/ring-client-api/lib/api/rest-client'; const { log, deviceManager, mediaManager } = sdk; const STREAM_TIMEOUT = 120000; +const black = fs.readFileSync('black.jpg'); const RingSignalingPrefix = ScryptedMimeTypes.RTCAVSignalingPrefix + 'ring/'; const RingDeviceSignalingPrefix = RingSignalingPrefix + 'x-'; @@ -22,7 +25,7 @@ const RingWebRtcAvSource: RTCAVSource = { }, }; -process.env.DEBUG='*'; +process.env.DEBUG = '*'; class RingCameraLight extends ScryptedDeviceBase implements OnOff { constructor(public camera: RingCameraDevice) { @@ -103,9 +106,22 @@ class RingCameraDevice extends ScryptedDeviceBase implements BufferConverter, De } async takePicture(options?: PictureOptions): Promise { + + + // watch for snapshot being blocked due to live stream const camera = this.findCamera(); - const snapshot = await camera.getSnapshot(); - return mediaManager.createMediaObject(snapshot, 'image/jpeg'); + if (!camera || camera.snapshotsAreBlocked) { + return mediaManager.createMediaObject(black, 'image/jpeg'); + } + + // trigger a refresh, but immediately use whatever is in cache. + camera.getSnapshot(); + const buffer = await this.plugin.client.request({ + url: clientApi(`snapshots/image/${camera.id}`), + responseType: 'buffer', + }); + + return mediaManager.createMediaObject(buffer, 'image/jpeg'); } async getPictureOptions(): Promise {