diff --git a/plugins/chromecast/src/main.ts b/plugins/chromecast/src/main.ts index 60676b8cb..ff5c5b2f6 100644 --- a/plugins/chromecast/src/main.ts +++ b/plugins/chromecast/src/main.ts @@ -88,7 +88,10 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng } client.removeAllListeners(); - client.close(); + try { + client.close(); + } catch (e) { + } } client.client.on('close', cleanup); client.on('error', err => { @@ -149,6 +152,14 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng } async load(media: string | MediaObject, options: MediaPlayerOptions) { + if (this.mediaPlayerPromise) { + try { + (await this.mediaPlayerPromise).close(); + } catch (e) { + } + this.mediaPlayerPromise = undefined; + this.mediaPlayerStatus = undefined; + } let url: string; let urlMimeType: string; @@ -341,15 +352,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng }); }) - player.getStatus((err, status) => { - if (err) { - reject(err); - return; - } - this.mediaPlayerStatus = status; - this.updateState(); - resolve(player); - }) + resolve(player); }); }); }); diff --git a/plugins/core/fs/examples/chromecast-view-camera.ts b/plugins/core/fs/examples/chromecast-view-camera.ts index 436049edb..e7373bb54 100644 --- a/plugins/core/fs/examples/chromecast-view-camera.ts +++ b/plugins/core/fs/examples/chromecast-view-camera.ts @@ -29,7 +29,7 @@ class ChromecastViewCameraExample implements StartStop { } async stop() { device.running = false; - return chromecast.stop(); + await chromecast.stop(); } } diff --git a/plugins/coreml/src/coreml/__init__.py b/plugins/coreml/src/coreml/__init__.py index d8389e29f..33bdf9f15 100644 --- a/plugins/coreml/src/coreml/__init__.py +++ b/plugins/coreml/src/coreml/__init__.py @@ -58,11 +58,11 @@ class CoreMLPlugin(PredictPlugin, scrypted_sdk.BufferConverter, scrypted_sdk.Set else: out_dict = self.model.predict({'image': input, 'confidenceThreshold': self.minThreshold }) - coordinatesList = out_dict['coordinates'] + coordinatesList = out_dict['coordinates'].astype(float) objs = [] - for index, confidenceList in enumerate(out_dict['confidence']): + for index, confidenceList in enumerate(out_dict['confidence'].astype(float)): values = confidenceList maxConfidenceIndex = max(range(len(values)), key=values.__getitem__) maxConfidence = confidenceList[maxConfidenceIndex] diff --git a/plugins/prebuffer-mixin/src/main.ts b/plugins/prebuffer-mixin/src/main.ts index 72a54e025..cfa80bf92 100644 --- a/plugins/prebuffer-mixin/src/main.ts +++ b/plugins/prebuffer-mixin/src/main.ts @@ -10,7 +10,7 @@ import { addTrackControls, parseSdp } from '@scrypted/common/src/sdp-utils'; import { SettingsMixinDeviceBase, SettingsMixinDeviceOptions } from "@scrypted/common/src/settings-mixin"; import { sleep } from '@scrypted/common/src/sleep'; import { createFragmentedMp4Parser, createMpegTsParser, StreamChunk, StreamParser } from '@scrypted/common/src/stream-parser'; -import sdk, { BufferConverter, DeviceProvider, DeviceState, EventListenerRegister, FFmpegInput, H264Info, MediaObject, MediaStreamDestination, MediaStreamOptions, MixinProvider, RequestMediaStreamOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoCameraConfiguration } from '@scrypted/sdk'; +import sdk, { BufferConverter, ChargeState, DeviceBase, DeviceProvider, DeviceState, EventListenerRegister, FFmpegInput, H264Info, MediaObject, MediaStreamDestination, MediaStreamOptions, MixinProvider, RequestMediaStreamOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoCameraConfiguration } from '@scrypted/sdk'; import { StorageSettings } from '@scrypted/sdk/storage-settings'; import crypto from 'crypto'; import { once } from 'events'; @@ -105,7 +105,10 @@ class PrebufferSession { rtspServerPath: string; rtspServerMutedPath: string; - constructor(public mixin: PrebufferMixin, public advertisedMediaStreamOptions: ResponseMediaStreamOptions, public stopInactive: boolean) { + batteryListener: EventListenerRegister; + chargerListener: EventListenerRegister; + + constructor(public mixin: PrebufferMixin, public advertisedMediaStreamOptions: ResponseMediaStreamOptions, public enabled: boolean, public forceBatteryPrebuffer: boolean) { this.storage = mixin.storage; this.console = mixin.console; this.mixinDevice = mixin.mixinDevice; @@ -129,6 +132,12 @@ class PrebufferSession { this.rtspServerMutedPath = crypto.randomBytes(8).toString('hex'); this.storage.setItem(rtspServerMutedPathKey, this.rtspServerMutedPath); } + + this.handleChargingBatteryEvents(); + } + + get stopInactive() { + return !this.enabled || this.shouldDisableBatteryPrebuffer(); } get canPrebuffer() { @@ -206,6 +215,14 @@ class PrebufferSession { parserSession.kill(new Error('rebroadcast disabled')); this.clearPrebuffers(); }); + if (this.batteryListener) { + this.batteryListener.removeListener(); + this.batteryListener = null; + } + if (this.chargerListener) { + this.chargerListener.removeListener(); + this.chargerListener = null; + } } ensurePrebufferSession() { @@ -934,6 +951,46 @@ class PrebufferSession { }, 10000); } + handleChargingBatteryEvents() { + if (!this.mixin.interfaces.includes(ScryptedInterface.Charger) || + !this.mixin.interfaces.includes(ScryptedInterface.Battery)) { + return; + } + + const checkDisablePrebuffer = async () => { + if (this.stopInactive) { + this.console.log(this.streamName, 'low battery or not charging, prebuffering and rebroadcasting will only work on demand') + if (!this.activeClients && this.parserSessionPromise) { + this.console.log(this.streamName, 'terminating rebroadcast due to low battery or not charging') + const session = await this.parserSessionPromise; + session.kill(new Error('low battery or not charging')); + } + } else { + this.ensurePrebufferSession(); + } + } + + const id = this.mixin.id; + if (!this.batteryListener) { + this.batteryListener = systemManager.listenDevice(id, ScryptedInterface.Battery, () => checkDisablePrebuffer()); + } + if (!this.chargerListener) { + this.chargerListener = systemManager.listenDevice(id, ScryptedInterface.Charger, () => checkDisablePrebuffer()); + } + } + + shouldDisableBatteryPrebuffer(): boolean | null { + if (!this.mixin.interfaces.includes(ScryptedInterface.Battery)) { + return null; + } + if (this.forceBatteryPrebuffer) { + return false; + } + const lowBattery = this.mixin.batteryLevel == null || this.mixin.batteryLevel < 20; + const hasCharger = this.mixin.interfaces.includes(ScryptedInterface.Charger); + return !hasCharger || lowBattery || this.mixin.chargeState !== ChargeState.Charging; + } + async handleRebroadcasterClient(options: { findSyncFrame: boolean, isActiveClient: boolean, @@ -1497,8 +1554,6 @@ class PrebufferMixin extends SettingsMixinDeviceBase implements Vid } } - const isBatteryPowered = this.mixinDeviceInterfaces.includes(ScryptedInterface.Battery); - if (!enabledIds.length) this.online = true; @@ -1524,22 +1579,27 @@ class PrebufferMixin extends SettingsMixinDeviceBase implements Vid } const name = mso?.name; const enabled = enabledIds.includes(id); - const stopInactive = (isBatteryPowered && !mso.allowBatteryPrebuffer) || !enabled; - session = new PrebufferSession(this, mso, stopInactive); + session = new PrebufferSession(this, mso, enabled, mso.allowBatteryPrebuffer); this.sessions.set(id, session); - if (isBatteryPowered && !mso.allowBatteryPrebuffer) { - this.console.log('camera is battery powered, prebuffering and rebroadcasting will only work on demand.'); + if (!enabled) { + this.console.log('stream', name, 'is not enabled and will be rebroadcast on demand.'); continue; } - if (!enabled) { - this.console.log('stream', name, 'will be rebroadcast on demand.'); - continue; + if (session.shouldDisableBatteryPrebuffer()) { + this.console.log('camera is battery powered and either not charging or on low battery, prebuffering and rebroadcasting will only work on demand.'); } (async () => { while (this.sessions.get(id) === session && !this.released) { + if (session.shouldDisableBatteryPrebuffer()) { + // since battery devices could be eligible for prebuffer, check periodically + // in the event the battery device becomes eligible again + await new Promise(resolve => setTimeout(resolve, 60000)); + continue; + } + session.ensurePrebufferSession(); let wasActive = false; try {