From d90e11ff68f85fa8d0dab24c149d45fe87a0028e Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Sun, 27 Mar 2022 12:32:39 -0700 Subject: [PATCH] webrtc: consolidate --- plugins/webrtc/README.md | 12 +++++-- plugins/webrtc/package.json | 4 +-- plugins/webrtc/src/main.ts | 70 ++++++++++++++++++++++++++++++------- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/plugins/webrtc/README.md b/plugins/webrtc/README.md index 381747c9c..bf77198f3 100644 --- a/plugins/webrtc/README.md +++ b/plugins/webrtc/README.md @@ -1,3 +1,11 @@ -# WebRTC Sink Plugin for Scrypted +# WebRTC Plugin for Scrypted -This plugin sends video feeds from Scrypted cameras to WebRTC clients. It must be installed and enabled to view cameras in the browser, Chromecast, Google Home, Alexa, and others. WebRTC Sink is a core plugin, and should be enabled on all cameras. +This plugin acts as a gateway for WebRTC clients and cameras. WebRTC is a core plugin, and should be enabled on all cameras. + +## WebRTC Viewer + +This plugin sends video feeds from Scrypted cameras to WebRTC clients such as Scrypted's management console, Chromecast, Google Home, Alexa, and others. + +## WebRTC Cameras + +This plugin imports video feeds from WebRTC cameras into Scrypted. It must be installed and enabled on Ring and Google (Gen 2) Cameras to import their video feeds into Scrypted. diff --git a/plugins/webrtc/package.json b/plugins/webrtc/package.json index bd2a09aca..57cf3e001 100644 --- a/plugins/webrtc/package.json +++ b/plugins/webrtc/package.json @@ -1,5 +1,5 @@ { - "name": "@scrypted/webrtc-sink", + "name": "@scrypted/webrtc", "version": "0.0.1", "scripts": { "prepublishOnly": "NODE_ENV=production scrypted-webpack", @@ -16,7 +16,7 @@ "webrtc" ], "scrypted": { - "name": "WebRTC Sink", + "name": "WebRTC Plugin", "type": "API", "interfaces": [ "MixinProvider" diff --git a/plugins/webrtc/src/main.ts b/plugins/webrtc/src/main.ts index 19f3452d5..14dddc3d5 100644 --- a/plugins/webrtc/src/main.ts +++ b/plugins/webrtc/src/main.ts @@ -1,5 +1,5 @@ import { MediaStreamTrack, RTCIceCandidate, RTCPeerConnection, RTCRtpCodecParameters } from "@koush/werift"; -import { Settings, RTCSignalingChannel, ScryptedDeviceType, ScryptedInterface, VideoCamera, Setting, SettingValue, RTCSessionControl, RTCSignalingClientOptions, RTCSignalingSession, FFMpegInput, ScryptedMimeTypes, RTCAVSignalingSetup, Intercom, RTCSignalingSendIceCandidate } from '@scrypted/sdk'; +import { Settings, RTCSignalingChannel, ScryptedDeviceType, ScryptedInterface, VideoCamera, Setting, SettingValue, RTCSessionControl, RTCSignalingClientOptions, RTCSignalingSession, FFMpegInput, ScryptedMimeTypes, RTCAVSignalingSetup, Intercom, RTCSignalingSendIceCandidate, RequestMediaStreamOptions, MediaObject, MediaStreamOptions } from '@scrypted/sdk'; import sdk from '@scrypted/sdk'; import { AutoenableMixinProvider } from '@scrypted/common/src/autoenable-mixin-provider'; import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; @@ -11,6 +11,7 @@ import { getH264DecoderArgs, getH264EncoderArgs } from '@scrypted/common/src/ffm import { RtspServer } from '@scrypted/common/src/rtsp-server'; import { createSdpInput } from '@scrypted/common/src/sdp-utils'; import child_process, { ChildProcess } from 'child_process'; +import { createRTCPeerConnectionSource, getRTCMediaStreamOptions } from '@scrypted/common/src/wrtc-to-rtsp'; const { mediaManager, systemManager } = sdk; @@ -91,6 +92,13 @@ class ScryptedSignalingSession implements RTCSignalingSession { class WebRTCMixin extends SettingsMixinDeviceBase implements RTCSignalingChannel { storageSettings = new StorageSettings(this, { + useUdp: { + title: 'Use SDP/UDP instead of RTSP/TCP', + description: 'Experimental', + type: 'boolean', + defaultValue: true, + hide: true, + }, addExtraData: { title: 'Add H264 Extra Data', description: 'Some cameras do not include H264 extra data in the stream and this causes live streaming to always fail (but recordings may be working). This is a inexpensive video filter and does not perform a transcode. Enable this setting only as necessary.', @@ -130,7 +138,7 @@ class WebRTCMixin extends SettingsMixinDeviceBase) { @@ -404,9 +412,39 @@ class WebRTCMixin extends SettingsMixinDeviceBase { return this.storageSettings.putSetting(key, value); } + + + createVideoStreamOptions() { + return getRTCMediaStreamOptions('webrtc', 'WebRTC', this.storageSettings.values.useUdp); + } + + async getVideoStream(options?: RequestMediaStreamOptions): Promise { + if (this.mixinDeviceInterfaces.includes(ScryptedInterface.VideoCamera) && options?.id !== 'webrtc') { + return this.mixinDevice.getVideoStream(options); + } + + const ffmpegInput = await createRTCPeerConnectionSource({ + console: this.console, + mediaStreamOptions: this.createVideoStreamOptions(), + channel: this.mixinDevice, + useUdp: this.storageSettings.values.useUdp, + }); + + return mediaManager.createFFmpegMediaObject(ffmpegInput); + } + + async getVideoStreamOptions(): Promise { + let ret: MediaStreamOptions[] = []; + if (this.mixinDeviceInterfaces.includes(ScryptedInterface.VideoCamera)) { + ret = await this.mixinDevice.getVideoStreamOptions(); + } + ret.push(this.createVideoStreamOptions()); + return ret; + } + } -class WebRTCSinkPlugin extends AutoenableMixinProvider { +class WebRTCPlugin extends AutoenableMixinProvider { constructor() { super(); this.on = this.on || false; @@ -415,14 +453,22 @@ class WebRTCSinkPlugin extends AutoenableMixinProvider { if (!supportedTypes.includes(type)) return; - if (!interfaces.includes(ScryptedInterface.VideoCamera)) - return; + // if this is a webrtc camera, also proxy the signaling channel too, + // for inflexible clients. + if (interfaces.includes(ScryptedInterface.RTCSignalingChannel)) { + return [ + ScryptedInterface.RTCSignalingChannel, + ScryptedInterface.VideoCamera, + ScryptedInterface.Settings, + ]; + } - return [ - '@scrypted/webrtc-sink', - ScryptedInterface.RTCSignalingChannel, - ScryptedInterface.Settings, - ]; + if (interfaces.includes(ScryptedInterface.VideoCamera)) { + return [ + ScryptedInterface.RTCSignalingChannel, + ScryptedInterface.Settings, + ]; + } } async getMixin(mixinDevice: any, mixinDeviceInterfaces: ScryptedInterface[], mixinDeviceState: { [key: string]: any; }): Promise { @@ -431,7 +477,7 @@ class WebRTCSinkPlugin extends AutoenableMixinProvider { mixinDeviceInterfaces, mixinDeviceState, group: 'WebRTC', - groupKey: 'webrtc-sink', + groupKey: 'webrtc', mixinProviderNativeId: this.nativeId, }) } @@ -441,4 +487,4 @@ class WebRTCSinkPlugin extends AutoenableMixinProvider { } } -export default new WebRTCSinkPlugin(); +export default new WebRTCPlugin();