From b76cb33e6cb44db64023355952058fc324dff0be Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 12 Jan 2022 22:53:36 -0800 Subject: [PATCH] homekit/unifi: dynamic bitrate support on remote streaming --- plugins/homekit/src/camera-mixin.ts | 11 ++++++++++- plugins/homekit/src/types/camera.ts | 18 ++++++++++++++++-- plugins/unifi-protect/README.md | 15 ++++----------- plugins/unifi-protect/package-lock.json | 4 ++-- plugins/unifi-protect/package.json | 2 +- plugins/unifi-protect/src/main.ts | 25 +++++++++++++++++++++---- 6 files changed, 54 insertions(+), 21 deletions(-) diff --git a/plugins/homekit/src/camera-mixin.ts b/plugins/homekit/src/camera-mixin.ts index 424ef9714..a4ffd6f4b 100644 --- a/plugins/homekit/src/camera-mixin.ts +++ b/plugins/homekit/src/camera-mixin.ts @@ -74,6 +74,7 @@ export class CameraMixin extends SettingsMixinDeviceBase implements Setting readonly: true, description: 'Transcoding audio and video for HomeKit is not recommended. Configure your camera using the camera web portal or app to output the correct HomeKit compatible codecs (h264/aac/2000kbps).', }) + settings.push({ title: 'Transcode Streaming', group: 'HomeKit Transcoding', @@ -88,7 +89,15 @@ export class CameraMixin extends SettingsMixinDeviceBase implements Setting type: 'boolean', key: 'transcodeStreamingHub', value: (this.storage.getItem('transcodeStreamingHub') === 'true').toString(), - description: 'Use FFMpeg to transcode streaming to a format supported by HomeKit.', + description: 'Remote Viewing through a HomeKit Hub only: Use FFMpeg to transcode streaming to a format supported by HomeKit.', + }); + settings.push({ + title: 'Dynamic Bitrate (Hub)', + group: 'HomeKit Transcoding', + type: 'boolean', + key: 'dynamicBitrate', + value: (this.storage.getItem('dynamicBitrate') === 'true').toString(), + description: 'Remote Viewing through a HomeKit Hub only: Adjust the bitrate of the native camera stream on demand to accomodate available bandwidth. This setting should be used on secondary streams (sub streams), and not the main stream connected to an NVR, as it will reduce the recording quality.', }); let showTranscodeArgs = this.storage.getItem('transcodeStreaming') === 'true' || this.storage.getItem('transcodeStreamingHub') === 'true'; diff --git a/plugins/homekit/src/types/camera.ts b/plugins/homekit/src/types/camera.ts index 22da0c598..d17f5c709 100644 --- a/plugins/homekit/src/types/camera.ts +++ b/plugins/homekit/src/types/camera.ts @@ -186,23 +186,37 @@ addSupportedType({ selectedStream = msos.find(mso => mso.name === streamingChannel); } - if (request.type === StreamRequestTypes.RECONFIGURE) { + const tryReconfigureBitrate = () => { + if (!isHomeKitHub) + return; + if (!device.interfaces.includes(ScryptedInterface.VideoCameraConfiguration)) return; + const dynamicBitrate = storage.getItem('dynamicBitrate') === 'true'; + if (!dynamicBitrate) + return; + const reconfigured = Object.assign({ video: { }, }, selectedStream || {}); - reconfigured.video.bitrate = request.video.max_bit_rate; + const bitrate = request.video.max_bit_rate * 1000; + reconfigured.video.bitrate = bitrate; + reconfigured.video.maxBitrate = bitrate; device.setVideoStreamOptions(reconfigured); console.log('reconfigure selected stream', selectedStream); + } + + if (request.type === StreamRequestTypes.RECONFIGURE) { + tryReconfigureBitrate(); return; } else { session.startRequest = request as StartStreamRequest; } + tryReconfigureBitrate(); // watch for data to verify other side is alive. session.videoReturn.on('data', () => debounce(() => { diff --git a/plugins/unifi-protect/README.md b/plugins/unifi-protect/README.md index d5654ca39..7395a0c5c 100644 --- a/plugins/unifi-protect/README.md +++ b/plugins/unifi-protect/README.md @@ -1,15 +1,8 @@ -# Unifi Protect Plugin +# Unifi Protect Plugin for Scrypted -## npm commands - * npm run scrypted-webpack - * npm run scrypted-deploy - * npm run scrypted-debug +The Unifi Protect Plugin connects your Unifi Cameras to Scrypted. The Protect appliance such as a Cloud Key or Dream Machine is required. -## scrypted distribution via npm - 1. Ensure package.json is set up properly for publishing on npm. - 2. npm publish +## Unifi Beta 1.21.0-beta.3 -## Visual Studio Code configuration +This beta has a bug in it that causes HomeKit Secure Video recordings to fail. Please roll back to a prior or stable release, and flag [this issue](https://community.ui.com/releases/UniFi-Protect-Application-1-21-0-beta-3/32c7bb7a-697d-4841-8b9f-eef49b8682e9#comment/ad1c2710-2451-4612-847f-6413eb8ec0db). -* If using a remote server, edit [.vscode/settings.json](blob/master/.vscode/settings.json) to specify the IP Address of the Scrypted server. -* Launch Scrypted Debugger from the launch menu. diff --git a/plugins/unifi-protect/package-lock.json b/plugins/unifi-protect/package-lock.json index d33ec67e9..7cf49b8a1 100644 --- a/plugins/unifi-protect/package-lock.json +++ b/plugins/unifi-protect/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/unifi-protect", - "version": "0.0.64", + "version": "0.0.66", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/unifi-protect", - "version": "0.0.64", + "version": "0.0.66", "license": "Apache", "dependencies": { "@koush/unifi-protect": "^0.0.6", diff --git a/plugins/unifi-protect/package.json b/plugins/unifi-protect/package.json index e9e9ad498..84a79dcbc 100644 --- a/plugins/unifi-protect/package.json +++ b/plugins/unifi-protect/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/unifi-protect", - "version": "0.0.64", + "version": "0.0.66", "description": "Unifi Protect Plugin for Scrypted", "author": "Scrypted", "license": "Apache", diff --git a/plugins/unifi-protect/src/main.ts b/plugins/unifi-protect/src/main.ts index 5fd69e236..111e8b97f 100644 --- a/plugins/unifi-protect/src/main.ts +++ b/plugins/unifi-protect/src/main.ts @@ -1,4 +1,4 @@ -import sdk, { ScryptedDeviceBase, DeviceProvider, Settings, Setting, ScryptedDeviceType, VideoCamera, MediaObject, Device, MotionSensor, ScryptedInterface, Camera, MediaStreamOptions, Intercom, ScryptedMimeTypes, FFMpegInput, ObjectDetector, PictureOptions, ObjectDetectionTypes, ObjectsDetected, ObjectDetectionResult, Notifier, SCRYPTED_MEDIA_SCHEME } from "@scrypted/sdk"; +import sdk, { ScryptedDeviceBase, DeviceProvider, Settings, Setting, ScryptedDeviceType, VideoCamera, MediaObject, Device, MotionSensor, ScryptedInterface, Camera, MediaStreamOptions, Intercom, ScryptedMimeTypes, FFMpegInput, ObjectDetector, PictureOptions, ObjectDetectionTypes, ObjectsDetected, ObjectDetectionResult, Notifier, SCRYPTED_MEDIA_SCHEME, VideoCameraConfiguration } from "@scrypted/sdk"; import { ProtectApi, ProtectCameraLcdMessagePayload } from "@koush/unifi-protect"; import { ProtectApiUpdates, ProtectNvrUpdatePayloadCameraUpdate, ProtectNvrUpdatePayloadEventAdd } from "@koush/unifi-protect"; import { ProtectCameraChannelConfig, ProtectCameraConfigInterface } from "@koush/unifi-protect"; @@ -12,7 +12,7 @@ const { log, deviceManager, mediaManager } = sdk; const defaultSensorTimeout = 30; -class UnifiCamera extends ScryptedDeviceBase implements Camera, VideoCamera, MotionSensor, Settings, ObjectDetector { +class UnifiCamera extends ScryptedDeviceBase implements Camera, VideoCamera, VideoCameraConfiguration, MotionSensor, Settings, ObjectDetector { protect: UnifiProtect; motionTimeout: NodeJS.Timeout; detectionTimeout: NodeJS.Timeout; @@ -229,6 +229,22 @@ class UnifiCamera extends ScryptedDeviceBase implements Camera, VideoCamera, Mot return this.getDefaultOrderedVideoStreamOptions(video); } + async setVideoStreamOptions(options: MediaStreamOptions): Promise { + const bitrate = options?.video?.bitrate; + const maxBitrate = options?.video?.maxBitrate; + if (!bitrate || !maxBitrate) + return; + + const camera = this.findCamera(); + const channel = camera.channels.find(channel => channel.id === options.id); + channel.bitrate = bitrate; + channel.maxBitrate = maxBitrate; + const cameraResult = await this.protect.api.updateChannels(camera); + if (!cameraResult) { + throw new Error("setVideoStreamOptions failed") + } + } + async getPictureOptions(): Promise { return; } @@ -245,7 +261,7 @@ class UnifiDoorbell extends UnifiCamera implements Intercom, Notifier { this.protect.api.updateCamera(this.findCamera(), { lcdMessage: payload, }) - + if (typeof media === 'string' && media.startsWith(SCRYPTED_MEDIA_SCHEME)) { media = await mediaManager.createMediaObjectFromUrl(media); } @@ -294,7 +310,7 @@ class UnifiProtect extends ScryptedDeviceBase implements Settings, DeviceProvide cameras: Map = new Map(); api: ProtectApi; startup: Promise; - runningEvents = new Map, resolve: (value: unknown) => void}>(); + runningEvents = new Map, resolve: (value: unknown) => void }>(); constructor(nativeId?: string, createOnly?: boolean) { super(nativeId); @@ -541,6 +557,7 @@ class UnifiProtect extends ScryptedDeviceBase implements Settings, DeviceProvide ScryptedInterface.Settings, ScryptedInterface.Camera, ScryptedInterface.VideoCamera, + ScryptedInterface.VideoCameraConfiguration, ScryptedInterface.MotionSensor, ], type: camera.featureFlags.hasChime