From aa85e7ec19fee068904bf114eef5dd359d943d28 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Sat, 6 Dec 2025 12:38:22 -0800 Subject: [PATCH] rebroadcast: avoid mjpeg codecs and warn --- plugins/prebuffer-mixin/package-lock.json | 4 +-- plugins/prebuffer-mixin/package.json | 2 +- .../prebuffer-mixin/src/stream-settings.ts | 30 +++++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/plugins/prebuffer-mixin/package-lock.json b/plugins/prebuffer-mixin/package-lock.json index 3db9d034a..8b66dbaa4 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.10.62", + "version": "0.10.63", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/prebuffer-mixin", - "version": "0.10.62", + "version": "0.10.63", "license": "Apache-2.0", "dependencies": { "@scrypted/common": "file:../../common", diff --git a/plugins/prebuffer-mixin/package.json b/plugins/prebuffer-mixin/package.json index da3055a61..11b737da4 100644 --- a/plugins/prebuffer-mixin/package.json +++ b/plugins/prebuffer-mixin/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/prebuffer-mixin", - "version": "0.10.62", + "version": "0.10.63", "description": "Video Stream Rebroadcast, Prebuffer, and Management Plugin for Scrypted.", "author": "Scrypted", "license": "Apache-2.0", diff --git a/plugins/prebuffer-mixin/src/stream-settings.ts b/plugins/prebuffer-mixin/src/stream-settings.ts index b0206d359..52b19543f 100644 --- a/plugins/prebuffer-mixin/src/stream-settings.ts +++ b/plugins/prebuffer-mixin/src/stream-settings.ts @@ -1,6 +1,5 @@ -import { getH264DecoderArgs } from "@scrypted/common/src/ffmpeg-hardware-acceleration"; import { MixinDeviceBase, ResponseMediaStreamOptions, VideoCamera } from "@scrypted/sdk"; -import { StorageSetting, StorageSettings } from "@scrypted/sdk/storage-settings"; +import { StorageSetting, StorageSettings, StorageSettingsDict } from "@scrypted/sdk/storage-settings"; export type StreamStorageSetting = StorageSetting & { prefersPrebuffer: boolean, @@ -13,6 +12,11 @@ function getStreamTypes(storageSettings: StreamStorageSettings return storageSettings; } +function msoHasJpegCodec(mso: ResponseMediaStreamOptions) { + const lower = mso?.video?.codec?.toLowerCase(); + return lower?.includes('jpeg') || lower?.includes('jpg'); +} + function pickBestStream(msos: ResponseMediaStreamOptions[], resolution: number) { if (!msos) return; @@ -20,6 +24,10 @@ function pickBestStream(msos: ResponseMediaStreamOptions[], resolution: number) let best: ResponseMediaStreamOptions; let bestScore: number; for (const mso of msos) { + if (msoHasJpegCodec(mso)) { + continue; + } + const score = Math.abs(mso.video?.width * mso.video?.height - resolution); if (!best || score < bestScore) { best = mso; @@ -80,6 +88,13 @@ export function createStreamSettings(device: MixinDeviceBase) { }); const storageSettings = new StorageSettings(device, { + hasMjpeg: { + subgroup, + title: 'Invalid Codecs', + type: 'html', + defaultValue: '

MJPEG streams detected. These streams are incompatible Scrypted and should be reconfigured to H264 using the camera\'s web admin if possible.

', + hide: true, + }, noAudio: { subgroup, title: 'No Audio', @@ -197,6 +212,15 @@ export function createStreamSettings(device: MixinDeviceBase) { try { const msos = await device.mixinDevice.getVideoStreamOptions(); + const hasMjpeg: StorageSettingsDict<'hasMjpeg'> = msos?.find(mso => msoHasJpegCodec(mso)) + ? { + hasMjpeg: { + hide: false, + }, + } + : undefined; + + enabledStreams = { defaultValue: getDefaultPrebufferedStreams(msos)?.map(mso => mso.name || mso.id), choices: msos.map((mso, index) => mso.name || mso.id), @@ -211,11 +235,13 @@ export function createStreamSettings(device: MixinDeviceBase) { lowResolutionStream: createStreamOptions(streamTypes.lowResolutionStream, msos), recordingStream: createStreamOptions(streamTypes.recordingStream, msos), remoteRecordingStream: createStreamOptions(streamTypes.remoteRecordingStream, msos), + ...hasMjpeg, } } else { return { enabledStreams, + ...hasMjpeg, } } }