@camera.ui/browser 0.0.146 → 0.0.148
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +89 -43
- package/dist/index.js +352 -143
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -86,7 +86,7 @@ import { TemperatureProperty } from '@camera.ui/sdk';
|
|
|
86
86
|
import { WatchSource } from 'vue';
|
|
87
87
|
import { WsTransport } from '@camera.ui/transport/transports/ws';
|
|
88
88
|
|
|
89
|
-
export declare function acquireSensorManager(
|
|
89
|
+
export declare function acquireSensorManager(rpcOrContext: RPCClient | ReactiveCameraDeviceContext): CachedSensorManager;
|
|
90
90
|
|
|
91
91
|
export declare interface ActivityMode extends ActivityModeState, ActivityModeActions {
|
|
92
92
|
}
|
|
@@ -163,6 +163,8 @@ export declare interface CameraStream {
|
|
|
163
163
|
readonly isPlaying: Ref<boolean>;
|
|
164
164
|
readonly activeMode: Ref<'webrtc' | 'webrtc/tcp' | 'mse'>;
|
|
165
165
|
readonly activeResolution: Ref<StreamingRole>;
|
|
166
|
+
readonly degradedFrom: Ref<StreamingRole | null>;
|
|
167
|
+
readonly unsupported: Ref<boolean>;
|
|
166
168
|
readonly hasAudio: Ref<boolean>;
|
|
167
169
|
readonly hasBackchannel: Ref<boolean>;
|
|
168
170
|
readonly error: Ref<Error | undefined>;
|
|
@@ -286,7 +288,7 @@ export declare function createCameraUiPlugin(input: CameraUiContext | CameraUiPl
|
|
|
286
288
|
|
|
287
289
|
export declare function createReactiveCameraDevice(rpcOrContext: RPCClient | ReactiveCameraDeviceContext, initialCamera: Camera): Promise<ReactiveCameraDevice>;
|
|
288
290
|
|
|
289
|
-
export declare function createSensorManager(rpcOrContext: RPCClient | ReactiveCameraDeviceContext
|
|
291
|
+
export declare function createSensorManager(rpcOrContext: RPCClient | ReactiveCameraDeviceContext): ReactiveSensorManager;
|
|
290
292
|
|
|
291
293
|
declare interface DBCamera extends BaseCamera {
|
|
292
294
|
sources: CameraInputSettings[];
|
|
@@ -392,6 +394,12 @@ export declare const isReactiveTemperatureInfo: (sensor: ReactiveSensor<any>) =>
|
|
|
392
394
|
|
|
393
395
|
/* Excluded from this release type: LeakSensorProperties */
|
|
394
396
|
|
|
397
|
+
/** @deprecated stub window for pre-standalone plugins, remove in the first minor after the standalone-sensors release */
|
|
398
|
+
declare interface LegacySensorNamespaces {
|
|
399
|
+
sensorSubject: string;
|
|
400
|
+
sensorRpc: string;
|
|
401
|
+
}
|
|
402
|
+
|
|
395
403
|
/* Excluded from this release type: LicensePlateProperty */
|
|
396
404
|
|
|
397
405
|
/* Excluded from this release type: LicensePlateSensorProperties */
|
|
@@ -466,29 +474,42 @@ export declare class NamespaceManager {
|
|
|
466
474
|
};
|
|
467
475
|
}
|
|
468
476
|
|
|
469
|
-
static pluginSensorNamespaces(pluginId: string,
|
|
477
|
+
static pluginSensorNamespaces(pluginId: string, sensorId: string): PluginSensorNamespaces {
|
|
470
478
|
return {
|
|
471
|
-
sensorStorageRpc: `plugin.${pluginId}.
|
|
479
|
+
sensorStorageRpc: `plugin.${pluginId}.sensor.${sensorId}.storage.rpc`,
|
|
472
480
|
};
|
|
473
481
|
}
|
|
474
482
|
|
|
475
|
-
static
|
|
483
|
+
static sensorRegistryNamespaces(): SensorRegistryNamespaces {
|
|
484
|
+
return {
|
|
485
|
+
sensorsSubject: 'sensors.subject',
|
|
486
|
+
sensorsRpc: 'sensors.rpc',
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
static sensorCameraViewNamespaces(cameraId: string): SensorCameraViewNamespaces {
|
|
476
491
|
return {
|
|
477
|
-
sensorSubject: `camera.${cameraId}.sensors.subject`,
|
|
478
|
-
sensorRpc: `camera.${cameraId}.sensors.rpc`,
|
|
479
492
|
sensorWriteSubject: `camera.${cameraId}.sensors.writes`,
|
|
480
493
|
};
|
|
481
494
|
}
|
|
482
495
|
|
|
483
|
-
static sensorEventNamespaces(
|
|
496
|
+
static sensorEventNamespaces(sensorId: string): SensorEventNamespaces {
|
|
484
497
|
return {
|
|
485
|
-
sensorSubject: `
|
|
498
|
+
sensorSubject: `sensor.${sensorId}.subject`,
|
|
486
499
|
};
|
|
487
500
|
}
|
|
488
501
|
|
|
489
|
-
static sensorProviderNamespaces(pluginId: string,
|
|
502
|
+
static sensorProviderNamespaces(pluginId: string, sensorId: string): SensorProviderNamespaces {
|
|
490
503
|
return {
|
|
491
|
-
sensorRpc: `plugin.${pluginId}.
|
|
504
|
+
sensorRpc: `plugin.${pluginId}.sensor.${sensorId}.rpc`,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** @deprecated stub window for pre-standalone plugins, remove in the first minor after the standalone-sensors release */
|
|
509
|
+
static legacySensorNamespaces(cameraId: string): LegacySensorNamespaces {
|
|
510
|
+
return {
|
|
511
|
+
sensorSubject: `camera.${cameraId}.sensors.subject`,
|
|
512
|
+
sensorRpc: `camera.${cameraId}.sensors.rpc`,
|
|
492
513
|
};
|
|
493
514
|
}
|
|
494
515
|
|
|
@@ -504,10 +525,6 @@ export declare class NamespaceManager {
|
|
|
504
525
|
};
|
|
505
526
|
}
|
|
506
527
|
|
|
507
|
-
static sensorControllerRpc(cameraId: string): string {
|
|
508
|
-
return `camera.${cameraId}.sensors.controller.rpc`;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
528
|
static terminalManagerNamespaces(): TerminalManagerNamespaces {
|
|
512
529
|
return {
|
|
513
530
|
terminalManagerSubject: 'terminalManager.subscriber',
|
|
@@ -782,7 +799,11 @@ export declare interface ReactiveSensor<TProperties extends object = Record<stri
|
|
|
782
799
|
readonly type: SensorType;
|
|
783
800
|
readonly name: string;
|
|
784
801
|
readonly displayName: Ref<string>;
|
|
802
|
+
readonly nativeId?: string;
|
|
785
803
|
readonly pluginId: string;
|
|
804
|
+
readonly assignedCameraIds: Ref<string[]>;
|
|
805
|
+
readonly exposed: Ref<boolean>;
|
|
806
|
+
readonly connected: Ref<boolean>;
|
|
786
807
|
readonly capabilities: Ref<string[]>;
|
|
787
808
|
readonly properties: TProperties;
|
|
788
809
|
getProperty<T = unknown>(property: string): T | undefined;
|
|
@@ -837,6 +858,8 @@ export declare interface ReactiveStream {
|
|
|
837
858
|
readonly requestedMode: Ref<VideoStreamingMode>;
|
|
838
859
|
readonly activeResolution: Ref<StreamingRole>;
|
|
839
860
|
readonly requestedResolution: Ref<StreamingRole>;
|
|
861
|
+
readonly degradedFrom: Ref<StreamingRole | null>;
|
|
862
|
+
readonly degradeReason: Ref<StreamDegradeReason | null>;
|
|
840
863
|
readonly source: Ref<CameraSource | undefined>;
|
|
841
864
|
readonly hasVideo: Ref<boolean>;
|
|
842
865
|
readonly hasAudio: Ref<boolean>;
|
|
@@ -882,7 +905,7 @@ declare interface RefreshedStates {
|
|
|
882
905
|
sensorStates: Record<string, SensorRefreshedState>;
|
|
883
906
|
}
|
|
884
907
|
|
|
885
|
-
export declare function releaseSensorManager(
|
|
908
|
+
export declare function releaseSensorManager(): void;
|
|
886
909
|
|
|
887
910
|
export declare function resetClientState(): void;
|
|
888
911
|
|
|
@@ -904,50 +927,57 @@ export { RPCClient }
|
|
|
904
927
|
/* Excluded from this release type: SecuritySystemProperty */
|
|
905
928
|
|
|
906
929
|
declare interface SensorAddedEvent {
|
|
907
|
-
cameraId: string;
|
|
908
930
|
sensor: StoredSensorData;
|
|
909
931
|
state: SensorRefreshedState;
|
|
910
932
|
}
|
|
911
933
|
|
|
912
|
-
declare interface
|
|
934
|
+
declare interface SensorAssignmentChangedEvent {
|
|
935
|
+
sensorId: string;
|
|
936
|
+
sensorType: SensorType;
|
|
913
937
|
cameraId: string;
|
|
938
|
+
assigned: boolean;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
export declare interface SensorCameraViewNamespaces {
|
|
942
|
+
sensorWriteSubject: string;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
declare interface SensorCapabilitiesChangedEvent {
|
|
914
946
|
sensorId: string;
|
|
915
947
|
capabilities: string[];
|
|
916
948
|
}
|
|
917
949
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
950
|
+
declare interface SensorConnectedChangedEvent {
|
|
951
|
+
sensorId: string;
|
|
952
|
+
sensorType: SensorType;
|
|
953
|
+
connected: boolean;
|
|
922
954
|
}
|
|
923
955
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
getSensorByType(sensorType: SensorType): StoredSensorData | undefined;
|
|
928
|
-
getSensorsByType(sensorType: SensorType): StoredSensorData[];
|
|
929
|
-
getSensorState(sensorId: string): SensorRefreshedState;
|
|
930
|
-
getSensorStates(): Record<string, SensorRefreshedState>;
|
|
931
|
-
getPropertyValue(sensorId: string, property: string): unknown;
|
|
932
|
-
getAllPropertyValues(sensorId: string): Record<string, unknown>;
|
|
933
|
-
setDisplayName(sensorId: string, displayName: string): void;
|
|
956
|
+
declare interface SensorDeletedEvent {
|
|
957
|
+
sensorId: string;
|
|
958
|
+
sensorType: SensorType;
|
|
934
959
|
}
|
|
935
960
|
|
|
936
961
|
declare interface SensorDisplayNameChangedEvent {
|
|
937
|
-
cameraId: string;
|
|
938
962
|
sensorId: string;
|
|
939
963
|
displayName: string;
|
|
940
964
|
}
|
|
941
965
|
|
|
942
966
|
export declare interface SensorEventMessage {
|
|
943
|
-
type: 'property:changed' | 'sensor:added' | 'sensor:
|
|
944
|
-
data: PropertyChangedEvent | SensorAddedEvent |
|
|
967
|
+
type: 'property:changed' | 'sensor:added' | 'sensor:deleted' | 'sensor:connected:changed' | 'sensor:displayName:changed' | 'sensor:capabilities:changed' | 'sensor:assignment:changed' | 'sensor:exposed:changed';
|
|
968
|
+
data: PropertyChangedEvent | SensorAddedEvent | SensorDeletedEvent | SensorConnectedChangedEvent | SensorDisplayNameChangedEvent | SensorCapabilitiesChangedEvent | SensorAssignmentChangedEvent | SensorExposedChangedEvent;
|
|
945
969
|
}
|
|
946
970
|
|
|
947
971
|
export declare interface SensorEventNamespaces {
|
|
948
972
|
sensorSubject: string;
|
|
949
973
|
}
|
|
950
974
|
|
|
975
|
+
declare interface SensorExposedChangedEvent {
|
|
976
|
+
sensorId: string;
|
|
977
|
+
sensorType: SensorType;
|
|
978
|
+
exposed: boolean;
|
|
979
|
+
}
|
|
980
|
+
|
|
951
981
|
export declare interface SensorProviderNamespaces {
|
|
952
982
|
sensorRpc: string;
|
|
953
983
|
}
|
|
@@ -959,10 +989,19 @@ export declare interface SensorRefreshedState {
|
|
|
959
989
|
displayName?: string;
|
|
960
990
|
}
|
|
961
991
|
|
|
962
|
-
declare interface
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
992
|
+
export declare interface SensorRegistryNamespaces {
|
|
993
|
+
sensorsSubject: string;
|
|
994
|
+
sensorsRpc: string;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
export declare interface SensorRegistryRPC {
|
|
998
|
+
getSensors(): StoredSensorData[];
|
|
999
|
+
getSensorRpc(sensorId: string): StoredSensorData | undefined;
|
|
1000
|
+
getSensorState(sensorId: string): SensorRefreshedState;
|
|
1001
|
+
getSensorStates(): Record<string, SensorRefreshedState>;
|
|
1002
|
+
getPropertyValue(sensorId: string, property: string): unknown;
|
|
1003
|
+
getAllPropertyValues(sensorId: string): Record<string, unknown>;
|
|
1004
|
+
setDisplayName(sensorId: string, displayName: string): void;
|
|
966
1005
|
}
|
|
967
1006
|
|
|
968
1007
|
/* Excluded from this release type: SirenControlProperties */
|
|
@@ -991,12 +1030,15 @@ export declare interface StorageRPC {
|
|
|
991
1030
|
|
|
992
1031
|
export declare interface StoredSensorData {
|
|
993
1032
|
id: string;
|
|
994
|
-
stableId: string;
|
|
995
|
-
globalId: string;
|
|
996
1033
|
type: SensorType;
|
|
997
1034
|
name: string;
|
|
998
1035
|
displayName: string;
|
|
1036
|
+
nativeId?: string;
|
|
999
1037
|
pluginId: string;
|
|
1038
|
+
assignedCameraIds: string[];
|
|
1039
|
+
boundCameraId?: string;
|
|
1040
|
+
exposed: boolean;
|
|
1041
|
+
connected: boolean;
|
|
1000
1042
|
properties: Record<string, unknown>;
|
|
1001
1043
|
capabilities: string[];
|
|
1002
1044
|
requiresFrames?: boolean;
|
|
@@ -1023,6 +1065,8 @@ export declare const STREAM_CONFIG: {
|
|
|
1023
1065
|
};
|
|
1024
1066
|
};
|
|
1025
1067
|
|
|
1068
|
+
declare type StreamDegradeReason = 'codec';
|
|
1069
|
+
|
|
1026
1070
|
export declare interface StreamOptions {
|
|
1027
1071
|
camera: MaybeRefOrGetter<ReactiveCameraDevice | undefined>;
|
|
1028
1072
|
videoElement: MaybeRefOrGetter<HTMLVideoElement | undefined | null>;
|
|
@@ -1034,7 +1078,7 @@ export declare interface StreamOptions {
|
|
|
1034
1078
|
webrtcConnectTimeout?: number;
|
|
1035
1079
|
}
|
|
1036
1080
|
|
|
1037
|
-
export declare type StreamStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'error' | 'closed';
|
|
1081
|
+
export declare type StreamStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'error' | 'unsupported' | 'closed';
|
|
1038
1082
|
|
|
1039
1083
|
/* Excluded from this release type: SwitchControlProperties */
|
|
1040
1084
|
|
|
@@ -1071,6 +1115,8 @@ export declare interface TerminalSessionInfo {
|
|
|
1071
1115
|
dimensions: { cols: number; rows: number };
|
|
1072
1116
|
}
|
|
1073
1117
|
|
|
1118
|
+
export declare function useAllSensors(): UseSensorsReturn;
|
|
1119
|
+
|
|
1074
1120
|
export declare function useAudioSensor(camera: CameraIdentifier): UseSensorTypedReturn<ReactiveAudioSensor>;
|
|
1075
1121
|
|
|
1076
1122
|
export declare function useCameraById(cameraIdOrName: MaybeRefOrGetter<string>): UseCameraByIdReturn;
|
|
@@ -1206,7 +1252,7 @@ export declare interface UseSensorsReturn {
|
|
|
1206
1252
|
error: Ref<Error | undefined>;
|
|
1207
1253
|
}
|
|
1208
1254
|
|
|
1209
|
-
export declare function useSensorStorage(
|
|
1255
|
+
export declare function useSensorStorage(sensorId: MaybeRefOrGetter<string | undefined>, pluginId: MaybeRefOrGetter<string | undefined>): UseStorageReturn;
|
|
1210
1256
|
|
|
1211
1257
|
export declare interface UseSensorsTypedReturn<T extends ReactiveSensor<any> = ReactiveSensor> {
|
|
1212
1258
|
sensors: ComputedRef<T[]>;
|
package/dist/index.js
CHANGED
|
@@ -149,21 +149,30 @@ var NamespaceManager = class {
|
|
|
149
149
|
cameraImplRpc: `plugin.${pluginId}.camera.${cameraId}.impl.rpc`
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
|
-
static pluginSensorNamespaces(pluginId,
|
|
153
|
-
return { sensorStorageRpc: `plugin.${pluginId}.
|
|
152
|
+
static pluginSensorNamespaces(pluginId, sensorId) {
|
|
153
|
+
return { sensorStorageRpc: `plugin.${pluginId}.sensor.${sensorId}.storage.rpc` };
|
|
154
154
|
}
|
|
155
|
-
static
|
|
155
|
+
static sensorRegistryNamespaces() {
|
|
156
156
|
return {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
sensorWriteSubject: `camera.${cameraId}.sensors.writes`
|
|
157
|
+
sensorsSubject: "sensors.subject",
|
|
158
|
+
sensorsRpc: "sensors.rpc"
|
|
160
159
|
};
|
|
161
160
|
}
|
|
162
|
-
static
|
|
163
|
-
return {
|
|
161
|
+
static sensorCameraViewNamespaces(cameraId) {
|
|
162
|
+
return { sensorWriteSubject: `camera.${cameraId}.sensors.writes` };
|
|
163
|
+
}
|
|
164
|
+
static sensorEventNamespaces(sensorId) {
|
|
165
|
+
return { sensorSubject: `sensor.${sensorId}.subject` };
|
|
164
166
|
}
|
|
165
|
-
static sensorProviderNamespaces(pluginId,
|
|
166
|
-
return { sensorRpc: `plugin.${pluginId}.
|
|
167
|
+
static sensorProviderNamespaces(pluginId, sensorId) {
|
|
168
|
+
return { sensorRpc: `plugin.${pluginId}.sensor.${sensorId}.rpc` };
|
|
169
|
+
}
|
|
170
|
+
/** @deprecated stub window for pre-standalone plugins, remove in the first minor after the standalone-sensors release */
|
|
171
|
+
static legacySensorNamespaces(cameraId) {
|
|
172
|
+
return {
|
|
173
|
+
sensorSubject: `camera.${cameraId}.sensors.subject`,
|
|
174
|
+
sensorRpc: `camera.${cameraId}.sensors.rpc`
|
|
175
|
+
};
|
|
167
176
|
}
|
|
168
177
|
static frameWorkerDetectionNamespaces(cameraId) {
|
|
169
178
|
return { detectionRpc: `camera.${cameraId}.frameWorker.detection.rpc` };
|
|
@@ -171,9 +180,6 @@ var NamespaceManager = class {
|
|
|
171
180
|
static detectionEventNamespaces(cameraId) {
|
|
172
181
|
return { detectionEventSubject: `camera.${cameraId}.events.subject` };
|
|
173
182
|
}
|
|
174
|
-
static sensorControllerRpc(cameraId) {
|
|
175
|
-
return `camera.${cameraId}.sensors.controller.rpc`;
|
|
176
|
-
}
|
|
177
183
|
static terminalManagerNamespaces() {
|
|
178
184
|
return {
|
|
179
185
|
terminalManagerSubject: "terminalManager.subscriber",
|
|
@@ -602,7 +608,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
602
608
|
//#region src/composables/useCamera.ts
|
|
603
609
|
async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
604
610
|
const cameraNamespaces = NamespaceManager.cameraNamespaces(initialCamera._id);
|
|
605
|
-
const sensorNamespaces = NamespaceManager.
|
|
611
|
+
const sensorNamespaces = NamespaceManager.sensorRegistryNamespaces();
|
|
606
612
|
const ctx = "rpc" in rpcOrContext && "value" in rpcOrContext.rpc ? rpcOrContext : void 0;
|
|
607
613
|
const rpcRef = ctx ? ctx.rpc : shallowRef(rpcOrContext);
|
|
608
614
|
let closeSubscription;
|
|
@@ -696,30 +702,47 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
|
696
702
|
case "frameWorkerState":
|
|
697
703
|
frameWorkerConnected.value = event.data;
|
|
698
704
|
break;
|
|
699
|
-
case "snapshot:updated":
|
|
700
|
-
setSnapshot(initialCamera._id, event.data.snapshot, Date.now());
|
|
701
|
-
break;
|
|
705
|
+
case "snapshot:updated": setSnapshot(initialCamera._id, event.data.snapshot, Date.now());
|
|
702
706
|
}
|
|
703
707
|
}
|
|
704
708
|
function handleSensorEvent(event) {
|
|
705
709
|
if (event.type === "sensor:added") {
|
|
706
710
|
const addedEvent = event.data;
|
|
711
|
+
if (!addedEvent.sensor.assignedCameraIds.includes(initialCamera._id)) return;
|
|
712
|
+
if (!addedEvent.sensor.connected) return;
|
|
707
713
|
sensorStates.value = {
|
|
708
714
|
...sensorStates.value,
|
|
709
715
|
[addedEvent.sensor.id]: addedEvent.state
|
|
710
716
|
};
|
|
711
|
-
} else if (event.type === "sensor:
|
|
712
|
-
const
|
|
717
|
+
} else if (event.type === "sensor:deleted") {
|
|
718
|
+
const deletedEvent = event.data;
|
|
713
719
|
const newStates = { ...sensorStates.value };
|
|
714
|
-
delete newStates[
|
|
720
|
+
delete newStates[deletedEvent.sensorId];
|
|
715
721
|
sensorStates.value = newStates;
|
|
722
|
+
} else if (event.type === "sensor:connected:changed") {
|
|
723
|
+
const connectedEvent = event.data;
|
|
724
|
+
if (connectedEvent.connected) refreshStates().catch(() => {});
|
|
725
|
+
else {
|
|
726
|
+
const newStates = { ...sensorStates.value };
|
|
727
|
+
delete newStates[connectedEvent.sensorId];
|
|
728
|
+
sensorStates.value = newStates;
|
|
729
|
+
}
|
|
730
|
+
} else if (event.type === "sensor:assignment:changed") {
|
|
731
|
+
const assignmentEvent = event.data;
|
|
732
|
+
if (assignmentEvent.cameraId !== initialCamera._id) return;
|
|
733
|
+
if (assignmentEvent.assigned) refreshStates().catch(() => {});
|
|
734
|
+
else {
|
|
735
|
+
const newStates = { ...sensorStates.value };
|
|
736
|
+
delete newStates[assignmentEvent.sensorId];
|
|
737
|
+
sensorStates.value = newStates;
|
|
738
|
+
}
|
|
716
739
|
}
|
|
717
740
|
}
|
|
718
741
|
async function init() {
|
|
719
742
|
closeSubscription?.();
|
|
720
743
|
closeSensorSubscription?.();
|
|
721
744
|
closeSubscription = await rpcCall(rpcRef, (client) => client.subscribe(cameraNamespaces.cameraSubject, handleCameraEvent));
|
|
722
|
-
closeSensorSubscription = await rpcCall(rpcRef, (client) => client.subscribe(sensorNamespaces.
|
|
745
|
+
closeSensorSubscription = await rpcCall(rpcRef, (client) => client.subscribe(sensorNamespaces.sensorsSubject, handleSensorEvent));
|
|
723
746
|
await refreshStates();
|
|
724
747
|
}
|
|
725
748
|
async function close() {
|
|
@@ -1112,9 +1135,12 @@ var isReactiveOccupancySensor = createSensorTypeGuard(SensorType.Occupancy);
|
|
|
1112
1135
|
var isReactiveSmokeSensor = createSensorTypeGuard(SensorType.Smoke);
|
|
1113
1136
|
var isReactiveLeakSensor = createSensorTypeGuard(SensorType.Leak);
|
|
1114
1137
|
var isReactiveGarageControl = createSensorTypeGuard(SensorType.Garage);
|
|
1115
|
-
function createReactiveSensor(data, state, rpcRef
|
|
1138
|
+
function createReactiveSensor(data, state, rpcRef) {
|
|
1116
1139
|
const displayName = ref(state.displayName ?? data.displayName ?? data.name);
|
|
1117
1140
|
const capabilities = ref(state.capabilities ?? data.capabilities ?? []);
|
|
1141
|
+
const assignedCameraIds = ref([...data.assignedCameraIds]);
|
|
1142
|
+
const exposed = ref(data.exposed);
|
|
1143
|
+
const connected = ref(data.connected);
|
|
1118
1144
|
const properties = reactive({ ...state.properties });
|
|
1119
1145
|
const capabilityCallbacks = [];
|
|
1120
1146
|
return {
|
|
@@ -1122,19 +1148,23 @@ function createReactiveSensor(data, state, rpcRef, cameraId) {
|
|
|
1122
1148
|
type: data.type,
|
|
1123
1149
|
name: data.name,
|
|
1124
1150
|
displayName,
|
|
1151
|
+
nativeId: data.nativeId,
|
|
1125
1152
|
pluginId: data.pluginId,
|
|
1153
|
+
assignedCameraIds,
|
|
1154
|
+
exposed,
|
|
1155
|
+
connected,
|
|
1126
1156
|
capabilities,
|
|
1127
1157
|
properties,
|
|
1128
1158
|
getProperty(property) {
|
|
1129
1159
|
return properties[property];
|
|
1130
1160
|
},
|
|
1131
1161
|
async setProperty(property, value) {
|
|
1132
|
-
const sensorNamespace = `plugin.${data.pluginId}.
|
|
1162
|
+
const sensorNamespace = `plugin.${data.pluginId}.sensor.${data.id}.rpc`;
|
|
1133
1163
|
await rpcCall(rpcRef, (client) => client.createProxy(sensorNamespace).updateValue(property, value));
|
|
1134
1164
|
},
|
|
1135
1165
|
async setDisplayName(newDisplayName) {
|
|
1136
|
-
const namespace = NamespaceManager.
|
|
1137
|
-
await rpcCall(rpcRef, (client) => client.createProxy(namespace.
|
|
1166
|
+
const namespace = NamespaceManager.sensorRegistryNamespaces();
|
|
1167
|
+
await rpcCall(rpcRef, (client) => client.createProxy(namespace.sensorsRpc).setDisplayName(data.id, newDisplayName));
|
|
1138
1168
|
},
|
|
1139
1169
|
hasCapability(capability) {
|
|
1140
1170
|
return capabilities.value.includes(capability);
|
|
@@ -1151,7 +1181,8 @@ function createReactiveSensor(data, state, rpcRef, cameraId) {
|
|
|
1151
1181
|
}
|
|
1152
1182
|
};
|
|
1153
1183
|
}
|
|
1154
|
-
function createSensorManager(rpcOrContext
|
|
1184
|
+
function createSensorManager(rpcOrContext) {
|
|
1185
|
+
const { sensorsSubject, sensorsRpc } = NamespaceManager.sensorRegistryNamespaces();
|
|
1155
1186
|
const ctx = "rpc" in rpcOrContext && "value" in rpcOrContext.rpc ? rpcOrContext : void 0;
|
|
1156
1187
|
const rpcRef = ctx ? ctx.rpc : shallowRef(rpcOrContext);
|
|
1157
1188
|
const sensorMap = shallowRef(/* @__PURE__ */ new Map());
|
|
@@ -1176,7 +1207,7 @@ function createSensorManager(rpcOrContext, cameraId, sensorSubjectNamespace, sen
|
|
|
1176
1207
|
}
|
|
1177
1208
|
async function subscribeToSensorEvents(sensorId) {
|
|
1178
1209
|
if (sensorSubscriptions.has(sensorId)) return;
|
|
1179
|
-
const namespace = NamespaceManager.sensorEventNamespaces(
|
|
1210
|
+
const namespace = NamespaceManager.sensorEventNamespaces(sensorId);
|
|
1180
1211
|
const unsubscribe = await rpcCall(rpcRef, (c) => c.subscribe(namespace.sensorSubject, (msg) => handlePerSensorEvent(sensorId, msg)));
|
|
1181
1212
|
sensorSubscriptions.set(sensorId, unsubscribe);
|
|
1182
1213
|
}
|
|
@@ -1191,17 +1222,33 @@ function createSensorManager(rpcOrContext, cameraId, sensorSubjectNamespace, sen
|
|
|
1191
1222
|
if (message.type === "sensor:added") {
|
|
1192
1223
|
const event = message.data;
|
|
1193
1224
|
if (sensorMap.value.has(event.sensor.id)) return;
|
|
1194
|
-
const reactiveSensor = createReactiveSensor(event.sensor, event.state, rpcRef
|
|
1225
|
+
const reactiveSensor = createReactiveSensor(event.sensor, event.state, rpcRef);
|
|
1195
1226
|
const newMap = new Map(sensorMap.value);
|
|
1196
1227
|
newMap.set(event.sensor.id, reactiveSensor);
|
|
1197
1228
|
sensorMap.value = newMap;
|
|
1198
1229
|
subscribeToSensorEvents(event.sensor.id);
|
|
1199
|
-
} else if (message.type === "sensor:
|
|
1230
|
+
} else if (message.type === "sensor:deleted") {
|
|
1200
1231
|
const event = message.data;
|
|
1201
1232
|
unsubscribeFromSensorEvents(event.sensorId);
|
|
1202
1233
|
const newMap = new Map(sensorMap.value);
|
|
1203
1234
|
newMap.delete(event.sensorId);
|
|
1204
1235
|
sensorMap.value = newMap;
|
|
1236
|
+
} else if (message.type === "sensor:connected:changed") {
|
|
1237
|
+
const event = message.data;
|
|
1238
|
+
const sensor = sensorMap.value.get(event.sensorId);
|
|
1239
|
+
if (sensor) sensor.connected.value = event.connected;
|
|
1240
|
+
} else if (message.type === "sensor:assignment:changed") {
|
|
1241
|
+
const event = message.data;
|
|
1242
|
+
const sensor = sensorMap.value.get(event.sensorId);
|
|
1243
|
+
if (!sensor) return;
|
|
1244
|
+
const cameras = new Set(sensor.assignedCameraIds.value);
|
|
1245
|
+
if (event.assigned) cameras.add(event.cameraId);
|
|
1246
|
+
else cameras.delete(event.cameraId);
|
|
1247
|
+
sensor.assignedCameraIds.value = [...cameras];
|
|
1248
|
+
} else if (message.type === "sensor:exposed:changed") {
|
|
1249
|
+
const event = message.data;
|
|
1250
|
+
const sensor = sensorMap.value.get(event.sensorId);
|
|
1251
|
+
if (sensor) sensor.exposed.value = event.exposed;
|
|
1205
1252
|
}
|
|
1206
1253
|
}
|
|
1207
1254
|
async function doInit() {
|
|
@@ -1211,12 +1258,12 @@ function createSensorManager(rpcOrContext, cameraId, sensorSubjectNamespace, sen
|
|
|
1211
1258
|
sensorSubscriptions.clear();
|
|
1212
1259
|
sensorMap.value = /* @__PURE__ */ new Map();
|
|
1213
1260
|
let pendingEvents = [];
|
|
1214
|
-
globalUnsubscribe = await rpcCall(rpcRef, (c) => c.subscribe(
|
|
1261
|
+
globalUnsubscribe = await rpcCall(rpcRef, (c) => c.subscribe(sensorsSubject, (msg) => {
|
|
1215
1262
|
if (pendingEvents) pendingEvents.push(msg);
|
|
1216
1263
|
else handleGlobalSensorEvent(msg);
|
|
1217
1264
|
}));
|
|
1218
1265
|
const [sensors, states] = await rpcCall(rpcRef, async (c) => {
|
|
1219
|
-
const proxy = c.createProxy(
|
|
1266
|
+
const proxy = c.createProxy(sensorsRpc);
|
|
1220
1267
|
return Promise.all([proxy.getSensors(), proxy.getSensorStates()]);
|
|
1221
1268
|
});
|
|
1222
1269
|
const newMap = /* @__PURE__ */ new Map();
|
|
@@ -1225,7 +1272,7 @@ function createSensorManager(rpcOrContext, cameraId, sensorSubjectNamespace, sen
|
|
|
1225
1272
|
properties: {},
|
|
1226
1273
|
capabilities: [],
|
|
1227
1274
|
type: sensor.type
|
|
1228
|
-
}, rpcRef
|
|
1275
|
+
}, rpcRef);
|
|
1229
1276
|
newMap.set(sensor.id, reactiveSensor);
|
|
1230
1277
|
}
|
|
1231
1278
|
sensorMap.value = newMap;
|
|
@@ -1281,7 +1328,7 @@ function createSensorManager(rpcOrContext, cameraId, sensorSubjectNamespace, sen
|
|
|
1281
1328
|
return sensors.value.some((s) => s.type === type);
|
|
1282
1329
|
},
|
|
1283
1330
|
async setDisplayName(sensorId, displayName) {
|
|
1284
|
-
await rpcCall(rpcRef, (c) => c.createProxy(
|
|
1331
|
+
await rpcCall(rpcRef, (c) => c.createProxy(sensorsRpc).setDisplayName(sensorId, displayName));
|
|
1285
1332
|
},
|
|
1286
1333
|
ensureInitialized,
|
|
1287
1334
|
reconnect,
|
|
@@ -1300,33 +1347,29 @@ function reconnectAllSensorManagers() {
|
|
|
1300
1347
|
cached.initPromise = cached.manager.reconnect();
|
|
1301
1348
|
});
|
|
1302
1349
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
return { manager: createSensorManager(rpcOrContext, cameraId, namespaces.sensorSubject, namespaces.sensorRpc) };
|
|
1307
|
-
});
|
|
1350
|
+
var GLOBAL_SENSOR_MANAGER_KEY = "sensors";
|
|
1351
|
+
function acquireSensorManager(rpcOrContext) {
|
|
1352
|
+
return sensorManagerCache.acquire(GLOBAL_SENSOR_MANAGER_KEY, () => ({ manager: createSensorManager(rpcOrContext) }));
|
|
1308
1353
|
}
|
|
1309
|
-
function releaseSensorManager(
|
|
1310
|
-
sensorManagerCache.release(
|
|
1354
|
+
function releaseSensorManager() {
|
|
1355
|
+
sensorManagerCache.release(GLOBAL_SENSOR_MANAGER_KEY);
|
|
1311
1356
|
}
|
|
1312
1357
|
function createSensorComposableState() {
|
|
1313
1358
|
return {
|
|
1314
|
-
|
|
1359
|
+
acquired: false,
|
|
1315
1360
|
cachedManager: void 0
|
|
1316
1361
|
};
|
|
1317
1362
|
}
|
|
1318
|
-
|
|
1363
|
+
function sensorsForCamera(manager, cameraId) {
|
|
1364
|
+
return manager.sensors.value.filter((sensor) => sensor.assignedCameraIds.value.includes(cameraId));
|
|
1365
|
+
}
|
|
1366
|
+
async function ensureSensorManager(state, cameraUi, isConnected, isLoading, error) {
|
|
1319
1367
|
if (!cameraUi.rpc.value || !isConnected) return void 0;
|
|
1320
|
-
if (state.currentCameraId && state.currentCameraId !== cameraId) {
|
|
1321
|
-
releaseSensorManager(state.currentCameraId);
|
|
1322
|
-
state.currentCameraId = void 0;
|
|
1323
|
-
state.cachedManager = void 0;
|
|
1324
|
-
}
|
|
1325
1368
|
isLoading.value = true;
|
|
1326
1369
|
error.value = void 0;
|
|
1327
1370
|
try {
|
|
1328
|
-
const cached = acquireSensorManager(
|
|
1329
|
-
state.
|
|
1371
|
+
const cached = acquireSensorManager(cameraUi);
|
|
1372
|
+
state.acquired = true;
|
|
1330
1373
|
state.cachedManager = cached.manager;
|
|
1331
1374
|
if (!cached.initPromise) cached.initPromise = cached.manager.ensureInitialized();
|
|
1332
1375
|
await cached.initPromise;
|
|
@@ -1339,9 +1382,9 @@ async function ensureSensorManager(state, cameraUi, isConnected, cameraId, isLoa
|
|
|
1339
1382
|
}
|
|
1340
1383
|
}
|
|
1341
1384
|
function cleanupSensorComposable(state) {
|
|
1342
|
-
if (state.
|
|
1343
|
-
releaseSensorManager(
|
|
1344
|
-
state.
|
|
1385
|
+
if (state.acquired) {
|
|
1386
|
+
releaseSensorManager();
|
|
1387
|
+
state.acquired = false;
|
|
1345
1388
|
state.cachedManager = void 0;
|
|
1346
1389
|
}
|
|
1347
1390
|
}
|
|
@@ -1359,7 +1402,7 @@ function useSensorById(camera, sensorId) {
|
|
|
1359
1402
|
() => toValue(sensorId)
|
|
1360
1403
|
], async ([connected, camId, senId]) => {
|
|
1361
1404
|
if (connected && camId && senId) {
|
|
1362
|
-
const manager = await ensureSensorManager(state, cameraUi, connected,
|
|
1405
|
+
const manager = await ensureSensorManager(state, cameraUi, connected, _isLoading, error);
|
|
1363
1406
|
sensor.value = manager?.getSensor(senId);
|
|
1364
1407
|
initialLoadDone.value = true;
|
|
1365
1408
|
} else {
|
|
@@ -1391,8 +1434,8 @@ function useSensorByType(camera, sensorType) {
|
|
|
1391
1434
|
() => toValue(sensorType)
|
|
1392
1435
|
], async ([connected, camId, type]) => {
|
|
1393
1436
|
if (connected && camId) {
|
|
1394
|
-
const manager = await ensureSensorManager(state, cameraUi, connected,
|
|
1395
|
-
sensor.value = manager
|
|
1437
|
+
const manager = await ensureSensorManager(state, cameraUi, connected, _isLoading, error);
|
|
1438
|
+
sensor.value = manager ? sensorsForCamera(manager, camId).find((s) => s.type === type) : void 0;
|
|
1396
1439
|
initialLoadDone.value = true;
|
|
1397
1440
|
} else {
|
|
1398
1441
|
cleanupSensorComposable(state);
|
|
@@ -1424,9 +1467,9 @@ function useSensorsByType(camera, sensorType) {
|
|
|
1424
1467
|
() => toValue(sensorType)
|
|
1425
1468
|
], async ([connected, camId, type]) => {
|
|
1426
1469
|
if (connected && camId) {
|
|
1427
|
-
const manager = await ensureSensorManager(state, cameraUi, connected,
|
|
1470
|
+
const manager = await ensureSensorManager(state, cameraUi, connected, _isLoading, error);
|
|
1428
1471
|
cachedManager.value = manager;
|
|
1429
|
-
sensorsRef.value = manager
|
|
1472
|
+
sensorsRef.value = manager ? sensorsForCamera(manager, camId).filter((s) => s.type === type) : [];
|
|
1430
1473
|
initialLoadDone.value = true;
|
|
1431
1474
|
} else {
|
|
1432
1475
|
cleanupSensorComposable(state);
|
|
@@ -1436,8 +1479,10 @@ function useSensorsByType(camera, sensorType) {
|
|
|
1436
1479
|
}, { immediate: true });
|
|
1437
1480
|
const sensors = computed(() => {
|
|
1438
1481
|
if (!cachedManager.value) return [];
|
|
1482
|
+
const camId = extractCameraId(toValue(camera));
|
|
1439
1483
|
const type = toValue(sensorType);
|
|
1440
|
-
|
|
1484
|
+
if (!camId) return [];
|
|
1485
|
+
return sensorsForCamera(cachedManager.value, camId).filter((s) => s.type === type);
|
|
1441
1486
|
});
|
|
1442
1487
|
tryOnScopeDispose(() => {
|
|
1443
1488
|
cleanupSensorComposable(state);
|
|
@@ -1460,9 +1505,9 @@ function useSensors(camera) {
|
|
|
1460
1505
|
const state = createSensorComposableState();
|
|
1461
1506
|
watch([isConnected, () => extractCameraId(toValue(camera))], async ([connected, camId]) => {
|
|
1462
1507
|
if (connected && camId) {
|
|
1463
|
-
const manager = await ensureSensorManager(state, cameraUi, connected,
|
|
1508
|
+
const manager = await ensureSensorManager(state, cameraUi, connected, _isLoading, error);
|
|
1464
1509
|
cachedManager.value = manager;
|
|
1465
|
-
sensorsRef.value = manager
|
|
1510
|
+
sensorsRef.value = manager ? sensorsForCamera(manager, camId) : [];
|
|
1466
1511
|
initialLoadDone.value = true;
|
|
1467
1512
|
} else {
|
|
1468
1513
|
cleanupSensorComposable(state);
|
|
@@ -1470,7 +1515,12 @@ function useSensors(camera) {
|
|
|
1470
1515
|
cachedManager.value = void 0;
|
|
1471
1516
|
}
|
|
1472
1517
|
}, { immediate: true });
|
|
1473
|
-
const sensors = computed(() =>
|
|
1518
|
+
const sensors = computed(() => {
|
|
1519
|
+
if (!cachedManager.value) return [];
|
|
1520
|
+
const camId = extractCameraId(toValue(camera));
|
|
1521
|
+
if (!camId) return [];
|
|
1522
|
+
return sensorsForCamera(cachedManager.value, camId);
|
|
1523
|
+
});
|
|
1474
1524
|
tryOnScopeDispose(() => {
|
|
1475
1525
|
cleanupSensorComposable(state);
|
|
1476
1526
|
sensorsRef.value = [];
|
|
@@ -1481,6 +1531,33 @@ function useSensors(camera) {
|
|
|
1481
1531
|
error
|
|
1482
1532
|
};
|
|
1483
1533
|
}
|
|
1534
|
+
function useAllSensors() {
|
|
1535
|
+
const cameraUi = useCameraUi();
|
|
1536
|
+
const { isConnected } = cameraUi;
|
|
1537
|
+
const cachedManager = shallowRef();
|
|
1538
|
+
const _isLoading = ref(false);
|
|
1539
|
+
const initialLoadDone = ref(false);
|
|
1540
|
+
const error = ref();
|
|
1541
|
+
const state = createSensorComposableState();
|
|
1542
|
+
watch([isConnected], async ([connected]) => {
|
|
1543
|
+
if (connected) {
|
|
1544
|
+
cachedManager.value = await ensureSensorManager(state, cameraUi, connected, _isLoading, error);
|
|
1545
|
+
initialLoadDone.value = true;
|
|
1546
|
+
} else {
|
|
1547
|
+
cleanupSensorComposable(state);
|
|
1548
|
+
cachedManager.value = void 0;
|
|
1549
|
+
}
|
|
1550
|
+
}, { immediate: true });
|
|
1551
|
+
const sensors = computed(() => cachedManager.value?.sensors.value ?? []);
|
|
1552
|
+
tryOnScopeDispose(() => {
|
|
1553
|
+
cleanupSensorComposable(state);
|
|
1554
|
+
});
|
|
1555
|
+
return {
|
|
1556
|
+
sensors,
|
|
1557
|
+
isLoading: computed(() => _isLoading.value || !initialLoadDone.value),
|
|
1558
|
+
error
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1484
1561
|
function useTypedSensor(camera, sensorType) {
|
|
1485
1562
|
const cameraUi = useCameraUi();
|
|
1486
1563
|
const { isConnected } = cameraUi;
|
|
@@ -1492,9 +1569,9 @@ function useTypedSensor(camera, sensorType) {
|
|
|
1492
1569
|
const state = createSensorComposableState();
|
|
1493
1570
|
watch([isConnected, () => extractCameraId(toValue(camera))], async ([connected, camId]) => {
|
|
1494
1571
|
if (connected && camId) {
|
|
1495
|
-
const manager = await ensureSensorManager(state, cameraUi, connected,
|
|
1572
|
+
const manager = await ensureSensorManager(state, cameraUi, connected, _isLoading, error);
|
|
1496
1573
|
cachedManager.value = manager;
|
|
1497
|
-
sensor.value = manager
|
|
1574
|
+
sensor.value = manager ? sensorsForCamera(manager, camId).find((s) => s.type === sensorType) : void 0;
|
|
1498
1575
|
initialLoadDone.value = true;
|
|
1499
1576
|
} else {
|
|
1500
1577
|
cleanupSensorComposable(state);
|
|
@@ -1503,11 +1580,13 @@ function useTypedSensor(camera, sensorType) {
|
|
|
1503
1580
|
}, { immediate: true });
|
|
1504
1581
|
watch(() => {
|
|
1505
1582
|
if (!cachedManager.value) return void 0;
|
|
1506
|
-
|
|
1583
|
+
const camId = extractCameraId(toValue(camera));
|
|
1584
|
+
if (!camId) return void 0;
|
|
1585
|
+
return sensorsForCamera(cachedManager.value, camId).find((s) => s.type === sensorType)?.id;
|
|
1507
1586
|
}, () => {
|
|
1508
1587
|
if (cachedManager.value) {
|
|
1509
|
-
const
|
|
1510
|
-
sensor.value =
|
|
1588
|
+
const camId = extractCameraId(toValue(camera));
|
|
1589
|
+
sensor.value = camId ? sensorsForCamera(cachedManager.value, camId).find((s) => s.type === sensorType) : void 0;
|
|
1511
1590
|
}
|
|
1512
1591
|
});
|
|
1513
1592
|
tryOnScopeDispose(() => {
|
|
@@ -1549,9 +1628,9 @@ function useClassifierSensors(camera) {
|
|
|
1549
1628
|
const state = createSensorComposableState();
|
|
1550
1629
|
watch([isConnected, () => extractCameraId(toValue(camera))], async ([connected, camId]) => {
|
|
1551
1630
|
if (connected && camId) {
|
|
1552
|
-
const manager = await ensureSensorManager(state, cameraUi, connected,
|
|
1631
|
+
const manager = await ensureSensorManager(state, cameraUi, connected, _isLoading, error);
|
|
1553
1632
|
cachedManager.value = manager;
|
|
1554
|
-
sensorsRef.value = manager
|
|
1633
|
+
sensorsRef.value = manager ? sensorsForCamera(manager, camId).filter((s) => s.type === SensorType.Classifier) : [];
|
|
1555
1634
|
initialLoadDone.value = true;
|
|
1556
1635
|
} else {
|
|
1557
1636
|
cleanupSensorComposable(state);
|
|
@@ -1559,7 +1638,12 @@ function useClassifierSensors(camera) {
|
|
|
1559
1638
|
cachedManager.value = void 0;
|
|
1560
1639
|
}
|
|
1561
1640
|
}, { immediate: true });
|
|
1562
|
-
const sensors = computed(() =>
|
|
1641
|
+
const sensors = computed(() => {
|
|
1642
|
+
if (!cachedManager.value) return [];
|
|
1643
|
+
const camId = extractCameraId(toValue(camera));
|
|
1644
|
+
if (!camId) return [];
|
|
1645
|
+
return sensorsForCamera(cachedManager.value, camId).filter((s) => s.type === SensorType.Classifier);
|
|
1646
|
+
});
|
|
1563
1647
|
tryOnScopeDispose(() => {
|
|
1564
1648
|
cleanupSensorComposable(state);
|
|
1565
1649
|
sensorsRef.value = [];
|
|
@@ -1663,8 +1747,8 @@ function getPluginStorageKey(pluginId) {
|
|
|
1663
1747
|
function getCameraStorageKey(pluginId, cameraId) {
|
|
1664
1748
|
return `plugin:${pluginId}:camera:${cameraId}`;
|
|
1665
1749
|
}
|
|
1666
|
-
function getSensorStorageKey(pluginId,
|
|
1667
|
-
return `plugin:${pluginId}:
|
|
1750
|
+
function getSensorStorageKey(pluginId, sensorId) {
|
|
1751
|
+
return `plugin:${pluginId}:sensor:${sensorId}`;
|
|
1668
1752
|
}
|
|
1669
1753
|
function acquireStorage(key, createProxy) {
|
|
1670
1754
|
return storageCache.acquire(key, () => {
|
|
@@ -1874,7 +1958,7 @@ function useCameraStorage(camera, pluginName) {
|
|
|
1874
1958
|
submitValue: operations.submitValue
|
|
1875
1959
|
};
|
|
1876
1960
|
}
|
|
1877
|
-
function useSensorStorage(
|
|
1961
|
+
function useSensorStorage(sensorId, pluginId) {
|
|
1878
1962
|
const { rpc, isConnected: clientConnected } = useCameraUi();
|
|
1879
1963
|
const config = shallowRef();
|
|
1880
1964
|
const _isLoading = ref(false);
|
|
@@ -1883,14 +1967,14 @@ function useSensorStorage(camera, sensorId, pluginId) {
|
|
|
1883
1967
|
const isConnected = ref(false);
|
|
1884
1968
|
const state = createStorageState();
|
|
1885
1969
|
const operations = createStorageOperations(state, config, _isLoading, error);
|
|
1886
|
-
function connect(
|
|
1970
|
+
function connect(senId, plugId) {
|
|
1887
1971
|
if (!rpc.value || !clientConnected.value) return false;
|
|
1888
1972
|
try {
|
|
1889
|
-
const storageKey = getSensorStorageKey(plugId,
|
|
1973
|
+
const storageKey = getSensorStorageKey(plugId, senId);
|
|
1890
1974
|
if (state.currentStorageKey && state.currentStorageKey !== storageKey) releaseStorage(state.currentStorageKey);
|
|
1891
1975
|
state.currentStorageKey = storageKey;
|
|
1892
1976
|
state.cachedStorage = acquireStorage(storageKey, () => {
|
|
1893
|
-
const namespaces = NamespaceManager.pluginSensorNamespaces(plugId,
|
|
1977
|
+
const namespaces = NamespaceManager.pluginSensorNamespaces(plugId, senId);
|
|
1894
1978
|
return rpc.value.createProxy(namespaces.sensorStorageRpc);
|
|
1895
1979
|
});
|
|
1896
1980
|
isConnected.value = true;
|
|
@@ -1902,23 +1986,21 @@ function useSensorStorage(camera, sensorId, pluginId) {
|
|
|
1902
1986
|
}
|
|
1903
1987
|
}
|
|
1904
1988
|
async function getConfig() {
|
|
1905
|
-
const cameraId = extractCameraId(toValue(camera));
|
|
1906
1989
|
const senId = toValue(sensorId);
|
|
1907
1990
|
const plugId = toValue(pluginId);
|
|
1908
|
-
if (!
|
|
1991
|
+
if (!senId || !plugId) return void 0;
|
|
1909
1992
|
if (!state.cachedStorage) {
|
|
1910
|
-
if (!connect(
|
|
1993
|
+
if (!connect(senId, plugId)) return void 0;
|
|
1911
1994
|
}
|
|
1912
1995
|
return operations.getConfig();
|
|
1913
1996
|
}
|
|
1914
1997
|
watch([
|
|
1915
1998
|
clientConnected,
|
|
1916
|
-
() => extractCameraId(toValue(camera)),
|
|
1917
1999
|
() => toValue(sensorId),
|
|
1918
2000
|
() => toValue(pluginId)
|
|
1919
|
-
], ([connected,
|
|
1920
|
-
if (connected &&
|
|
1921
|
-
connect(
|
|
2001
|
+
], ([connected, senId, plugId]) => {
|
|
2002
|
+
if (connected && senId && plugId) {
|
|
2003
|
+
connect(senId, plugId);
|
|
1922
2004
|
operations.getConfig();
|
|
1923
2005
|
} else cleanupStorage(state, isConnected, config);
|
|
1924
2006
|
initialSetupDone.value = true;
|
|
@@ -2106,14 +2188,12 @@ function createActivityMode(options) {
|
|
|
2106
2188
|
startIdleTimer();
|
|
2107
2189
|
}
|
|
2108
2190
|
break;
|
|
2109
|
-
case "activity":
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
}, resolvedConfig.activityTimeout);
|
|
2116
|
-
break;
|
|
2191
|
+
case "activity": if (hasActivity.value) {
|
|
2192
|
+
inStandby.value = false;
|
|
2193
|
+
if (!isStreamPlaying()) onStreamStart();
|
|
2194
|
+
} else activityTimeout = setTimeout(() => {
|
|
2195
|
+
if (mode.value === "activity" && !hasActivity.value) goToStandby();
|
|
2196
|
+
}, resolvedConfig.activityTimeout);
|
|
2117
2197
|
}
|
|
2118
2198
|
}
|
|
2119
2199
|
function startIdleTimer() {
|
|
@@ -2154,7 +2234,6 @@ function createActivityMode(options) {
|
|
|
2154
2234
|
if (!hasActivity.value) activityTimeout = setTimeout(() => {
|
|
2155
2235
|
goToStandby();
|
|
2156
2236
|
}, resolvedConfig.activityTimeout);
|
|
2157
|
-
break;
|
|
2158
2237
|
}
|
|
2159
2238
|
}
|
|
2160
2239
|
function resetIdleTimer() {
|
|
@@ -2293,7 +2372,7 @@ var STREAM_CONFIG = {
|
|
|
2293
2372
|
ICE_SERVERS: [{ urls: "stun:stun.l.google.com:19302" }]
|
|
2294
2373
|
},
|
|
2295
2374
|
MSE: {
|
|
2296
|
-
BUFFER_SIZE:
|
|
2375
|
+
BUFFER_SIZE: 2097152,
|
|
2297
2376
|
BUFFER_WINDOW: 5
|
|
2298
2377
|
},
|
|
2299
2378
|
CODECS: {
|
|
@@ -2322,6 +2401,7 @@ var STREAM_CONFIG = {
|
|
|
2322
2401
|
};
|
|
2323
2402
|
//#endregion
|
|
2324
2403
|
//#region src/streaming/utils.ts
|
|
2404
|
+
var H265_TEST_CODEC = "hvc1.1.6.L153.B0";
|
|
2325
2405
|
function safariVersion() {
|
|
2326
2406
|
return navigator.userAgent.match(/version\/(\d+)/i);
|
|
2327
2407
|
}
|
|
@@ -2354,14 +2434,27 @@ function isWebRTCCompatibleVideo(codec) {
|
|
|
2354
2434
|
if (codec === "H265") return isH265Supported();
|
|
2355
2435
|
return false;
|
|
2356
2436
|
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2437
|
+
var mseH265;
|
|
2438
|
+
var webrtcH265;
|
|
2439
|
+
function canPlayH265(transport) {
|
|
2440
|
+
if (transport === "auto") return canPlayH265("webrtc") || canPlayH265("mse");
|
|
2441
|
+
if (transport === "mse") {
|
|
2442
|
+
if (mseH265 === void 0) mseH265 = !!getMediaSourceConstructor()?.isTypeSupported(`video/mp4; codecs="${H265_TEST_CODEC}"`);
|
|
2443
|
+
return mseH265;
|
|
2444
|
+
}
|
|
2445
|
+
if (webrtcH265 === void 0) try {
|
|
2446
|
+
webrtcH265 = !!(RTCRtpReceiver?.getCapabilities("video")?.codecs)?.some((c) => c.mimeType.toLowerCase().includes("h265") || c.mimeType.toLowerCase().includes("hevc"));
|
|
2362
2447
|
} catch {
|
|
2363
|
-
|
|
2448
|
+
webrtcH265 = false;
|
|
2364
2449
|
}
|
|
2450
|
+
return webrtcH265;
|
|
2451
|
+
}
|
|
2452
|
+
function reportH265DecodeFailure(transport) {
|
|
2453
|
+
if (transport === "mse") mseH265 = false;
|
|
2454
|
+
else webrtcH265 = false;
|
|
2455
|
+
}
|
|
2456
|
+
function isH265Supported() {
|
|
2457
|
+
return canPlayH265("webrtc");
|
|
2365
2458
|
}
|
|
2366
2459
|
function checkWebRTCCompatibility(probe) {
|
|
2367
2460
|
const audioStreams = probe.audio.filter((s) => s.direction === "sendonly");
|
|
@@ -2741,9 +2834,7 @@ async function processWebRTCMessage(handler, msg) {
|
|
|
2741
2834
|
case "webrtc/answer":
|
|
2742
2835
|
await handler.handleAnswer(msg.value);
|
|
2743
2836
|
break;
|
|
2744
|
-
case "webrtc/candidate":
|
|
2745
|
-
if (msg.value) await handler.handleCandidate(msg.value);
|
|
2746
|
-
break;
|
|
2837
|
+
case "webrtc/candidate": if (msg.value) await handler.handleCandidate(msg.value);
|
|
2747
2838
|
}
|
|
2748
2839
|
}
|
|
2749
2840
|
//#endregion
|
|
@@ -2755,6 +2846,8 @@ var StreamConnection = class {
|
|
|
2755
2846
|
requestedMode;
|
|
2756
2847
|
activeResolution;
|
|
2757
2848
|
requestedResolution;
|
|
2849
|
+
degradedFrom;
|
|
2850
|
+
degradeReason;
|
|
2758
2851
|
source;
|
|
2759
2852
|
hasVideo;
|
|
2760
2853
|
hasAudio;
|
|
@@ -2766,14 +2859,20 @@ var StreamConnection = class {
|
|
|
2766
2859
|
paused;
|
|
2767
2860
|
nativeWidth;
|
|
2768
2861
|
nativeHeight;
|
|
2862
|
+
camera;
|
|
2863
|
+
videoElement;
|
|
2864
|
+
containerElement;
|
|
2865
|
+
target;
|
|
2866
|
+
isReady;
|
|
2867
|
+
effectiveMode;
|
|
2868
|
+
triedSourceIds = /* @__PURE__ */ new Set();
|
|
2769
2869
|
options;
|
|
2870
|
+
wsTransport;
|
|
2871
|
+
scope = effectScope(true);
|
|
2770
2872
|
connectionGeneration = 0;
|
|
2771
2873
|
abortController = new AbortController();
|
|
2772
|
-
|
|
2773
|
-
offTabVisible;
|
|
2774
|
-
offTabPaused;
|
|
2874
|
+
probedSourceId;
|
|
2775
2875
|
wasPausedByVisibility = false;
|
|
2776
|
-
wsTransport;
|
|
2777
2876
|
wsHandle;
|
|
2778
2877
|
webrtcHandler;
|
|
2779
2878
|
mseHandler;
|
|
@@ -2783,15 +2882,11 @@ var StreamConnection = class {
|
|
|
2783
2882
|
lastMediaStream = null;
|
|
2784
2883
|
stopWatchers = [];
|
|
2785
2884
|
mseMonitorInterval;
|
|
2885
|
+
offTabVisible;
|
|
2886
|
+
offTabPaused;
|
|
2786
2887
|
onVideoPauseBound;
|
|
2787
2888
|
onVideoPlayBound;
|
|
2788
2889
|
onVideoResizeBound;
|
|
2789
|
-
camera;
|
|
2790
|
-
videoElement;
|
|
2791
|
-
containerElement;
|
|
2792
|
-
target;
|
|
2793
|
-
isReady;
|
|
2794
|
-
effectiveMode;
|
|
2795
2890
|
startWsConnectTimeout;
|
|
2796
2891
|
stopWsConnectTimeout;
|
|
2797
2892
|
startConnectTimeout;
|
|
@@ -2810,6 +2905,8 @@ var StreamConnection = class {
|
|
|
2810
2905
|
this.status = ref("idle");
|
|
2811
2906
|
this.activeMode = ref("webrtc");
|
|
2812
2907
|
this.activeResolution = ref("low-resolution");
|
|
2908
|
+
this.degradedFrom = ref(null);
|
|
2909
|
+
this.degradeReason = ref(null);
|
|
2813
2910
|
this.source = shallowRef();
|
|
2814
2911
|
this.hasVideo = ref(false);
|
|
2815
2912
|
this.hasAudio = ref(false);
|
|
@@ -2846,11 +2943,18 @@ var StreamConnection = class {
|
|
|
2846
2943
|
this.activeMode.value = "mse";
|
|
2847
2944
|
if (this.mseHandler?.isReady) this.status.value = "connected";
|
|
2848
2945
|
else this.startMSE();
|
|
2849
|
-
} else
|
|
2946
|
+
} else {
|
|
2947
|
+
this.markSourceUndecodable();
|
|
2948
|
+
this.restart();
|
|
2949
|
+
}
|
|
2950
|
+
else if (this.status.value === "connected" && !this.isPlaying.value && !this.abortController.signal.aborted && this.sourceLikelyH265()) this.handleNoFramesWhileConnected();
|
|
2850
2951
|
}, STREAM_CONFIG.WEBRTC.CONNECT_TIMEOUT, { immediate: false });
|
|
2851
2952
|
const mseConnectTimeout = useTimeoutFn(() => {
|
|
2852
2953
|
if (this.abortController.signal.aborted || this.status.value === "connected" || this.status.value === "closed") return;
|
|
2853
|
-
if (this.mseHandler && !this.mseHandler.isReady)
|
|
2954
|
+
if (this.mseHandler && !this.mseHandler.isReady) {
|
|
2955
|
+
this.markSourceUndecodable();
|
|
2956
|
+
this.restart();
|
|
2957
|
+
}
|
|
2854
2958
|
}, STREAM_CONFIG.WEBRTC.CONNECT_TIMEOUT, { immediate: false });
|
|
2855
2959
|
const reconnectTimeout = useTimeoutFn(() => {
|
|
2856
2960
|
if (!this.abortController.signal.aborted) this.restart();
|
|
@@ -2901,22 +3005,43 @@ var StreamConnection = class {
|
|
|
2901
3005
|
this.cleanup();
|
|
2902
3006
|
this.abortController = new AbortController();
|
|
2903
3007
|
const gen = ++this.connectionGeneration;
|
|
3008
|
+
this.triedSourceIds.clear();
|
|
2904
3009
|
this.status.value = "connecting";
|
|
2905
3010
|
this.error.value = void 0;
|
|
2906
3011
|
this.isPlaying.value = false;
|
|
2907
3012
|
try {
|
|
2908
|
-
|
|
3013
|
+
const init = this.initializeSource();
|
|
3014
|
+
if (init === "unsupported") {
|
|
3015
|
+
log.debug("start() — no source with a playable codec");
|
|
3016
|
+
this.setUnsupported();
|
|
3017
|
+
return;
|
|
3018
|
+
}
|
|
3019
|
+
if (!init) {
|
|
2909
3020
|
log.debug("start() — no streaming source available");
|
|
2910
3021
|
this.error.value = /* @__PURE__ */ new Error("No streaming source available");
|
|
2911
3022
|
this.status.value = "error";
|
|
2912
3023
|
return;
|
|
2913
3024
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
3025
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3026
|
+
const probedSourceId = this.source.value?._id;
|
|
3027
|
+
const probe = await this.probeStream();
|
|
3028
|
+
if (gen !== this.connectionGeneration) {
|
|
3029
|
+
log.debug("start() — generation stale after probe, aborting");
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
if (probe && probedSourceId && this.probeSaysUnplayable(probe)) this.triedSourceIds.add(probedSourceId);
|
|
3033
|
+
const reinit = this.initializeSource();
|
|
3034
|
+
if (reinit === "unsupported") {
|
|
3035
|
+
this.setUnsupported();
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
if (!reinit) {
|
|
3039
|
+
this.error.value = /* @__PURE__ */ new Error("No streaming source available");
|
|
3040
|
+
this.status.value = "error";
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
if (!probe || this.source.value?._id === probedSourceId) break;
|
|
2918
3044
|
}
|
|
2919
|
-
this.initializeSource();
|
|
2920
3045
|
log.debug("start() — connecting WebSocket");
|
|
2921
3046
|
this.connectWebSocket();
|
|
2922
3047
|
} catch (err) {
|
|
@@ -2940,15 +3065,27 @@ var StreamConnection = class {
|
|
|
2940
3065
|
if (this.requestedMode.value === mode) return;
|
|
2941
3066
|
this.requestedMode.value = mode;
|
|
2942
3067
|
this.activeMode.value = mode === "auto" ? "webrtc" : mode;
|
|
3068
|
+
if (this.status.value === "unsupported") {
|
|
3069
|
+
this.stop();
|
|
3070
|
+
await this.start();
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
2943
3073
|
if (this.status.value === "connected") await this.restart();
|
|
2944
3074
|
}
|
|
2945
3075
|
async setResolution(resolution) {
|
|
2946
3076
|
if (this.requestedResolution.value === resolution) return;
|
|
2947
3077
|
this.requestedResolution.value = resolution;
|
|
3078
|
+
if (this.status.value === "unsupported") {
|
|
3079
|
+
this.stop();
|
|
3080
|
+
await this.start();
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
2948
3083
|
const result = this.getSourceForResolution(resolution);
|
|
2949
|
-
if (result && result.source._id !== this.source.value?._id) {
|
|
3084
|
+
if (result && result !== "unsupported" && result.source._id !== this.source.value?._id) {
|
|
2950
3085
|
this.source.value = result.source;
|
|
2951
3086
|
this.activeResolution.value = result.effectiveResolution;
|
|
3087
|
+
this.degradedFrom.value = result.degradedFrom;
|
|
3088
|
+
this.degradeReason.value = result.degradedFrom ? "codec" : null;
|
|
2952
3089
|
if (this.status.value === "connected") await this.restart();
|
|
2953
3090
|
}
|
|
2954
3091
|
}
|
|
@@ -3023,7 +3160,12 @@ var StreamConnection = class {
|
|
|
3023
3160
|
return;
|
|
3024
3161
|
}
|
|
3025
3162
|
try {
|
|
3026
|
-
|
|
3163
|
+
const init = this.initializeSource();
|
|
3164
|
+
if (init === "unsupported") {
|
|
3165
|
+
this.setUnsupported();
|
|
3166
|
+
return;
|
|
3167
|
+
}
|
|
3168
|
+
if (!init) {
|
|
3027
3169
|
this.error.value = /* @__PURE__ */ new Error("No streaming source available");
|
|
3028
3170
|
this.status.value = "error";
|
|
3029
3171
|
return;
|
|
@@ -3057,6 +3199,12 @@ var StreamConnection = class {
|
|
|
3057
3199
|
this.restart();
|
|
3058
3200
|
});
|
|
3059
3201
|
this.stopWatchers.push(stopAuthWatch);
|
|
3202
|
+
const stopSourcesWatch = watch(() => this.camera.value?.sources.value, () => {
|
|
3203
|
+
if (this.status.value !== "unsupported") return;
|
|
3204
|
+
this.stop();
|
|
3205
|
+
this.start();
|
|
3206
|
+
});
|
|
3207
|
+
this.stopWatchers.push(stopSourcesWatch);
|
|
3060
3208
|
const stopModeWatch = watch(() => toValue(this.options.mode), (newMode) => {
|
|
3061
3209
|
if (newMode !== void 0 && newMode !== this.requestedMode.value) this.setMode(newMode);
|
|
3062
3210
|
});
|
|
@@ -3107,6 +3255,9 @@ var StreamConnection = class {
|
|
|
3107
3255
|
if (newCamera !== oldCamera) {
|
|
3108
3256
|
if (this.status.value === "connected") {
|
|
3109
3257
|
if (newCamera && newVideo && newClient) this.restart();
|
|
3258
|
+
} else if (this.status.value === "unsupported" && newCamera && newVideo && newClient) {
|
|
3259
|
+
this.stop();
|
|
3260
|
+
this.start();
|
|
3110
3261
|
}
|
|
3111
3262
|
} else if (newVideo !== oldVideo && newVideo) {
|
|
3112
3263
|
if (newVideo.srcObject instanceof MediaStream) {
|
|
@@ -3144,6 +3295,14 @@ var StreamConnection = class {
|
|
|
3144
3295
|
});
|
|
3145
3296
|
this.stopWatchers.push(stopCameraWatch);
|
|
3146
3297
|
}
|
|
3298
|
+
isSourcePlayable(source) {
|
|
3299
|
+
if (this.triedSourceIds.has(source._id)) return false;
|
|
3300
|
+
if (source.videoCodec !== "H265") return true;
|
|
3301
|
+
const mode = this.requestedMode.value;
|
|
3302
|
+
if (mode === "auto") return canPlayH265("auto");
|
|
3303
|
+
if (mode === "mse") return canPlayH265("mse");
|
|
3304
|
+
return canPlayH265("webrtc");
|
|
3305
|
+
}
|
|
3147
3306
|
getSourceForResolution(resolution) {
|
|
3148
3307
|
const cam = this.camera.value;
|
|
3149
3308
|
if (!cam) return void 0;
|
|
@@ -3152,43 +3311,89 @@ var StreamConnection = class {
|
|
|
3152
3311
|
"mid-resolution",
|
|
3153
3312
|
"low-resolution"
|
|
3154
3313
|
];
|
|
3155
|
-
const
|
|
3156
|
-
|
|
3157
|
-
source: exactSource,
|
|
3158
|
-
effectiveResolution: resolution
|
|
3159
|
-
};
|
|
3160
|
-
const startIndex = resolutionOrder.indexOf(resolution);
|
|
3314
|
+
const startIndex = Math.max(0, resolutionOrder.indexOf(resolution));
|
|
3315
|
+
let skippedUnplayable = false;
|
|
3161
3316
|
for (let i = startIndex; i < resolutionOrder.length; i++) {
|
|
3162
3317
|
const src = cam.sources.value.find((s) => s.role === resolutionOrder[i]);
|
|
3163
|
-
if (src)
|
|
3318
|
+
if (!src) continue;
|
|
3319
|
+
if (!this.isSourcePlayable(src)) {
|
|
3320
|
+
skippedUnplayable = true;
|
|
3321
|
+
continue;
|
|
3322
|
+
}
|
|
3323
|
+
return {
|
|
3164
3324
|
source: src,
|
|
3165
|
-
effectiveResolution: resolutionOrder[i]
|
|
3325
|
+
effectiveResolution: resolutionOrder[i],
|
|
3326
|
+
degradedFrom: skippedUnplayable ? resolution : null
|
|
3166
3327
|
};
|
|
3167
3328
|
}
|
|
3168
3329
|
const fallback = cam.streamSource.value;
|
|
3169
|
-
if (fallback) return {
|
|
3330
|
+
if (fallback && this.isSourcePlayable(fallback)) return {
|
|
3170
3331
|
source: fallback,
|
|
3171
|
-
effectiveResolution: fallback.role ?? "low-resolution"
|
|
3332
|
+
effectiveResolution: fallback.role ?? "low-resolution",
|
|
3333
|
+
degradedFrom: skippedUnplayable ? resolution : null
|
|
3172
3334
|
};
|
|
3335
|
+
if (skippedUnplayable || fallback) return "unsupported";
|
|
3173
3336
|
}
|
|
3174
3337
|
initializeSource() {
|
|
3175
3338
|
const result = this.getSourceForResolution(this.requestedResolution.value);
|
|
3176
3339
|
if (!result) return false;
|
|
3340
|
+
if (result === "unsupported") return "unsupported";
|
|
3177
3341
|
this.source.value = result.source;
|
|
3178
3342
|
this.activeResolution.value = result.effectiveResolution;
|
|
3343
|
+
this.degradedFrom.value = result.degradedFrom;
|
|
3344
|
+
this.degradeReason.value = result.degradedFrom ? "codec" : null;
|
|
3345
|
+
if (this.probedSourceId && this.probedSourceId !== result.source._id) {
|
|
3346
|
+
this.probeInfo.value = void 0;
|
|
3347
|
+
this.probedSourceId = void 0;
|
|
3348
|
+
this.hasVideo.value = false;
|
|
3349
|
+
this.hasAudio.value = false;
|
|
3350
|
+
this.hasBackchannel.value = false;
|
|
3351
|
+
}
|
|
3179
3352
|
return true;
|
|
3180
3353
|
}
|
|
3354
|
+
setUnsupported() {
|
|
3355
|
+
const codec = this.camera.value?.sources.value.find((s) => s.videoCodec)?.videoCodec ?? "H265";
|
|
3356
|
+
this.error.value = /* @__PURE__ */ new Error(`${codec} cannot be played on this device`);
|
|
3357
|
+
this.status.value = "unsupported";
|
|
3358
|
+
}
|
|
3359
|
+
probeSaysUnplayable(probe) {
|
|
3360
|
+
const video = probe.video.filter((v) => v.direction === "sendonly");
|
|
3361
|
+
if (!video.length) return false;
|
|
3362
|
+
if (!video.every((v) => v.codec === "H265")) return false;
|
|
3363
|
+
const mode = this.requestedMode.value;
|
|
3364
|
+
return !canPlayH265(mode === "auto" ? "auto" : mode === "mse" ? "mse" : "webrtc");
|
|
3365
|
+
}
|
|
3366
|
+
sourceLikelyH265() {
|
|
3367
|
+
const src = this.source.value;
|
|
3368
|
+
if (!src) return false;
|
|
3369
|
+
if (src.videoCodec === "H265") return true;
|
|
3370
|
+
if (src.videoCodec !== void 0) return false;
|
|
3371
|
+
const video = this.probeInfo.value?.video.filter((v) => v.direction === "sendonly") ?? [];
|
|
3372
|
+
return video.length > 0 && video.every((v) => v.codec === "H265");
|
|
3373
|
+
}
|
|
3374
|
+
markSourceUndecodable() {
|
|
3375
|
+
const src = this.source.value;
|
|
3376
|
+
if (!src || !this.sourceLikelyH265()) return;
|
|
3377
|
+
this.triedSourceIds.add(src._id);
|
|
3378
|
+
}
|
|
3379
|
+
handleNoFramesWhileConnected() {
|
|
3380
|
+
reportH265DecodeFailure(this.activeMode.value === "mse" ? "mse" : "webrtc");
|
|
3381
|
+
this.markSourceUndecodable();
|
|
3382
|
+
this.restart();
|
|
3383
|
+
}
|
|
3181
3384
|
async probeStream() {
|
|
3182
3385
|
const cam = this.camera.value;
|
|
3183
|
-
|
|
3386
|
+
const source = this.source.value;
|
|
3387
|
+
if (!cam || !source || this.abortController.signal.aborted) return void 0;
|
|
3184
3388
|
try {
|
|
3185
|
-
const probe = await cam.probeStream(
|
|
3389
|
+
const probe = await cam.probeStream(source._id, {
|
|
3186
3390
|
video: true,
|
|
3187
3391
|
audio: ["pcma", "opus"],
|
|
3188
3392
|
microphone: true
|
|
3189
3393
|
});
|
|
3190
3394
|
if (probe && !this.abortController.signal.aborted) {
|
|
3191
3395
|
this.probeInfo.value = probe;
|
|
3396
|
+
this.probedSourceId = source._id;
|
|
3192
3397
|
this.hasBackchannel.value = probe.audio.some((a) => a.direction === "recvonly");
|
|
3193
3398
|
this.hasAudio.value = probe.audio.filter((a) => a.direction === "sendonly").length > 0;
|
|
3194
3399
|
this.hasVideo.value = probe.video.filter((v) => v.direction === "sendonly").length > 0;
|
|
@@ -3262,12 +3467,10 @@ var StreamConnection = class {
|
|
|
3262
3467
|
case "mse":
|
|
3263
3468
|
if (this.mseHandler && typeof msg.value === "string") this.mseHandler.initializeBuffer(msg.value);
|
|
3264
3469
|
break;
|
|
3265
|
-
case "error":
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
}
|
|
3270
|
-
break;
|
|
3470
|
+
case "error": if (this.requestedMode.value !== "auto") {
|
|
3471
|
+
this.error.value = new Error(msg.value);
|
|
3472
|
+
this.status.value = "error";
|
|
3473
|
+
}
|
|
3271
3474
|
}
|
|
3272
3475
|
}
|
|
3273
3476
|
async startWebRTC(mode) {
|
|
@@ -3294,7 +3497,6 @@ var StreamConnection = class {
|
|
|
3294
3497
|
if (this.abortController.signal.aborted) return;
|
|
3295
3498
|
const video = this.videoElement.value;
|
|
3296
3499
|
if (!video) return;
|
|
3297
|
-
this.stopConnectTimeout();
|
|
3298
3500
|
this.activeMode.value = this.requestedMode.value === "auto" ? "webrtc" : this.requestedMode.value;
|
|
3299
3501
|
this.lastMediaStream = stream;
|
|
3300
3502
|
video.srcObject = stream;
|
|
@@ -3393,7 +3595,10 @@ var StreamConnection = class {
|
|
|
3393
3595
|
this.startWebRTC("webrtc");
|
|
3394
3596
|
}
|
|
3395
3597
|
handleFirstFrame() {
|
|
3396
|
-
if (!this.isPlaying.value && !this.abortController.signal.aborted)
|
|
3598
|
+
if (!this.isPlaying.value && !this.abortController.signal.aborted) {
|
|
3599
|
+
this.stopConnectTimeout();
|
|
3600
|
+
this.isPlaying.value = true;
|
|
3601
|
+
}
|
|
3397
3602
|
}
|
|
3398
3603
|
monitorMseBuffer() {
|
|
3399
3604
|
const video = this.videoElement.value;
|
|
@@ -3800,11 +4005,13 @@ function useCameraStream(options) {
|
|
|
3800
4005
|
const isPlaying = computed(() => currentStream.value?.isPlaying.value ?? false);
|
|
3801
4006
|
const activeMode = computed(() => currentStream.value?.activeMode.value ?? "webrtc");
|
|
3802
4007
|
const activeResolution = computed(() => currentStream.value?.activeResolution.value ?? "low-resolution");
|
|
4008
|
+
const degradedFrom = computed(() => currentStream.value?.degradedFrom.value ?? null);
|
|
4009
|
+
const unsupported = computed(() => status.value === "unsupported");
|
|
3803
4010
|
const hasAudio = computed(() => currentStream.value?.hasAudio.value ?? false);
|
|
3804
4011
|
const hasBackchannel = computed(() => currentStream.value?.hasBackchannel.value ?? false);
|
|
3805
4012
|
const error = computed(() => currentStream.value?.error.value);
|
|
3806
4013
|
const isReconnecting = computed(() => status.value === "reconnecting");
|
|
3807
|
-
const isBusy = computed(() => cameraDeviceLoading.value || !isPlaying.value && status.value !== "error");
|
|
4014
|
+
const isBusy = computed(() => cameraDeviceLoading.value || !isPlaying.value && status.value !== "error" && status.value !== "unsupported");
|
|
3808
4015
|
const hasSound = computed(() => hasAudio.value && status.value === "connected");
|
|
3809
4016
|
const hasIntercom = computed(() => Boolean(typeof navigator !== "undefined" && navigator.mediaDevices) && hasBackchannel.value);
|
|
3810
4017
|
const muted = computed(() => currentStream.value?.muted.value ?? true);
|
|
@@ -4196,6 +4403,8 @@ function useCameraStream(options) {
|
|
|
4196
4403
|
isPlaying,
|
|
4197
4404
|
activeMode,
|
|
4198
4405
|
activeResolution,
|
|
4406
|
+
degradedFrom,
|
|
4407
|
+
unsupported,
|
|
4199
4408
|
hasAudio,
|
|
4200
4409
|
hasBackchannel,
|
|
4201
4410
|
error,
|
|
@@ -4441,4 +4650,4 @@ function useTerminal() {
|
|
|
4441
4650
|
};
|
|
4442
4651
|
}
|
|
4443
4652
|
//#endregion
|
|
4444
|
-
export { CAMERA_UI_INJECTION_KEY, NamespaceManager, STREAM_CONFIG, acquireSensorManager, clearCameraCache, clearOAuthCache, clearPluginCache, clearSensorCache, clearSnapshotCache, clearStorageCache, createActivityMode, createCameraUiPlugin, createReactiveCameraDevice, createSensorManager, getSnapshotUrl, isReactiveAudioSensor, isReactiveBatteryInfo, isReactiveClassifierSensor, isReactiveContactSensor, isReactiveDoorbellTrigger, isReactiveFaceSensor, isReactiveGarageControl, isReactiveHumidityInfo, isReactiveLeakSensor, isReactiveLicensePlateSensor, isReactiveLightControl, isReactiveLockControl, isReactiveMotionSensor, isReactiveObjectSensor, isReactiveOccupancySensor, isReactivePTZControl, isReactiveSecuritySystem, isReactiveSirenControl, isReactiveSmokeSensor, isReactiveSwitchControl, isReactiveTemperatureInfo, refreshClientSubscriptions, releaseSensorManager, resetClientState, rpcCall, useAudioSensor, useCameraById, useCameraStorage, useCameraStream, useCameraUi, useClassifierSensors, useCoreManager, useCuiFullscreen, useDeviceManager, useFaceSensor, useLicensePlateSensor, useMotionSensor, useOAuth, useObjectSensor, usePTZControl, usePlugin, usePluginStorage, useRpcCall, useRpcSubscription, useSensorById, useSensorByType, useSensorStorage, useSensors, useSensorsByType, useSnapshot, useTabVisibility, useTerminal, useTopmostFullscreenElement };
|
|
4653
|
+
export { CAMERA_UI_INJECTION_KEY, NamespaceManager, STREAM_CONFIG, acquireSensorManager, clearCameraCache, clearOAuthCache, clearPluginCache, clearSensorCache, clearSnapshotCache, clearStorageCache, createActivityMode, createCameraUiPlugin, createReactiveCameraDevice, createSensorManager, getSnapshotUrl, isReactiveAudioSensor, isReactiveBatteryInfo, isReactiveClassifierSensor, isReactiveContactSensor, isReactiveDoorbellTrigger, isReactiveFaceSensor, isReactiveGarageControl, isReactiveHumidityInfo, isReactiveLeakSensor, isReactiveLicensePlateSensor, isReactiveLightControl, isReactiveLockControl, isReactiveMotionSensor, isReactiveObjectSensor, isReactiveOccupancySensor, isReactivePTZControl, isReactiveSecuritySystem, isReactiveSirenControl, isReactiveSmokeSensor, isReactiveSwitchControl, isReactiveTemperatureInfo, refreshClientSubscriptions, releaseSensorManager, resetClientState, rpcCall, useAllSensors, useAudioSensor, useCameraById, useCameraStorage, useCameraStream, useCameraUi, useClassifierSensors, useCoreManager, useCuiFullscreen, useDeviceManager, useFaceSensor, useLicensePlateSensor, useMotionSensor, useOAuth, useObjectSensor, usePTZControl, usePlugin, usePluginStorage, useRpcCall, useRpcSubscription, useSensorById, useSensorByType, useSensorStorage, useSensors, useSensorsByType, useSnapshot, useTabVisibility, useTerminal, useTopmostFullscreenElement };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camera.ui/browser",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.148",
|
|
4
4
|
"description": "camera.ui browser client",
|
|
5
5
|
"author": "seydx (https://github.com/cameraui/clients)",
|
|
6
6
|
"type": "module",
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@camera.ui/logger": ">=0.0.2",
|
|
33
33
|
"@camera.ui/rpc": ">=1.0.10",
|
|
34
|
-
"@camera.ui/sdk": ">=
|
|
34
|
+
"@camera.ui/sdk": ">=1.2.0",
|
|
35
35
|
"@camera.ui/transport": ">=0.0.1",
|
|
36
|
-
"@vueuse/core": ">=14.
|
|
36
|
+
"@vueuse/core": ">=14.4.0",
|
|
37
37
|
"vue": ">=3.5.40"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@camera.ui/logger": "file:../../packages/logger",
|
|
41
41
|
"@camera.ui/rpc": "^1.0.10",
|
|
42
|
-
"@camera.ui/sdk": "~
|
|
42
|
+
"@camera.ui/sdk": "~1.2.0",
|
|
43
43
|
"@camera.ui/transport": "file:../../packages/transport",
|
|
44
44
|
"@eneris/push-receiver": "^4.3.1",
|
|
45
45
|
"@microsoft/api-extractor": "^7.58.12",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@vue/eslint-config-typescript": "^14.9.0",
|
|
52
52
|
"@vue/language-core": "^3.3.8",
|
|
53
53
|
"@vue/tsconfig": "^0.9.1",
|
|
54
|
-
"@vueuse/core": "^14.
|
|
54
|
+
"@vueuse/core": "^14.4.0",
|
|
55
55
|
"@webgpu/types": "^0.1.71",
|
|
56
56
|
"eslint": "9.39.2",
|
|
57
57
|
"eslint-plugin-vue": "^10.10.0",
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"typescript": "5.9.3",
|
|
62
62
|
"typescript-eslint": "^8.65.0",
|
|
63
63
|
"unplugin-dts": "^1.0.3",
|
|
64
|
-
"updates": "^17.
|
|
65
|
-
"vite": "^8.
|
|
64
|
+
"updates": "^17.20.1",
|
|
65
|
+
"vite": "^8.2.0",
|
|
66
66
|
"vitest": "^4.1.10",
|
|
67
67
|
"vue": "^3.5.40",
|
|
68
68
|
"vue-tsc": "^3.3.8"
|