@apocaliss92/nodelink-js 0.6.6 → 0.6.7

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/dist/index.cjs CHANGED
@@ -8431,7 +8431,6 @@ __export(index_exports, {
8431
8431
  DUAL_LENS_DUAL_MOTION_MODELS: () => DUAL_LENS_DUAL_MOTION_MODELS,
8432
8432
  DUAL_LENS_MODELS: () => DUAL_LENS_MODELS,
8433
8433
  DUAL_LENS_SINGLE_MOTION_MODELS: () => DUAL_LENS_SINGLE_MOTION_MODELS,
8434
- Go2rtcTcpServer: () => Go2rtcTcpServer,
8435
8434
  H264RtpDepacketizer: () => H264RtpDepacketizer,
8436
8435
  H265RtpDepacketizer: () => H265RtpDepacketizer,
8437
8436
  HlsSessionManager: () => HlsSessionManager,
@@ -14898,6 +14897,38 @@ function createRtspFlow(transport, videoType) {
14898
14897
  return new H265Flow(transport);
14899
14898
  }
14900
14899
 
14900
+ // src/baichuan/stream/rtpVideoTimestamp.ts
14901
+ var U32 = 4294967296;
14902
+ var DEFAULT_VIDEO_CLOCK_RATE = 9e4;
14903
+ function deriveRtpVideoTimestamp(state, frameMicroseconds, clockRate = DEFAULT_VIDEO_CLOCK_RATE) {
14904
+ if (frameMicroseconds === null || frameMicroseconds === void 0 || !Number.isFinite(frameMicroseconds)) {
14905
+ return { timestamp: state.timestamp, state };
14906
+ }
14907
+ const curRaw = frameMicroseconds >>> 0;
14908
+ if (state.baseUnwrappedUs === void 0) {
14909
+ return {
14910
+ timestamp: state.timestamp,
14911
+ state: {
14912
+ ...state,
14913
+ unwrappedUs: curRaw,
14914
+ lastRawUs: curRaw,
14915
+ baseUnwrappedUs: curRaw,
14916
+ baseTimestamp: state.timestamp
14917
+ }
14918
+ };
14919
+ }
14920
+ const lastRaw = state.lastRawUs ?? curRaw;
14921
+ let forwardDelta = curRaw - lastRaw >>> 0;
14922
+ if (forwardDelta > U32 / 2) forwardDelta = 0;
14923
+ const unwrappedUs = (state.unwrappedUs ?? curRaw) + forwardDelta;
14924
+ const deltaUs = unwrappedUs - state.baseUnwrappedUs;
14925
+ const timestamp = state.baseTimestamp + Math.round(deltaUs * clockRate / 1e6) >>> 0;
14926
+ return {
14927
+ timestamp,
14928
+ state: { ...state, unwrappedUs, lastRawUs: curRaw, timestamp }
14929
+ };
14930
+ }
14931
+
14901
14932
  // src/baichuan/stream/BaichuanRtspServer.ts
14902
14933
  init_H264Converter();
14903
14934
  init_H265Converter();
@@ -15472,7 +15503,10 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events5.E
15472
15503
  let useTcpInterleaved = false;
15473
15504
  let clientUdpSocket = null;
15474
15505
  let clientUdpSocketAudio = null;
15506
+ let cleanedUp = false;
15475
15507
  const cleanup = () => {
15508
+ if (cleanedUp) return;
15509
+ cleanedUp = true;
15476
15510
  const sessionDurationMs = Date.now() - connectTime;
15477
15511
  const res = this.clientResources.get(clientId);
15478
15512
  const framesSent = res?.framesSent ?? 0;
@@ -16220,22 +16254,25 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events5.E
16220
16254
  resources.rtpVideoTimestamp = 0;
16221
16255
  if (resources.rtpVideoBaseTimestamp === void 0)
16222
16256
  resources.rtpVideoBaseTimestamp = resources.rtpVideoTimestamp;
16223
- if (resources.rtpVideoBaseMicroseconds === void 0) {
16257
+ const { timestamp, state } = deriveRtpVideoTimestamp(
16258
+ {
16259
+ timestamp: resources.rtpVideoTimestamp,
16260
+ baseTimestamp: resources.rtpVideoBaseTimestamp,
16261
+ unwrappedUs: resources.rtpVideoUnwrappedUs,
16262
+ lastRawUs: resources.rtpVideoLastRawUs,
16263
+ baseUnwrappedUs: resources.rtpVideoBaseUnwrappedUs
16264
+ },
16265
+ frameMicroseconds,
16266
+ videoClockRate
16267
+ );
16268
+ resources.rtpVideoTimestamp = timestamp;
16269
+ resources.rtpVideoBaseTimestamp = state.baseTimestamp;
16270
+ resources.rtpVideoUnwrappedUs = state.unwrappedUs;
16271
+ resources.rtpVideoLastRawUs = state.lastRawUs;
16272
+ resources.rtpVideoBaseUnwrappedUs = state.baseUnwrappedUs;
16273
+ resources.rtpVideoLastTimestamp = timestamp;
16274
+ if (resources.rtpVideoBaseMicroseconds === void 0)
16224
16275
  resources.rtpVideoBaseMicroseconds = frameMicroseconds >>> 0;
16225
- resources.rtpVideoLastTimestamp = resources.rtpVideoTimestamp;
16226
- return;
16227
- }
16228
- const baseUs = resources.rtpVideoBaseMicroseconds >>> 0;
16229
- const curUs = frameMicroseconds >>> 0;
16230
- const deltaUs = curUs - baseUs >>> 0;
16231
- const baseTs = (resources.rtpVideoBaseTimestamp ?? 0) >>> 0;
16232
- let ts = baseTs + Math.round(deltaUs * videoClockRate / 1e6) >>> 0;
16233
- const last = resources.rtpVideoLastTimestamp;
16234
- if (last !== void 0 && ts <= last >>> 0) {
16235
- ts = (last >>> 0) + 1 >>> 0;
16236
- }
16237
- resources.rtpVideoTimestamp = ts;
16238
- resources.rtpVideoLastTimestamp = ts;
16239
16276
  };
16240
16277
  const sendVideoAccessUnit = (videoType, accessUnitAnnexB, advanceTimestamp = true) => {
16241
16278
  const nals = _BaichuanRtspServer.splitAnnexBNals(accessUnitAnnexB);
@@ -38230,811 +38267,8 @@ async function createReplayHttpServer(options) {
38230
38267
  // src/index.ts
38231
38268
  init_BaichuanVideoStream();
38232
38269
 
38233
- // src/baichuan/stream/Go2rtcTcpServer.ts
38234
- var import_node_events8 = require("events");
38235
- var net4 = __toESM(require("net"), 1);
38236
- init_H264Converter();
38237
- init_H265Converter();
38238
- var AsyncBoundedQueue2 = class {
38239
- maxItems;
38240
- queue = [];
38241
- waiting;
38242
- closed = false;
38243
- constructor(maxItems) {
38244
- this.maxItems = Math.max(1, maxItems | 0);
38245
- }
38246
- push(item) {
38247
- if (this.closed) return;
38248
- if (this.waiting) {
38249
- const { resolve } = this.waiting;
38250
- this.waiting = void 0;
38251
- resolve({ value: item, done: false });
38252
- return;
38253
- }
38254
- this.queue.push(item);
38255
- if (this.queue.length > this.maxItems) {
38256
- this.queue.splice(0, this.queue.length - this.maxItems);
38257
- }
38258
- }
38259
- close() {
38260
- if (this.closed) return;
38261
- this.closed = true;
38262
- if (this.waiting) {
38263
- const { resolve } = this.waiting;
38264
- this.waiting = void 0;
38265
- resolve({ value: void 0, done: true });
38266
- }
38267
- }
38268
- async next() {
38269
- if (this.closed) return { value: void 0, done: true };
38270
- const item = this.queue.shift();
38271
- if (item !== void 0) return { value: item, done: false };
38272
- return await new Promise((resolve) => {
38273
- this.waiting = { resolve };
38274
- });
38275
- }
38276
- };
38277
- var NativeStreamFanout2 = class {
38278
- opts;
38279
- queues = /* @__PURE__ */ new Map();
38280
- source = null;
38281
- running = false;
38282
- pumpPromise = null;
38283
- constructor(opts) {
38284
- this.opts = opts;
38285
- }
38286
- start() {
38287
- if (this.running) return;
38288
- this.running = true;
38289
- this.source = this.opts.createSource();
38290
- this.pumpPromise = (async () => {
38291
- try {
38292
- for await (const frame of this.source) {
38293
- try {
38294
- this.opts.onFrame?.(frame);
38295
- } catch {
38296
- }
38297
- for (const q of this.queues.values()) {
38298
- q.push(frame);
38299
- }
38300
- }
38301
- } catch (e) {
38302
- this.opts.onError?.(e);
38303
- } finally {
38304
- for (const q of this.queues.values()) q.close();
38305
- this.queues.clear();
38306
- this.running = false;
38307
- this.opts.onEnd?.();
38308
- }
38309
- })();
38310
- }
38311
- subscribe(id) {
38312
- const q = new AsyncBoundedQueue2(this.opts.maxQueueItems);
38313
- this.queues.set(id, q);
38314
- const self = this;
38315
- return (async function* () {
38316
- try {
38317
- while (true) {
38318
- const r = await q.next();
38319
- if (r.done) return;
38320
- yield r.value;
38321
- }
38322
- } finally {
38323
- q.close();
38324
- self.queues.delete(id);
38325
- }
38326
- })();
38327
- }
38328
- async stop() {
38329
- if (!this.running) return;
38330
- this.running = false;
38331
- const src = this.source;
38332
- this.source = null;
38333
- for (const q of this.queues.values()) q.close();
38334
- this.queues.clear();
38335
- try {
38336
- await src?.return(void 0);
38337
- } catch {
38338
- }
38339
- try {
38340
- await this.pumpPromise;
38341
- } catch {
38342
- }
38343
- this.pumpPromise = null;
38344
- }
38345
- };
38346
- var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events8.EventEmitter {
38347
- api;
38348
- channel;
38349
- profile;
38350
- variant;
38351
- listenHost;
38352
- listenPort;
38353
- logger;
38354
- deviceId;
38355
- gracePeriodMs;
38356
- prebufferMaxMs;
38357
- maxBufferBytes;
38358
- streamTimeoutMs;
38359
- prestartStream;
38360
- active = false;
38361
- server;
38362
- resolvedPort;
38363
- // Native stream
38364
- nativeFanout = null;
38365
- nativeStreamActive = false;
38366
- // Set only by stopNativeStream() (explicit teardown) so the fanout's onEnd
38367
- // callback can short-circuit cleanup/restart logic. NOT set by the inactivity-
38368
- // timeout force-restart path — that flow wants onEnd to run and decide
38369
- // whether to restart based on prestartStream / connected clients.
38370
- nativeStreamStopping = false;
38371
- // Pending retry timer for the unbounded auto-restart loop. When a stream
38372
- // start fails transiently (camera in maintenance reboot, idle-disconnect
38373
- // race, etc.) we keep trying with exponential backoff until either the
38374
- // server is stopped or a frame finally arrives.
38375
- nativeStreamRetryTimer;
38376
- nativeStreamRetryDelayMs = 0;
38377
- dedicatedSessionRelease;
38378
- detectedVideoType;
38379
- // Client tracking
38380
- connectedClients = /* @__PURE__ */ new Set();
38381
- clientSockets = /* @__PURE__ */ new Map();
38382
- stopGraceTimer;
38383
- // Stream health monitoring
38384
- lastFrameAt = 0;
38385
- streamHealthTimer;
38386
- totalFramesReceived = 0;
38387
- totalVideoFramesWritten = 0;
38388
- // Prebuffer
38389
- prebuffer = [];
38390
- // Audio metadata — populated on first valid ADTS AAC frame.
38391
- // Exposed via getAudioInfo() for the stream-diagnostics feature.
38392
- audioInfo = null;
38393
- constructor(options) {
38394
- super();
38395
- this.api = options.api;
38396
- this.channel = options.channel;
38397
- this.profile = options.profile;
38398
- this.variant = options.variant ?? "default";
38399
- this.listenHost = options.listenHost ?? "127.0.0.1";
38400
- this.listenPort = options.listenPort ?? 0;
38401
- this.logger = options.logger ?? console;
38402
- this.deviceId = options.deviceId;
38403
- this.gracePeriodMs = options.gracePeriodMs ?? 3e4;
38404
- this.prebufferMaxMs = options.prebufferMs ?? 3e3;
38405
- this.maxBufferBytes = options.maxBufferBytes ?? 1e8;
38406
- this.streamTimeoutMs = options.streamTimeoutMs ?? 15e3;
38407
- this.prestartStream = options.prestartStream ?? true;
38408
- }
38409
- // -----------------------------------------------------------------------
38410
- // Public API
38411
- // -----------------------------------------------------------------------
38412
- /** Start listening. Resolves once the TCP server is bound. */
38413
- async start() {
38414
- if (this.active) return;
38415
- this.active = true;
38416
- this.server = net4.createServer((socket) => this.handleClient(socket));
38417
- this.server.on("error", (err) => {
38418
- this.logger.error?.(`[Go2rtcTcpServer] server error: ${err.message}`);
38419
- this.emit("error", err);
38420
- });
38421
- await new Promise((resolve, reject) => {
38422
- this.server.listen(this.listenPort, this.listenHost, () => {
38423
- const addr = this.server.address();
38424
- this.resolvedPort = addr.port;
38425
- this.logger.info?.(
38426
- `[Go2rtcTcpServer] listening on ${addr.address}:${addr.port} channel=${this.channel} profile=${this.profile}`
38427
- );
38428
- this.emit("listening", { host: addr.address, port: addr.port });
38429
- resolve();
38430
- });
38431
- this.server.once("error", reject);
38432
- });
38433
- if (this.prestartStream) {
38434
- this.logger.info?.(
38435
- `[Go2rtcTcpServer] pre-starting native stream channel=${this.channel} profile=${this.profile}`
38436
- );
38437
- this.startNativeStream();
38438
- }
38439
- }
38440
- /** Stop the server and all active streams. */
38441
- async stop() {
38442
- if (!this.active) return;
38443
- this.active = false;
38444
- clearTimeout(this.stopGraceTimer);
38445
- this.clearNativeStreamRetry();
38446
- this.stopStreamHealthMonitor();
38447
- for (const [id, sock] of this.clientSockets) {
38448
- sock.destroy();
38449
- this.connectedClients.delete(id);
38450
- }
38451
- this.clientSockets.clear();
38452
- await this.stopNativeStream();
38453
- if (this.server) {
38454
- await new Promise((resolve) => {
38455
- this.server.close(() => resolve());
38456
- });
38457
- this.server = void 0;
38458
- }
38459
- this.prebuffer = [];
38460
- this.resolvedPort = void 0;
38461
- this.emit("close");
38462
- }
38463
- /** The actual port the server is listening on (available after start()). */
38464
- get port() {
38465
- return this.resolvedPort;
38466
- }
38467
- /** The go2rtc-compatible source URL. */
38468
- get go2rtcSourceUrl() {
38469
- if (this.resolvedPort == null) return void 0;
38470
- return `tcp://127.0.0.1:${this.resolvedPort}`;
38471
- }
38472
- /** Number of currently connected clients. */
38473
- get clientCount() {
38474
- return this.connectedClients.size;
38475
- }
38476
- // -----------------------------------------------------------------------
38477
- // Diagnostic subscription API (implements DiagnosticStreamServer)
38478
- //
38479
- // Matches the shape of BaichuanRtspServer's diagnostic API so the
38480
- // stream-diagnostic feature in the Manager app can drive either backend
38481
- // with identical code.
38482
- // -----------------------------------------------------------------------
38483
- /**
38484
- * Subscribe to the raw native stream for diagnostic purposes.
38485
- * The subscriber receives the same frames the MPEG-TS muxer consumes
38486
- * (pre-muxing). Counts as a "consumer" so the native stream is kept alive
38487
- * for the lifetime of the subscription. If the stream is not already
38488
- * running (battery camera, prestart=false), this starts it.
38489
- */
38490
- async subscribeDiagnostic(id) {
38491
- this.connectedClients.add(`diag:${id}`);
38492
- if (!this.nativeStreamActive) {
38493
- await this.startNativeStream();
38494
- }
38495
- if (!this.nativeFanout) {
38496
- this.connectedClients.delete(`diag:${id}`);
38497
- throw new Error(
38498
- "Go2rtcTcpServer: native stream failed to start \u2014 cannot subscribe diagnostic"
38499
- );
38500
- }
38501
- return this.nativeFanout.subscribe(`diag:${id}`);
38502
- }
38503
- /** Unsubscribe a diagnostic session and release its consumer slot. */
38504
- unsubscribeDiagnostic(id) {
38505
- this.removeClient(`diag:${id}`, "diagnostic unsubscribe");
38506
- }
38507
- /**
38508
- * Returns ADTS AAC audio metadata detected from the native stream, or
38509
- * null if no audio frame has been observed yet (e.g. video-only cameras
38510
- * or before the first audio packet arrives).
38511
- */
38512
- getAudioInfo() {
38513
- return this.audioInfo;
38514
- }
38515
- // -----------------------------------------------------------------------
38516
- // Client handling
38517
- // -----------------------------------------------------------------------
38518
- handleClient(socket) {
38519
- const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
38520
- socket.setNoDelay(true);
38521
- this.connectedClients.add(clientId);
38522
- this.clientSockets.set(clientId, socket);
38523
- this.logger.info?.(
38524
- `[Go2rtcTcpServer] client connected id=${clientId} total=${this.connectedClients.size}`
38525
- );
38526
- this.emit("client", clientId);
38527
- if (this.stopGraceTimer) {
38528
- clearTimeout(this.stopGraceTimer);
38529
- this.stopGraceTimer = void 0;
38530
- }
38531
- if (!this.nativeStreamActive) {
38532
- this.startNativeStream();
38533
- }
38534
- this.feedClient(clientId, socket).catch((err) => {
38535
- this.logger.warn?.(
38536
- `[Go2rtcTcpServer] feedClient error id=${clientId}: ${err}`
38537
- );
38538
- });
38539
- const cleanup = (reason) => {
38540
- this.removeClient(clientId, reason);
38541
- socket.destroy();
38542
- };
38543
- socket.on("error", (err) => cleanup(`error: ${err.message}`));
38544
- socket.on("close", (hadError) => cleanup(hadError ? "close (with error)" : "close (clean)"));
38545
- }
38546
- async feedClient(clientId, socket) {
38547
- const fanoutDeadline = Date.now() + 3e4;
38548
- while (this.active && !this.nativeFanout) {
38549
- if (socket.destroyed) return;
38550
- if (Date.now() > fanoutDeadline) {
38551
- this.logger.warn?.(
38552
- `[Go2rtcTcpServer] fanout not ready after 30s, dropping client ${clientId}`
38553
- );
38554
- return;
38555
- }
38556
- await new Promise((r) => setTimeout(r, 100));
38557
- }
38558
- if (!this.active || !this.nativeFanout) return;
38559
- const subscription = this.nativeFanout.subscribe(clientId);
38560
- let muxer = null;
38561
- const prebufferSnap = this.prebuffer.slice();
38562
- let lastIdrIdx = -1;
38563
- for (let i = prebufferSnap.length - 1; i >= 0; i--) {
38564
- if (prebufferSnap[i].isKeyframe) {
38565
- lastIdrIdx = i;
38566
- break;
38567
- }
38568
- }
38569
- if (lastIdrIdx >= 0) {
38570
- const replay = prebufferSnap.slice(lastIdrIdx);
38571
- this.logger.info?.(
38572
- `[Go2rtcTcpServer] prebuffer replay client=${clientId} frames=${replay.length}`
38573
- );
38574
- if (!muxer) {
38575
- muxer = new MpegTsMuxer({
38576
- videoType: this.detectedVideoType ?? "H264",
38577
- includeAudio: true
38578
- });
38579
- }
38580
- for (const entry of replay) {
38581
- if (socket.destroyed) return;
38582
- let ts;
38583
- if (!entry.audio) {
38584
- ts = muxer.muxVideo(entry.data, entry.pts, entry.isKeyframe);
38585
- } else {
38586
- ts = muxer.muxAudio(entry.data, entry.pts);
38587
- }
38588
- if (ts.length > 0) socket.write(ts);
38589
- }
38590
- }
38591
- let seenKeyframe = lastIdrIdx >= 0;
38592
- let liveFrameCount = 0;
38593
- let liveVideoWritten = 0;
38594
- let lastLogAt = Date.now();
38595
- try {
38596
- this.logger.info?.(
38597
- `[Go2rtcTcpServer] entering live loop client=${clientId} seenKeyframe=${seenKeyframe}`
38598
- );
38599
- for await (const frame of subscription) {
38600
- if (socket.destroyed || !this.active) {
38601
- this.logger.info?.(
38602
- `[Go2rtcTcpServer] live loop exit client=${clientId} destroyed=${socket.destroyed} active=${this.active}`
38603
- );
38604
- break;
38605
- }
38606
- liveFrameCount++;
38607
- if (frame.audio) {
38608
- if (muxer) {
38609
- const pts2 = frame.microseconds ?? Date.now() * 1e3;
38610
- const ts2 = muxer.muxAudio(frame.data, pts2);
38611
- if (ts2.length > 0) socket.write(ts2);
38612
- }
38613
- continue;
38614
- }
38615
- const annexB = this.convertVideoFrame(frame);
38616
- if (!annexB) continue;
38617
- const isKf = this.isAnnexBKeyframe(annexB, frame.videoType);
38618
- if (!seenKeyframe) {
38619
- if (!isKf) continue;
38620
- seenKeyframe = true;
38621
- this.logger.info?.(
38622
- `[Go2rtcTcpServer] first live keyframe client=${clientId} after ${liveFrameCount} frames`
38623
- );
38624
- if (!muxer) {
38625
- muxer = new MpegTsMuxer({
38626
- videoType: frame.videoType ?? this.detectedVideoType ?? "H264",
38627
- includeAudio: true
38628
- });
38629
- }
38630
- }
38631
- const pts = frame.microseconds ?? Date.now() * 1e3;
38632
- const ts = muxer.muxVideo(annexB, pts, isKf);
38633
- socket.write(ts);
38634
- liveVideoWritten++;
38635
- this.totalVideoFramesWritten++;
38636
- if (Date.now() - lastLogAt > 1e4) {
38637
- this.logger.info?.(
38638
- `[Go2rtcTcpServer] live stats client=${clientId} received=${liveFrameCount} written=${liveVideoWritten} bufLen=${socket.writableLength}`
38639
- );
38640
- lastLogAt = Date.now();
38641
- }
38642
- if (socket.writableLength > this.maxBufferBytes) {
38643
- this.logger.warn?.(
38644
- `[Go2rtcTcpServer] buffer overflow (${socket.writableLength} bytes), dropping client ${clientId}`
38645
- );
38646
- socket.destroy();
38647
- break;
38648
- }
38649
- }
38650
- this.logger.info?.(
38651
- `[Go2rtcTcpServer] live loop ended naturally client=${clientId} received=${liveFrameCount} written=${liveVideoWritten}`
38652
- );
38653
- } finally {
38654
- await subscription.return(void 0).catch(() => {
38655
- });
38656
- }
38657
- }
38658
- // -----------------------------------------------------------------------
38659
- // Frame conversion
38660
- // -----------------------------------------------------------------------
38661
- /**
38662
- * Convert a native video frame to Annex-B.
38663
- * Returns null for audio frames (handled separately by muxAudio).
38664
- */
38665
- convertVideoFrame(frame) {
38666
- if (frame.audio) return null;
38667
- if (frame.data.length === 0) return null;
38668
- try {
38669
- if (frame.videoType === "H264") {
38670
- return convertToAnnexB(frame.data);
38671
- }
38672
- if (frame.videoType === "H265") {
38673
- return convertToAnnexB2(frame.data);
38674
- }
38675
- } catch {
38676
- }
38677
- return frame.data;
38678
- }
38679
- /** Check if an Annex-B buffer contains a keyframe (IDR for H.264, IRAP for H.265). */
38680
- isAnnexBKeyframe(annexB, videoType) {
38681
- try {
38682
- if (videoType === "H264") {
38683
- const nals = _Go2rtcTcpServer.splitAnnexBNals(annexB);
38684
- return nals.some((n) => n.length >= 1 && (n[0] & 31) === 5);
38685
- }
38686
- if (videoType === "H265") {
38687
- const nals = splitAnnexBToNalPayloads2(annexB);
38688
- return nals.some(
38689
- (n) => n.length >= 2 && isH265Irap(n[0] >> 1 & 63)
38690
- );
38691
- }
38692
- } catch {
38693
- }
38694
- return false;
38695
- }
38696
- /** Split Annex-B byte stream into individual NAL units. */
38697
- static splitAnnexBNals(buf) {
38698
- const nals = [];
38699
- let i = 0;
38700
- while (i < buf.length) {
38701
- if (i + 2 < buf.length && buf[i] === 0 && buf[i + 1] === 0) {
38702
- let scLen;
38703
- if (buf[i + 2] === 1) {
38704
- scLen = 3;
38705
- } else if (i + 3 < buf.length && buf[i + 2] === 0 && buf[i + 3] === 1) {
38706
- scLen = 4;
38707
- } else {
38708
- i++;
38709
- continue;
38710
- }
38711
- const nalStart = i + scLen;
38712
- let nalEnd = buf.length;
38713
- for (let j = nalStart; j < buf.length - 2; j++) {
38714
- if (buf[j] === 0 && buf[j + 1] === 0 && (buf[j + 2] === 1 || j + 3 < buf.length && buf[j + 2] === 0 && buf[j + 3] === 1)) {
38715
- nalEnd = j;
38716
- break;
38717
- }
38718
- }
38719
- if (nalEnd > nalStart) {
38720
- nals.push(buf.subarray(nalStart, nalEnd));
38721
- }
38722
- i = nalEnd;
38723
- } else {
38724
- i++;
38725
- }
38726
- }
38727
- return nals;
38728
- }
38729
- // -----------------------------------------------------------------------
38730
- // ADTS AAC parsing (used for audio metadata exposed via getAudioInfo)
38731
- // -----------------------------------------------------------------------
38732
- /** True if `b` starts with an ADTS AAC syncword (0xFFF). */
38733
- static isAdtsAacFrame(b) {
38734
- return b.length >= 2 && b[0] === 255 && (b[1] & 240) === 240;
38735
- }
38736
- /**
38737
- * Parse an ADTS header into {sampleRate, channels, AudioSpecificConfig hex}.
38738
- * Returns null when the buffer is not a valid ADTS frame.
38739
- */
38740
- static parseAdtsSamplingInfo(b) {
38741
- if (b.length < 7) return null;
38742
- if (!_Go2rtcTcpServer.isAdtsAacFrame(b)) return null;
38743
- const samplingIndex = b[2] >> 2 & 15;
38744
- const sampleRates = [
38745
- 96e3,
38746
- 88200,
38747
- 64e3,
38748
- 48e3,
38749
- 44100,
38750
- 32e3,
38751
- 24e3,
38752
- 22050,
38753
- 16e3,
38754
- 12e3,
38755
- 11025,
38756
- 8e3,
38757
- 7350
38758
- ];
38759
- const sampleRate = sampleRates[samplingIndex] ?? null;
38760
- if (!sampleRate) return null;
38761
- const channelConfig = (b[2] & 1) << 2 | b[3] >> 6 & 3;
38762
- const channels = channelConfig === 0 ? 1 : channelConfig;
38763
- const profile = b[2] >> 6 & 3;
38764
- const audioObjectType = profile + 1;
38765
- const asc = audioObjectType << 11 | samplingIndex << 7 | channelConfig << 3;
38766
- const configHex = Buffer.from([asc >> 8 & 255, asc & 255]).toString(
38767
- "hex"
38768
- );
38769
- return { sampleRate, channels, configHex };
38770
- }
38771
- // -----------------------------------------------------------------------
38772
- // Native stream management
38773
- // -----------------------------------------------------------------------
38774
- /**
38775
- * Schedule another startNativeStream() attempt after the given delay.
38776
- * Idempotent: a no-op if a retry is already scheduled, the server is no
38777
- * longer active, or an explicit stop is in progress. Implements unbounded
38778
- * exponential backoff (5s → 60s) so a camera that stays unreachable for
38779
- * minutes (e.g. nightly maintenance reboot) eventually recovers without
38780
- * manual intervention — see issue #16.
38781
- */
38782
- scheduleNativeStreamRetry(reason) {
38783
- if (!this.active) return;
38784
- if (this.nativeStreamStopping) return;
38785
- if (this.nativeStreamRetryTimer) return;
38786
- const delay = this.nativeStreamRetryDelayMs > 0 ? this.nativeStreamRetryDelayMs : 5e3;
38787
- this.logger.info?.(
38788
- `[Go2rtcTcpServer] scheduling native stream retry in ${(delay / 1e3).toFixed(0)}s (reason=${reason})`
38789
- );
38790
- this.nativeStreamRetryTimer = setTimeout(() => {
38791
- this.nativeStreamRetryTimer = void 0;
38792
- if (!this.active) return;
38793
- if (this.nativeStreamStopping) return;
38794
- this.startNativeStream().catch((err) => {
38795
- this.logger.warn?.(
38796
- `[Go2rtcTcpServer] retry of startNativeStream threw: ${err instanceof Error ? err.message : err}`
38797
- );
38798
- });
38799
- }, delay);
38800
- this.nativeStreamRetryDelayMs = Math.min(delay * 2, 6e4);
38801
- }
38802
- /**
38803
- * Cancel any pending retry timer and reset the backoff. Called on explicit
38804
- * stop and on first-frame-received so the next failure starts the backoff
38805
- * window from scratch.
38806
- */
38807
- clearNativeStreamRetry() {
38808
- if (this.nativeStreamRetryTimer) {
38809
- clearTimeout(this.nativeStreamRetryTimer);
38810
- this.nativeStreamRetryTimer = void 0;
38811
- }
38812
- this.nativeStreamRetryDelayMs = 0;
38813
- }
38814
- async startNativeStream() {
38815
- if (this.nativeStreamActive) return;
38816
- if (!this.api.isReady) {
38817
- if (this.api.isClosed) {
38818
- this.logger.warn?.(
38819
- `[Go2rtcTcpServer] API has been explicitly closed \u2014 stream cannot start`
38820
- );
38821
- return;
38822
- }
38823
- try {
38824
- this.logger.info?.(
38825
- `[Go2rtcTcpServer] API not ready (idle disconnect?), calling ensureConnected`
38826
- );
38827
- await this.api.ensureConnected();
38828
- } catch (e) {
38829
- this.logger.warn?.(
38830
- `[Go2rtcTcpServer] ensureConnected failed: ${e}`
38831
- );
38832
- this.scheduleNativeStreamRetry("ensureConnected failed");
38833
- return;
38834
- }
38835
- }
38836
- this.nativeStreamActive = true;
38837
- let dedicatedClient;
38838
- if (this.deviceId) {
38839
- try {
38840
- const session = await this.api.createDedicatedSession(
38841
- `live:${this.deviceId}:ch${this.channel}:${this.profile}`
38842
- );
38843
- dedicatedClient = session.client;
38844
- this.dedicatedSessionRelease = session.release;
38845
- } catch (e) {
38846
- this.logger.warn?.(
38847
- `[Go2rtcTcpServer] failed to acquire dedicated session, using shared socket: ${e}`
38848
- );
38849
- }
38850
- }
38851
- this.logger.info?.(
38852
- `[Go2rtcTcpServer] native stream starting channel=${this.channel} profile=${this.profile} dedicated=${!!dedicatedClient}`
38853
- );
38854
- let hadFrames = false;
38855
- this.nativeFanout = new NativeStreamFanout2({
38856
- maxQueueItems: 200,
38857
- createSource: () => createNativeStream(this.api, this.channel, this.profile, {
38858
- variant: this.variant,
38859
- ...dedicatedClient ? { client: dedicatedClient } : {}
38860
- }),
38861
- onFrame: (frame) => {
38862
- if (!hadFrames) {
38863
- this.clearNativeStreamRetry();
38864
- }
38865
- hadFrames = true;
38866
- this.lastFrameAt = Date.now();
38867
- this.totalFramesReceived++;
38868
- if (!frame.audio && (frame.videoType === "H264" || frame.videoType === "H265")) {
38869
- this.detectedVideoType = frame.videoType;
38870
- }
38871
- let prebufData;
38872
- let isKeyframe;
38873
- if (frame.audio) {
38874
- if (frame.data.length === 0) return;
38875
- if (!this.audioInfo) {
38876
- const parsed = _Go2rtcTcpServer.parseAdtsSamplingInfo(frame.data);
38877
- if (parsed) {
38878
- this.audioInfo = { codec: "aac-adts", ...parsed };
38879
- }
38880
- }
38881
- prebufData = frame.data;
38882
- isKeyframe = false;
38883
- } else {
38884
- const annexB = this.convertVideoFrame(frame);
38885
- if (!annexB || annexB.length === 0) return;
38886
- prebufData = annexB;
38887
- isKeyframe = this.isAnnexBKeyframe(annexB, frame.videoType);
38888
- }
38889
- const pts = frame.microseconds ?? Date.now() * 1e3;
38890
- this.prebuffer.push({
38891
- data: Buffer.from(prebufData),
38892
- time: Date.now(),
38893
- isKeyframe,
38894
- audio: frame.audio,
38895
- pts
38896
- });
38897
- const cutoff = Date.now() - this.prebufferMaxMs;
38898
- let trimIdx = 0;
38899
- while (trimIdx < this.prebuffer.length && this.prebuffer[trimIdx].time < cutoff) {
38900
- trimIdx++;
38901
- }
38902
- if (trimIdx > 0) this.prebuffer.splice(0, trimIdx);
38903
- },
38904
- onError: (error) => {
38905
- this.logger.warn?.(`[Go2rtcTcpServer] native stream error: ${error}`);
38906
- },
38907
- onEnd: () => {
38908
- if (this.nativeStreamStopping) return;
38909
- this.nativeStreamActive = false;
38910
- this.nativeFanout = null;
38911
- this.stopStreamHealthMonitor();
38912
- const silenceMs = this.lastFrameAt > 0 ? Date.now() - this.lastFrameAt : -1;
38913
- const diagnosis = silenceMs > this.streamTimeoutMs ? "camera stopped sending frames" : silenceMs >= 0 ? "stream source closed" : "no frames were ever received";
38914
- this.logger.warn?.(
38915
- `[Go2rtcTcpServer] native stream ended diagnosis="${diagnosis}" lastFrame=${silenceMs >= 0 ? `${(silenceMs / 1e3).toFixed(1)}s ago` : "never"} totalRx=${this.totalFramesReceived} clients=${this.connectedClients.size}`
38916
- );
38917
- if (this.dedicatedSessionRelease) {
38918
- this.dedicatedSessionRelease().catch(() => {
38919
- });
38920
- this.dedicatedSessionRelease = void 0;
38921
- }
38922
- if (!this.prestartStream) {
38923
- this.logger.info?.(
38924
- `[Go2rtcTcpServer] battery native stream ended hadFrames=${hadFrames} channel=${this.channel} profile=${this.profile} \u2014 dropping ${this.connectedClients.size} client(s) to prevent wake loop`
38925
- );
38926
- for (const [, sock] of this.clientSockets) {
38927
- sock.destroy();
38928
- }
38929
- } else if (this.active) {
38930
- if (typeof this.api.isStreamProfileRejected === "function" && this.api.isStreamProfileRejected(this.channel, this.profile)) {
38931
- this.logger.warn?.(
38932
- `[Go2rtcTcpServer] profile rejected by device channel=${this.channel} profile=${this.profile} \u2014 not restarting`
38933
- );
38934
- for (const [, sock] of this.clientSockets) {
38935
- sock.destroy();
38936
- }
38937
- return;
38938
- }
38939
- this.logger.info?.(
38940
- `[Go2rtcTcpServer] restarting native stream (clients=${this.connectedClients.size}, prestart=${this.prestartStream})`
38941
- );
38942
- this.startNativeStream();
38943
- }
38944
- }
38945
- });
38946
- this.nativeFanout.start();
38947
- this.startStreamHealthMonitor();
38948
- }
38949
- async stopNativeStream() {
38950
- this.nativeStreamStopping = true;
38951
- this.nativeStreamActive = false;
38952
- this.clearNativeStreamRetry();
38953
- this.stopStreamHealthMonitor();
38954
- const fanout = this.nativeFanout;
38955
- this.nativeFanout = null;
38956
- try {
38957
- if (fanout) {
38958
- await fanout.stop();
38959
- }
38960
- this.prebuffer = [];
38961
- if (this.dedicatedSessionRelease) {
38962
- await this.dedicatedSessionRelease().catch(() => {
38963
- });
38964
- this.dedicatedSessionRelease = void 0;
38965
- }
38966
- } finally {
38967
- this.nativeStreamStopping = false;
38968
- }
38969
- }
38970
- // -----------------------------------------------------------------------
38971
- // Stream health monitoring
38972
- // -----------------------------------------------------------------------
38973
- startStreamHealthMonitor() {
38974
- this.stopStreamHealthMonitor();
38975
- if (this.streamTimeoutMs <= 0) return;
38976
- this.lastFrameAt = Date.now();
38977
- this.streamHealthTimer = setInterval(() => {
38978
- if (!this.nativeStreamActive || !this.active) {
38979
- this.stopStreamHealthMonitor();
38980
- return;
38981
- }
38982
- const silenceMs = Date.now() - this.lastFrameAt;
38983
- if (silenceMs > this.streamTimeoutMs) {
38984
- this.logger.warn?.(
38985
- `[Go2rtcTcpServer] stream inactivity timeout: no frames for ${(silenceMs / 1e3).toFixed(1)}s (threshold=${this.streamTimeoutMs}ms), totalReceived=${this.totalFramesReceived} clients=${this.connectedClients.size} \u2014 forcing stream restart`
38986
- );
38987
- this.stopStreamHealthMonitor();
38988
- const fanout = this.nativeFanout;
38989
- if (fanout) {
38990
- this.nativeStreamActive = false;
38991
- this.nativeFanout = null;
38992
- fanout.stop().catch(() => {
38993
- });
38994
- }
38995
- }
38996
- }, Math.min(this.streamTimeoutMs / 2, 5e3));
38997
- }
38998
- stopStreamHealthMonitor() {
38999
- if (this.streamHealthTimer) {
39000
- clearInterval(this.streamHealthTimer);
39001
- this.streamHealthTimer = void 0;
39002
- }
39003
- }
39004
- // -----------------------------------------------------------------------
39005
- // Client lifecycle
39006
- // -----------------------------------------------------------------------
39007
- removeClient(clientId, reason) {
39008
- if (!this.connectedClients.has(clientId)) return;
39009
- this.connectedClients.delete(clientId);
39010
- this.clientSockets.delete(clientId);
39011
- const silenceMs = this.lastFrameAt > 0 ? Date.now() - this.lastFrameAt : -1;
39012
- const silenceInfo = silenceMs >= 0 ? ` lastFrame=${(silenceMs / 1e3).toFixed(1)}s ago` : "";
39013
- this.logger.info?.(
39014
- `[Go2rtcTcpServer] client disconnected id=${clientId} reason=${reason ?? "unknown"} remaining=${this.connectedClients.size} totalRx=${this.totalFramesReceived} totalTx=${this.totalVideoFramesWritten}${silenceInfo}`
39015
- );
39016
- this.emit("clientDisconnected", clientId);
39017
- if (this.connectedClients.size === 0 && !this.prestartStream) {
39018
- this.scheduleStop();
39019
- }
39020
- }
39021
- scheduleStop() {
39022
- if (this.stopGraceTimer) return;
39023
- this.logger.info?.(
39024
- `[Go2rtcTcpServer] no clients, scheduling stream stop in ${this.gracePeriodMs}ms`
39025
- );
39026
- this.stopGraceTimer = setTimeout(async () => {
39027
- this.stopGraceTimer = void 0;
39028
- if (this.connectedClients.size === 0 && this.nativeStreamActive) {
39029
- this.logger.info?.("[Go2rtcTcpServer] grace period expired, stopping native stream");
39030
- await this.stopNativeStream();
39031
- }
39032
- }, this.gracePeriodMs);
39033
- }
39034
- };
39035
-
39036
38270
  // src/baichuan/stream/BaichuanHttpStreamServer.ts
39037
- var import_node_events9 = require("events");
38271
+ var import_node_events8 = require("events");
39038
38272
  var import_node_child_process10 = require("child_process");
39039
38273
  var http4 = __toESM(require("http"), 1);
39040
38274
  var NAL_START_CODE_4B4 = Buffer.from([0, 0, 0, 1]);
@@ -39081,7 +38315,7 @@ function isH264KeyframeFromAnnexB(annexB) {
39081
38315
  }
39082
38316
  return false;
39083
38317
  }
39084
- var BaichuanHttpStreamServer = class extends import_node_events9.EventEmitter {
38318
+ var BaichuanHttpStreamServer = class extends import_node_events8.EventEmitter {
39085
38319
  videoStream;
39086
38320
  listenPort;
39087
38321
  path;
@@ -39353,15 +38587,15 @@ var BaichuanHttpStreamServer = class extends import_node_events9.EventEmitter {
39353
38587
  };
39354
38588
 
39355
38589
  // src/baichuan/stream/BaichuanMjpegServer.ts
39356
- var import_node_events11 = require("events");
38590
+ var import_node_events10 = require("events");
39357
38591
  var http5 = __toESM(require("http"), 1);
39358
38592
 
39359
38593
  // src/baichuan/stream/MjpegTransformer.ts
39360
- var import_node_events10 = require("events");
38594
+ var import_node_events9 = require("events");
39361
38595
  var import_node_child_process11 = require("child_process");
39362
38596
  var JPEG_SOI = Buffer.from([255, 216]);
39363
38597
  var JPEG_EOI = Buffer.from([255, 217]);
39364
- var MjpegTransformer = class extends import_node_events10.EventEmitter {
38598
+ var MjpegTransformer = class extends import_node_events9.EventEmitter {
39365
38599
  options;
39366
38600
  ffmpeg = null;
39367
38601
  started = false;
@@ -39560,7 +38794,7 @@ Content-Length: ${frame.length}\r
39560
38794
  // src/baichuan/stream/BaichuanMjpegServer.ts
39561
38795
  init_H264Converter();
39562
38796
  init_H265Converter();
39563
- var BaichuanMjpegServer = class extends import_node_events11.EventEmitter {
38797
+ var BaichuanMjpegServer = class extends import_node_events10.EventEmitter {
39564
38798
  options;
39565
38799
  clients = /* @__PURE__ */ new Map();
39566
38800
  httpServer = null;
@@ -39841,14 +39075,14 @@ var BaichuanMjpegServer = class extends import_node_events11.EventEmitter {
39841
39075
  };
39842
39076
 
39843
39077
  // src/baichuan/stream/BaichuanWebRTCServer.ts
39844
- var import_node_events13 = require("events");
39078
+ var import_node_events12 = require("events");
39845
39079
  init_BcMediaAnnexBDecoder();
39846
39080
 
39847
39081
  // src/baichuan/stream/AacToOpusTranscoder.ts
39848
39082
  var import_node_child_process12 = require("child_process");
39849
39083
  var import_node_dgram3 = require("dgram");
39850
- var import_node_events12 = require("events");
39851
- var AacToOpusTranscoder = class extends import_node_events12.EventEmitter {
39084
+ var import_node_events11 = require("events");
39085
+ var AacToOpusTranscoder = class extends import_node_events11.EventEmitter {
39852
39086
  opts;
39853
39087
  socket = null;
39854
39088
  ffmpeg = null;
@@ -40065,7 +39299,7 @@ function getH264NalType(nalUnit) {
40065
39299
  function getH265NalType2(nalUnit) {
40066
39300
  return nalUnit[0] >> 1 & 63;
40067
39301
  }
40068
- var BaichuanWebRTCServer = class extends import_node_events13.EventEmitter {
39302
+ var BaichuanWebRTCServer = class extends import_node_events12.EventEmitter {
40069
39303
  options;
40070
39304
  sessions = /* @__PURE__ */ new Map();
40071
39305
  sessionIdCounter = 0;
@@ -41057,7 +40291,7 @@ Error: ${err}`
41057
40291
  };
41058
40292
 
41059
40293
  // src/baichuan/stream/BaichuanHlsServer.ts
41060
- var import_node_events14 = require("events");
40294
+ var import_node_events13 = require("events");
41061
40295
  var import_node_fs = __toESM(require("fs"), 1);
41062
40296
  var import_promises3 = __toESM(require("fs/promises"), 1);
41063
40297
  var import_node_os3 = __toESM(require("os"), 1);
@@ -41137,7 +40371,7 @@ function getNalTypes(codec, annexB) {
41137
40371
  }
41138
40372
  });
41139
40373
  }
41140
- var BaichuanHlsServer = class extends import_node_events14.EventEmitter {
40374
+ var BaichuanHlsServer = class extends import_node_events13.EventEmitter {
41141
40375
  api;
41142
40376
  channel;
41143
40377
  profile;
@@ -41706,10 +40940,10 @@ async function pingHost(host, timeoutMs = 3e3) {
41706
40940
  return false;
41707
40941
  }
41708
40942
  async function tcpReachabilityProbe(host, port, timeoutMs) {
41709
- const net7 = await import("net");
40943
+ const net6 = await import("net");
41710
40944
  return new Promise((resolve) => {
41711
40945
  let settled = false;
41712
- const socket = new net7.Socket();
40946
+ const socket = new net6.Socket();
41713
40947
  const timer = setTimeout(() => {
41714
40948
  if (settled) return;
41715
40949
  settled = true;
@@ -42302,10 +41536,10 @@ async function autoDetectDeviceType(inputs) {
42302
41536
  }
42303
41537
 
42304
41538
  // src/multifocal/compositeRtspServer.ts
42305
- var import_node_events15 = require("events");
41539
+ var import_node_events14 = require("events");
42306
41540
  var import_node_child_process14 = require("child_process");
42307
- var net5 = __toESM(require("net"), 1);
42308
- var CompositeRtspServer = class extends import_node_events15.EventEmitter {
41541
+ var net4 = __toESM(require("net"), 1);
41542
+ var CompositeRtspServer = class extends import_node_events14.EventEmitter {
42309
41543
  options;
42310
41544
  compositeStream = null;
42311
41545
  rtspServer = null;
@@ -42371,7 +41605,7 @@ var CompositeRtspServer = class extends import_node_events15.EventEmitter {
42371
41605
  const width = widerStreamInfo?.width ?? 1920;
42372
41606
  const height = widerStreamInfo?.height ?? 1080;
42373
41607
  const fps = widerStreamInfo?.frameRate ?? 25;
42374
- this.rtspServer = net5.createServer((socket) => {
41608
+ this.rtspServer = net4.createServer((socket) => {
42375
41609
  this.handleRtspConnection(socket);
42376
41610
  });
42377
41611
  await new Promise((resolve, reject) => {
@@ -43081,8 +42315,8 @@ var RtspBackchannel = class _RtspBackchannel {
43081
42315
  };
43082
42316
 
43083
42317
  // src/baichuan/stream/BaichuanRtspBackchannelServer.ts
43084
- var import_node_events16 = require("events");
43085
- var net6 = __toESM(require("net"), 1);
42318
+ var import_node_events15 = require("events");
42319
+ var net5 = __toESM(require("net"), 1);
43086
42320
  var crypto3 = __toESM(require("crypto"), 1);
43087
42321
  var md5Hex = (s) => crypto3.createHash("md5").update(s).digest("hex");
43088
42322
  var RTCP_KEEPALIVE_INTERVAL_MS = 1e4;
@@ -43138,7 +42372,7 @@ function extractPublicEndpoint(url, requestText) {
43138
42372
  if (hostHeader) return hostHeader;
43139
42373
  return null;
43140
42374
  }
43141
- var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends import_node_events16.EventEmitter {
42375
+ var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends import_node_events15.EventEmitter {
43142
42376
  listenHost;
43143
42377
  listenPort;
43144
42378
  logger;
@@ -43255,7 +42489,7 @@ var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends
43255
42489
  async start() {
43256
42490
  if (this.server) return;
43257
42491
  await new Promise((resolve, reject) => {
43258
- const server = net6.createServer((socket) => this.handleConnection(socket));
42492
+ const server = net5.createServer((socket) => this.handleConnection(socket));
43259
42493
  const onError = (err) => {
43260
42494
  server.removeListener("error", onError);
43261
42495
  reject(err);
@@ -44283,7 +43517,6 @@ function buildInitialStatus(config) {
44283
43517
  DUAL_LENS_DUAL_MOTION_MODELS,
44284
43518
  DUAL_LENS_MODELS,
44285
43519
  DUAL_LENS_SINGLE_MOTION_MODELS,
44286
- Go2rtcTcpServer,
44287
43520
  H264RtpDepacketizer,
44288
43521
  H265RtpDepacketizer,
44289
43522
  HlsSessionManager,