@apocaliss92/nodelink-js 0.4.11 → 0.4.13

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.
@@ -0,0 +1,2723 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/baichuan/stream/BaichuanVideoStream.ts
9
+ import { EventEmitter } from "events";
10
+ import * as fs2 from "fs";
11
+ import * as path2 from "path";
12
+
13
+ // src/protocol/crypto.ts
14
+ import { createCipheriv, createDecipheriv, createHash } from "crypto";
15
+
16
+ // src/protocol/constants.ts
17
+ var BC_TCP_DEFAULT_PORT = 9e3;
18
+ var BC_MAGIC = Buffer.from([240, 222, 188, 10]);
19
+ var BC_MAGIC_REV = Buffer.from([160, 203, 237, 15]);
20
+ var BC_XML_KEY = Uint8Array.from([
21
+ 31,
22
+ 45,
23
+ 60,
24
+ 75,
25
+ 90,
26
+ 105,
27
+ 120,
28
+ 255
29
+ ]);
30
+ var BC_AES_IV = Buffer.from("0123456789abcdef", "utf8");
31
+ var BC_CLASS_LEGACY = 25876;
32
+ var BC_CLASS_MODERN_20 = 26132;
33
+ var BC_CLASS_MODERN_24 = 25620;
34
+ var BC_CLASS_MODERN_24_ALT = 0;
35
+ var BC_CLASS_FILE_DOWNLOAD = 25730;
36
+ function bcHeaderHasPayloadOffset(messageClass) {
37
+ return messageClass === BC_CLASS_MODERN_24 || messageClass === BC_CLASS_MODERN_24_ALT || messageClass === BC_CLASS_FILE_DOWNLOAD;
38
+ }
39
+ var BC_CMD_ID_LOGIN = 1;
40
+ var BC_CMD_ID_LOGOUT = 2;
41
+ var BC_CMD_ID_VIDEO = 3;
42
+ var BC_CMD_ID_VIDEO_STOP = 4;
43
+ var BC_CMD_ID_FILE_INFO_LIST_REPLAY = 5;
44
+ var BC_CMD_ID_FILE_INFO_LIST_STOP = 7;
45
+ var BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO = 8;
46
+ var BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD = 13;
47
+ var BC_CMD_ID_FILE_INFO_LIST_OPEN = 14;
48
+ var BC_CMD_ID_FILE_INFO_LIST_GET = 15;
49
+ var BC_CMD_ID_FILE_INFO_LIST_CLOSE = 16;
50
+ var BC_CMD_ID_FIND_REC_VIDEO_OPEN = 272;
51
+ var BC_CMD_ID_FIND_REC_VIDEO_GET = 273;
52
+ var BC_CMD_ID_FIND_REC_VIDEO_CLOSE = 274;
53
+ var BC_CMD_ID_COVER_PREVIEW = 298;
54
+ var BC_CMD_ID_COVER_STANDALONE_458 = 458;
55
+ var BC_CMD_ID_COVER_STANDALONE_459 = 459;
56
+ var BC_CMD_ID_COVER_STANDALONE_460 = 460;
57
+ var BC_CMD_ID_COVER_STANDALONE_461 = 461;
58
+ var BC_CMD_ID_COVER_STANDALONE_462 = 462;
59
+ var BC_CMD_ID_COVER_RESPONSE = 138;
60
+ var BC_CMD_ID_TALK_ABILITY = 10;
61
+ var BC_CMD_ID_TALK_RESET = 11;
62
+ var BC_CMD_ID_TALK_CONFIG = 201;
63
+ var BC_CMD_ID_TALK = 202;
64
+ var BC_CMD_ID_PTZ_CONTROL = 18;
65
+ var BC_CMD_ID_PTZ_CONTROL_PRESET = 19;
66
+ var BC_CMD_ID_GET_PTZ_PRESET = 190;
67
+ var BC_CMD_ID_GET_PTZ_POSITION = 433;
68
+ var BC_CMD_ID_GET_ZOOM_FOCUS = 294;
69
+ var BC_CMD_ID_SET_ZOOM_FOCUS = 295;
70
+ var BC_CMD_ID_GET_BATTERY_INFO_LIST = 252;
71
+ var BC_CMD_ID_GET_BATTERY_INFO = 253;
72
+ var BC_CMD_ID_UDP_KEEP_ALIVE = 234;
73
+ var BC_CMD_ID_GET_PIR_INFO = 212;
74
+ var BC_CMD_ID_SET_PIR_INFO = 213;
75
+ var BC_CMD_ID_GET_MOTION_ALARM = 46;
76
+ var BC_CMD_ID_SET_MOTION_ALARM = 47;
77
+ var BC_CMD_ID_ALARM_EVENT_LIST = 33;
78
+ var BC_CMD_ID_GET_AI_ALARM = 342;
79
+ var BC_CMD_ID_SET_AI_ALARM = 343;
80
+ var BC_CMD_ID_GET_AUDIO_ALARM = 547;
81
+ var BC_CMD_ID_AUDIO_ALARM_PLAY = 263;
82
+ var BC_CMD_ID_GET_WHITE_LED = 289;
83
+ var BC_CMD_ID_SET_WHITE_LED_STATE = 288;
84
+ var BC_CMD_ID_SET_WHITE_LED_TASK = 290;
85
+ var BC_CMD_ID_FLOODLIGHT_STATUS_LIST = 291;
86
+ var BC_CMD_ID_ABILITY_INFO = 151;
87
+ var BC_CMD_ID_SUPPORT = 199;
88
+ var BC_CMD_ID_PING = 93;
89
+ var BC_CMD_ID_CHANNEL_INFO_ALL = 145;
90
+ var BC_CMD_ID_GET_OSD_DATETIME = 44;
91
+ var BC_CMD_ID_SET_OSD_DATETIME = 45;
92
+ var BC_CMD_ID_GET_RECORD_CFG = 54;
93
+ var BC_CMD_ID_GET_ABILITY_SUPPORT = 58;
94
+ var BC_CMD_ID_GET_FTP_TASK = 70;
95
+ var BC_CMD_ID_GET_VERSION_INFO = 80;
96
+ var BC_CMD_ID_GET_RECORD = 81;
97
+ var BC_CMD_ID_GET_HDD_INFO_LIST = 102;
98
+ var BC_CMD_ID_GET_WIFI_SIGNAL = 115;
99
+ var BC_CMD_ID_GET_WIFI = 116;
100
+ var BC_CMD_ID_GET_ONLINE_USER_LIST = 120;
101
+ var BC_CMD_ID_GET_DAY_RECORDS = 142;
102
+ var BC_CMD_ID_GET_STREAM_INFO_LIST = 146;
103
+ var BC_CMD_ID_GET_LED_STATE = 208;
104
+ var BC_CMD_ID_GET_EMAIL_TASK = 217;
105
+ var BC_CMD_ID_GET_AUDIO_TASK = 232;
106
+ var BC_CMD_ID_GET_AUDIO_CFG = 264;
107
+ var BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD = 296;
108
+ var BC_CMD_ID_GET_TIMELAPSE_CFG = 319;
109
+ var BC_CMD_ID_GET_AI_DENOISE = 439;
110
+ var BC_CMD_ID_GET_KIT_AP_CFG = 481;
111
+ var BC_CMD_ID_GET_REC_ENC_CFG = 507;
112
+ var BC_CMD_ID_GET_ACCESS_USER_LIST = 511;
113
+ var BC_CMD_ID_GET_SLEEP_STATE = 574;
114
+ var BC_CMD_ID_GET_VIDEO_INPUT = 26;
115
+ var BC_CMD_ID_GET_SYSTEM_GENERAL = 104;
116
+ var BC_CMD_ID_GET_SUPPORT = 199;
117
+ var BC_CMD_ID_GET_AI_CFG = 299;
118
+ var BC_CMD_ID_SET_AI_CFG = 300;
119
+ var BC_CMD_ID_GET_SIREN_STATUS = 547;
120
+ var BC_CMD_ID_SET_AUDIO_TASK = 231;
121
+ var BC_CMD_ID_SET_VIDEO_INPUT = 25;
122
+ var BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD = 297;
123
+ var BC_CMD_ID_GET_ENC = 56;
124
+ var BC_CMD_ID_SET_ENC = 57;
125
+ var BC_CMD_ID_GET_PRIVACY_MASK = 52;
126
+ var BC_CMD_ID_SET_PRIVACY_MASK = 53;
127
+ var BC_CMD_ID_SET_AI_DENOISE = 440;
128
+ var BC_CMD_ID_SET_LED_STATE = 209;
129
+ var BC_CMD_ID_SET_AUDIO_CFG = 265;
130
+ var BC_CMD_ID_SET_RECORD = 82;
131
+ var BC_CMD_ID_SET_RECORD_CFG = 55;
132
+ var BC_CMD_ID_SET_EMAIL_TASK = 216;
133
+ var BC_CMD_ID_GET_PUSH_TASK = 219;
134
+ var BC_CMD_ID_SET_PUSH_TASK = 218;
135
+ var BC_CMD_ID_GET_AUTO_FOCUS = 224;
136
+ var BC_CMD_ID_SET_AUTO_FOCUS = 225;
137
+ var BC_CMD_ID_GET_EMAIL = 42;
138
+ var BC_CMD_ID_SET_EMAIL = 43;
139
+ var BC_CMD_ID_TEST_EMAIL = 141;
140
+ var BC_CMD_ID_GET_NTP = 38;
141
+ var BC_CMD_ID_SET_NTP = 39;
142
+ var BC_CMD_ID_SET_SYSTEM_GENERAL = 105;
143
+ var BC_CMD_ID_GET_DST = 106;
144
+ var BC_CMD_ID_SET_DST = 107;
145
+ var BC_CMD_ID_GET_AUTO_REBOOT = 101;
146
+ var BC_CMD_ID_SET_AUTO_REBOOT = 100;
147
+ var BC_CMD_ID_CMD_123 = 123;
148
+ var BC_CMD_ID_CMD_209 = 209;
149
+ var BC_CMD_ID_CMD_265 = 265;
150
+ var BC_CMD_ID_CMD_440 = 440;
151
+ var BC_CMD_ID_PUSH_VIDEO_INPUT = 78;
152
+ var BC_CMD_ID_PUSH_SERIAL = 79;
153
+ var BC_CMD_ID_PUSH_NET_INFO = 464;
154
+ var BC_CMD_ID_PUSH_DINGDONG_LIST = 484;
155
+ var BC_CMD_ID_PUSH_SLEEP_STATUS = 623;
156
+ var BC_CMD_ID_PUSH_COORDINATE_POINT_LIST = 723;
157
+ var BC_CMD_ID_DING_DONG_CTRL = 483;
158
+ var BC_CMD_ID_GET_DING_DONG_LIST = 484;
159
+ var BC_CMD_ID_DING_DONG_OPT = 485;
160
+ var BC_CMD_ID_GET_DING_DONG_CFG = 486;
161
+ var BC_CMD_ID_SET_DING_DONG_CFG = 487;
162
+ var BC_CMD_ID_QUICK_REPLY_PLAY = 349;
163
+ var BC_CMD_ID_GET_DING_DONG_SILENT = 609;
164
+ var BC_CMD_ID_SET_DING_DONG_SILENT = 610;
165
+
166
+ // src/protocol/crypto.ts
167
+ function md5HexUpper(input) {
168
+ return createHash("md5").update(input, "utf8").digest("hex").toUpperCase();
169
+ }
170
+ function md5StrModern(input) {
171
+ return md5HexUpper(input).slice(0, 31);
172
+ }
173
+ function deriveAesKey(nonce, password) {
174
+ const keyStr = md5StrModern(`${nonce}-${password}`).slice(0, 16);
175
+ return Buffer.from(keyStr, "utf8");
176
+ }
177
+ function bcEncrypt(buf, offset) {
178
+ const off = offset & 255;
179
+ const out = Buffer.allocUnsafe(buf.length);
180
+ for (let i = 0; i < buf.length; i++) {
181
+ const key = BC_XML_KEY[(off + i) % BC_XML_KEY.length];
182
+ out[i] = buf[i] ^ key ^ off;
183
+ }
184
+ return out;
185
+ }
186
+ function bcDecrypt(buf, offset) {
187
+ return bcEncrypt(buf, offset);
188
+ }
189
+ function aesEncrypt(buf, key) {
190
+ if (buf.length === 0) return Buffer.alloc(0);
191
+ const cipher = createCipheriv("aes-128-cfb", key, BC_AES_IV);
192
+ cipher.setAutoPadding(false);
193
+ return Buffer.concat([cipher.update(buf), cipher.final()]);
194
+ }
195
+ function aesDecrypt(buf, key) {
196
+ if (buf.length === 0) return Buffer.alloc(0);
197
+ const decipher = createDecipheriv("aes-128-cfb", key, BC_AES_IV);
198
+ decipher.setAutoPadding(false);
199
+ return Buffer.concat([decipher.update(buf), decipher.final()]);
200
+ }
201
+ var AesStreamDecryptor = class {
202
+ decipher = null;
203
+ key;
204
+ constructor(key) {
205
+ this.key = key;
206
+ }
207
+ /**
208
+ * Reset the cipher state (start decrypting with fresh IV).
209
+ * Call this when a new BcMedia packet is detected.
210
+ */
211
+ reset() {
212
+ this.decipher = createDecipheriv("aes-128-cfb", this.key, BC_AES_IV);
213
+ this.decipher.setAutoPadding(false);
214
+ }
215
+ /**
216
+ * Decrypt a chunk using the current cipher state.
217
+ * Automatically resets if no cipher exists.
218
+ *
219
+ * @param buf - Encrypted buffer to decrypt
220
+ * @returns Decrypted buffer
221
+ */
222
+ update(buf) {
223
+ if (buf.length === 0) return Buffer.alloc(0);
224
+ if (!this.decipher) this.reset();
225
+ return this.decipher.update(buf);
226
+ }
227
+ /**
228
+ * Check if the decryptor has been initialized.
229
+ */
230
+ isInitialized() {
231
+ return this.decipher !== null;
232
+ }
233
+ };
234
+
235
+ // src/debug/DebugConfig.ts
236
+ import * as fs from "fs";
237
+ import * as path from "path";
238
+ function normalizeDebugOptions(opts) {
239
+ const general = opts?.general === true;
240
+ const debugRtsp = opts?.debugRtsp === true;
241
+ const traceNativeStream = opts?.traceNativeStream === true;
242
+ const traceRecordings = opts?.traceRecordings === true;
243
+ const traceTalk = opts?.traceTalk === true;
244
+ const traceEvents = opts?.traceEvents === true;
245
+ const dumpEnabled = opts?.dump?.enabled === true;
246
+ const dumpDir = opts?.dump?.dir && opts.dump.dir.trim() || path.join(process.cwd(), "test", "frames-debug");
247
+ const dumpBcMedia = opts?.dump?.bcmedia ?? dumpEnabled;
248
+ const dumpNals = opts?.dump?.nals ?? dumpEnabled;
249
+ return {
250
+ general,
251
+ debugRtsp,
252
+ traceNativeStream,
253
+ traceRecordings,
254
+ traceTalk,
255
+ traceEvents,
256
+ dumpEnabled,
257
+ dumpDir,
258
+ dumpBcMedia,
259
+ dumpNals
260
+ };
261
+ }
262
+ function recordingsTraceLog(cfg, logger, tag, message) {
263
+ if (!cfg?.traceRecordings || !logger) return;
264
+ logger.log(`[${tag}] ${message}`);
265
+ }
266
+ function ensureDumpDir(cfg) {
267
+ if (!cfg.dumpEnabled) return;
268
+ fs.mkdirSync(cfg.dumpDir, { recursive: true });
269
+ }
270
+ function debugLog(cfg, logger, tag, message) {
271
+ if (!cfg.general) return;
272
+ logger.debug(`[${tag}] ${message}`);
273
+ }
274
+ function traceLog(cfg, logger, tag, message) {
275
+ if (!cfg.traceNativeStream) return;
276
+ logger.debug(`[${tag}] ${message}`);
277
+ }
278
+ function talkTraceLog(cfg, logger, tag, message) {
279
+ if (!cfg.traceTalk) return;
280
+ logger.debug(`[${tag}] ${message}`);
281
+ }
282
+ function eventTraceLog(cfg, logger, tag, message) {
283
+ if (!cfg.traceEvents) return;
284
+ logger.debug(`[${tag}] ${message}`);
285
+ }
286
+
287
+ // src/baichuan/stream/BcMediaParser.ts
288
+ var MAGIC_INFO_V1 = 825241649;
289
+ var MAGIC_INFO_V2 = 842018865;
290
+ var MAGIC_IFRAME_START = 1667510320;
291
+ var MAGIC_IFRAME_END = 1667510329;
292
+ var MAGIC_PFRAME_START = 1667510576;
293
+ var MAGIC_PFRAME_END = 1667510585;
294
+ var MAGIC_AAC = 1651979568;
295
+ var MAGIC_ADPCM = 1651978544;
296
+ var PAD_SIZE = 8;
297
+ function parseBcMedia(buf) {
298
+ if (buf.length < 4) return null;
299
+ const magic = buf.readUInt32LE(0);
300
+ if (magic === MAGIC_INFO_V1) {
301
+ return parseInfoV1(buf);
302
+ } else if (magic === MAGIC_INFO_V2) {
303
+ return parseInfoV2(buf);
304
+ } else if (magic >= MAGIC_IFRAME_START && magic <= MAGIC_IFRAME_END) {
305
+ return parseIframe(buf);
306
+ } else if (magic >= MAGIC_PFRAME_START && magic <= MAGIC_PFRAME_END) {
307
+ return parsePframe(buf);
308
+ } else if (magic === MAGIC_AAC) {
309
+ return parseAac(buf);
310
+ } else if (magic === MAGIC_ADPCM) {
311
+ return parseAdpcm(buf);
312
+ }
313
+ return null;
314
+ }
315
+ function parseInfoV1(buf) {
316
+ if (buf.length < 32) return null;
317
+ const headerSize = buf.readUInt32LE(4);
318
+ if (headerSize !== 32) return null;
319
+ const media = {
320
+ type: "InfoV1",
321
+ videoWidth: buf.readUInt32LE(8),
322
+ videoHeight: buf.readUInt32LE(12),
323
+ fps: buf.readUInt8(17),
324
+ startYear: buf.readUInt8(18),
325
+ startMonth: buf.readUInt8(19),
326
+ startDay: buf.readUInt8(20),
327
+ startHour: buf.readUInt8(21),
328
+ startMin: buf.readUInt8(22),
329
+ startSeconds: buf.readUInt8(23),
330
+ endYear: buf.readUInt8(24),
331
+ endMonth: buf.readUInt8(25),
332
+ endDay: buf.readUInt8(26),
333
+ endHour: buf.readUInt8(27),
334
+ endMin: buf.readUInt8(28),
335
+ endSeconds: buf.readUInt8(29)
336
+ };
337
+ return { media, consumed: 32 };
338
+ }
339
+ function parseInfoV2(buf) {
340
+ if (buf.length < 32) return null;
341
+ const headerSize = buf.readUInt32LE(4);
342
+ if (headerSize !== 32) return null;
343
+ const media = {
344
+ type: "InfoV2",
345
+ videoWidth: buf.readUInt32LE(8),
346
+ videoHeight: buf.readUInt32LE(12),
347
+ fps: buf.readUInt8(17),
348
+ startYear: buf.readUInt8(18),
349
+ startMonth: buf.readUInt8(19),
350
+ startDay: buf.readUInt8(20),
351
+ startHour: buf.readUInt8(21),
352
+ startMin: buf.readUInt8(22),
353
+ startSeconds: buf.readUInt8(23),
354
+ endYear: buf.readUInt8(24),
355
+ endMonth: buf.readUInt8(25),
356
+ endDay: buf.readUInt8(26),
357
+ endHour: buf.readUInt8(27),
358
+ endMin: buf.readUInt8(28),
359
+ endSeconds: buf.readUInt8(29)
360
+ };
361
+ return { media, consumed: 32 };
362
+ }
363
+ function parseIframe(buf) {
364
+ if (buf.length < 20) return null;
365
+ const videoTypeStr = buf.toString("utf8", 4, 8);
366
+ if (videoTypeStr !== "H264" && videoTypeStr !== "H265") return null;
367
+ const videoType = videoTypeStr;
368
+ const payloadSize = buf.readUInt32LE(8);
369
+ const additionalHeaderSize = buf.readUInt32LE(12);
370
+ const microseconds = buf.readUInt32LE(16);
371
+ let offset = 20;
372
+ const unknown = buf.readUInt32LE(offset);
373
+ offset += 4;
374
+ let time;
375
+ if (buf.length < offset + additionalHeaderSize) return null;
376
+ const additionalHeader = buf.subarray(offset, offset + additionalHeaderSize);
377
+ if (additionalHeaderSize >= 4) {
378
+ time = additionalHeader.readUInt32LE(0);
379
+ }
380
+ offset += additionalHeaderSize;
381
+ if (buf.length < offset + payloadSize) return null;
382
+ const data = buf.subarray(offset, offset + payloadSize);
383
+ offset += payloadSize;
384
+ const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - payloadSize % PAD_SIZE;
385
+ if (buf.length < offset + padSize) return null;
386
+ offset += padSize;
387
+ const media = {
388
+ type: "Iframe",
389
+ videoType,
390
+ microseconds,
391
+ ...time !== void 0 ? { time } : {},
392
+ additionalHeader,
393
+ additionalHeaderSize,
394
+ unknown,
395
+ data
396
+ };
397
+ return { media, consumed: offset };
398
+ }
399
+ function parsePframe(buf) {
400
+ if (buf.length < 20) return null;
401
+ const videoTypeStr = buf.toString("utf8", 4, 8);
402
+ if (videoTypeStr !== "H264" && videoTypeStr !== "H265") return null;
403
+ const videoType = videoTypeStr;
404
+ const payloadSize = buf.readUInt32LE(8);
405
+ const additionalHeaderSize = buf.readUInt32LE(12);
406
+ const microseconds = buf.readUInt32LE(16);
407
+ let offset = 20;
408
+ const unknown = buf.readUInt32LE(offset);
409
+ offset += 4;
410
+ if (buf.length < offset + additionalHeaderSize) return null;
411
+ const additionalHeader = buf.subarray(offset, offset + additionalHeaderSize);
412
+ offset += additionalHeaderSize;
413
+ if (buf.length < offset + payloadSize) return null;
414
+ const data = buf.subarray(offset, offset + payloadSize);
415
+ offset += payloadSize;
416
+ const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - payloadSize % PAD_SIZE;
417
+ if (buf.length < offset + padSize) return null;
418
+ offset += padSize;
419
+ const media = {
420
+ type: "Pframe",
421
+ videoType,
422
+ microseconds,
423
+ additionalHeader,
424
+ additionalHeaderSize,
425
+ unknown,
426
+ data
427
+ };
428
+ return { media, consumed: offset };
429
+ }
430
+ function parseAac(buf) {
431
+ if (buf.length < 12) return null;
432
+ const payloadSize = buf.readUInt16LE(4);
433
+ const payloadSizeB = buf.readUInt16LE(6);
434
+ if (payloadSize !== payloadSizeB) return null;
435
+ const headerLen = 8;
436
+ const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - payloadSize % PAD_SIZE;
437
+ const totalLen = headerLen + payloadSize + padSize;
438
+ if (buf.length < totalLen) return null;
439
+ const data = buf.subarray(headerLen, headerLen + payloadSize);
440
+ const media = {
441
+ type: "Aac",
442
+ data
443
+ };
444
+ return { media, consumed: totalLen };
445
+ }
446
+ function parseAdpcm(buf) {
447
+ if (buf.length < 12) return null;
448
+ const payloadSize = buf.readUInt16LE(4);
449
+ const payloadSizeB = buf.readUInt16LE(6);
450
+ if (payloadSize !== payloadSizeB) return null;
451
+ const magicData = buf.readUInt16LE(8);
452
+ if (magicData !== 256) return null;
453
+ const halfBlockSize = buf.readUInt16LE(10);
454
+ void halfBlockSize;
455
+ const subHeaderSize = 4;
456
+ if (payloadSize < subHeaderSize) return null;
457
+ const blockSize = payloadSize - subHeaderSize;
458
+ const headerLen = 12;
459
+ const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - payloadSize % PAD_SIZE;
460
+ const totalLen = headerLen + blockSize + padSize;
461
+ if (buf.length < totalLen) return null;
462
+ const data = buf.subarray(headerLen, headerLen + blockSize);
463
+ const media = {
464
+ type: "Adpcm",
465
+ data
466
+ };
467
+ return { media, consumed: totalLen };
468
+ }
469
+
470
+ // src/baichuan/stream/BcMediaCodec.ts
471
+ var BcMediaCodec = class {
472
+ buffer = Buffer.alloc(0);
473
+ strict;
474
+ amountSkipped = 0;
475
+ logger;
476
+ onUnknownChunk;
477
+ constructor(strict = false, logger) {
478
+ this.strict = strict;
479
+ this.logger = logger;
480
+ }
481
+ /** Register a listener that fires for every unknown chunk before recovery. */
482
+ setUnknownChunkListener(listener) {
483
+ this.onUnknownChunk = listener;
484
+ }
485
+ /**
486
+ * Push data into the codec buffer and try to parse complete BcMedia packets.
487
+ * Returns an array of complete BcMedia packets found.
488
+ *
489
+ * @param chunk - New data chunk to add to buffer
490
+ * @returns Array of complete BcMedia packets (empty if none complete yet)
491
+ */
492
+ decode(chunk) {
493
+ this.buffer = this.buffer.length === 0 ? chunk : Buffer.concat([this.buffer, chunk]);
494
+ const results = [];
495
+ while (this.buffer.length >= 4) {
496
+ const result = parseBcMedia(this.buffer);
497
+ if (result) {
498
+ if (this.amountSkipped > 0) {
499
+ if (this.strict) {
500
+ this.logger?.warn(`[BcMediaCodec] Recovered stream after skipping ${this.amountSkipped} bytes`);
501
+ } else {
502
+ this.logger?.warn(`[BcMediaCodec] Recovered stream after skipping ${this.amountSkipped} bytes`);
503
+ }
504
+ this.amountSkipped = 0;
505
+ }
506
+ results.push(result.media);
507
+ this.buffer = this.buffer.subarray(result.consumed);
508
+ } else {
509
+ const isKnownMagic = (magic2) => {
510
+ const isInfoV1 = magic2 === 825241649;
511
+ const isInfoV2 = magic2 === 842018865;
512
+ const isIFrame = magic2 >= 1667510320 && magic2 <= 1667510329;
513
+ const isPFrame = magic2 >= 1667510576 && magic2 <= 1667510585;
514
+ const isAac = magic2 === 1651979568;
515
+ const isAdpcm = magic2 === 1651978544;
516
+ return isInfoV1 || isInfoV2 || isIFrame || isPFrame || isAac || isAdpcm;
517
+ };
518
+ const magic = this.buffer.readUInt32LE(0);
519
+ const startsWithKnownMagic = isKnownMagic(magic);
520
+ if (startsWithKnownMagic) {
521
+ break;
522
+ }
523
+ if (this.strict) {
524
+ throw new Error(`[BcMediaCodec] Invalid data in stream (no valid magic at buffer start, len=${this.buffer.length})`);
525
+ }
526
+ if (this.amountSkipped === 0) {
527
+ this.logger?.warn(`[BcMediaCodec] Error in stream, attempting to recover...`);
528
+ }
529
+ let next = -1;
530
+ for (const off of [528, 1056, 1584]) {
531
+ if (this.buffer.length >= off + 4 && isKnownMagic(this.buffer.readUInt32LE(off))) {
532
+ next = off;
533
+ break;
534
+ }
535
+ }
536
+ if (next < 0) {
537
+ for (let i = 1; i <= this.buffer.length - 4; i++) {
538
+ if (isKnownMagic(this.buffer.readUInt32LE(i))) {
539
+ next = i;
540
+ break;
541
+ }
542
+ }
543
+ }
544
+ if (next > 0) {
545
+ if (this.onUnknownChunk) {
546
+ this.onUnknownChunk({
547
+ magic,
548
+ preview: Buffer.from(this.buffer.subarray(0, Math.min(256, next))),
549
+ skipped: next
550
+ });
551
+ }
552
+ this.amountSkipped += next;
553
+ this.buffer = this.buffer.subarray(next);
554
+ continue;
555
+ }
556
+ if (this.buffer.length > 3) {
557
+ const keep = 3;
558
+ this.amountSkipped += this.buffer.length - keep;
559
+ this.buffer = this.buffer.subarray(this.buffer.length - keep);
560
+ }
561
+ break;
562
+ }
563
+ }
564
+ return results;
565
+ }
566
+ /**
567
+ * Get remaining buffer (for debugging)
568
+ */
569
+ getRemainingBuffer() {
570
+ return this.buffer;
571
+ }
572
+ /**
573
+ * Clear the buffer (useful for resetting the codec)
574
+ */
575
+ clear() {
576
+ this.buffer = Buffer.alloc(0);
577
+ this.amountSkipped = 0;
578
+ }
579
+ };
580
+
581
+ // src/baichuan/stream/H264Converter.ts
582
+ var NAL_START_CODE_4B = Buffer.from([0, 0, 0, 1]);
583
+ var NAL_START_CODE_3B = Buffer.from([0, 0, 1]);
584
+ function hasStartCodes(data) {
585
+ if (data.length < 4) return false;
586
+ if (data.subarray(0, 4).equals(NAL_START_CODE_4B)) return true;
587
+ if (data.subarray(0, 3).equals(NAL_START_CODE_3B)) return true;
588
+ return false;
589
+ }
590
+ function tryConvertWithLengthReader(data, readLen) {
591
+ const result = [];
592
+ let offset = 0;
593
+ let nalCount = 0;
594
+ while (offset < data.length) {
595
+ if (offset + 4 > data.length) return null;
596
+ const nalLength = readLen(data, offset);
597
+ offset += 4;
598
+ if (nalLength <= 0) return null;
599
+ if (nalLength > data.length - offset) return null;
600
+ result.push(NAL_START_CODE_4B);
601
+ result.push(data.subarray(offset, offset + nalLength));
602
+ offset += nalLength;
603
+ nalCount++;
604
+ }
605
+ if (nalCount === 0) return null;
606
+ return Buffer.concat(result);
607
+ }
608
+ function tryConvertWithLengthReader16(data, readLen) {
609
+ const result = [];
610
+ let offset = 0;
611
+ let nalCount = 0;
612
+ while (offset < data.length) {
613
+ if (offset + 2 > data.length) return null;
614
+ const nalLength = readLen(data, offset);
615
+ offset += 2;
616
+ if (nalLength <= 0) return null;
617
+ if (nalLength > data.length - offset) return null;
618
+ result.push(NAL_START_CODE_4B);
619
+ result.push(data.subarray(offset, offset + nalLength));
620
+ offset += nalLength;
621
+ nalCount++;
622
+ }
623
+ if (nalCount === 0) return null;
624
+ return Buffer.concat(result);
625
+ }
626
+ function tryConvertWithLengthReader24(data, endian) {
627
+ const result = [];
628
+ let offset = 0;
629
+ let nalCount = 0;
630
+ const readLen24 = (buf, at) => {
631
+ if (at + 3 > buf.length) return 0;
632
+ const b0 = buf[at];
633
+ const b1 = buf[at + 1];
634
+ const b2 = buf[at + 2];
635
+ return endian === "be" ? (b0 << 16 | b1 << 8 | b2) >>> 0 : (b2 << 16 | b1 << 8 | b0) >>> 0;
636
+ };
637
+ while (offset < data.length) {
638
+ if (offset + 3 > data.length) return null;
639
+ const nalLength = readLen24(data, offset);
640
+ offset += 3;
641
+ if (nalLength <= 0) return null;
642
+ if (nalLength > data.length - offset) return null;
643
+ result.push(NAL_START_CODE_4B);
644
+ result.push(data.subarray(offset, offset + nalLength));
645
+ offset += nalLength;
646
+ nalCount++;
647
+ }
648
+ if (nalCount === 0) return null;
649
+ return Buffer.concat(result);
650
+ }
651
+ function looksLikeSingleH264Nal(nalPayload) {
652
+ if (nalPayload.length < 1) return false;
653
+ const b0 = nalPayload[0];
654
+ if (b0 === void 0) return false;
655
+ if ((b0 & 128) !== 0) return false;
656
+ const nalType = b0 & 31;
657
+ return nalType >= 1 && nalType <= 23;
658
+ }
659
+ function depacketizeRtpAggregationToAnnexB(payload) {
660
+ if (payload.length < 1) return null;
661
+ const nalHeader = payload[0];
662
+ const nalType = nalHeader & 31;
663
+ const out = [];
664
+ const pushNal = (nal) => {
665
+ if (nal.length === 0) return;
666
+ out.push(NAL_START_CODE_4B, nal);
667
+ };
668
+ if (nalType === 24) {
669
+ let off = 1;
670
+ while (off + 2 <= payload.length) {
671
+ const size = payload.readUInt16BE(off);
672
+ off += 2;
673
+ if (size <= 0 || off + size > payload.length) return null;
674
+ pushNal(payload.subarray(off, off + size));
675
+ off += size;
676
+ }
677
+ return out.length ? Buffer.concat(out) : null;
678
+ }
679
+ if (nalType === 25) {
680
+ let off = 1 + 2;
681
+ if (off > payload.length) return null;
682
+ while (off + 2 <= payload.length) {
683
+ const size = payload.readUInt16BE(off);
684
+ off += 2;
685
+ if (size <= 0 || off + size > payload.length) return null;
686
+ pushNal(payload.subarray(off, off + size));
687
+ off += size;
688
+ }
689
+ return out.length ? Buffer.concat(out) : null;
690
+ }
691
+ if (nalType === 26) {
692
+ let off = 1 + 2;
693
+ if (off > payload.length) return null;
694
+ while (off + 2 <= payload.length) {
695
+ const size = payload.readUInt16BE(off);
696
+ off += 2;
697
+ if (off + 1 + 2 > payload.length) return null;
698
+ off += 1;
699
+ off += 2;
700
+ if (size <= 0 || off + size > payload.length) return null;
701
+ pushNal(payload.subarray(off, off + size));
702
+ off += size;
703
+ }
704
+ return out.length ? Buffer.concat(out) : null;
705
+ }
706
+ if (nalType === 27) {
707
+ let off = 1 + 2;
708
+ if (off > payload.length) return null;
709
+ while (off + 2 <= payload.length) {
710
+ const size = payload.readUInt16BE(off);
711
+ off += 2;
712
+ if (off + 1 + 3 > payload.length) return null;
713
+ off += 1;
714
+ off += 3;
715
+ if (size <= 0 || off + size > payload.length) return null;
716
+ pushNal(payload.subarray(off, off + size));
717
+ off += size;
718
+ }
719
+ return out.length ? Buffer.concat(out) : null;
720
+ }
721
+ return null;
722
+ }
723
+ function convertToAnnexB(data) {
724
+ if (hasStartCodes(data)) {
725
+ return data;
726
+ }
727
+ const sc4 = Buffer.from([0, 0, 0, 1]);
728
+ const sc3 = Buffer.from([0, 0, 1]);
729
+ const maxScan = Math.min(64, data.length);
730
+ const idx4 = data.subarray(0, maxScan).indexOf(sc4);
731
+ if (idx4 > 0) return data.subarray(idx4);
732
+ const idx3 = data.subarray(0, maxScan).indexOf(sc3);
733
+ if (idx3 > 0) return data.subarray(idx3);
734
+ const be = tryConvertWithLengthReader(data, (b, o) => b.readUInt32BE(o));
735
+ if (be) return be;
736
+ const le = tryConvertWithLengthReader(data, (b, o) => b.readUInt32LE(o));
737
+ if (le) return le;
738
+ const be24 = tryConvertWithLengthReader24(data, "be");
739
+ if (be24) return be24;
740
+ const le24 = tryConvertWithLengthReader24(data, "le");
741
+ if (le24) return le24;
742
+ const be16 = tryConvertWithLengthReader16(data, (b, o) => b.readUInt16BE(o));
743
+ if (be16) return be16;
744
+ const le16 = tryConvertWithLengthReader16(data, (b, o) => b.readUInt16LE(o));
745
+ if (le16) return le16;
746
+ const agg = depacketizeRtpAggregationToAnnexB(data);
747
+ if (agg) return agg;
748
+ if (looksLikeSingleH264Nal(data)) {
749
+ return Buffer.concat([NAL_START_CODE_4B, data]);
750
+ }
751
+ return data;
752
+ }
753
+ function splitAnnexBToNalPayloads(annexB) {
754
+ const starts = [];
755
+ for (let i = 0; i < annexB.length - 3; i++) {
756
+ if (annexB[i] === 0 && annexB[i + 1] === 0) {
757
+ if (annexB[i + 2] === 1) {
758
+ starts.push({ idx: i, len: 3 });
759
+ i += 2;
760
+ } else if (annexB[i + 2] === 0 && annexB[i + 3] === 1) {
761
+ starts.push({ idx: i, len: 4 });
762
+ i += 3;
763
+ }
764
+ }
765
+ }
766
+ if (starts.length === 0) return [];
767
+ const out = [];
768
+ for (let s = 0; s < starts.length; s++) {
769
+ const st = starts[s];
770
+ const start = st.idx + st.len;
771
+ const end = starts[s + 1] ? starts[s + 1].idx : annexB.length;
772
+ if (end > start) out.push(annexB.subarray(start, end));
773
+ }
774
+ return out;
775
+ }
776
+ function isValidH264AnnexBAccessUnit(annexB) {
777
+ if (!hasStartCodes(annexB)) return false;
778
+ const nals = splitAnnexBToNalPayloads(annexB);
779
+ if (nals.length === 0) return false;
780
+ for (const nal of nals) {
781
+ if (nal.length < 1) return false;
782
+ const b0 = nal[0];
783
+ if (b0 === void 0) return false;
784
+ if ((b0 & 128) !== 0) return false;
785
+ const nalType = b0 & 31;
786
+ if (nalType === 0 || nalType >= 24) return false;
787
+ }
788
+ return true;
789
+ }
790
+ function isH264KeyframeAnnexB(annexB) {
791
+ const nals = splitAnnexBToNalPayloads(annexB);
792
+ let hasSps = false;
793
+ let hasPps = false;
794
+ let hasIdr = false;
795
+ for (const nal of nals) {
796
+ const t = (nal[0] ?? 0) & 31;
797
+ if (t === 7) hasSps = true;
798
+ if (t === 8) hasPps = true;
799
+ if (t === 5) hasIdr = true;
800
+ }
801
+ return hasIdr && hasSps && hasPps;
802
+ }
803
+ var H264RtpDepacketizer = class _H264RtpDepacketizer {
804
+ fuNalHeader = null;
805
+ fuParts = [];
806
+ static parseRtpPayload(packet) {
807
+ if (!packet || packet.length < 12) return null;
808
+ const version = packet[0] >> 6 & 3;
809
+ if (version !== 2) return null;
810
+ const padding = (packet[0] & 32) !== 0;
811
+ const extension = (packet[0] & 16) !== 0;
812
+ const csrcCount = packet[0] & 15;
813
+ let offset = 12 + csrcCount * 4;
814
+ if (offset > packet.length) return null;
815
+ if (extension) {
816
+ if (offset + 4 > packet.length) return null;
817
+ const extLenWords = packet.readUInt16BE(offset + 2);
818
+ offset += 4 + extLenWords * 4;
819
+ if (offset > packet.length) return null;
820
+ }
821
+ let end = packet.length;
822
+ if (padding) {
823
+ const padLen = packet[packet.length - 1];
824
+ if (padLen <= 0 || padLen > packet.length) return null;
825
+ end = packet.length - padLen;
826
+ if (end < offset) return null;
827
+ }
828
+ if (end <= offset) return null;
829
+ return packet.subarray(offset, end);
830
+ }
831
+ reset() {
832
+ this.fuNalHeader = null;
833
+ this.fuParts = [];
834
+ }
835
+ push(payload) {
836
+ if (payload.length === 0) return [];
837
+ const rtpPayload = _H264RtpDepacketizer.parseRtpPayload(payload);
838
+ if (rtpPayload) payload = rtpPayload;
839
+ if (hasStartCodes(payload)) return [payload];
840
+ const b0 = payload[0];
841
+ if ((b0 & 128) !== 0) return [];
842
+ const nalType = b0 & 31;
843
+ if (nalType >= 1 && nalType <= 23) {
844
+ return [Buffer.concat([NAL_START_CODE_4B, payload])];
845
+ }
846
+ if (nalType === 24) {
847
+ if (payload.length < 1 + 2) return [];
848
+ let off = 1;
849
+ const out = [];
850
+ while (off + 2 <= payload.length) {
851
+ const size = payload.readUInt16BE(off);
852
+ off += 2;
853
+ if (size <= 0 || off + size > payload.length) return [];
854
+ const nal = payload.subarray(off, off + size);
855
+ off += size;
856
+ if (nal.length < 1) return [];
857
+ if ((nal[0] & 128) !== 0) return [];
858
+ const t = nal[0] & 31;
859
+ if (t === 0 || t >= 24) return [];
860
+ out.push(Buffer.concat([NAL_START_CODE_4B, nal]));
861
+ }
862
+ return out;
863
+ }
864
+ if (nalType === 28 || nalType === 29) {
865
+ if (payload.length < 2) return [];
866
+ const fuIndicator = payload[0];
867
+ const fuHeader = payload[1];
868
+ const start = (fuHeader & 128) !== 0;
869
+ const end = (fuHeader & 64) !== 0;
870
+ const origType = fuHeader & 31;
871
+ const reconstructedHeader = fuIndicator & 224 | origType;
872
+ let off = 2;
873
+ if (nalType === 29) {
874
+ if (payload.length < off + 2) return [];
875
+ off += 2;
876
+ }
877
+ const frag = payload.subarray(off);
878
+ if (start) {
879
+ this.fuNalHeader = reconstructedHeader;
880
+ this.fuParts = [frag];
881
+ } else if (this.fuNalHeader != null) {
882
+ this.fuParts.push(frag);
883
+ } else {
884
+ return [];
885
+ }
886
+ if (end && this.fuNalHeader != null) {
887
+ const nal = Buffer.concat([Buffer.from([this.fuNalHeader]), ...this.fuParts]);
888
+ this.reset();
889
+ return [Buffer.concat([NAL_START_CODE_4B, nal])];
890
+ }
891
+ return [];
892
+ }
893
+ return [];
894
+ }
895
+ };
896
+ function convertToLengthPrefixed(data) {
897
+ const result = [];
898
+ let offset = 0;
899
+ while (offset < data.length) {
900
+ let startCodeOffset = -1;
901
+ let startCodeLength = 0;
902
+ if (offset + 4 <= data.length && data.subarray(offset, offset + 4).equals(NAL_START_CODE_4B)) {
903
+ startCodeOffset = offset;
904
+ startCodeLength = 4;
905
+ } else if (offset + 3 <= data.length && data.subarray(offset, offset + 3).equals(NAL_START_CODE_3B)) {
906
+ startCodeOffset = offset;
907
+ startCodeLength = 3;
908
+ }
909
+ if (startCodeOffset === -1) {
910
+ result.push(data.subarray(offset));
911
+ break;
912
+ }
913
+ offset = startCodeOffset + startCodeLength;
914
+ let nextStartCode = -1;
915
+ let nextStartCodeLength = 0;
916
+ for (let i = offset; i < data.length - 3; i++) {
917
+ if (i + 4 <= data.length && data.subarray(i, i + 4).equals(NAL_START_CODE_4B)) {
918
+ nextStartCode = i;
919
+ nextStartCodeLength = 4;
920
+ break;
921
+ }
922
+ if (i + 3 <= data.length && data.subarray(i, i + 3).equals(NAL_START_CODE_3B)) {
923
+ nextStartCode = i;
924
+ nextStartCodeLength = 3;
925
+ break;
926
+ }
927
+ }
928
+ const nalEnd = nextStartCode !== -1 ? nextStartCode : data.length;
929
+ const nalData = data.subarray(offset, nalEnd);
930
+ const lengthBuf = Buffer.alloc(4);
931
+ lengthBuf.writeUInt32BE(nalData.length, 0);
932
+ result.push(lengthBuf);
933
+ result.push(nalData);
934
+ offset = nalEnd;
935
+ }
936
+ return Buffer.concat(result);
937
+ }
938
+
939
+ // src/baichuan/stream/H265Converter.ts
940
+ var NAL_START_CODE_4B2 = Buffer.from([0, 0, 0, 1]);
941
+ var NAL_START_CODE_3B2 = Buffer.from([0, 0, 1]);
942
+ function hasStartCodes2(data) {
943
+ if (data.length < 4) return false;
944
+ if (data.subarray(0, 4).equals(NAL_START_CODE_4B2)) return true;
945
+ if (data.subarray(0, 3).equals(NAL_START_CODE_3B2)) return true;
946
+ return false;
947
+ }
948
+ function tryConvertWithLengthReader2(data, readLen) {
949
+ const result = [];
950
+ let offset = 0;
951
+ let nalCount = 0;
952
+ while (offset < data.length) {
953
+ if (offset + 4 > data.length) return null;
954
+ const nalLength = readLen(data, offset);
955
+ offset += 4;
956
+ if (nalLength <= 0) return null;
957
+ if (nalLength > data.length - offset) return null;
958
+ result.push(NAL_START_CODE_4B2);
959
+ result.push(data.subarray(offset, offset + nalLength));
960
+ offset += nalLength;
961
+ nalCount++;
962
+ }
963
+ if (nalCount === 0) return null;
964
+ return Buffer.concat(result);
965
+ }
966
+ function tryConvertWithLengthReader162(data, readLen) {
967
+ const result = [];
968
+ let offset = 0;
969
+ let nalCount = 0;
970
+ while (offset < data.length) {
971
+ if (offset + 2 > data.length) return null;
972
+ const nalLength = readLen(data, offset);
973
+ offset += 2;
974
+ if (nalLength <= 0) return null;
975
+ if (nalLength > data.length - offset) return null;
976
+ result.push(NAL_START_CODE_4B2);
977
+ result.push(data.subarray(offset, offset + nalLength));
978
+ offset += nalLength;
979
+ nalCount++;
980
+ }
981
+ if (nalCount === 0) return null;
982
+ return Buffer.concat(result);
983
+ }
984
+ function tryConvertWithLengthReader242(data, endian) {
985
+ const result = [];
986
+ let offset = 0;
987
+ let nalCount = 0;
988
+ const readLen24 = (buf, at) => {
989
+ if (at + 3 > buf.length) return 0;
990
+ const b0 = buf[at];
991
+ const b1 = buf[at + 1];
992
+ const b2 = buf[at + 2];
993
+ return endian === "be" ? (b0 << 16 | b1 << 8 | b2) >>> 0 : (b2 << 16 | b1 << 8 | b0) >>> 0;
994
+ };
995
+ while (offset < data.length) {
996
+ if (offset + 3 > data.length) return null;
997
+ const nalLength = readLen24(data, offset);
998
+ offset += 3;
999
+ if (nalLength <= 0) return null;
1000
+ if (nalLength > data.length - offset) return null;
1001
+ result.push(NAL_START_CODE_4B2);
1002
+ result.push(data.subarray(offset, offset + nalLength));
1003
+ offset += nalLength;
1004
+ nalCount++;
1005
+ }
1006
+ if (nalCount === 0) return null;
1007
+ return Buffer.concat(result);
1008
+ }
1009
+ function looksLikeSingleH265Nal(nalPayload) {
1010
+ if (nalPayload.length < 2) return false;
1011
+ const b0 = nalPayload[0];
1012
+ const b1 = nalPayload[1];
1013
+ if (b0 === void 0 || b1 === void 0) return false;
1014
+ if ((b0 & 128) !== 0) return false;
1015
+ const nalType = b0 >> 1 & 63;
1016
+ return nalType <= 40;
1017
+ }
1018
+ function convertToAnnexB2(data) {
1019
+ if (hasStartCodes2(data)) {
1020
+ return data;
1021
+ }
1022
+ const sc4 = Buffer.from([0, 0, 0, 1]);
1023
+ const sc3 = Buffer.from([0, 0, 1]);
1024
+ const maxScan = Math.min(64, data.length);
1025
+ const idx4 = data.subarray(0, maxScan).indexOf(sc4);
1026
+ if (idx4 > 0) return data.subarray(idx4);
1027
+ const idx3 = data.subarray(0, maxScan).indexOf(sc3);
1028
+ if (idx3 > 0) return data.subarray(idx3);
1029
+ const be = tryConvertWithLengthReader2(data, (b, o) => b.readUInt32BE(o));
1030
+ if (be) return be;
1031
+ const le = tryConvertWithLengthReader2(data, (b, o) => b.readUInt32LE(o));
1032
+ if (le) return le;
1033
+ const be24 = tryConvertWithLengthReader242(data, "be");
1034
+ if (be24) return be24;
1035
+ const le24 = tryConvertWithLengthReader242(data, "le");
1036
+ if (le24) return le24;
1037
+ const be16 = tryConvertWithLengthReader162(data, (b, o) => b.readUInt16BE(o));
1038
+ if (be16) return be16;
1039
+ const le16 = tryConvertWithLengthReader162(data, (b, o) => b.readUInt16LE(o));
1040
+ if (le16) return le16;
1041
+ if (looksLikeSingleH265Nal(data)) {
1042
+ return Buffer.concat([NAL_START_CODE_4B2, data]);
1043
+ }
1044
+ return data;
1045
+ }
1046
+ var H265RtpDepacketizer = class _H265RtpDepacketizer {
1047
+ fuParts = null;
1048
+ reset() {
1049
+ this.fuParts = null;
1050
+ }
1051
+ static parseRtpPayload(packet) {
1052
+ if (!packet || packet.length < 12) return null;
1053
+ const version = packet[0] >> 6 & 3;
1054
+ if (version !== 2) return null;
1055
+ const padding = (packet[0] & 32) !== 0;
1056
+ const extension = (packet[0] & 16) !== 0;
1057
+ const csrcCount = packet[0] & 15;
1058
+ let offset = 12 + csrcCount * 4;
1059
+ if (offset > packet.length) return null;
1060
+ if (extension) {
1061
+ if (offset + 4 > packet.length) return null;
1062
+ const extLenWords = packet.readUInt16BE(offset + 2);
1063
+ offset += 4 + extLenWords * 4;
1064
+ if (offset > packet.length) return null;
1065
+ }
1066
+ let end = packet.length;
1067
+ if (padding) {
1068
+ const padLen = packet[packet.length - 1];
1069
+ if (padLen <= 0 || padLen > packet.length) return null;
1070
+ end = packet.length - padLen;
1071
+ if (end < offset) return null;
1072
+ }
1073
+ if (end <= offset) return null;
1074
+ return packet.subarray(offset, end);
1075
+ }
1076
+ push(payload) {
1077
+ if (!payload || payload.length < 2) return [];
1078
+ const rtpPayload = _H265RtpDepacketizer.parseRtpPayload(payload);
1079
+ if (rtpPayload) payload = rtpPayload;
1080
+ const h0 = payload[0];
1081
+ const h1 = payload[1];
1082
+ if ((h0 & 128) !== 0) return [];
1083
+ const nalType = h0 >> 1 & 63;
1084
+ if (nalType === 48) {
1085
+ let off = 2;
1086
+ const out = [];
1087
+ while (off + 2 <= payload.length) {
1088
+ const size = payload.readUInt16BE(off);
1089
+ off += 2;
1090
+ if (size <= 0 || off + size > payload.length) return [];
1091
+ const nal = payload.subarray(off, off + size);
1092
+ off += size;
1093
+ if (nal.length) out.push(NAL_START_CODE_4B2, nal);
1094
+ }
1095
+ return out.length ? [Buffer.concat(out)] : [];
1096
+ }
1097
+ if (nalType === 49) {
1098
+ if (payload.length < 3) return [];
1099
+ const fuHeader = payload[2];
1100
+ const start = (fuHeader & 128) !== 0;
1101
+ const end = (fuHeader & 64) !== 0;
1102
+ const origType = fuHeader & 63;
1103
+ const orig0 = h0 & 129 | (origType & 63) << 1;
1104
+ const orig1 = h1;
1105
+ const frag = payload.subarray(3);
1106
+ if (start) {
1107
+ this.fuParts = [NAL_START_CODE_4B2, Buffer.from([orig0, orig1]), frag];
1108
+ } else {
1109
+ if (!this.fuParts) return [];
1110
+ this.fuParts.push(frag);
1111
+ }
1112
+ if (end) {
1113
+ if (!this.fuParts) return [];
1114
+ const out = Buffer.concat(this.fuParts);
1115
+ this.fuParts = null;
1116
+ return [out];
1117
+ }
1118
+ return [];
1119
+ }
1120
+ return [Buffer.concat([NAL_START_CODE_4B2, payload])];
1121
+ }
1122
+ };
1123
+ function splitAnnexBToNalPayloads2(annexB) {
1124
+ const starts = [];
1125
+ for (let i = 0; i < annexB.length - 3; i++) {
1126
+ if (annexB[i] === 0 && annexB[i + 1] === 0) {
1127
+ if (annexB[i + 2] === 1) {
1128
+ starts.push({ idx: i, len: 3 });
1129
+ i += 2;
1130
+ } else if (annexB[i + 2] === 0 && annexB[i + 3] === 1) {
1131
+ starts.push({ idx: i, len: 4 });
1132
+ i += 3;
1133
+ }
1134
+ }
1135
+ }
1136
+ if (starts.length === 0) return [];
1137
+ const out = [];
1138
+ for (let s = 0; s < starts.length; s++) {
1139
+ const st = starts[s];
1140
+ const start = st.idx + st.len;
1141
+ const end = starts[s + 1] ? starts[s + 1].idx : annexB.length;
1142
+ if (end > start) out.push(annexB.subarray(start, end));
1143
+ }
1144
+ return out;
1145
+ }
1146
+ function getH265NalType(nalPayload) {
1147
+ if (nalPayload.length < 1) return null;
1148
+ const b0 = nalPayload[0];
1149
+ if (b0 === void 0) return null;
1150
+ if ((b0 & 128) !== 0) return null;
1151
+ return b0 >> 1 & 63;
1152
+ }
1153
+ function isH265Irap(nalType) {
1154
+ return nalType >= 16 && nalType <= 23;
1155
+ }
1156
+ function isValidH265AnnexBAccessUnit(annexB) {
1157
+ if (!hasStartCodes2(annexB)) return false;
1158
+ const nals = splitAnnexBToNalPayloads2(annexB);
1159
+ if (nals.length === 0) return false;
1160
+ for (const nal of nals) {
1161
+ if (nal.length < 2) return false;
1162
+ const b0 = nal[0];
1163
+ if (b0 === void 0) return false;
1164
+ if ((b0 & 128) !== 0) return false;
1165
+ const nalType = getH265NalType(nal);
1166
+ if (nalType === null) return false;
1167
+ if (nalType > 40) return false;
1168
+ }
1169
+ return true;
1170
+ }
1171
+ function isH265KeyframeAnnexB(annexB) {
1172
+ const nals = splitAnnexBToNalPayloads2(annexB);
1173
+ let hasVps = false;
1174
+ let hasSps = false;
1175
+ let hasPps = false;
1176
+ let hasIrap = false;
1177
+ for (const nal of nals) {
1178
+ const nalType = getH265NalType(nal);
1179
+ if (nalType === null) continue;
1180
+ if (nalType === 32) hasVps = true;
1181
+ if (nalType === 33) hasSps = true;
1182
+ if (nalType === 34) hasPps = true;
1183
+ if (isH265Irap(nalType)) hasIrap = true;
1184
+ }
1185
+ return hasIrap && hasVps && hasSps && hasPps;
1186
+ }
1187
+ function extractVpsFromAnnexB(annexB) {
1188
+ const nals = splitAnnexBToNalPayloads2(annexB);
1189
+ for (const nal of nals) {
1190
+ const nalType = getH265NalType(nal);
1191
+ if (nalType === 32) {
1192
+ return nal;
1193
+ }
1194
+ }
1195
+ return null;
1196
+ }
1197
+ function extractSpsFromAnnexB(annexB) {
1198
+ const nals = splitAnnexBToNalPayloads2(annexB);
1199
+ for (const nal of nals) {
1200
+ const nalType = getH265NalType(nal);
1201
+ if (nalType === 33) {
1202
+ return nal;
1203
+ }
1204
+ }
1205
+ return null;
1206
+ }
1207
+ function extractPpsFromAnnexB(annexB) {
1208
+ const nals = splitAnnexBToNalPayloads2(annexB);
1209
+ for (const nal of nals) {
1210
+ const nalType = getH265NalType(nal);
1211
+ if (nalType === 34) {
1212
+ return nal;
1213
+ }
1214
+ }
1215
+ return null;
1216
+ }
1217
+
1218
+ // src/baichuan/stream/BcMediaAnnexBDecoder.ts
1219
+ var ANNEXB_START_CODE_4B = Buffer.from([0, 0, 0, 1]);
1220
+ function detectVideoCodecFromNal(data) {
1221
+ if (!data || data.length < 5) return null;
1222
+ let nalStart = -1;
1223
+ for (let i = 0; i < Math.min(data.length - 4, 100); i++) {
1224
+ if (data[i] === 0 && data[i + 1] === 0) {
1225
+ if (data[i + 2] === 0 && data[i + 3] === 1) {
1226
+ nalStart = i + 4;
1227
+ break;
1228
+ }
1229
+ if (data[i + 2] === 1) {
1230
+ nalStart = i + 3;
1231
+ break;
1232
+ }
1233
+ }
1234
+ }
1235
+ if (nalStart < 0 && data.length >= 5) {
1236
+ const len = data.readUInt32BE(0);
1237
+ if (len > 0 && len < data.length - 4) {
1238
+ nalStart = 4;
1239
+ }
1240
+ }
1241
+ if (nalStart < 0 || nalStart >= data.length) return null;
1242
+ const nalByte = data[nalStart];
1243
+ if (nalByte === void 0) return null;
1244
+ const forbiddenBit264 = nalByte >> 7 & 1;
1245
+ const h264Type = nalByte & 31;
1246
+ if (forbiddenBit264 === 0 && h264Type > 0 && h264Type <= 12) {
1247
+ if (h264Type === 7 || h264Type === 8) {
1248
+ return "H264";
1249
+ }
1250
+ if (h264Type === 5) {
1251
+ return "H264";
1252
+ }
1253
+ if (h264Type === 1) {
1254
+ const nalRefIdc = nalByte >> 5 & 3;
1255
+ if (nalRefIdc >= 1) {
1256
+ return "H264";
1257
+ }
1258
+ }
1259
+ }
1260
+ if (nalStart + 1 < data.length) {
1261
+ const nalByte2 = data[nalStart + 1];
1262
+ if (nalByte2 !== void 0) {
1263
+ const forbiddenBit = nalByte >> 7 & 1;
1264
+ const hevcType = nalByte >> 1 & 63;
1265
+ const temporalId = nalByte2 & 7;
1266
+ if (forbiddenBit === 0 && temporalId > 0 && hevcType <= 40) {
1267
+ if (hevcType === 32 || hevcType === 33 || hevcType === 34) {
1268
+ return "H265";
1269
+ }
1270
+ if (hevcType === 19 || hevcType === 20 || hevcType === 21) {
1271
+ return "H265";
1272
+ }
1273
+ if (hevcType <= 1 && nalByte <= 3) {
1274
+ return "H265";
1275
+ }
1276
+ }
1277
+ }
1278
+ }
1279
+ return null;
1280
+ }
1281
+ var BcMediaAnnexBDecoder = class {
1282
+ codec;
1283
+ logger;
1284
+ onVideoAccessUnit;
1285
+ onAudioFrame;
1286
+ stats = {
1287
+ bytesIn: 0,
1288
+ bytesOut: 0,
1289
+ audioBytesOut: 0,
1290
+ packets: 0,
1291
+ videoPackets: 0,
1292
+ audioPackets: 0,
1293
+ aacPackets: 0,
1294
+ adpcmPackets: 0,
1295
+ keyframes: 0,
1296
+ videoType: null,
1297
+ audioType: null,
1298
+ infos: [],
1299
+ recoveredSkips: 0
1300
+ };
1301
+ lastH264Sps = null;
1302
+ lastH264Pps = null;
1303
+ lastH265Vps = null;
1304
+ lastH265Sps = null;
1305
+ lastH265Pps = null;
1306
+ constructor(params) {
1307
+ this.logger = params?.logger;
1308
+ this.onVideoAccessUnit = params?.onVideoAccessUnit;
1309
+ this.onAudioFrame = params?.onAudioFrame;
1310
+ this.codec = new BcMediaCodec(params?.strict ?? false, this.logger);
1311
+ }
1312
+ getStats() {
1313
+ return { ...this.stats, infos: [...this.stats.infos] };
1314
+ }
1315
+ /**
1316
+ * Push arbitrary bytes from a Baichuan/BcMedia transport into the decoder.
1317
+ * Emits complete Annex-B access units via callback.
1318
+ */
1319
+ push(chunk) {
1320
+ if (!chunk || chunk.length === 0) return;
1321
+ this.stats.bytesIn += chunk.length;
1322
+ const packets = this.codec.decode(chunk);
1323
+ this.stats.packets += packets.length;
1324
+ for (const media of packets) {
1325
+ this.handleMedia(media);
1326
+ }
1327
+ }
1328
+ handleMedia(media) {
1329
+ if (media.type === "InfoV1" || media.type === "InfoV2") {
1330
+ const info = media;
1331
+ this.stats.infos.push({
1332
+ type: media.type,
1333
+ videoWidth: info.videoWidth,
1334
+ videoHeight: info.videoHeight,
1335
+ fps: info.fps,
1336
+ startYear: info.startYear,
1337
+ startMonth: info.startMonth,
1338
+ startDay: info.startDay,
1339
+ startHour: info.startHour,
1340
+ startMin: info.startMin,
1341
+ startSeconds: info.startSeconds,
1342
+ endYear: info.endYear,
1343
+ endMonth: info.endMonth,
1344
+ endDay: info.endDay,
1345
+ endHour: info.endHour,
1346
+ endMin: info.endMin,
1347
+ endSeconds: info.endSeconds
1348
+ });
1349
+ return;
1350
+ }
1351
+ if (media.type === "Aac" || media.type === "Adpcm") {
1352
+ const audioMedia = media;
1353
+ this.stats.audioPackets++;
1354
+ if (media.type === "Aac") {
1355
+ this.stats.aacPackets++;
1356
+ } else {
1357
+ this.stats.adpcmPackets++;
1358
+ }
1359
+ if (this.stats.audioType == null) {
1360
+ this.stats.audioType = media.type;
1361
+ }
1362
+ if (this.onAudioFrame) {
1363
+ this.stats.audioBytesOut += audioMedia.data.length;
1364
+ this.onAudioFrame({
1365
+ audioType: media.type,
1366
+ data: audioMedia.data
1367
+ });
1368
+ }
1369
+ return;
1370
+ }
1371
+ if (media.type !== "Iframe" && media.type !== "Pframe") return;
1372
+ this.stats.videoPackets++;
1373
+ const microseconds = media.microseconds;
1374
+ const raw = media.data;
1375
+ let videoType = media.videoType;
1376
+ const detectedType = detectVideoCodecFromNal(raw);
1377
+ if (detectedType != null && detectedType !== videoType) {
1378
+ this.logger?.debug?.(
1379
+ `[BcMediaAnnexBDecoder] Codec mismatch: reported ${videoType}, detected ${detectedType}`
1380
+ );
1381
+ videoType = detectedType;
1382
+ }
1383
+ if (this.stats.videoType == null) this.stats.videoType = videoType;
1384
+ let annexB = videoType === "H265" ? convertToAnnexB2(raw) : convertToAnnexB(raw);
1385
+ const isKeyframe = media.type === "Iframe" || (videoType === "H265" ? isH265KeyframeAnnexB(annexB) : isH264KeyframeAnnexB(annexB));
1386
+ if (videoType === "H264") {
1387
+ const nals = splitAnnexBToNalPayloads(annexB);
1388
+ for (const nal of nals) {
1389
+ const t = (nal[0] ?? 0) & 31;
1390
+ if (t === 7) this.lastH264Sps = nal;
1391
+ if (t === 8) this.lastH264Pps = nal;
1392
+ }
1393
+ if (isKeyframe) {
1394
+ const hasSps = nals.some((nal) => ((nal[0] ?? 0) & 31) === 7);
1395
+ const hasPps = nals.some((nal) => ((nal[0] ?? 0) & 31) === 8);
1396
+ const toPrepend = [];
1397
+ if (!hasSps && this.lastH264Sps)
1398
+ toPrepend.push(ANNEXB_START_CODE_4B, this.lastH264Sps);
1399
+ if (!hasPps && this.lastH264Pps)
1400
+ toPrepend.push(ANNEXB_START_CODE_4B, this.lastH264Pps);
1401
+ if (toPrepend.length > 0)
1402
+ annexB = Buffer.concat([...toPrepend, annexB]);
1403
+ }
1404
+ } else {
1405
+ const vps = extractVpsFromAnnexB(annexB);
1406
+ const sps = extractSpsFromAnnexB(annexB);
1407
+ const pps = extractPpsFromAnnexB(annexB);
1408
+ if (vps) this.lastH265Vps = vps;
1409
+ if (sps) this.lastH265Sps = sps;
1410
+ if (pps) this.lastH265Pps = pps;
1411
+ if (isKeyframe) {
1412
+ const hasVps = vps != null;
1413
+ const hasSps = sps != null;
1414
+ const hasPps = pps != null;
1415
+ const toPrepend = [];
1416
+ if (!hasVps && this.lastH265Vps)
1417
+ toPrepend.push(ANNEXB_START_CODE_4B, this.lastH265Vps);
1418
+ if (!hasSps && this.lastH265Sps)
1419
+ toPrepend.push(ANNEXB_START_CODE_4B, this.lastH265Sps);
1420
+ if (!hasPps && this.lastH265Pps)
1421
+ toPrepend.push(ANNEXB_START_CODE_4B, this.lastH265Pps);
1422
+ if (toPrepend.length > 0)
1423
+ annexB = Buffer.concat([...toPrepend, annexB]);
1424
+ }
1425
+ }
1426
+ if (isKeyframe) this.stats.keyframes++;
1427
+ this.stats.bytesOut += annexB.length;
1428
+ this.onVideoAccessUnit?.({ videoType, annexB, microseconds, isKeyframe });
1429
+ }
1430
+ };
1431
+
1432
+ // src/baichuan/stream/BaichuanVideoStream.ts
1433
+ var NAL_START_CODE_4B3 = Buffer.from([0, 0, 0, 1]);
1434
+ var WATCHDOG_TICK_MS = 1e3;
1435
+ var WATCHDOG_MAX_RESTARTS_PER_MINUTE = 3;
1436
+ var AsyncFsQueue = class {
1437
+ running = false;
1438
+ queue = [];
1439
+ maxQueueSize;
1440
+ constructor(maxQueueSize) {
1441
+ this.maxQueueSize = maxQueueSize;
1442
+ }
1443
+ enqueue(task) {
1444
+ if (this.queue.length >= this.maxQueueSize) return;
1445
+ this.queue.push(task);
1446
+ this.pump();
1447
+ }
1448
+ pump() {
1449
+ if (this.running) return;
1450
+ this.running = true;
1451
+ void this.run();
1452
+ }
1453
+ async run() {
1454
+ try {
1455
+ while (this.queue.length > 0) {
1456
+ const task = this.queue.shift();
1457
+ if (!task) continue;
1458
+ try {
1459
+ await task();
1460
+ } catch {
1461
+ }
1462
+ }
1463
+ } finally {
1464
+ this.running = false;
1465
+ }
1466
+ }
1467
+ };
1468
+ function removeEmulationPreventionBytes(rbsp) {
1469
+ const out = [];
1470
+ for (let i = 0; i < rbsp.length; i++) {
1471
+ if (i >= 2 && rbsp[i] === 3 && rbsp[i - 1] === 0 && rbsp[i - 2] === 0) {
1472
+ continue;
1473
+ }
1474
+ out.push(rbsp[i]);
1475
+ }
1476
+ return Buffer.from(out);
1477
+ }
1478
+ var BitReader = class {
1479
+ b;
1480
+ bitPos = 0;
1481
+ constructor(buf) {
1482
+ this.b = buf;
1483
+ }
1484
+ readBits(n) {
1485
+ if (n <= 0) return 0;
1486
+ let v = 0;
1487
+ for (let i = 0; i < n; i++) {
1488
+ const bytePos = this.bitPos >> 3;
1489
+ if (bytePos >= this.b.length) return null;
1490
+ const bitInByte = 7 - (this.bitPos & 7);
1491
+ const bit = this.b[bytePos] >> bitInByte & 1;
1492
+ v = v << 1 | bit;
1493
+ this.bitPos++;
1494
+ }
1495
+ return v >>> 0;
1496
+ }
1497
+ readUE() {
1498
+ let zeros = 0;
1499
+ while (true) {
1500
+ const bit = this.readBits(1);
1501
+ if (bit == null) return null;
1502
+ if (bit === 0) zeros++;
1503
+ else break;
1504
+ if (zeros > 31) return null;
1505
+ }
1506
+ const rest = zeros ? this.readBits(zeros) : 0;
1507
+ if (rest == null) return null;
1508
+ return (1 << zeros) - 1 + rest >>> 0;
1509
+ }
1510
+ };
1511
+ function parseSpsIdFromNal(nalPayload) {
1512
+ if (((nalPayload[0] ?? 0) & 31) !== 7) return null;
1513
+ const rbsp = removeEmulationPreventionBytes(nalPayload.subarray(1));
1514
+ const r = new BitReader(rbsp);
1515
+ if (r.readBits(8) == null) return null;
1516
+ if (r.readBits(8) == null) return null;
1517
+ if (r.readBits(8) == null) return null;
1518
+ return r.readUE();
1519
+ }
1520
+ function parsePpsIdsFromNal(nalPayload) {
1521
+ if (((nalPayload[0] ?? 0) & 31) !== 8) return null;
1522
+ const rbsp = removeEmulationPreventionBytes(nalPayload.subarray(1));
1523
+ const r = new BitReader(rbsp);
1524
+ const ppsId = r.readUE();
1525
+ const spsId = r.readUE();
1526
+ if (ppsId == null || spsId == null) return null;
1527
+ return { ppsId, spsId };
1528
+ }
1529
+ function parseSlicePpsIdFromNal(nalPayload) {
1530
+ const t = (nalPayload[0] ?? 0) & 31;
1531
+ if (t !== 1 && t !== 5) return null;
1532
+ const rbsp = removeEmulationPreventionBytes(nalPayload.subarray(1));
1533
+ const r = new BitReader(rbsp);
1534
+ if (r.readUE() == null) return null;
1535
+ if (r.readUE() == null) return null;
1536
+ return r.readUE();
1537
+ }
1538
+ var BaichuanVideoStream = class _BaichuanVideoStream extends EventEmitter {
1539
+ client;
1540
+ api;
1541
+ channel;
1542
+ profile;
1543
+ variant;
1544
+ logger;
1545
+ active = false;
1546
+ videoFrameHandler;
1547
+ expectedStreamTypes;
1548
+ activeMsgNum;
1549
+ cmdId;
1550
+ acceptAnyStreamType;
1551
+ lockedChannelId;
1552
+ bcMediaCodec;
1553
+ /**
1554
+ * Diagnostic-only accessor for the BcMedia codec. Used by tools that need to
1555
+ * inspect unknown chunks (for example to discover undocumented audio
1556
+ * sub-packets the parser currently skips). Not part of the supported public
1557
+ * surface — do not rely on it in application code.
1558
+ */
1559
+ get _bcMediaCodec() {
1560
+ return this.bcMediaCodec;
1561
+ }
1562
+ debugH264LogsLeft;
1563
+ debugSavedSamples;
1564
+ warnedNonAnnexBOnce = false;
1565
+ // "RTP-like" depacketizer (some models encapsulate NAL units in FU-A/STAP)
1566
+ depacketizer = new H264RtpDepacketizer();
1567
+ depacketizerH265 = new H265RtpDepacketizer();
1568
+ dumpChunkIdx = 0;
1569
+ dumpNalLines = 0;
1570
+ dumpIo = new AsyncFsQueue(200);
1571
+ spsById = /* @__PURE__ */ new Map();
1572
+ // NAL payload (without start code) - H.264
1573
+ ppsById = /* @__PURE__ */ new Map();
1574
+ // NAL payload + mapping - H.264
1575
+ lastSps = null;
1576
+ // H.264
1577
+ lastPps = null;
1578
+ // H.264
1579
+ lastPrependedPpsId = null;
1580
+ // H.264
1581
+ // H.265 parameter sets
1582
+ lastVps = null;
1583
+ // H.265 VPS
1584
+ lastSpsH265 = null;
1585
+ // H.265 SPS
1586
+ lastPpsH265 = null;
1587
+ // H.265 PPS
1588
+ lastPrependedParamSetsH265 = false;
1589
+ // Track if we've prepended H.265 param sets
1590
+ // Stateful AES decryptor for fragmented BcMedia packets (full_aes mode)
1591
+ // In CFB mode, continuation frames must use the cipher state from previous frames.
1592
+ aesStreamDecryptor = null;
1593
+ // Latest frame dimensions reported by BcMedia InfoV1/V2 packets.
1594
+ // Used to attach width/height context to the `additionalHeader` event so
1595
+ // consumers can normalize box coordinates to a fraction of the stream size.
1596
+ latestFrameWidth;
1597
+ latestFrameHeight;
1598
+ // Teardown returned by ReolinkBaichuanApi._registerVideoStreamForDetection.
1599
+ // Called from stop() to detach the detection bridge.
1600
+ detectionTeardown;
1601
+ /**
1602
+ * Pending startup error stashed when emitSafeError is called before any
1603
+ * "error" listener is registered (e.g. camera returns 400 during start()).
1604
+ * The rfc4571-server's waitForKeyframe can consume this immediately instead
1605
+ * of waiting for the full keyframe timeout.
1606
+ */
1607
+ _pendingStartupError;
1608
+ /** Consume and clear any pending startup error. */
1609
+ consumePendingStartupError() {
1610
+ const err = this._pendingStartupError;
1611
+ this._pendingStartupError = void 0;
1612
+ return err;
1613
+ }
1614
+ emitSafeError(err) {
1615
+ if (!this.active) {
1616
+ this.logger?.warn?.(
1617
+ `[BaichuanVideoStream] Suppressed error after stop: ${err.message}`
1618
+ );
1619
+ return;
1620
+ }
1621
+ if (this.listenerCount("error") === 0) {
1622
+ this.logger?.warn?.(
1623
+ `[BaichuanVideoStream] Unhandled stream error: ${err.message}`
1624
+ );
1625
+ this._pendingStartupError = err;
1626
+ return;
1627
+ }
1628
+ this.emit("error", err);
1629
+ }
1630
+ lastMediaAtMs = 0;
1631
+ watchdogTimer;
1632
+ restarting = false;
1633
+ restartWindowStartMs = 0;
1634
+ restartCountInWindow = 0;
1635
+ idleRestartMs;
1636
+ // Note: reassembly happens at the BcMediaCodec transport level, so we do not
1637
+ // accumulate frames here.
1638
+ static scoreBcMediaLike(b) {
1639
+ if (b.length < 4) return { score: -1, first: -1 };
1640
+ const maxScan = Math.min(64 * 1024, b.length - 4);
1641
+ let count = 0;
1642
+ let first = -1;
1643
+ for (let i = 0; i <= maxScan; i++) {
1644
+ const magic = b.readUInt32LE(i);
1645
+ const isInfoV1 = magic === 825241649;
1646
+ const isInfoV2 = magic === 842018865;
1647
+ const isIFrame = magic >= 1667510320 && magic <= 1667510329;
1648
+ const isPFrame = magic >= 1667510576 && magic <= 1667510585;
1649
+ const isAac = magic === 1651979568;
1650
+ const isAdpcm = magic === 1651978544;
1651
+ if (isInfoV1 || isInfoV2 || isIFrame || isPFrame || isAac || isAdpcm) {
1652
+ count++;
1653
+ if (first < 0) first = i;
1654
+ if (count > 32 && first === 0) break;
1655
+ }
1656
+ }
1657
+ return { score: count * 1e3 - (first < 0 ? 5e4 : first), first };
1658
+ }
1659
+ chooseDecryptedOrRawCandidate(params) {
1660
+ const { raw, enc, channelId, allowResync, encryptLen } = params;
1661
+ if (encryptLen !== void 0 && encryptLen > 0 && encryptLen < raw.length) {
1662
+ const encryptedPart = raw.subarray(0, encryptLen);
1663
+ const clearPart = raw.subarray(encryptLen);
1664
+ const decryptedPart = this.client.tryDecryptBinary(
1665
+ encryptedPart,
1666
+ channelId,
1667
+ enc
1668
+ );
1669
+ const chosen2 = Buffer.concat([decryptedPart, clearPart]);
1670
+ if (!allowResync) return chosen2;
1671
+ const best2 = _BaichuanVideoStream.scoreBcMediaLike(chosen2);
1672
+ return best2.first > 0 ? chosen2.subarray(best2.first) : chosen2;
1673
+ }
1674
+ if (enc.kind === "full_aes") {
1675
+ const key = enc.key;
1676
+ const isReplayMode = this.cmdId === 5;
1677
+ const freshDecrypted = aesDecrypt(raw, key);
1678
+ const freshScore = _BaichuanVideoStream.scoreBcMediaLike(freshDecrypted);
1679
+ const rawScore2 = _BaichuanVideoStream.scoreBcMediaLike(raw);
1680
+ const startsWithMagic = freshScore.first === 0 && freshScore.score > 0;
1681
+ if (!isReplayMode) {
1682
+ const chosen3 = freshScore.score > rawScore2.score ? freshDecrypted : raw;
1683
+ if (!allowResync) return chosen3;
1684
+ const best3 = _BaichuanVideoStream.scoreBcMediaLike(chosen3);
1685
+ return best3.first > 0 ? chosen3.subarray(best3.first) : chosen3;
1686
+ }
1687
+ if (startsWithMagic && freshDecrypted.length >= 24) {
1688
+ const magic = freshDecrypted.readUInt32LE(0);
1689
+ const isIFrame = magic >= 1667510320 && magic <= 1667510329;
1690
+ const isPFrame = magic >= 1667510576 && magic <= 1667510585;
1691
+ if ((isIFrame || isPFrame) && freshDecrypted.length >= 24) {
1692
+ const additionalHeaderSize = freshDecrypted.readUInt32LE(12);
1693
+ const headerLen = 24 + additionalHeaderSize;
1694
+ if (headerLen > 0 && headerLen < raw.length) {
1695
+ const rawPayloadStart = raw.subarray(headerLen, headerLen + 4);
1696
+ const hasRawStartCode = rawPayloadStart.length >= 4 && rawPayloadStart[0] === 0 && rawPayloadStart[1] === 0 && (rawPayloadStart[2] === 1 || rawPayloadStart[2] === 0 && rawPayloadStart[3] === 1);
1697
+ if (hasRawStartCode) {
1698
+ const headerDecrypted = aesDecrypt(
1699
+ raw.subarray(0, headerLen),
1700
+ key
1701
+ );
1702
+ const clearPayload = raw.subarray(headerLen);
1703
+ const chosen3 = Buffer.concat([headerDecrypted, clearPayload]);
1704
+ if (!allowResync) return chosen3;
1705
+ const best3 = _BaichuanVideoStream.scoreBcMediaLike(chosen3);
1706
+ return best3.first > 0 ? chosen3.subarray(best3.first) : chosen3;
1707
+ }
1708
+ }
1709
+ }
1710
+ const IFRAME_ENCRYPT_BOUNDARY = 1024;
1711
+ if (raw.length > IFRAME_ENCRYPT_BOUNDARY) {
1712
+ const encryptedPart = raw.subarray(0, IFRAME_ENCRYPT_BOUNDARY);
1713
+ const clearPart = raw.subarray(IFRAME_ENCRYPT_BOUNDARY);
1714
+ const decryptedPart = aesDecrypt(encryptedPart, key);
1715
+ const chosen3 = Buffer.concat([decryptedPart, clearPart]);
1716
+ if (!allowResync) return chosen3;
1717
+ const best3 = _BaichuanVideoStream.scoreBcMediaLike(chosen3);
1718
+ return best3.first > 0 ? chosen3.subarray(best3.first) : chosen3;
1719
+ }
1720
+ {
1721
+ const chosen3 = freshDecrypted;
1722
+ if (!allowResync) return chosen3;
1723
+ const best3 = _BaichuanVideoStream.scoreBcMediaLike(chosen3);
1724
+ return best3.first > 0 ? chosen3.subarray(best3.first) : chosen3;
1725
+ }
1726
+ }
1727
+ if (rawScore2.first === 0 && rawScore2.score > freshScore.score) {
1728
+ const chosen3 = raw;
1729
+ if (!allowResync) return chosen3;
1730
+ const best3 = _BaichuanVideoStream.scoreBcMediaLike(chosen3);
1731
+ return best3.first > 0 ? chosen3.subarray(best3.first) : chosen3;
1732
+ }
1733
+ if (this.aesStreamDecryptor && this.aesStreamDecryptor.isInitialized()) {
1734
+ const statefulDecrypted = this.aesStreamDecryptor.update(raw);
1735
+ return statefulDecrypted;
1736
+ }
1737
+ const chosen2 = freshScore.score > rawScore2.score ? freshDecrypted : raw;
1738
+ if (!allowResync) return chosen2;
1739
+ const best2 = _BaichuanVideoStream.scoreBcMediaLike(chosen2);
1740
+ return best2.first > 0 ? chosen2.subarray(best2.first) : chosen2;
1741
+ }
1742
+ const rawScore = _BaichuanVideoStream.scoreBcMediaLike(raw);
1743
+ const dec = this.client.enc.kind === "aes" || this.client.enc.kind === "bc" ? this.client.tryDecryptBinary(raw, channelId, enc) : raw;
1744
+ const decScore = _BaichuanVideoStream.scoreBcMediaLike(dec);
1745
+ const chosen = decScore.score > rawScore.score ? dec : raw;
1746
+ if (!allowResync) return chosen;
1747
+ const best = _BaichuanVideoStream.scoreBcMediaLike(chosen);
1748
+ return best.first > 0 ? chosen.subarray(best.first) : chosen;
1749
+ }
1750
+ constructor(options) {
1751
+ super();
1752
+ this.client = options.client;
1753
+ this.api = options.api;
1754
+ this.channel = options.channel;
1755
+ this.profile = options.profile;
1756
+ this.variant = options.variant ?? "default";
1757
+ this.logger = options.logger;
1758
+ this.cmdId = options.cmdId ?? 3;
1759
+ this.acceptAnyStreamType = options.acceptAnyStreamType ?? false;
1760
+ this.expectedStreamTypes = this.profile === "sub" ? /* @__PURE__ */ new Set([1, 3]) : /* @__PURE__ */ new Set([0, 2]);
1761
+ if (this.variant === "telephoto") this.expectedStreamTypes.add(0);
1762
+ this.bcMediaCodec = new BcMediaCodec(false, this.logger);
1763
+ const dbg = this.client.getDebugConfig();
1764
+ this.debugH264LogsLeft = dbg.traceNativeStream ? 200 : 0;
1765
+ this.debugSavedSamples = false;
1766
+ this.dumpChunkIdx = 0;
1767
+ this.spsById = /* @__PURE__ */ new Map();
1768
+ this.ppsById = /* @__PURE__ */ new Map();
1769
+ this.lastSps = null;
1770
+ this.lastPps = null;
1771
+ this.lastPrependedPpsId = null;
1772
+ this.lastVps = null;
1773
+ this.lastSpsH265 = null;
1774
+ this.lastPpsH265 = null;
1775
+ this.lastPrependedParamSetsH265 = false;
1776
+ if (options.msgNum !== void 0) {
1777
+ this.activeMsgNum = options.msgNum;
1778
+ }
1779
+ const transport = this.client.getTransport?.();
1780
+ this.idleRestartMs = transport === "udp" ? 6e4 : 3e4;
1781
+ }
1782
+ noteMediaActivity() {
1783
+ this.lastMediaAtMs = Date.now();
1784
+ }
1785
+ startWatchdog() {
1786
+ if (!this.api) return;
1787
+ if (this.watchdogTimer) return;
1788
+ this.restartWindowStartMs = Date.now();
1789
+ this.restartCountInWindow = 0;
1790
+ this.watchdogTimer = setInterval(() => {
1791
+ void this.watchdogTick();
1792
+ }, WATCHDOG_TICK_MS);
1793
+ }
1794
+ stopWatchdog() {
1795
+ if (this.watchdogTimer) clearInterval(this.watchdogTimer);
1796
+ this.watchdogTimer = void 0;
1797
+ }
1798
+ async watchdogTick() {
1799
+ if (!this.active) return;
1800
+ if (!this.api) return;
1801
+ if (this.restarting) return;
1802
+ if (this.lastMediaAtMs <= 0) return;
1803
+ const now = Date.now();
1804
+ const idleMs = now - this.lastMediaAtMs;
1805
+ if (idleMs < this.idleRestartMs) return;
1806
+ if (now - this.restartWindowStartMs > 6e4) {
1807
+ this.restartWindowStartMs = now;
1808
+ this.restartCountInWindow = 0;
1809
+ }
1810
+ if (this.restartCountInWindow >= WATCHDOG_MAX_RESTARTS_PER_MINUTE) {
1811
+ this.logger?.warn(
1812
+ `[BaichuanVideoStream] Watchdog: idle for ${idleMs}ms, but restart budget exceeded (${WATCHDOG_MAX_RESTARTS_PER_MINUTE}/min); leaving stream as-is`
1813
+ );
1814
+ this.lastMediaAtMs = now;
1815
+ return;
1816
+ }
1817
+ this.restartCountInWindow++;
1818
+ void this.restartNativeStream({ reason: `idle ${idleMs}ms` });
1819
+ }
1820
+ async restartNativeStream(params) {
1821
+ if (!this.api) return;
1822
+ if (!this.active) return;
1823
+ if (this.restarting) return;
1824
+ this.restarting = true;
1825
+ try {
1826
+ const transport = this.client.getTransport?.() ?? "unknown";
1827
+ const msgNum = this.activeMsgNum ?? "unknown";
1828
+ this.logger?.warn(
1829
+ `[BaichuanVideoStream] Watchdog restarting native stream (channel=${this.channel} profile=${this.profile} expectedStreamTypes=[${[
1830
+ ...this.expectedStreamTypes
1831
+ ].join(
1832
+ ","
1833
+ )}] msgNum=${msgNum} transport=${transport} reason=${params.reason})`
1834
+ );
1835
+ this.depacketizer.reset();
1836
+ this.depacketizerH265.reset();
1837
+ this.bcMediaCodec.clear();
1838
+ this.activeMsgNum = void 0;
1839
+ this.lockedChannelId = void 0;
1840
+ this.lastPrependedPpsId = null;
1841
+ this.lastPrependedParamSetsH265 = false;
1842
+ try {
1843
+ await this.api.stopVideoStream(this.channel, this.profile, {
1844
+ variant: this.variant,
1845
+ client: this.client
1846
+ });
1847
+ } catch {
1848
+ }
1849
+ await this.api.startVideoStream(this.channel, this.profile, {
1850
+ variant: this.variant,
1851
+ client: this.client
1852
+ });
1853
+ try {
1854
+ const getMsgNum = this.api.getActiveVideoMsgNumWithVariant;
1855
+ const v = typeof getMsgNum === "function" ? getMsgNum(this.channel, this.profile, this.variant) : void 0;
1856
+ if (v !== void 0) this.activeMsgNum = v;
1857
+ } catch {
1858
+ }
1859
+ this.lastMediaAtMs = Date.now();
1860
+ } catch (error) {
1861
+ const err = error instanceof Error ? error : new Error(String(error));
1862
+ this.emitSafeError(err);
1863
+ } finally {
1864
+ this.restarting = false;
1865
+ }
1866
+ }
1867
+ /**
1868
+ * Start video stream.
1869
+ * Listens to `push` events and processes cmd_id=3 frames carrying BcMedia packets.
1870
+ */
1871
+ async start() {
1872
+ if (this.active) {
1873
+ throw new Error("Video stream already active");
1874
+ }
1875
+ this.depacketizer.reset();
1876
+ this.depacketizerH265.reset();
1877
+ let totalFramesReceived = 0;
1878
+ let totalMediaPackets = 0;
1879
+ this.videoFrameHandler = (frame) => {
1880
+ if (frame.header.cmdId !== this.cmdId) return;
1881
+ if (this.activeMsgNum !== void 0 && frame.header.msgNum !== this.activeMsgNum) {
1882
+ const allowMsgNum0Fallback = this.acceptAnyStreamType && frame.header.msgNum === 0;
1883
+ if (!allowMsgNum0Fallback) {
1884
+ const frameCount = this._msgNumMismatchCount = (this._msgNumMismatchCount || 0) + 1;
1885
+ if (frameCount <= 5 && this.client.getDebugConfig().general) {
1886
+ this.logger?.log(
1887
+ `[BaichuanVideoStream] Frame msgNum mismatch: received=${frame.header.msgNum}, expected=${this.activeMsgNum}, channel=${this.channel}, profile=${this.profile}, variant=${this.variant} (frame discarded)`
1888
+ );
1889
+ }
1890
+ return;
1891
+ }
1892
+ }
1893
+ if (!this.acceptAnyStreamType && !this.expectedStreamTypes.has(frame.header.streamType)) {
1894
+ const frameCount = this._streamTypeMismatchCount = (this._streamTypeMismatchCount || 0) + 1;
1895
+ if (frameCount <= 5 && this.client.getDebugConfig().general) {
1896
+ this.logger?.log(
1897
+ `[BaichuanVideoStream] Frame streamType mismatch: received=${frame.header.streamType}, expectedAny=[${[
1898
+ ...this.expectedStreamTypes
1899
+ ].join(
1900
+ ","
1901
+ )}], channel=${this.channel}, profile=${this.profile}, variant=${this.variant} (frame discarded)`
1902
+ );
1903
+ }
1904
+ return;
1905
+ }
1906
+ if (this.lockedChannelId === void 0) {
1907
+ this.lockedChannelId = frame.header.channelId;
1908
+ } else if (frame.header.channelId !== this.lockedChannelId) {
1909
+ const frameCount = this._channelIdMismatchCount = (this._channelIdMismatchCount || 0) + 1;
1910
+ if (frameCount <= 5) {
1911
+ this.logger?.warn(
1912
+ `[BaichuanVideoStream] Frame channelId mismatch: received=${frame.header.channelId}, locked=${this.lockedChannelId}, streamType=${frame.header.streamType}, msgNum=${frame.header.msgNum}, activeMsgNum=${this.activeMsgNum ?? "unknown"}, channel=${this.channel}, profile=${this.profile}, variant=${this.variant} (frame discarded)`
1913
+ );
1914
+ }
1915
+ return;
1916
+ }
1917
+ totalFramesReceived++;
1918
+ const dbg = this.client.getDebugConfig();
1919
+ const rtspDebug = dbg.debugRtsp;
1920
+ if (totalFramesReceived === 1) {
1921
+ if (rtspDebug) {
1922
+ this.logger?.log(
1923
+ `[BaichuanVideoStream] First cmd_id=${this.cmdId} frame received (bodyLen: ${frame.body.length}, channelId: ${frame.header.channelId})`
1924
+ );
1925
+ }
1926
+ }
1927
+ if (totalFramesReceived % 10 === 0 || totalFramesReceived <= 5) {
1928
+ if (rtspDebug) {
1929
+ this.logger?.log(
1930
+ `[BaichuanVideoStream] Received ${totalFramesReceived} Baichuan frames (cmd_id=${this.cmdId})`
1931
+ );
1932
+ }
1933
+ }
1934
+ const enc = this.client.enc;
1935
+ const rawCandidate = frame.payload.length > 0 ? frame.payload : frame.body;
1936
+ let dataToParse = rawCandidate;
1937
+ if (frame.payload.length === 0) {
1938
+ let searchStart = 0;
1939
+ const extensionEnd = rawCandidate.indexOf(Buffer.from("</Extension>"));
1940
+ const bodyEnd = rawCandidate.indexOf(Buffer.from("</body>"));
1941
+ if (extensionEnd !== -1)
1942
+ searchStart = extensionEnd + Buffer.from("</Extension>").length;
1943
+ else if (bodyEnd !== -1)
1944
+ searchStart = bodyEnd + Buffer.from("</body>").length;
1945
+ dataToParse = rawCandidate.subarray(searchStart);
1946
+ }
1947
+ let encryptLen;
1948
+ if (frame.extension && frame.extension.length > 0) {
1949
+ try {
1950
+ const extDec = this.client.tryDecryptXml(
1951
+ frame.extension,
1952
+ frame.header.channelId,
1953
+ enc
1954
+ );
1955
+ const encryptLenMatch = extDec.match(
1956
+ /<encryptLen>(\d+)<\/encryptLen>/i
1957
+ );
1958
+ if (encryptLenMatch && encryptLenMatch[1]) {
1959
+ encryptLen = parseInt(encryptLenMatch[1], 10);
1960
+ }
1961
+ } catch {
1962
+ }
1963
+ }
1964
+ const dataAfterXml = this.chooseDecryptedOrRawCandidate({
1965
+ raw: dataToParse,
1966
+ enc,
1967
+ channelId: frame.header.channelId,
1968
+ // Some NVR/Hub streams appear to include non-media bytes even when payloadOffset is present.
1969
+ // Allow a one-time resync at startup to avoid delaying the first keyframe.
1970
+ allowResync: frame.payload.length === 0 || totalFramesReceived <= 10 && totalMediaPackets === 0,
1971
+ ...encryptLen !== void 0 ? { encryptLen } : {}
1972
+ });
1973
+ if (this.bcMediaCodec.getRemainingBuffer().length === 0 && dataAfterXml.length <= 600) {
1974
+ const s = _BaichuanVideoStream.scoreBcMediaLike(dataAfterXml);
1975
+ if (s.first < 0) {
1976
+ return;
1977
+ }
1978
+ }
1979
+ if (totalFramesReceived === 1) {
1980
+ if (rtspDebug) {
1981
+ this.logger?.log(
1982
+ `[BaichuanVideoStream] Data after XML: ${dataAfterXml.length} bytes, first 32 bytes: ${dataAfterXml.subarray(0, Math.min(32, dataAfterXml.length)).toString("hex")}`
1983
+ );
1984
+ }
1985
+ }
1986
+ if (dbg.dumpEnabled) ensureDumpDir(dbg);
1987
+ if (dbg.dumpBcMedia && this.dumpChunkIdx < 200) {
1988
+ const outDir = dbg.dumpDir;
1989
+ const idx = String(this.dumpChunkIdx).padStart(4, "0");
1990
+ const chunkPath = path2.join(outDir, `bcmedia_chunk_${idx}.bin`);
1991
+ const infoPath = path2.join(outDir, "bcmedia_info.json");
1992
+ const chunk = Buffer.from(dataAfterXml);
1993
+ const writeInfo = this.dumpChunkIdx === 0;
1994
+ const infoJson = writeInfo ? JSON.stringify(
1995
+ {
1996
+ note: "Chunks fed into the BcMedia decoder (after XML stripping/alignment).",
1997
+ profile: this.profile,
1998
+ channel: this.channel,
1999
+ encKind: this.client.enc.kind
2000
+ },
2001
+ null,
2002
+ 2
2003
+ ) : "";
2004
+ this.dumpIo.enqueue(async () => {
2005
+ await fs2.promises.writeFile(chunkPath, chunk);
2006
+ if (writeInfo) {
2007
+ await fs2.promises.writeFile(infoPath, infoJson);
2008
+ }
2009
+ });
2010
+ this.dumpChunkIdx++;
2011
+ }
2012
+ const mediaPackets = this.bcMediaCodec.decode(dataAfterXml);
2013
+ totalMediaPackets += mediaPackets.length;
2014
+ const packetTypes = /* @__PURE__ */ new Map();
2015
+ for (const pkt of mediaPackets) {
2016
+ packetTypes.set(pkt.type, (packetTypes.get(pkt.type) || 0) + 1);
2017
+ }
2018
+ let videoFramesEmitted = 0;
2019
+ let audioFramesEmitted = 0;
2020
+ for (const media of mediaPackets) {
2021
+ const maybeCacheParamSets = (annexB, source, videoType) => {
2022
+ if (videoType === "H264") {
2023
+ const nals = splitAnnexBToNalPayloads(annexB);
2024
+ for (const nal of nals) {
2025
+ const t = (nal[0] ?? 0) & 31;
2026
+ if (t === 7) {
2027
+ const id = parseSpsIdFromNal(nal);
2028
+ if (id != null) {
2029
+ if (!isPlausibleH264Sps(nal)) continue;
2030
+ this.spsById.set(id, nal);
2031
+ if (dbg.traceNativeStream) {
2032
+ this.logger?.warn(
2033
+ `[BaichuanVideoStream] Cached H.264 SPS id=${id} len=${nal.length}`
2034
+ );
2035
+ }
2036
+ }
2037
+ if (isPlausibleH264Sps(nal)) this.lastSps = nal;
2038
+ }
2039
+ if (t === 8) {
2040
+ const ids = parsePpsIdsFromNal(nal);
2041
+ if (ids) {
2042
+ const sps = this.spsById.get(ids.spsId);
2043
+ if (sps && !isPlausibleH264Sps(sps)) continue;
2044
+ this.ppsById.set(ids.ppsId, { nal, spsId: ids.spsId });
2045
+ if (dbg.traceNativeStream) {
2046
+ this.logger?.warn(
2047
+ `[BaichuanVideoStream] Cached H.264 PPS id=${ids.ppsId} (spsId=${ids.spsId}) len=${nal.length}`
2048
+ );
2049
+ }
2050
+ }
2051
+ this.lastPps = nal;
2052
+ }
2053
+ }
2054
+ } else if (videoType === "H265") {
2055
+ const vps = extractVpsFromAnnexB(annexB);
2056
+ if (vps) {
2057
+ this.lastVps = vps;
2058
+ if (dbg.traceNativeStream) {
2059
+ this.logger?.warn(
2060
+ `[BaichuanVideoStream] Cached H.265 VPS len=${vps.length}`
2061
+ );
2062
+ }
2063
+ }
2064
+ const sps = extractSpsFromAnnexB(annexB);
2065
+ if (sps) {
2066
+ this.lastSpsH265 = sps;
2067
+ if (dbg.traceNativeStream) {
2068
+ this.logger?.warn(
2069
+ `[BaichuanVideoStream] Cached H.265 SPS len=${sps.length}`
2070
+ );
2071
+ }
2072
+ }
2073
+ const pps = extractPpsFromAnnexB(annexB);
2074
+ if (pps) {
2075
+ this.lastPpsH265 = pps;
2076
+ if (dbg.traceNativeStream) {
2077
+ this.logger?.warn(
2078
+ `[BaichuanVideoStream] Cached H.265 PPS len=${pps.length}`
2079
+ );
2080
+ }
2081
+ }
2082
+ }
2083
+ };
2084
+ const prependParamSetsIfNeeded = (annexB, videoType, isPframe = false) => {
2085
+ if (videoType === "H264") {
2086
+ const nals = splitAnnexBToNalPayloads(annexB);
2087
+ if (nals.length === 0) return annexB;
2088
+ const types = nals.map((n) => (n[0] ?? 0) & 31);
2089
+ const hasVcl = types.some(
2090
+ (t) => t === 1 || t === 5 || t === 19 || t === 20
2091
+ );
2092
+ if (isPframe && !hasVcl) {
2093
+ if (dbg.traceNativeStream) {
2094
+ this.logger?.warn(
2095
+ `[BaichuanVideoStream] Dropping P-frame without VCL (only param sets): types=${types.join(",")}`
2096
+ );
2097
+ }
2098
+ return Buffer.alloc(0);
2099
+ }
2100
+ if (types.includes(7) && types.includes(8)) {
2101
+ let ppsIdFromSlice = null;
2102
+ for (const nal of nals) {
2103
+ const t = (nal[0] ?? 0) & 31;
2104
+ if (t === 1 || t === 5) {
2105
+ ppsIdFromSlice = parseSlicePpsIdFromNal(nal);
2106
+ break;
2107
+ }
2108
+ }
2109
+ if (ppsIdFromSlice != null && ppsIdFromSlice <= 255) {
2110
+ this.lastPrependedPpsId = ppsIdFromSlice;
2111
+ } else {
2112
+ this.lastPrependedPpsId = -1;
2113
+ }
2114
+ return annexB;
2115
+ }
2116
+ if (!hasVcl) return annexB;
2117
+ let ppsId = null;
2118
+ for (const nal of nals) {
2119
+ const t = (nal[0] ?? 0) & 31;
2120
+ if (t === 1 || t === 5) {
2121
+ ppsId = parseSlicePpsIdFromNal(nal);
2122
+ break;
2123
+ }
2124
+ }
2125
+ if (dbg.traceNativeStream) {
2126
+ this.logger?.warn(
2127
+ `[BaichuanVideoStream] Slice references ppsId=${ppsId ?? "?"} lastPrepended=${this.lastPrependedPpsId ?? "?"}`
2128
+ );
2129
+ }
2130
+ if (ppsId == null || ppsId > 255) {
2131
+ if (this.lastPrependedPpsId != null) return annexB;
2132
+ if (!this.lastSps || !this.lastPps) return annexB;
2133
+ this.lastPrependedPpsId = -1;
2134
+ return Buffer.concat([
2135
+ NAL_START_CODE_4B3,
2136
+ this.lastSps,
2137
+ NAL_START_CODE_4B3,
2138
+ this.lastPps,
2139
+ annexB
2140
+ ]);
2141
+ }
2142
+ if (this.lastPrependedPpsId === ppsId) return annexB;
2143
+ const pps = this.ppsById.get(ppsId);
2144
+ if (pps) {
2145
+ const sps = this.spsById.get(pps.spsId);
2146
+ if (sps) {
2147
+ this.lastPrependedPpsId = ppsId;
2148
+ return Buffer.concat([
2149
+ NAL_START_CODE_4B3,
2150
+ sps,
2151
+ NAL_START_CODE_4B3,
2152
+ pps.nal,
2153
+ annexB
2154
+ ]);
2155
+ }
2156
+ }
2157
+ return Buffer.alloc(0);
2158
+ } else if (videoType === "H265") {
2159
+ const nals = splitAnnexBToNalPayloads2(annexB);
2160
+ if (nals.length === 0) return annexB;
2161
+ const types = nals.map((n) => getH265NalType(n)).filter((t) => t !== null);
2162
+ const hasVcl = types.some(
2163
+ (t) => t >= 0 && t <= 9 || t >= 16 && t <= 23
2164
+ );
2165
+ if (isPframe && !hasVcl) {
2166
+ if (dbg.traceNativeStream) {
2167
+ this.logger?.warn(
2168
+ `[BaichuanVideoStream] Dropping H.265 P-frame without VCL (only param sets): types=${types.join(",")}`
2169
+ );
2170
+ }
2171
+ return Buffer.alloc(0);
2172
+ }
2173
+ if (types.includes(32) && types.includes(33) && types.includes(34))
2174
+ return annexB;
2175
+ if (!hasVcl) return annexB;
2176
+ if (this.lastPrependedParamSetsH265) return annexB;
2177
+ if (!this.lastVps || !this.lastSpsH265 || !this.lastPpsH265)
2178
+ return annexB;
2179
+ this.lastPrependedParamSetsH265 = true;
2180
+ if (dbg.traceNativeStream) {
2181
+ this.logger?.warn(
2182
+ `[BaichuanVideoStream] Prepending H.265 VPS/SPS/PPS to frame`
2183
+ );
2184
+ }
2185
+ return Buffer.concat([
2186
+ NAL_START_CODE_4B3,
2187
+ this.lastVps,
2188
+ NAL_START_CODE_4B3,
2189
+ this.lastSpsH265,
2190
+ NAL_START_CODE_4B3,
2191
+ this.lastPpsH265,
2192
+ annexB
2193
+ ]);
2194
+ }
2195
+ return annexB;
2196
+ };
2197
+ const dumpNalSummary = (annexB, label, microseconds) => {
2198
+ if (!dbg.dumpNals) return;
2199
+ try {
2200
+ if (dbg.dumpEnabled) ensureDumpDir(dbg);
2201
+ const outDir = dbg.dumpDir;
2202
+ const nals = splitAnnexBToNalPayloads(annexB);
2203
+ const types = nals.map((n) => (n[0] ?? 0) & 31);
2204
+ let slicePpsId = null;
2205
+ const spsIds = [];
2206
+ const ppsIds = [];
2207
+ for (const nal of nals) {
2208
+ const t = (nal[0] ?? 0) & 31;
2209
+ if ((t === 1 || t === 5) && slicePpsId == null) {
2210
+ slicePpsId = parseSlicePpsIdFromNal(nal);
2211
+ }
2212
+ if (t === 7) {
2213
+ const id = parseSpsIdFromNal(nal);
2214
+ if (id != null) spsIds.push(id);
2215
+ }
2216
+ if (t === 8) {
2217
+ const ids = parsePpsIdsFromNal(nal);
2218
+ if (ids) ppsIds.push(ids.ppsId);
2219
+ }
2220
+ }
2221
+ if (this.dumpNalLines >= 2e4) return;
2222
+ const line = JSON.stringify({
2223
+ label,
2224
+ microseconds,
2225
+ len: annexB.length,
2226
+ nalTypes: types,
2227
+ slicePpsId,
2228
+ auSpsIds: spsIds,
2229
+ auPpsIds: ppsIds,
2230
+ cachedSps: this.spsById.size,
2231
+ cachedPps: this.ppsById.size,
2232
+ lastPrependedPpsId: this.lastPrependedPpsId
2233
+ }) + "\n";
2234
+ this.dumpNalLines++;
2235
+ const outPath = path2.join(outDir, "nal_dump.ndjson");
2236
+ this.dumpIo.enqueue(async () => {
2237
+ await fs2.promises.appendFile(outPath, line);
2238
+ });
2239
+ } catch {
2240
+ }
2241
+ };
2242
+ const isPlausibleH264Sps = (nal) => {
2243
+ if (nal.length < 4) return false;
2244
+ if (((nal[0] ?? 0) & 31) !== 7) return false;
2245
+ const profileIdc = nal[1] ?? 0;
2246
+ const levelIdc = nal[3] ?? 0;
2247
+ const knownProfiles = /* @__PURE__ */ new Set([66, 77, 88, 100, 110, 122, 244]);
2248
+ if (!knownProfiles.has(profileIdc)) return false;
2249
+ if (levelIdc === 0 || levelIdc > 255) return false;
2250
+ return true;
2251
+ };
2252
+ if (media.type === "Iframe") {
2253
+ let videoType = media.videoType;
2254
+ const detectedCodec = detectVideoCodecFromNal(media.data);
2255
+ if (detectedCodec && detectedCodec !== videoType) {
2256
+ if (dbg.traceNativeStream) {
2257
+ this.logger?.warn(
2258
+ `[BaichuanVideoStream] Codec mismatch in Iframe: header says ${videoType}, NAL says ${detectedCodec} - using ${detectedCodec}`
2259
+ );
2260
+ }
2261
+ videoType = detectedCodec;
2262
+ }
2263
+ if (media.additionalHeader && media.additionalHeader.length > 0) {
2264
+ this.emit("additionalHeader", {
2265
+ raw: media.additionalHeader,
2266
+ frameType: "Iframe",
2267
+ videoType,
2268
+ microseconds: media.microseconds,
2269
+ ...this.latestFrameWidth !== void 0 ? { frameWidth: this.latestFrameWidth } : {},
2270
+ ...this.latestFrameHeight !== void 0 ? { frameHeight: this.latestFrameHeight } : {}
2271
+ });
2272
+ }
2273
+ const annexBData = videoType === "H265" ? convertToAnnexB2(media.data) : convertToAnnexB(media.data);
2274
+ const isKeyframe = true;
2275
+ maybeCacheParamSets(annexBData, "Iframe", videoType);
2276
+ const outAnnex = prependParamSetsIfNeeded(annexBData, videoType);
2277
+ if (outAnnex.length === 0) {
2278
+ if (dbg.traceNativeStream) {
2279
+ this.logger?.warn(
2280
+ `[BaichuanVideoStream] Iframe DROPPED: outAnnex is empty`
2281
+ );
2282
+ }
2283
+ continue;
2284
+ }
2285
+ dumpNalSummary(outAnnex, "Iframe", media.microseconds);
2286
+ if (videoType === "H264") {
2287
+ if (!isValidH264AnnexBAccessUnit(outAnnex) || !isH264KeyframeAnnexB(outAnnex)) {
2288
+ if (dbg.traceNativeStream) {
2289
+ this.logger?.warn(
2290
+ `[BaichuanVideoStream] Dropping invalid H.264 Iframe (Annex-B) len=${outAnnex.length}`
2291
+ );
2292
+ }
2293
+ continue;
2294
+ }
2295
+ } else if (videoType === "H265") {
2296
+ if (!isValidH265AnnexBAccessUnit(outAnnex)) {
2297
+ if (dbg.traceNativeStream) {
2298
+ this.logger?.warn(
2299
+ `[BaichuanVideoStream] Dropping invalid H.265 Iframe (Annex-B) len=${outAnnex.length} first16=${outAnnex.subarray(0, 16).toString("hex")}`
2300
+ );
2301
+ }
2302
+ continue;
2303
+ }
2304
+ if (!isH265KeyframeAnnexB(outAnnex)) {
2305
+ if (dbg.traceNativeStream) {
2306
+ this.logger?.warn(
2307
+ `[BaichuanVideoStream] H.265 Iframe missing VPS/SPS/PPS or IRAP, but continuing len=${outAnnex.length}`
2308
+ );
2309
+ }
2310
+ }
2311
+ }
2312
+ if (dbg.traceNativeStream && !this.debugSavedSamples) {
2313
+ try {
2314
+ const outDir = dbg.dumpDir;
2315
+ fs2.mkdirSync(outDir, { recursive: true });
2316
+ if (media.type === "Iframe" && hasStartCodes(annexBData)) {
2317
+ fs2.writeFileSync(
2318
+ path2.join(outDir, "iframe_annexb.bin"),
2319
+ annexBData
2320
+ );
2321
+ }
2322
+ } catch {
2323
+ }
2324
+ }
2325
+ if (!this.warnedNonAnnexBOnce && !hasStartCodes(annexBData)) {
2326
+ this.warnedNonAnnexBOnce = true;
2327
+ const b = media.data;
2328
+ const head = b.subarray(0, Math.min(24, b.length)).toString("hex");
2329
+ const headAnnex = annexBData.subarray(0, Math.min(24, annexBData.length)).toString("hex");
2330
+ this.logger?.warn(
2331
+ `[BaichuanVideoStream] WARNING: non-AnnexB frame after conversion (${media.type} ${media.videoType}) len=${b.length} head=${head} convertedLen=${annexBData.length} convertedHead=${headAnnex}`
2332
+ );
2333
+ }
2334
+ this.emit("videoFrame", outAnnex);
2335
+ this.emit("videoAccessUnit", {
2336
+ data: outAnnex,
2337
+ isKeyframe,
2338
+ videoType,
2339
+ // Use the detected/corrected videoType, not media.videoType
2340
+ microseconds: media.microseconds,
2341
+ ...media.type === "Iframe" && "time" in media ? media.time !== void 0 ? { time: media.time } : {} : {}
2342
+ });
2343
+ videoFramesEmitted++;
2344
+ if (totalFramesReceived <= 5 || videoFramesEmitted <= 5) {
2345
+ const sc = hasStartCodes(annexBData) ? "yes" : "no";
2346
+ if (rtspDebug) {
2347
+ this.logger?.log(
2348
+ `[BaichuanVideoStream] Emitted ${media.type} (${media.videoType}) ${media.data.length} bytes -> ${annexBData.length} bytes (Annex-B, startCode:${sc})`
2349
+ );
2350
+ }
2351
+ }
2352
+ } else if (media.type === "Pframe") {
2353
+ const chunk = media.data;
2354
+ if (media.additionalHeader && media.additionalHeader.length > 0) {
2355
+ const detected = detectVideoCodecFromNal(chunk);
2356
+ const videoTypeForHeader = detected ?? media.videoType;
2357
+ this.emit("additionalHeader", {
2358
+ raw: media.additionalHeader,
2359
+ frameType: "Pframe",
2360
+ videoType: videoTypeForHeader,
2361
+ microseconds: media.microseconds,
2362
+ ...this.latestFrameWidth !== void 0 ? { frameWidth: this.latestFrameWidth } : {},
2363
+ ...this.latestFrameHeight !== void 0 ? { frameHeight: this.latestFrameHeight } : {}
2364
+ });
2365
+ }
2366
+ let videoType = media.videoType;
2367
+ const detectedCodec = detectVideoCodecFromNal(chunk);
2368
+ if (detectedCodec && detectedCodec !== videoType) {
2369
+ videoType = detectedCodec;
2370
+ }
2371
+ const annexBOrRaw = hasStartCodes(chunk) ? chunk : videoType === "H265" ? convertToAnnexB2(chunk) : convertToAnnexB(chunk);
2372
+ const parts = hasStartCodes(annexBOrRaw) ? [annexBOrRaw] : videoType === "H265" ? this.depacketizerH265.push(chunk) : this.depacketizer.push(chunk);
2373
+ if (parts.length === 0) {
2374
+ continue;
2375
+ }
2376
+ for (const p of parts) {
2377
+ maybeCacheParamSets(p, "Pframe", videoType);
2378
+ const outP0 = prependParamSetsIfNeeded(p, videoType, true);
2379
+ if (outP0.length === 0) continue;
2380
+ const outP = outP0;
2381
+ dumpNalSummary(outP, "Pframe", media.microseconds);
2382
+ const isValid = videoType === "H265" ? isValidH265AnnexBAccessUnit(outP) : isValidH264AnnexBAccessUnit(outP);
2383
+ if (!isValid) {
2384
+ if (dbg.traceNativeStream && this.debugH264LogsLeft > 0) {
2385
+ this.debugH264LogsLeft--;
2386
+ const head = outP.subarray(0, Math.min(24, outP.length)).toString("hex");
2387
+ this.logger?.warn(
2388
+ `[BaichuanVideoStream] Dropping invalid Pframe (${videoType}): len=${outP.length} head=${head}`
2389
+ );
2390
+ }
2391
+ continue;
2392
+ }
2393
+ this.emit("videoFrame", outP);
2394
+ this.emit("videoAccessUnit", {
2395
+ data: outP,
2396
+ isKeyframe: false,
2397
+ videoType,
2398
+ microseconds: media.microseconds
2399
+ });
2400
+ videoFramesEmitted++;
2401
+ }
2402
+ }
2403
+ if (media.type === "Aac" || media.type === "Adpcm") {
2404
+ audioFramesEmitted++;
2405
+ this.emit("audioFrame", media.data);
2406
+ }
2407
+ if (media.type === "InfoV1" || media.type === "InfoV2") {
2408
+ if (media.videoWidth > 0) this.latestFrameWidth = media.videoWidth;
2409
+ if (media.videoHeight > 0) this.latestFrameHeight = media.videoHeight;
2410
+ }
2411
+ }
2412
+ if (totalFramesReceived <= 10 || totalFramesReceived % 20 === 0 && (videoFramesEmitted > 0 || audioFramesEmitted > 0)) {
2413
+ if (rtspDebug) {
2414
+ this.logger?.log(
2415
+ `[BaichuanVideoStream] Frame #${totalFramesReceived}: emitted ${videoFramesEmitted} video frames, ${audioFramesEmitted} audio frames`
2416
+ );
2417
+ }
2418
+ }
2419
+ if (videoFramesEmitted > 0 || audioFramesEmitted > 0) {
2420
+ this.noteMediaActivity();
2421
+ }
2422
+ if (videoFramesEmitted > 0 && (totalFramesReceived <= 10 || totalFramesReceived % 50 === 0)) {
2423
+ let totalVideoFrames = 0;
2424
+ }
2425
+ };
2426
+ this.client.on("push", this.videoFrameHandler);
2427
+ this.active = true;
2428
+ this.startWatchdog();
2429
+ if (this.api && typeof this.api._registerVideoStreamForDetection === "function") {
2430
+ this.detectionTeardown = this.api._registerVideoStreamForDetection(this, {
2431
+ channel: this.channel,
2432
+ profile: this.profile
2433
+ });
2434
+ }
2435
+ this.lastMediaAtMs = Date.now();
2436
+ if (this.api) {
2437
+ try {
2438
+ if (this.variant === "default") {
2439
+ try {
2440
+ await this.api.stopVideoStream(this.channel, this.profile, {
2441
+ variant: "default",
2442
+ client: this.client
2443
+ });
2444
+ } catch {
2445
+ }
2446
+ } else {
2447
+ try {
2448
+ await this.api.stopVideoStream(this.channel, this.profile, {
2449
+ variant: this.variant,
2450
+ client: this.client
2451
+ });
2452
+ this.logger?.log(
2453
+ `[BaichuanVideoStream] Successfully stopped existing variant stream: ${this.variant}`
2454
+ );
2455
+ } catch (e) {
2456
+ this.logger?.log(
2457
+ `[BaichuanVideoStream] Error stopping variant stream ${this.variant} (may not exist): ${e instanceof Error ? e.message : String(e)}`
2458
+ );
2459
+ }
2460
+ }
2461
+ await new Promise((resolve) => setTimeout(resolve, 100));
2462
+ const startPromise = this.api.startVideoStream(
2463
+ this.channel,
2464
+ this.profile,
2465
+ { variant: this.variant, client: this.client }
2466
+ );
2467
+ const updateActiveMsgNum = () => {
2468
+ try {
2469
+ const getMsgNum = this.api.getActiveVideoMsgNumWithVariant;
2470
+ const v = typeof getMsgNum === "function" ? getMsgNum(this.channel, this.profile, this.variant) : void 0;
2471
+ if (v !== void 0) this.activeMsgNum = v;
2472
+ } catch {
2473
+ }
2474
+ };
2475
+ updateActiveMsgNum();
2476
+ if (this.client.getTransport?.() === "udp") {
2477
+ await startPromise;
2478
+ } else {
2479
+ await Promise.race([
2480
+ startPromise,
2481
+ new Promise((resolve) => setTimeout(resolve, 400))
2482
+ ]);
2483
+ }
2484
+ updateActiveMsgNum();
2485
+ void startPromise.then(() => updateActiveMsgNum()).catch((e) => {
2486
+ const err = e instanceof Error ? e : new Error(String(e));
2487
+ this.emitSafeError(err);
2488
+ });
2489
+ } catch (error) {
2490
+ const err = error instanceof Error ? error : new Error(String(error));
2491
+ if (this.client.getTransport?.() === "udp") {
2492
+ this.stopWatchdog();
2493
+ this.active = false;
2494
+ if (this.videoFrameHandler)
2495
+ this.client.off("push", this.videoFrameHandler);
2496
+ this.videoFrameHandler = void 0;
2497
+ throw err;
2498
+ }
2499
+ this.emitSafeError(err);
2500
+ }
2501
+ }
2502
+ }
2503
+ // isVideoFrame, isAudioFrame, and extractVideoData are no longer needed
2504
+ // BcMediaParser handles all frame parsing and extraction
2505
+ /**
2506
+ * Stop video stream.
2507
+ */
2508
+ async stop() {
2509
+ if (!this.active) return;
2510
+ this.stopWatchdog();
2511
+ this.depacketizer.reset();
2512
+ this.depacketizerH265.reset();
2513
+ if (this.videoFrameHandler) {
2514
+ this.client.removeListener("push", this.videoFrameHandler);
2515
+ }
2516
+ this.videoFrameHandler = void 0;
2517
+ this.bcMediaCodec.clear();
2518
+ this.activeMsgNum = void 0;
2519
+ if (this.api) {
2520
+ try {
2521
+ await this.api.stopVideoStream(this.channel, this.profile, {
2522
+ variant: this.variant,
2523
+ client: this.client
2524
+ });
2525
+ } catch (error) {
2526
+ this.emitSafeError(
2527
+ error instanceof Error ? error : new Error(String(error))
2528
+ );
2529
+ }
2530
+ }
2531
+ this.active = false;
2532
+ if (this.detectionTeardown) {
2533
+ try {
2534
+ this.detectionTeardown();
2535
+ } catch {
2536
+ }
2537
+ this.detectionTeardown = void 0;
2538
+ }
2539
+ this.emit("close");
2540
+ }
2541
+ isActive() {
2542
+ return this.active;
2543
+ }
2544
+ };
2545
+
2546
+ export {
2547
+ __require,
2548
+ BC_TCP_DEFAULT_PORT,
2549
+ BC_MAGIC,
2550
+ BC_MAGIC_REV,
2551
+ BC_XML_KEY,
2552
+ BC_AES_IV,
2553
+ BC_CLASS_LEGACY,
2554
+ BC_CLASS_MODERN_20,
2555
+ BC_CLASS_MODERN_24,
2556
+ BC_CLASS_MODERN_24_ALT,
2557
+ BC_CLASS_FILE_DOWNLOAD,
2558
+ bcHeaderHasPayloadOffset,
2559
+ BC_CMD_ID_LOGIN,
2560
+ BC_CMD_ID_LOGOUT,
2561
+ BC_CMD_ID_VIDEO,
2562
+ BC_CMD_ID_VIDEO_STOP,
2563
+ BC_CMD_ID_FILE_INFO_LIST_REPLAY,
2564
+ BC_CMD_ID_FILE_INFO_LIST_STOP,
2565
+ BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO,
2566
+ BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,
2567
+ BC_CMD_ID_FILE_INFO_LIST_OPEN,
2568
+ BC_CMD_ID_FILE_INFO_LIST_GET,
2569
+ BC_CMD_ID_FILE_INFO_LIST_CLOSE,
2570
+ BC_CMD_ID_FIND_REC_VIDEO_OPEN,
2571
+ BC_CMD_ID_FIND_REC_VIDEO_GET,
2572
+ BC_CMD_ID_FIND_REC_VIDEO_CLOSE,
2573
+ BC_CMD_ID_COVER_PREVIEW,
2574
+ BC_CMD_ID_COVER_STANDALONE_458,
2575
+ BC_CMD_ID_COVER_STANDALONE_459,
2576
+ BC_CMD_ID_COVER_STANDALONE_460,
2577
+ BC_CMD_ID_COVER_STANDALONE_461,
2578
+ BC_CMD_ID_COVER_STANDALONE_462,
2579
+ BC_CMD_ID_COVER_RESPONSE,
2580
+ BC_CMD_ID_TALK_ABILITY,
2581
+ BC_CMD_ID_TALK_RESET,
2582
+ BC_CMD_ID_TALK_CONFIG,
2583
+ BC_CMD_ID_TALK,
2584
+ BC_CMD_ID_PTZ_CONTROL,
2585
+ BC_CMD_ID_PTZ_CONTROL_PRESET,
2586
+ BC_CMD_ID_GET_PTZ_PRESET,
2587
+ BC_CMD_ID_GET_PTZ_POSITION,
2588
+ BC_CMD_ID_GET_ZOOM_FOCUS,
2589
+ BC_CMD_ID_SET_ZOOM_FOCUS,
2590
+ BC_CMD_ID_GET_BATTERY_INFO_LIST,
2591
+ BC_CMD_ID_GET_BATTERY_INFO,
2592
+ BC_CMD_ID_UDP_KEEP_ALIVE,
2593
+ BC_CMD_ID_GET_PIR_INFO,
2594
+ BC_CMD_ID_SET_PIR_INFO,
2595
+ BC_CMD_ID_GET_MOTION_ALARM,
2596
+ BC_CMD_ID_SET_MOTION_ALARM,
2597
+ BC_CMD_ID_ALARM_EVENT_LIST,
2598
+ BC_CMD_ID_GET_AI_ALARM,
2599
+ BC_CMD_ID_SET_AI_ALARM,
2600
+ BC_CMD_ID_GET_AUDIO_ALARM,
2601
+ BC_CMD_ID_AUDIO_ALARM_PLAY,
2602
+ BC_CMD_ID_GET_WHITE_LED,
2603
+ BC_CMD_ID_SET_WHITE_LED_STATE,
2604
+ BC_CMD_ID_SET_WHITE_LED_TASK,
2605
+ BC_CMD_ID_FLOODLIGHT_STATUS_LIST,
2606
+ BC_CMD_ID_ABILITY_INFO,
2607
+ BC_CMD_ID_SUPPORT,
2608
+ BC_CMD_ID_PING,
2609
+ BC_CMD_ID_CHANNEL_INFO_ALL,
2610
+ BC_CMD_ID_GET_OSD_DATETIME,
2611
+ BC_CMD_ID_SET_OSD_DATETIME,
2612
+ BC_CMD_ID_GET_RECORD_CFG,
2613
+ BC_CMD_ID_GET_ABILITY_SUPPORT,
2614
+ BC_CMD_ID_GET_FTP_TASK,
2615
+ BC_CMD_ID_GET_VERSION_INFO,
2616
+ BC_CMD_ID_GET_RECORD,
2617
+ BC_CMD_ID_GET_HDD_INFO_LIST,
2618
+ BC_CMD_ID_GET_WIFI_SIGNAL,
2619
+ BC_CMD_ID_GET_WIFI,
2620
+ BC_CMD_ID_GET_ONLINE_USER_LIST,
2621
+ BC_CMD_ID_GET_DAY_RECORDS,
2622
+ BC_CMD_ID_GET_STREAM_INFO_LIST,
2623
+ BC_CMD_ID_GET_LED_STATE,
2624
+ BC_CMD_ID_GET_EMAIL_TASK,
2625
+ BC_CMD_ID_GET_AUDIO_TASK,
2626
+ BC_CMD_ID_GET_AUDIO_CFG,
2627
+ BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,
2628
+ BC_CMD_ID_GET_TIMELAPSE_CFG,
2629
+ BC_CMD_ID_GET_AI_DENOISE,
2630
+ BC_CMD_ID_GET_KIT_AP_CFG,
2631
+ BC_CMD_ID_GET_REC_ENC_CFG,
2632
+ BC_CMD_ID_GET_ACCESS_USER_LIST,
2633
+ BC_CMD_ID_GET_SLEEP_STATE,
2634
+ BC_CMD_ID_GET_VIDEO_INPUT,
2635
+ BC_CMD_ID_GET_SYSTEM_GENERAL,
2636
+ BC_CMD_ID_GET_SUPPORT,
2637
+ BC_CMD_ID_GET_AI_CFG,
2638
+ BC_CMD_ID_SET_AI_CFG,
2639
+ BC_CMD_ID_GET_SIREN_STATUS,
2640
+ BC_CMD_ID_SET_AUDIO_TASK,
2641
+ BC_CMD_ID_SET_VIDEO_INPUT,
2642
+ BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD,
2643
+ BC_CMD_ID_GET_ENC,
2644
+ BC_CMD_ID_SET_ENC,
2645
+ BC_CMD_ID_GET_PRIVACY_MASK,
2646
+ BC_CMD_ID_SET_PRIVACY_MASK,
2647
+ BC_CMD_ID_SET_AI_DENOISE,
2648
+ BC_CMD_ID_SET_LED_STATE,
2649
+ BC_CMD_ID_SET_AUDIO_CFG,
2650
+ BC_CMD_ID_SET_RECORD,
2651
+ BC_CMD_ID_SET_RECORD_CFG,
2652
+ BC_CMD_ID_SET_EMAIL_TASK,
2653
+ BC_CMD_ID_GET_PUSH_TASK,
2654
+ BC_CMD_ID_SET_PUSH_TASK,
2655
+ BC_CMD_ID_GET_AUTO_FOCUS,
2656
+ BC_CMD_ID_SET_AUTO_FOCUS,
2657
+ BC_CMD_ID_GET_EMAIL,
2658
+ BC_CMD_ID_SET_EMAIL,
2659
+ BC_CMD_ID_TEST_EMAIL,
2660
+ BC_CMD_ID_GET_NTP,
2661
+ BC_CMD_ID_SET_NTP,
2662
+ BC_CMD_ID_SET_SYSTEM_GENERAL,
2663
+ BC_CMD_ID_GET_DST,
2664
+ BC_CMD_ID_SET_DST,
2665
+ BC_CMD_ID_GET_AUTO_REBOOT,
2666
+ BC_CMD_ID_SET_AUTO_REBOOT,
2667
+ BC_CMD_ID_CMD_123,
2668
+ BC_CMD_ID_CMD_209,
2669
+ BC_CMD_ID_CMD_265,
2670
+ BC_CMD_ID_CMD_440,
2671
+ BC_CMD_ID_PUSH_VIDEO_INPUT,
2672
+ BC_CMD_ID_PUSH_SERIAL,
2673
+ BC_CMD_ID_PUSH_NET_INFO,
2674
+ BC_CMD_ID_PUSH_DINGDONG_LIST,
2675
+ BC_CMD_ID_PUSH_SLEEP_STATUS,
2676
+ BC_CMD_ID_PUSH_COORDINATE_POINT_LIST,
2677
+ BC_CMD_ID_DING_DONG_CTRL,
2678
+ BC_CMD_ID_GET_DING_DONG_LIST,
2679
+ BC_CMD_ID_DING_DONG_OPT,
2680
+ BC_CMD_ID_GET_DING_DONG_CFG,
2681
+ BC_CMD_ID_SET_DING_DONG_CFG,
2682
+ BC_CMD_ID_QUICK_REPLY_PLAY,
2683
+ BC_CMD_ID_GET_DING_DONG_SILENT,
2684
+ BC_CMD_ID_SET_DING_DONG_SILENT,
2685
+ md5HexUpper,
2686
+ md5StrModern,
2687
+ deriveAesKey,
2688
+ bcEncrypt,
2689
+ bcDecrypt,
2690
+ aesEncrypt,
2691
+ aesDecrypt,
2692
+ AesStreamDecryptor,
2693
+ normalizeDebugOptions,
2694
+ recordingsTraceLog,
2695
+ debugLog,
2696
+ traceLog,
2697
+ talkTraceLog,
2698
+ eventTraceLog,
2699
+ parseBcMedia,
2700
+ BcMediaCodec,
2701
+ hasStartCodes,
2702
+ convertToAnnexB,
2703
+ splitAnnexBToNalPayloads,
2704
+ isValidH264AnnexBAccessUnit,
2705
+ isH264KeyframeAnnexB,
2706
+ H264RtpDepacketizer,
2707
+ convertToLengthPrefixed,
2708
+ hasStartCodes2,
2709
+ convertToAnnexB2,
2710
+ H265RtpDepacketizer,
2711
+ splitAnnexBToNalPayloads2,
2712
+ getH265NalType,
2713
+ isH265Irap,
2714
+ isValidH265AnnexBAccessUnit,
2715
+ isH265KeyframeAnnexB,
2716
+ extractVpsFromAnnexB,
2717
+ extractSpsFromAnnexB,
2718
+ extractPpsFromAnnexB,
2719
+ detectVideoCodecFromNal,
2720
+ BcMediaAnnexBDecoder,
2721
+ BaichuanVideoStream
2722
+ };
2723
+ //# sourceMappingURL=chunk-C57QV7IL.js.map