@apocaliss92/nodelink-js 0.2.3 → 0.2.5
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/README.md +6 -2
- package/dist/{DiagnosticsTools-FNLGCOVA.js → DiagnosticsTools-2JQRV5FE.js} +2 -2
- package/dist/{chunk-NLTB7GTA.js → chunk-APEEZ4UN.js} +10 -9
- package/dist/{chunk-NLTB7GTA.js.map → chunk-APEEZ4UN.js.map} +1 -1
- package/dist/{chunk-RWYEGEWG.js → chunk-EG5IY3CM.js} +77 -9
- package/dist/chunk-EG5IY3CM.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +82 -13
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +144 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +64 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-RWYEGEWG.js.map +0 -1
- /package/dist/{DiagnosticsTools-FNLGCOVA.js.map → DiagnosticsTools-2JQRV5FE.js.map} +0 -0
package/dist/cli/rtsp-server.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
BaichuanRtspServer,
|
|
4
4
|
ReolinkBaichuanApi,
|
|
5
5
|
autoDetectDeviceType
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-EG5IY3CM.js";
|
|
7
7
|
import {
|
|
8
8
|
__require
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-APEEZ4UN.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/rtsp-server.ts
|
|
12
12
|
function parseArgs() {
|
package/dist/index.cjs
CHANGED
|
@@ -3000,14 +3000,6 @@ var init_BaichuanVideoStream = __esm({
|
|
|
3000
3000
|
this.profile,
|
|
3001
3001
|
{ variant: this.variant, client: this.client }
|
|
3002
3002
|
);
|
|
3003
|
-
if (this.client.getTransport?.() === "udp") {
|
|
3004
|
-
await startPromise;
|
|
3005
|
-
} else {
|
|
3006
|
-
await Promise.race([
|
|
3007
|
-
startPromise,
|
|
3008
|
-
new Promise((resolve) => setTimeout(resolve, 400))
|
|
3009
|
-
]);
|
|
3010
|
-
}
|
|
3011
3003
|
const updateActiveMsgNum = () => {
|
|
3012
3004
|
try {
|
|
3013
3005
|
const getMsgNum = this.api.getActiveVideoMsgNumWithVariant;
|
|
@@ -3017,6 +3009,15 @@ var init_BaichuanVideoStream = __esm({
|
|
|
3017
3009
|
}
|
|
3018
3010
|
};
|
|
3019
3011
|
updateActiveMsgNum();
|
|
3012
|
+
if (this.client.getTransport?.() === "udp") {
|
|
3013
|
+
await startPromise;
|
|
3014
|
+
} else {
|
|
3015
|
+
await Promise.race([
|
|
3016
|
+
startPromise,
|
|
3017
|
+
new Promise((resolve) => setTimeout(resolve, 400))
|
|
3018
|
+
]);
|
|
3019
|
+
}
|
|
3020
|
+
updateActiveMsgNum();
|
|
3020
3021
|
void startPromise.then(() => updateActiveMsgNum()).catch((e) => {
|
|
3021
3022
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
3022
3023
|
this.emitSafeError(err);
|
|
@@ -12542,8 +12543,24 @@ var BaichuanEventEmitter = class {
|
|
|
12542
12543
|
}
|
|
12543
12544
|
};
|
|
12544
12545
|
async function* createNativeStream(api, channel, profile, options) {
|
|
12546
|
+
let client = options?.client;
|
|
12547
|
+
let dedicatedRelease;
|
|
12548
|
+
if (!client) {
|
|
12549
|
+
const variantSuffix = options?.variant && options.variant !== "default" ? `:${options.variant}` : "";
|
|
12550
|
+
const sessionKey = `live:native${variantSuffix}:ch${channel}:${profile}`;
|
|
12551
|
+
try {
|
|
12552
|
+
api.logger?.info?.(`[createNativeStream] acquiring dedicated session key=${sessionKey}`);
|
|
12553
|
+
const session = await api.createDedicatedSession(sessionKey);
|
|
12554
|
+
client = session.client;
|
|
12555
|
+
dedicatedRelease = session.release;
|
|
12556
|
+
api.logger?.info?.(`[createNativeStream] dedicated session acquired key=${sessionKey}`);
|
|
12557
|
+
} catch (e) {
|
|
12558
|
+
api.logger?.warn?.(`[createNativeStream] dedicated session failed, using shared client: ${e instanceof Error ? e.message : e}`);
|
|
12559
|
+
client = api.client;
|
|
12560
|
+
}
|
|
12561
|
+
}
|
|
12545
12562
|
const videoStream = new BaichuanVideoStream({
|
|
12546
|
-
client
|
|
12563
|
+
client,
|
|
12547
12564
|
api,
|
|
12548
12565
|
channel,
|
|
12549
12566
|
profile,
|
|
@@ -12557,9 +12574,15 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
12557
12574
|
let closed = false;
|
|
12558
12575
|
const onError = (_error) => {
|
|
12559
12576
|
closed = true;
|
|
12577
|
+
api.logger?.warn?.(
|
|
12578
|
+
`[createNativeStream] stream error \u2192 closed channel=${channel} profile=${profile} error=${_error?.message ?? _error}`
|
|
12579
|
+
);
|
|
12560
12580
|
};
|
|
12561
12581
|
const onClose = () => {
|
|
12562
12582
|
closed = true;
|
|
12583
|
+
api.logger?.warn?.(
|
|
12584
|
+
`[createNativeStream] stream close \u2192 closed channel=${channel} profile=${profile}`
|
|
12585
|
+
);
|
|
12563
12586
|
};
|
|
12564
12587
|
try {
|
|
12565
12588
|
videoStream.on("error", onError);
|
|
@@ -12670,6 +12693,10 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
12670
12693
|
}
|
|
12671
12694
|
videoStream.removeListener("error", onError);
|
|
12672
12695
|
videoStream.removeListener("close", onClose);
|
|
12696
|
+
if (dedicatedRelease) {
|
|
12697
|
+
dedicatedRelease().catch(() => {
|
|
12698
|
+
});
|
|
12699
|
+
}
|
|
12673
12700
|
}
|
|
12674
12701
|
}
|
|
12675
12702
|
|
|
@@ -12909,6 +12936,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
12909
12936
|
tcpRtpFraming;
|
|
12910
12937
|
active = false;
|
|
12911
12938
|
flow;
|
|
12939
|
+
deviceId;
|
|
12940
|
+
dedicatedSessionRelease;
|
|
12912
12941
|
// Authentication
|
|
12913
12942
|
authCredentials = [];
|
|
12914
12943
|
requireAuth;
|
|
@@ -13075,6 +13104,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13075
13104
|
this.path = options.path ?? `/stream/${this.profile}`;
|
|
13076
13105
|
this.logger = options.logger ?? console;
|
|
13077
13106
|
this.tcpRtpFraming = options.tcpRtpFraming ?? "rfc4571";
|
|
13107
|
+
this.deviceId = options.deviceId;
|
|
13078
13108
|
this.authCredentials = options.credentials ?? [];
|
|
13079
13109
|
this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
|
|
13080
13110
|
const transport = this.api.client.getTransport();
|
|
@@ -14460,14 +14490,31 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14460
14490
|
this.firstAudioPromise = new Promise((resolve) => {
|
|
14461
14491
|
this.firstAudioResolve = resolve;
|
|
14462
14492
|
});
|
|
14493
|
+
let dedicatedClient;
|
|
14494
|
+
const variantSuffix = this.variant && this.variant !== "default" ? `:${this.variant}` : "";
|
|
14495
|
+
const deviceIdPart = this.deviceId ?? "rtsp-server";
|
|
14496
|
+
const sessionKey = `live:${deviceIdPart}:ch${this.channel}:${this.profile}${variantSuffix}`;
|
|
14497
|
+
try {
|
|
14498
|
+
const session = await this.api.createDedicatedSession(sessionKey, this.logger);
|
|
14499
|
+
dedicatedClient = session.client;
|
|
14500
|
+
this.dedicatedSessionRelease = session.release;
|
|
14501
|
+
this.logger.info(
|
|
14502
|
+
`[rebroadcast] dedicated session acquired sessionKey=${sessionKey}`
|
|
14503
|
+
);
|
|
14504
|
+
} catch (e) {
|
|
14505
|
+
this.logger.warn(
|
|
14506
|
+
`[rebroadcast] failed to acquire dedicated session, falling back to shared socket: ${e}`
|
|
14507
|
+
);
|
|
14508
|
+
}
|
|
14463
14509
|
this.logger.info(
|
|
14464
|
-
`[rebroadcast] native stream starting profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
|
|
14510
|
+
`[rebroadcast] native stream starting profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size} dedicated=${!!dedicatedClient}`
|
|
14465
14511
|
);
|
|
14466
14512
|
await this.flow.startKeepAlive(this.api);
|
|
14467
14513
|
this.nativeFanout = new NativeStreamFanout({
|
|
14468
14514
|
maxQueueItems: 200,
|
|
14469
14515
|
createSource: () => createNativeStream(this.api, this.channel, this.profile, {
|
|
14470
|
-
variant: this.variant
|
|
14516
|
+
variant: this.variant,
|
|
14517
|
+
...dedicatedClient ? { client: dedicatedClient } : {}
|
|
14471
14518
|
}),
|
|
14472
14519
|
onFrame: (frame) => {
|
|
14473
14520
|
if (frame.audio) {
|
|
@@ -14525,6 +14572,12 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14525
14572
|
this.firstFrameResolve = null;
|
|
14526
14573
|
this.nativeFanout = null;
|
|
14527
14574
|
this.prebuffer = [];
|
|
14575
|
+
if (this.dedicatedSessionRelease) {
|
|
14576
|
+
const release = this.dedicatedSessionRelease;
|
|
14577
|
+
this.dedicatedSessionRelease = void 0;
|
|
14578
|
+
release().catch(() => {
|
|
14579
|
+
});
|
|
14580
|
+
}
|
|
14528
14581
|
this.logger.info(
|
|
14529
14582
|
`[rebroadcast] native stream ended (camera sleeping or connection lost) profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
|
|
14530
14583
|
);
|
|
@@ -14594,6 +14647,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14594
14647
|
await fanout.stop();
|
|
14595
14648
|
}
|
|
14596
14649
|
this.prebuffer = [];
|
|
14650
|
+
if (this.dedicatedSessionRelease) {
|
|
14651
|
+
const release = this.dedicatedSessionRelease;
|
|
14652
|
+
this.dedicatedSessionRelease = void 0;
|
|
14653
|
+
try {
|
|
14654
|
+
await release();
|
|
14655
|
+
} catch {
|
|
14656
|
+
}
|
|
14657
|
+
}
|
|
14597
14658
|
if (this.tempStreamGenerator) {
|
|
14598
14659
|
try {
|
|
14599
14660
|
await this.tempStreamGenerator.return(void 0);
|
|
@@ -14603,14 +14664,22 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14603
14664
|
}
|
|
14604
14665
|
}
|
|
14605
14666
|
/**
|
|
14606
|
-
* Remove a client and
|
|
14667
|
+
* Remove a client and schedule native stream stop if no clients remain.
|
|
14668
|
+
* Uses a grace period so rapid reconnects (e.g. Frigate polling) reuse the running stream
|
|
14669
|
+
* and benefit from the prebuffer instead of waiting for a fresh keyframe.
|
|
14607
14670
|
*/
|
|
14608
14671
|
removeClient(clientId) {
|
|
14609
14672
|
if (this.connectedClients.has(clientId)) {
|
|
14610
14673
|
this.connectedClients.delete(clientId);
|
|
14611
14674
|
this.emit("clientDisconnected", clientId);
|
|
14612
14675
|
if (this.connectedClients.size === 0) {
|
|
14613
|
-
|
|
14676
|
+
this.clearNoClientAutoStopTimer();
|
|
14677
|
+
this.noClientAutoStopTimer = setTimeout(() => {
|
|
14678
|
+
if (this.connectedClients.size === 0) {
|
|
14679
|
+
void this.stopNativeStream();
|
|
14680
|
+
}
|
|
14681
|
+
}, 3e4);
|
|
14682
|
+
this.noClientAutoStopTimer?.unref?.();
|
|
14614
14683
|
}
|
|
14615
14684
|
}
|
|
14616
14685
|
}
|
|
@@ -34182,6 +34251,7 @@ var BaichuanHlsServer = class extends import_node_events10.EventEmitter {
|
|
|
34182
34251
|
segmentDuration;
|
|
34183
34252
|
playlistSize;
|
|
34184
34253
|
ffmpegPath;
|
|
34254
|
+
externalVideoStream;
|
|
34185
34255
|
log;
|
|
34186
34256
|
outputDir = null;
|
|
34187
34257
|
createdTempDir = false;
|
|
@@ -34208,6 +34278,7 @@ var BaichuanHlsServer = class extends import_node_events10.EventEmitter {
|
|
|
34208
34278
|
this.outputDir = options.outputDir;
|
|
34209
34279
|
this.createdTempDir = false;
|
|
34210
34280
|
}
|
|
34281
|
+
this.externalVideoStream = options.externalVideoStream;
|
|
34211
34282
|
this.log = options.logger ?? (() => {
|
|
34212
34283
|
});
|
|
34213
34284
|
}
|
|
@@ -34232,12 +34303,16 @@ var BaichuanHlsServer = class extends import_node_events10.EventEmitter {
|
|
|
34232
34303
|
this.playlistPath = import_node_path3.default.join(this.outputDir, "playlist.m3u8");
|
|
34233
34304
|
this.segmentPattern = import_node_path3.default.join(this.outputDir, "segment_%05d.ts");
|
|
34234
34305
|
this.log("info", `Starting HLS stream to ${this.outputDir}`);
|
|
34235
|
-
this.
|
|
34236
|
-
this.
|
|
34237
|
-
|
|
34238
|
-
this.
|
|
34239
|
-
|
|
34240
|
-
|
|
34306
|
+
if (this.externalVideoStream) {
|
|
34307
|
+
this.nativeStream = this.wrapVideoStreamAsGenerator(this.externalVideoStream);
|
|
34308
|
+
} else {
|
|
34309
|
+
this.nativeStream = createNativeStream(
|
|
34310
|
+
this.api,
|
|
34311
|
+
this.channel,
|
|
34312
|
+
this.profile,
|
|
34313
|
+
this.variant ? { variant: this.variant } : void 0
|
|
34314
|
+
);
|
|
34315
|
+
}
|
|
34241
34316
|
this.pumpPromise = this.pumpNativeToFfmpeg();
|
|
34242
34317
|
this.startedAt = /* @__PURE__ */ new Date();
|
|
34243
34318
|
this.state = "running";
|
|
@@ -34356,6 +34431,56 @@ var BaichuanHlsServer = class extends import_node_events10.EventEmitter {
|
|
|
34356
34431
|
// ============================================================================
|
|
34357
34432
|
// Private Methods
|
|
34358
34433
|
// ============================================================================
|
|
34434
|
+
/**
|
|
34435
|
+
* Wrap a BaichuanVideoStream's videoAccessUnit events into an async generator
|
|
34436
|
+
* compatible with createNativeStream's output format.
|
|
34437
|
+
*/
|
|
34438
|
+
async *wrapVideoStreamAsGenerator(videoStream) {
|
|
34439
|
+
const queue = [];
|
|
34440
|
+
let resolve = null;
|
|
34441
|
+
let done = false;
|
|
34442
|
+
const onFrame = (au) => {
|
|
34443
|
+
queue.push(au);
|
|
34444
|
+
resolve?.();
|
|
34445
|
+
resolve = null;
|
|
34446
|
+
};
|
|
34447
|
+
const onClose = () => {
|
|
34448
|
+
done = true;
|
|
34449
|
+
resolve?.();
|
|
34450
|
+
resolve = null;
|
|
34451
|
+
};
|
|
34452
|
+
const onError = () => {
|
|
34453
|
+
done = true;
|
|
34454
|
+
resolve?.();
|
|
34455
|
+
resolve = null;
|
|
34456
|
+
};
|
|
34457
|
+
videoStream.on("videoAccessUnit", onFrame);
|
|
34458
|
+
videoStream.on("close", onClose);
|
|
34459
|
+
videoStream.on("error", onError);
|
|
34460
|
+
try {
|
|
34461
|
+
while (!done) {
|
|
34462
|
+
if (queue.length === 0) {
|
|
34463
|
+
await new Promise((r) => {
|
|
34464
|
+
resolve = r;
|
|
34465
|
+
});
|
|
34466
|
+
}
|
|
34467
|
+
while (queue.length > 0) {
|
|
34468
|
+
const frame = queue.shift();
|
|
34469
|
+
yield {
|
|
34470
|
+
audio: false,
|
|
34471
|
+
data: frame.data,
|
|
34472
|
+
videoType: frame.videoType,
|
|
34473
|
+
isKeyframe: frame.isKeyframe,
|
|
34474
|
+
microseconds: frame.microseconds
|
|
34475
|
+
};
|
|
34476
|
+
}
|
|
34477
|
+
}
|
|
34478
|
+
} finally {
|
|
34479
|
+
videoStream.removeListener("videoAccessUnit", onFrame);
|
|
34480
|
+
videoStream.removeListener("close", onClose);
|
|
34481
|
+
videoStream.removeListener("error", onError);
|
|
34482
|
+
}
|
|
34483
|
+
}
|
|
34359
34484
|
async pumpNativeToFfmpeg() {
|
|
34360
34485
|
if (!this.nativeStream || !this.playlistPath || !this.segmentPattern) {
|
|
34361
34486
|
return;
|