rebroadcast: support rtsps in RTSPClient.

google-device-access: fixup interface shenanigans between camera types.
This commit is contained in:
Koushik Dutta
2022-03-02 11:51:10 -08:00
parent a1ab6502d8
commit 56c22eea09
10 changed files with 40 additions and 18 deletions

View File

@@ -4,6 +4,7 @@ import { randomBytes } from 'crypto';
import { StreamChunk, StreamParser } from './stream-parser';
import dgram from 'dgram';
import net from 'net';
import tls from 'tls';
export const RTSP_FRAME_MAGIC = 36;
@@ -103,7 +104,17 @@ export class RtspClient extends RtspBase {
constructor(public url: string) {
super();
const u = new URL(url);
this.client = net.connect(parseInt(u.port) || 554, u.hostname);
const port = parseInt(u.port) || 554;
if (url.startsWith('rtsps')) {
this.client = tls.connect({
rejectUnauthorized: false,
port,
host: u.hostname,
})
}
else {
this.client = net.connect(port, u.hostname);
}
}
async request(method: string, headers?: Headers, path?: string, body?: Buffer) {

View File

@@ -18,8 +18,6 @@ const { deviceManager, mediaManager, endpointManager, systemManager } = sdk;
const refreshFrequency = 60;
const black = fs.readFileSync('unavailable.jpg');
const readmeV1 = fs.readFileSync('README-camera-v1.md').toString();
const readmeV2 = fs.readFileSync('README-camera-v2.md').toString();
@@ -35,10 +33,20 @@ function getSdmRtspMediaStreamOptions(): MediaStreamOptions {
codec: 'aac',
},
source: 'cloud',
tool: 'scrypted',
userConfigurable: false,
};
}
function deviceHasEventImages(device: any) {
return !!device?.traits?.['sdm.devices.traits.CameraEventImage'];
}
function deviceIsWebRtc(device: any) {
return device?.traits?.['sdm.devices.traits.CameraLiveStream']?.supportedProtocols?.includes('WEB_RTC');
}
const RtcMediaStreamOptionsId = 'webrtc';
function getSdmRtcMediaStreamOptions(): MediaStreamOptions {
@@ -198,7 +206,7 @@ class NestCamera extends ScryptedDeviceBase implements Readme, Camera, VideoCame
}
// try to fetch the latest event image if one is queued
const hasEventImages = !!this.device?.traits?.['sdm.devices.traits.CameraEventImage'];
const hasEventImages = deviceHasEventImages(this.device);
if (hasEventImages && this.lastMotionEventId) {
const eventId = this.lastMotionEventId;
this.lastMotionEventId = undefined;
@@ -217,8 +225,7 @@ class NestCamera extends ScryptedDeviceBase implements Readme, Camera, VideoCame
return mediaManager.createMediaObject(data, 'image/jpeg');
}
// send "no snapshot available" image
return mediaManager.createMediaObject(black, 'image/jpeg');
throw new Error('snapshot unavailable');
}
async getPictureOptions(): Promise<PictureOptions[]> {
@@ -248,7 +255,7 @@ class NestCamera extends ScryptedDeviceBase implements Readme, Camera, VideoCame
}
get isWebRtc() {
return this.device?.traits?.['sdm.devices.traits.CameraLiveStream']?.supportedProtocols?.includes('WEB_RTC');
return deviceIsWebRtc(this.device);
}
async getVideoStream(options?: RequestMediaStreamOptions): Promise<MediaObject> {
@@ -763,14 +770,18 @@ class GoogleSmartDeviceAccess extends ScryptedDeviceBase implements OauthClient,
const interfaces = [
ScryptedInterface.BufferConverter,
ScryptedInterface.RTCSignalingChannel,
ScryptedInterface.VideoCamera,
ScryptedInterface.Camera,
ScryptedInterface.MotionSensor,
ScryptedInterface.ObjectDetector,
ScryptedInterface.Readme,
];
if (deviceHasEventImages(device))
interfaces.push(ScryptedInterface.Camera);
if (deviceIsWebRtc(device))
interfaces.push(ScryptedInterface.RTCSignalingChannel);
let type = ScryptedDeviceType.Camera;
if (device.type === 'sdm.devices.types.DOORBELL') {
interfaces.push(ScryptedInterface.BinarySensor);

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.1.171",
"version": "0.1.172",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/prebuffer-mixin",
"version": "0.1.171",
"version": "0.1.172",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.1.171",
"version": "0.1.172",
"description": "Rebroadcast and Prebuffer for VideoCameras.",
"author": "Scrypted",
"license": "Apache-2.0",

View File

@@ -208,7 +208,7 @@ class PrebufferSession {
title: 'Detected Keyframe Interval',
description: "Configuring your camera to 4 seconds is recommended (IDR aka Frame Interval = FPS * 4 seconds).",
readonly: true,
value: ((this.detectedIdrInterval || 0) / 1000).toString() || 'none',
value: (this.detectedIdrInterval || 0) / 1000 || 'unknown',
},
);
}

View File

@@ -1329,7 +1329,7 @@ export interface RTCSignalingClient {
* strict requirements and expectations on client setup.
*/
export interface RTCSignalingChannel {
startRTCSignalingSession(session: RTCSignalingSession, options?: RTCSignalingClientOptions): Promise<RTCSessionControl|void>;
startRTCSignalingSession(session: RTCSignalingSession, options?: RTCSignalingClientOptions): Promise<RTCSessionControl|undefined>;
}
export interface RTCAVSignalingSetup {

View File

@@ -703,7 +703,7 @@ class PushHandler:
pass
class RTCSignalingChannel:
async def startRTCSignalingSession(self, session: RTCSignalingSession, options: RTCSignalingClientOptions = None) -> None | RTCSessionControl:
async def startRTCSignalingSession(self, session: RTCSignalingSession, options: RTCSignalingClientOptions = None) -> RTCSessionControl:
pass
pass

View File

@@ -1364,7 +1364,7 @@ export interface RTCSignalingClient {
* strict requirements and expectations on client setup.
*/
export interface RTCSignalingChannel {
startRTCSignalingSession(session: RTCSignalingSession, options?: RTCSignalingClientOptions): Promise<RTCSessionControl | void>;
startRTCSignalingSession(session: RTCSignalingSession, options?: RTCSignalingClientOptions): Promise<RTCSessionControl | undefined>;
}
export interface RTCAVSignalingSetup {
/**

View File

@@ -1992,7 +1992,7 @@ export interface RTCSignalingClient {
* strict requirements and expectations on client setup.
*/
export interface RTCSignalingChannel {
startRTCSignalingSession(session: RTCSignalingSession, options?: RTCSignalingClientOptions): Promise<RTCSessionControl|void>;
startRTCSignalingSession(session: RTCSignalingSession, options?: RTCSignalingClientOptions): Promise<RTCSessionControl|undefined>;
}
export interface RTCAVSignalingSetup {

View File

@@ -703,7 +703,7 @@ class PushHandler:
pass
class RTCSignalingChannel:
async def startRTCSignalingSession(self, session: RTCSignalingSession, options: RTCSignalingClientOptions = None) -> None | RTCSessionControl:
async def startRTCSignalingSession(self, session: RTCSignalingSession, options: RTCSignalingClientOptions = None) -> RTCSessionControl:
pass
pass