webrtc: update werift, datachannel connectRPCObject, publish

This commit is contained in:
Koushik Dutta
2025-08-31 21:14:42 -07:00
parent 9de2b480ff
commit ec49e4630f
9 changed files with 56 additions and 37 deletions

View File

@@ -15,14 +15,14 @@
"typescript": "^5.5.3"
},
"devDependencies": {
"@types/node": "^20.11.0",
"@types/node": "^20.19.11",
"monaco-editor": "^0.50.0",
"ts-node": "^10.9.2"
}
},
"../sdk": {
"name": "@scrypted/sdk",
"version": "0.5.29",
"version": "0.5.39",
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.27.1",
@@ -3340,11 +3340,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.11.0",
"version": "20.19.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz",
"integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
"undici-types": "~6.21.0"
}
},
"node_modules/acorn": {
@@ -3479,7 +3481,9 @@
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},

View File

@@ -17,7 +17,7 @@
"typescript": "^5.5.3"
},
"devDependencies": {
"@types/node": "^20.11.0",
"@types/node": "^20.19.11",
"monaco-editor": "^0.50.0",
"ts-node": "^10.9.2"
}

View File

@@ -9,6 +9,16 @@ export function createAsyncQueue<T>() {
const waiting: Deferred<T>[] = [];
const queued: { item: T, dequeued?: Deferred<void> }[] = [];
const wait = async (index: number) => {
const q = queued[index];
if (!q)
return;
if (!q.dequeued) {
q.dequeued = new Deferred<void>();
}
return q.dequeued.promise;
}
const dequeue = async () => {
if (queued.length) {
const { item, dequeued: enqueue } = queued.shift()!;
@@ -66,7 +76,7 @@ export function createAsyncQueue<T>() {
dequeued?.reject(new Error('abort'));
};
dequeued?.promise.catch(() => {}).finally(() => signal.removeEventListener('abort', h));
dequeued?.promise.catch(() => { }).finally(() => signal.removeEventListener('abort', h));
signal.addEventListener('abort', h);
return true;
@@ -154,13 +164,14 @@ export function createAsyncQueue<T>() {
dequeue,
get queue() {
return queue();
}
},
wait,
}
}
export function createAsyncQueueFromGenerator<T>(generator: AsyncGenerator<T>) {
const q = createAsyncQueue<T>();
(async() => {
(async () => {
try {
for await (const i of generator) {
await q.enqueue(i);

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/webrtc",
"version": "0.2.82",
"version": "0.2.83",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/webrtc",
"version": "0.2.82",
"version": "0.2.83",
"dependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/webrtc",
"version": "0.2.82",
"version": "0.2.83",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",

View File

@@ -1,21 +1,18 @@
import { MediaStreamTrack, PeerConfig, RTCDataChannel, RTCPeerConnection, RTCRtpTransceiver, RtpPacket } from "./werift";
import { Deferred } from "@scrypted/common/src/deferred";
import sdk, { FFmpegInput, FFmpegTranscodeStream, Intercom, MediaObject, MediaStreamDestination, MediaStreamFeedback, RequestMediaStream, RTCAVSignalingSetup, RTCConnectionManagement, RTCGeneratorDataChannel, RTCInputMediaObjectTrack, RTCOutputMediaObjectTrack, RTCSignalingOptions, RTCSignalingSession, ScryptedInterface, ScryptedMimeTypes } from "@scrypted/sdk";
import { ScryptedSessionControl } from "./session-control";
import { optionalVideoCodec, opusAudioCodecOnly, requiredAudioCodecs, requiredVideoCodec } from "./webrtc-required-codecs";
import { logIsLocalIceTransport } from "./werift-util";
import { addVideoFilterArguments } from "@scrypted/common/src/ffmpeg-helpers";
import { connectRTCSignalingClients, legacyGetSignalingSessionOptions } from "@scrypted/common/src/rtc-signaling";
import { getSpsPps, getSpsPpsVps, MSection } from "@scrypted/common/src/sdp-utils";
import sdk, { FFmpegInput, FFmpegTranscodeStream, Intercom, MediaObject, MediaStreamDestination, MediaStreamFeedback, RequestMediaStream, RTCAVSignalingSetup, RTCConnectionManagement, RTCInputMediaObjectTrack, RTCOutputMediaObjectTrack, RTCSignalingOptions, RTCSignalingSession, ScryptedInterface, ScryptedMimeTypes } from "@scrypted/sdk";
import { H264Repacketizer } from "../../homekit/src/types/camera/h264-packetizer";
import { OpusRepacketizer } from "../../homekit/src/types/camera/opus-repacketizer";
import { H265Repacketizer } from "./h265-packetizer";
import { logConnectionState, waitClosed, waitConnected, waitIceConnected } from "./peerconnection-util";
import { RtpCodecCopy, RtpTrack, RtpTracks, startRtpForwarderProcess } from "./rtp-forwarders";
import { getAudioCodec, getFFmpegRtpAudioOutputArguments } from "./webrtc-required-codecs";
import { ScryptedSessionControl } from "./session-control";
import { getAudioCodec, getFFmpegRtpAudioOutputArguments, optionalVideoCodec, opusAudioCodecOnly, requiredAudioCodecs, requiredVideoCodec } from "./webrtc-required-codecs";
import { MediaStreamTrack, PeerConfig, RTCPeerConnection, RTCRtpTransceiver, RtpPacket } from "./werift";
import { WeriftSignalingSession } from "./werift-signaling-session";
import { logIsLocalIceTransport } from "./werift-util";
function getDebugModeH264EncoderArgs() {
return [

View File

@@ -4,7 +4,7 @@ import { timeoutPromise } from '@scrypted/common/src/promise-utils';
import { legacyGetSignalingSessionOptions } from '@scrypted/common/src/rtc-signaling';
import { SettingsMixinDeviceBase, SettingsMixinDeviceOptions } from '@scrypted/common/src/settings-mixin';
import { createZygote } from '@scrypted/common/src/zygote';
import sdk, { DeviceCreator, DeviceCreatorSettings, DeviceProvider, FFmpegInput, ForkWorker, Intercom, MediaConverter, MediaObject, MediaObjectOptions, MixinProvider, RTCSessionControl, RTCSignalingChannel, RTCSignalingClient, RTCSignalingOptions, RTCSignalingSession, RequestMediaStream, RequestMediaStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, SettingValue, Settings, VideoCamera, WritableDeviceState } from '@scrypted/sdk';
import sdk, { DeviceCreator, DeviceCreatorSettings, DeviceProvider, FFmpegInput, ForkWorker, Intercom, MediaConverter, MediaObject, MediaObjectOptions, MixinProvider, PluginFork, RTCSessionControl, RTCSignalingChannel, RTCSignalingClient, RTCSignalingOptions, RTCSignalingSession, RequestMediaStream, RequestMediaStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, SettingValue, Settings, VideoCamera, WritableDeviceState } from '@scrypted/sdk';
import { StorageSettings } from '@scrypted/sdk/storage-settings';
import { RpcPeer } from '@scrypted/server/src/rpc';
import crypto from 'crypto';
@@ -27,9 +27,12 @@ defaultPeerConfig.headerExtensions = {
audio: [],
};
function delayWorkerExit(worker: ForkWorker) {
function delayWorkerExit(f: PluginFork<ReturnType<typeof fork>>) {
setTimeout(() => {
worker.terminate();
f.result.then(r => r.then(rr => rr.exit())).catch(() => { });
}, 1000);
setTimeout(() => {
f.worker.terminate();
}, 10000);
}
@@ -175,13 +178,13 @@ class WebRTCMixin extends SettingsMixinDeviceBase<RTCSignalingClient & VideoCame
const pcc = pcClose();
pcc.finally(() => {
this.webrtcIntercom = undefined;
delayWorkerExit(result.worker);
delayWorkerExit(result);
});
return mediaObject;
}
catch (e) {
delayWorkerExit(result.worker);
delayWorkerExit(result);
throw e;
}
}
@@ -375,7 +378,7 @@ export class WebRTCPlugin extends AutoenableMixinProvider implements DeviceCreat
);
}
catch (e) {
delayWorkerExit(result.worker);
delayWorkerExit(result);
throw e;
}
await connection.negotiateRTCSignalingSession();
@@ -404,7 +407,7 @@ export class WebRTCPlugin extends AutoenableMixinProvider implements DeviceCreat
const mediaStreamUrl = rtcSource.mediaObject;
return await mediaManager.convertMediaObjectToJSON<FFmpegInput>(mediaStreamUrl, ScryptedMimeTypes.FFmpegInput);
} catch (e) {
delayWorkerExit(result.worker);
delayWorkerExit(result);
throw e;
}
}
@@ -424,11 +427,11 @@ export class WebRTCPlugin extends AutoenableMixinProvider implements DeviceCreat
try {
const connection = await timeoutPromise(2 * 60 * 1000, this.convertToRTCConnectionManagement(result, data, fromMimeType, toMimeType, options));
// wait a bit to allow ffmpegs to get terminated by the thread.
connection.waitClosed().finally(() => delayWorkerExit(result.worker));
connection.waitClosed().finally(() => delayWorkerExit(result));
return connection;
}
catch (e) {
delayWorkerExit(result.worker);
delayWorkerExit(result);
throw e;
}
}
@@ -438,7 +441,7 @@ export class WebRTCPlugin extends AutoenableMixinProvider implements DeviceCreat
return await timeoutPromise(2 * 60 * 1000, this.convertToFFmpegInput(result, data, fromMimeType, toMimeType, options));
}
catch (e) {
delayWorkerExit(result.worker);
delayWorkerExit(result);
throw e;
}
}
@@ -699,8 +702,12 @@ async function createConnection(
return connection;
}
export async function fork() {
return {
exit() {
delayProcessExit();
},
async createRTCPeerConnectionSource(options: {
__json_copy_serialize_children: true,
mixinId: string,
@@ -710,13 +717,15 @@ export async function fork() {
maximumCompatibilityMode: boolean,
}): Promise<RTCPeerConnectionPipe> {
try {
return await createRTCPeerConnectionSource({
const ret = await createRTCPeerConnectionSource({
nativeId: this.nativeId,
mixinId: options.mixinId,
mediaStreamOptions: options.mediaStreamOptions,
startRTCSignalingSession: (session) => options.startRTCSignalingSession(session),
maximumCompatibilityMode: options.maximumCompatibilityMode,
});
ret.pcClose().finally(() => delayProcessExit());
return ret;
}
catch (e) {
delayProcessExit();
@@ -736,7 +745,9 @@ export async function fork() {
ipv4Ban?: string[];
}) {
try {
return await createConnection(clientSession, requireOpus, maximumCompatibilityMode, clientOptions, options);
const ret = await createConnection(clientSession, requireOpus, maximumCompatibilityMode, clientOptions, options);
ret.waitClosed().finally(() => delayProcessExit());
return ret;
}
catch (e) {
delayProcessExit();

View File

@@ -367,10 +367,6 @@ export async function createRTCPeerConnectionSource(options: {
};
}
interface ReceivedRtpPacket extends RtpPacket {
uptime?: number;
}
export function getRTCMediaStreamOptions(id: string, name: string): ResponseMediaStreamOptions {
return {
// set by consumer