From cd59125ada3e334bcc77f112a728b517ff59ea3e Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 9 Mar 2023 20:24:08 -0800 Subject: [PATCH] eufy: revert --- plugins/eufy/package-lock.json | 14 --- plugins/eufy/package.json | 1 - plugins/eufy/src/main.ts | 164 ++++++++------------------------- 3 files changed, 38 insertions(+), 141 deletions(-) diff --git a/plugins/eufy/package-lock.json b/plugins/eufy/package-lock.json index 8e5d60c08..0b72d543e 100644 --- a/plugins/eufy/package-lock.json +++ b/plugins/eufy/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.1", "dependencies": { "@scrypted/common": "file:../../common", - "@scrypted/h264-repacketizer": "file:../../packages/h264-repacketizer ", "@scrypted/sdk": "file:../../sdk", "@types/node": "^18.14.6" }, @@ -32,15 +31,6 @@ "@types/node": "^16.9.0" } }, - "../../packages/h264-repacketizer": { - "version": "0.0.6", - "license": "ISC", - "devDependencies": { - "@types/node": "^18.11.18", - "rimraf": "^4.1.1", - "typescript": "^4.7.4" - } - }, "../../sdk": { "name": "@scrypted/sdk", "version": "0.2.84", @@ -219,10 +209,6 @@ "resolved": "../../common", "link": true }, - "node_modules/@scrypted/h264-repacketizer": { - "resolved": "../../packages/h264-repacketizer", - "link": true - }, "node_modules/@scrypted/sdk": { "resolved": "../../sdk", "link": true diff --git a/plugins/eufy/package.json b/plugins/eufy/package.json index 965606cea..c3e078f64 100644 --- a/plugins/eufy/package.json +++ b/plugins/eufy/package.json @@ -31,7 +31,6 @@ "dependencies": { "@scrypted/sdk": "file:../../sdk", "@scrypted/common": "file:../../common", - "@scrypted/h264-repacketizer": "file:../../packages/h264-repacketizer ", "@types/node": "^18.14.6" }, "optionalDependencies": { diff --git a/plugins/eufy/src/main.ts b/plugins/eufy/src/main.ts index c50006830..85fddf0d5 100644 --- a/plugins/eufy/src/main.ts +++ b/plugins/eufy/src/main.ts @@ -1,58 +1,22 @@ -import { getNaluTypesInNalu, listenSingleRtspClient } from '@scrypted/common/src/rtsp-server'; -import { addTrackControls, parseSdp } from '@scrypted/common/src/sdp-utils'; -import { H264Repacketizer, NAL_TYPE_IDR, NAL_TYPE_NON_IDR, splitH264NaluStartCode } from '@scrypted/h264-repacketizer/src/index'; -import sdk, { Battery, Camera, Device, DeviceProvider, MediaObject, MediaStreamUrl, RequestPictureOptions, ResponseMediaStreamOptions, ResponsePictureOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; +import sdk, { Battery, Camera, Device, DeviceProvider, FFmpegInput, MediaObject, RequestPictureOptions, ResponseMediaStreamOptions, ResponsePictureOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; import { StorageSettings } from '@scrypted/sdk/storage-settings'; import eufy, { EufySecurity } from 'eufy-security-client'; -import { RtpHeader, RtpPacket } from '../../../external/werift/packages/rtp/src/rtp/rtp'; import { LocalLivestreamManager } from './stream'; +import { listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; const { deviceManager, mediaManager } = sdk; -let sdp: string; -if (false) { - sdp = `v=0 - o=- 0 0 IN IP4 127.0.0.1 - t=0 0 - m=video 0 RTP/AVP 96 - c=IN IP4 0.0.0.0 - a=recvonly - a=rtpmap:96 H264/90000 - m=audio 0 RTP/AVP 97 - c=IN IP4 0.0.0.0 - a=recvonly - a=rtpmap:97 mpeg4-generic/16000/1 - a=fmtp:104 profile-level-id=15; streamtype=5; mode=AAC-hbr; config=1408;SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1; - `; - -} -else { - - sdp = `v=0 - o=- 0 0 IN IP4 127.0.0.1 - t=0 0 - m=video 0 RTP/AVP 96 - c=IN IP4 0.0.0.0 - a=recvonly - a=rtpmap:96 H264/90000 - `; -} - - -sdp = addTrackControls(sdp); -const parsedSdp = parseSdp(sdp); -const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video'); -const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio'); - class EufyCamera extends ScryptedDeviceBase implements Camera, VideoCamera, Battery { client: EufySecurity; device: eufy.Camera; + livestreamManager: LocalLivestreamManager constructor(nativeId: string, client: EufySecurity, device: eufy.Camera) { super(nativeId); this.client = client; this.device = device; - + this.livestreamManager = new LocalLivestreamManager(this.client, this.device, false, this.console); + // this.batteryLevel = this.device.getBatteryValue() as number; } @@ -68,94 +32,42 @@ class EufyCamera extends ScryptedDeviceBase implements Camera, VideoCamera, Batt getVideoStream(options?: ResponseMediaStreamOptions): Promise { return this.createVideoStream(options); } - + async getVideoStreamOptions(): Promise { return [ { id: 'p2p', name: 'P2P', video: { - codec: 'h264', - }, - audio: { - codec: 'aac', + codec: 'h264', }, + source: 'cloud', + tool: 'ffmpeg', userConfigurable: false, - tool: 'scrypted', } ]; } async createVideoStream(options?: ResponseMediaStreamOptions): Promise { - const rtspServer = await listenSingleRtspClient(); - rtspServer.rtspServerPromise.then(async rtsp => { - rtsp.sdp = sdp; - await rtsp.handlePlayback(); - - const h264Packetizer = new H264Repacketizer(this.console, 65535, undefined); - let videoSequenceNumber = 1; - let audioSequenceNumber = 1; - const firstTimestamp = Date.now(); - let lastVideoTimestamp = firstTimestamp; - let lastAudioTimestamp = firstTimestamp; - try { - const livestreamManager = new LocalLivestreamManager(this.client, this.device, false, this.console); - const proxyStream = await livestreamManager.getLocalLivestream(); - proxyStream.videostream.on('close', () => rtsp.client.destroy()); - rtsp.client.on('close', () => livestreamManager.stopLocalLiveStream()); - proxyStream.videostream.on('readable', () => { - const allData: Buffer = proxyStream.videostream.read(); - const splits = splitH264NaluStartCode(allData); - if (!splits.length) - throw new Error('expected nalu start code'); - - for (const nalu of splits) { - const timestamp = Math.floor(((lastVideoTimestamp - firstTimestamp) / 1000) * 90000); - const naluTypes = getNaluTypesInNalu(nalu); - const header = new RtpHeader({ - sequenceNumber: videoSequenceNumber++, - timestamp: timestamp, - payloadType: 96, - }); - const rtp = new RtpPacket(header, nalu); - - const packets = h264Packetizer.repacketize(rtp); - for (const packet of packets) { - rtsp.sendTrack(videoTrack.control, packet.serialize(), false); - } - - if (naluTypes.has(NAL_TYPE_NON_IDR) || naluTypes.has(NAL_TYPE_IDR)) { - lastVideoTimestamp = Date.now(); - } - } - }); - - if (audioTrack) { - proxyStream.audiostream.on('readable', () => { - const allData: Buffer = proxyStream.audiostream.read(); - const timestamp = Math.floor(((lastAudioTimestamp - firstTimestamp) / 1000) * 16000); - const header = new RtpHeader({ - sequenceNumber: audioSequenceNumber++, - timestamp: timestamp, - payloadType: 97, - }); - const rtp = new RtpPacket(header, allData); - rtsp.sendTrack(audioTrack.control, rtp.serialize(), false); - lastAudioTimestamp = Date.now(); - }); - } - } - catch (e) { - rtsp.client.destroy(); - } + const tcp = await listenZeroSingleClient(); + const proxyStream = await this.livestreamManager.getLocalLivestream(); + tcp.clientPromise.then(socket => { + proxyStream.videostream.pipe(socket); }); - const input: MediaStreamUrl = { - url: rtspServer.url, - mediaStreamOptions: options, + + const input: FFmpegInput = { + url: undefined, + inputArguments:[ + '-f', + 'h264', + '-i', + tcp.url + ], + mediaStreamOptions: options, }; - return mediaManager.createMediaObject(input, ScryptedMimeTypes.MediaStreamUrl); + return mediaManager.createFFmpegMediaObject(input); } } @@ -186,18 +98,18 @@ class EufyPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings noStore: true, }, }); - + constructor() { super(); this.tryLogin() } getSettings(): Promise { - return this.storageSettings.getSettings(); + return this.storageSettings.getSettings(); } putSetting(key: string, value: SettingValue): Promise { - return this.storageSettings.putSetting(key, value); + return this.storageSettings.putSetting(key, value); } async tryLogin(twoFactorCode?: string) { @@ -211,7 +123,7 @@ class EufyPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings await this.initializeClient(); try { - await this.client.connect({ verifyCode: twoFactorCode, force: false }); + await this.client.connect({verifyCode: twoFactorCode, force: false}); this.console.debug(`[${this.name}] (${new Date().toLocaleString()}) Client connected.`); } catch (e) { this.log.a('Login failed: if you have 2FA enabled, check your email or texts for your code, then enter it into the Two Factor Code setting to conplete login.'); @@ -247,22 +159,22 @@ class EufyPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings return; } this.console.info(`[${this.name}] (${new Date().toLocaleString()}) Device discovered: `, eufyDevice.getName(), eufyDevice.getModel()); - + const nativeId = eufyDevice.getSerial(); - + const interfaces = [ - ScryptedInterface.Camera, - ScryptedInterface.VideoCamera + ScryptedInterface.Camera, + ScryptedInterface.VideoCamera ]; if (eufyDevice.hasBattery()) - interfaces.push(ScryptedInterface.Battery); - + interfaces.push(ScryptedInterface.Battery); + const device: Device = { info: { - model: eufyDevice.getModel(), - manufacturer: 'Eufy', - firmware: eufyDevice.getSoftwareVersion(), - serialNumber: nativeId + model: eufyDevice.getModel(), + manufacturer: 'Eufy', + firmware: eufyDevice.getSoftwareVersion(), + serialNumber: nativeId }, nativeId, name: eufyDevice.getName(),