@apocaliss92/nodelink-js 0.4.21 → 0.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaichuanVideoStream-NTIGPHYJ.js +7 -0
- package/dist/{DiagnosticsTools-HO3VI6D5.js → DiagnosticsTools-ILDDJZL7.js} +3 -3
- package/dist/{chunk-S2UTGNFN.js → chunk-AHY4L7JI.js} +2 -2
- package/dist/{chunk-C57QV7IL.js → chunk-GVWJGQPT.js} +5 -1
- package/dist/chunk-GVWJGQPT.js.map +1 -0
- package/dist/{chunk-EZ7WNPLB.js → chunk-P4X5OU25.js} +521 -19
- package/dist/chunk-P4X5OU25.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +506 -14
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +3 -3
- package/dist/index.cjs +534 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +327 -1
- package/dist/index.d.ts +343 -0
- package/dist/index.js +31 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/BaichuanVideoStream-HGPU2MZ3.js +0 -7
- package/dist/chunk-C57QV7IL.js.map +0 -1
- package/dist/chunk-EZ7WNPLB.js.map +0 -1
- /package/dist/{BaichuanVideoStream-HGPU2MZ3.js.map → BaichuanVideoStream-NTIGPHYJ.js.map} +0 -0
- /package/dist/{DiagnosticsTools-HO3VI6D5.js.map → DiagnosticsTools-ILDDJZL7.js.map} +0 -0
- /package/dist/{chunk-S2UTGNFN.js.map → chunk-AHY4L7JI.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apocaliss92/nodelink-js",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.23",
|
|
4
4
|
"description": "TypeScript library implementing Reolink Baichuan protocol (control + streaming) with CGI and RTSP helpers. Full TypeScript support with comprehensive type definitions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/baichuan/stream/BaichuanVideoStream.ts","../src/protocol/crypto.ts","../src/protocol/constants.ts","../src/debug/DebugConfig.ts","../src/baichuan/stream/BcMediaParser.ts","../src/baichuan/stream/BcMediaCodec.ts","../src/baichuan/stream/H264Converter.ts","../src/baichuan/stream/H265Converter.ts","../src/baichuan/stream/BcMediaAnnexBDecoder.ts"],"sourcesContent":["/**\n * Baichuan Video Stream - Builds a video stream from the native Baichuan protocol.\n *\n * Receives video frames via `push` events and exposes them as access units.\n */\n\nimport { BaichuanClient } from \"../../client/BaichuanClient\";\nimport type { BaichuanFrame } from \"../../protocol/framing\";\nimport { EventEmitter } from \"node:events\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { StreamProfile } from \"../../reolink/baichuan/types\";\nimport type { ReolinkBaichuanApi } from \"../../reolink/baichuan/ReolinkBaichuanApi\";\nimport type { NativeVideoStreamVariant } from \"../../reolink/baichuan/types\";\nimport type { EncryptionProtocol } from \"../../protocol/crypto\";\nimport { aesDecrypt, AesStreamDecryptor } from \"../../protocol/crypto\";\nimport { ensureDumpDir, type Logger } from \"../../debug/DebugConfig\";\nimport { BcMediaCodec } from \"./BcMediaCodec\";\nimport {\n convertToAnnexB,\n hasStartCodes,\n H264RtpDepacketizer,\n isValidH264AnnexBAccessUnit,\n isH264KeyframeAnnexB,\n splitAnnexBToNalPayloads,\n} from \"./H264Converter\";\nimport {\n convertToAnnexB as convertH265ToAnnexB,\n isValidH265AnnexBAccessUnit,\n isH265KeyframeAnnexB,\n splitAnnexBToNalPayloads as splitH265AnnexBToNalPayloads,\n extractVpsFromAnnexB,\n extractSpsFromAnnexB,\n extractPpsFromAnnexB,\n getH265NalType,\n H265RtpDepacketizer,\n} from \"./H265Converter\";\nimport { detectVideoCodecFromNal } from \"./BcMediaAnnexBDecoder\";\n\nconst NAL_START_CODE_4B = Buffer.from([0x00, 0x00, 0x00, 0x01]);\n\nconst WATCHDOG_TICK_MS = 1000;\nconst WATCHDOG_MAX_RESTARTS_PER_MINUTE = 3;\n\nclass AsyncFsQueue {\n private running = false;\n private readonly queue: Array<() => Promise<void>> = [];\n private readonly maxQueueSize: number;\n\n constructor(maxQueueSize: number) {\n this.maxQueueSize = maxQueueSize;\n }\n\n enqueue(task: () => Promise<void>): void {\n if (this.queue.length >= this.maxQueueSize) return;\n this.queue.push(task);\n this.pump();\n }\n\n private pump(): void {\n if (this.running) return;\n this.running = true;\n void this.run();\n }\n\n private async run(): Promise<void> {\n try {\n while (this.queue.length > 0) {\n const task = this.queue.shift();\n if (!task) continue;\n try {\n await task();\n } catch {\n // ignore\n }\n }\n } finally {\n this.running = false;\n }\n }\n}\n\n// --- Minimal H.264 bit parsing helpers (for SPS/PPS IDs + slice pps_id) ---\nfunction removeEmulationPreventionBytes(rbsp: Buffer): Buffer {\n // Remove 0x03 after 00 00 (Annex-B emulation prevention)\n const out: number[] = [];\n for (let i = 0; i < rbsp.length; i++) {\n if (\n i >= 2 &&\n rbsp[i] === 0x03 &&\n rbsp[i - 1] === 0x00 &&\n rbsp[i - 2] === 0x00\n ) {\n continue;\n }\n out.push(rbsp[i]!);\n }\n return Buffer.from(out);\n}\n\nclass BitReader {\n private readonly b: Buffer;\n private bitPos = 0;\n constructor(buf: Buffer) {\n this.b = buf;\n }\n readBits(n: number): number | null {\n if (n <= 0) return 0;\n let v = 0;\n for (let i = 0; i < n; i++) {\n const bytePos = this.bitPos >> 3;\n if (bytePos >= this.b.length) return null;\n const bitInByte = 7 - (this.bitPos & 7);\n const bit = (this.b[bytePos]! >> bitInByte) & 1;\n v = (v << 1) | bit;\n this.bitPos++;\n }\n return v >>> 0;\n }\n readUE(): number | null {\n // unsigned Exp-Golomb\n let zeros = 0;\n while (true) {\n const bit = this.readBits(1);\n if (bit == null) return null;\n if (bit === 0) zeros++;\n else break;\n if (zeros > 31) return null;\n }\n const rest = zeros ? this.readBits(zeros) : 0;\n if (rest == null) return null;\n return ((1 << zeros) - 1 + rest) >>> 0;\n }\n}\n\nfunction parseSpsIdFromNal(nalPayload: Buffer): number | null {\n // nalPayload includes nal header byte (type=7)\n if (((nalPayload[0] ?? 0) & 0x1f) !== 7) return null;\n const rbsp = removeEmulationPreventionBytes(nalPayload.subarray(1));\n const r = new BitReader(rbsp);\n // profile_idc (8), constraint flags+reserved (8), level_idc (8)\n if (r.readBits(8) == null) return null;\n if (r.readBits(8) == null) return null;\n if (r.readBits(8) == null) return null;\n return r.readUE();\n}\n\nfunction parsePpsIdsFromNal(\n nalPayload: Buffer,\n): { ppsId: number; spsId: number } | null {\n if (((nalPayload[0] ?? 0) & 0x1f) !== 8) return null;\n const rbsp = removeEmulationPreventionBytes(nalPayload.subarray(1));\n const r = new BitReader(rbsp);\n const ppsId = r.readUE();\n const spsId = r.readUE();\n if (ppsId == null || spsId == null) return null;\n return { ppsId, spsId };\n}\n\nfunction parseSlicePpsIdFromNal(nalPayload: Buffer): number | null {\n const t = (nalPayload[0] ?? 0) & 0x1f;\n if (t !== 1 && t !== 5) return null;\n const rbsp = removeEmulationPreventionBytes(nalPayload.subarray(1));\n const r = new BitReader(rbsp);\n // first_mb_in_slice, slice_type, pic_parameter_set_id\n if (r.readUE() == null) return null;\n if (r.readUE() == null) return null;\n return r.readUE();\n}\n\nexport interface BaichuanVideoStreamOptions {\n client: BaichuanClient;\n api?: ReolinkBaichuanApi; // Optional API for requesting stream\n channel: number;\n profile: StreamProfile; // \"main\" | \"sub\" | \"ext\"\n /** Native-only: TrackMix tele/autotrack variants (usually on NVR/Hub). */\n variant?: NativeVideoStreamVariant | undefined;\n logger?: Logger;\n\n /**\n * cmdId that carries the BcMedia payload.\n * Live stream typically uses 3; recording replay typically uses 5.\n */\n cmdId?: number;\n /**\n * If provided, frames with other msgNum will be discarded.\n * Useful for replay (where the caller reserves the msgNum).\n */\n msgNum?: number;\n /** If true, do not filter by streamType (only by cmdId/msgNum). */\n acceptAnyStreamType?: boolean;\n}\n\n/**\n * BaichuanVideoStream - Handles video streaming via the Baichuan protocol.\n *\n * Video frames arrive as `push` events from `BaichuanClient`.\n * This stream receives frames, decodes BcMedia packets and emits H.264/H.265 access units.\n *\n * - cmd_id is the stream message id (typically 3)\n * - streamType: 0 for main/ext, 1 for sub\n * - body/payload contains embedded BcMedia packets (H.264/H.265 + audio)\n */\nexport class BaichuanVideoStream extends EventEmitter<{\n videoFrame: [Buffer]; // Decoded video frame (H.264/H.265 Annex-B access unit)\n /**\n * Richer (non-breaking) event: access unit with metadata.\n * Useful for servers (HTTP/RTSP) to wait for a keyframe and/or prepend SPS/PPS.\n */\n videoAccessUnit: [\n {\n data: Buffer;\n isKeyframe: boolean;\n videoType: \"H264\" | \"H265\";\n microseconds: number;\n time?: number;\n },\n ];\n /**\n * Raw BcMedia `additionalHeader` block emitted for every I-frame and P-frame.\n * Carries Reolink's per-frame side-channel metadata (detection bounding boxes).\n * `frameWidth`/`frameHeight` come from the most recent BcMedia InfoV1/V2 packet\n * so consumers can normalize pixel coordinates against the actual stream size.\n */\n additionalHeader: [\n {\n raw: Buffer;\n frameType: \"Iframe\" | \"Pframe\";\n videoType: \"H264\" | \"H265\";\n microseconds: number;\n frameWidth?: number;\n frameHeight?: number;\n },\n ];\n audioFrame: [Buffer]; // Audio frame (if present)\n error: [Error];\n close: [];\n}> {\n private client: BaichuanClient;\n private api: ReolinkBaichuanApi | undefined;\n private channel: number;\n private profile: StreamProfile;\n private variant: NativeVideoStreamVariant;\n private logger: Logger | undefined;\n private active = false;\n private videoFrameHandler: ((frame: BaichuanFrame) => void) | undefined;\n private readonly expectedStreamTypes: Set<number>;\n private activeMsgNum: number | undefined;\n private readonly cmdId: number;\n private readonly acceptAnyStreamType: boolean;\n private lockedChannelId: number | undefined;\n private bcMediaCodec: BcMediaCodec;\n /**\n * Diagnostic-only accessor for the BcMedia codec. Used by tools that need to\n * inspect unknown chunks (for example to discover undocumented audio\n * sub-packets the parser currently skips). Not part of the supported public\n * surface — do not rely on it in application code.\n */\n get _bcMediaCodec(): BcMediaCodec {\n return this.bcMediaCodec;\n }\n private debugH264LogsLeft: number;\n private debugSavedSamples: boolean;\n private warnedNonAnnexBOnce = false;\n // \"RTP-like\" depacketizer (some models encapsulate NAL units in FU-A/STAP)\n private readonly depacketizer = new H264RtpDepacketizer();\n private readonly depacketizerH265 = new H265RtpDepacketizer();\n private dumpChunkIdx = 0;\n private dumpNalLines = 0;\n private readonly dumpIo = new AsyncFsQueue(200);\n private spsById = new Map<number, Buffer>(); // NAL payload (without start code) - H.264\n private ppsById = new Map<number, { nal: Buffer; spsId: number }>(); // NAL payload + mapping - H.264\n private lastSps: Buffer | null = null; // H.264\n private lastPps: Buffer | null = null; // H.264\n private lastPrependedPpsId: number | null = null; // H.264\n // H.265 parameter sets\n private lastVps: Buffer | null = null; // H.265 VPS\n private lastSpsH265: Buffer | null = null; // H.265 SPS\n private lastPpsH265: Buffer | null = null; // H.265 PPS\n private lastPrependedParamSetsH265 = false; // Track if we've prepended H.265 param sets\n\n // Stateful AES decryptor for fragmented BcMedia packets (full_aes mode)\n // In CFB mode, continuation frames must use the cipher state from previous frames.\n private aesStreamDecryptor: AesStreamDecryptor | null = null;\n\n // Latest frame dimensions reported by BcMedia InfoV1/V2 packets.\n // Used to attach width/height context to the `additionalHeader` event so\n // consumers can normalize box coordinates to a fraction of the stream size.\n private latestFrameWidth: number | undefined;\n private latestFrameHeight: number | undefined;\n\n // Teardown returned by ReolinkBaichuanApi._registerVideoStreamForDetection.\n // Called from stop() to detach the detection bridge.\n private detectionTeardown: (() => void) | undefined;\n\n /**\n * Pending startup error stashed when emitSafeError is called before any\n * \"error\" listener is registered (e.g. camera returns 400 during start()).\n * The rfc4571-server's waitForKeyframe can consume this immediately instead\n * of waiting for the full keyframe timeout.\n */\n private _pendingStartupError: Error | undefined;\n\n /** Consume and clear any pending startup error. */\n consumePendingStartupError(): Error | undefined {\n const err = this._pendingStartupError;\n this._pendingStartupError = undefined;\n return err;\n }\n\n private emitSafeError(err: Error): void {\n // If we're no longer active, this is almost always a late/rejected in-flight request.\n // Emitting 'error' with no listeners will crash the process, so guard both cases.\n if (!this.active) {\n this.logger?.warn?.(\n `[BaichuanVideoStream] Suppressed error after stop: ${err.message}`,\n );\n return;\n }\n\n if (this.listenerCount(\"error\") === 0) {\n this.logger?.warn?.(\n `[BaichuanVideoStream] Unhandled stream error: ${err.message}`,\n );\n // Stash the error so the rfc4571-server can consume it immediately\n // instead of waiting for the full keyframe timeout.\n this._pendingStartupError = err;\n return;\n }\n\n this.emit(\"error\", err);\n }\n private lastMediaAtMs = 0;\n private watchdogTimer: NodeJS.Timeout | undefined;\n private restarting = false;\n private restartWindowStartMs = 0;\n private restartCountInWindow = 0;\n private readonly idleRestartMs: number;\n // Note: reassembly happens at the BcMediaCodec transport level, so we do not\n // accumulate frames here.\n\n private static scoreBcMediaLike(b: Buffer): { score: number; first: number } {\n if (b.length < 4) return { score: -1, first: -1 };\n const maxScan = Math.min(64 * 1024, b.length - 4);\n let count = 0;\n let first = -1;\n for (let i = 0; i <= maxScan; i++) {\n const magic = b.readUInt32LE(i);\n const isInfoV1 = magic === 0x31303031; // \"1001\"\n const isInfoV2 = magic === 0x32303031; // \"1002\"\n const isIFrame = magic >= 0x63643030 && magic <= 0x63643039; // \"cd00\"..\"cd09\"\n const isPFrame = magic >= 0x63643130 && magic <= 0x63643139; // \"cd10\"..\"cd19\"\n const isAac = magic === 0x62773530; // \"bw50\"\n const isAdpcm = magic === 0x62773130; // \"bw10\"\n if (isInfoV1 || isInfoV2 || isIFrame || isPFrame || isAac || isAdpcm) {\n count++;\n if (first < 0) first = i;\n if (count > 32 && first === 0) break;\n }\n }\n return { score: count * 1000 - (first < 0 ? 50000 : first), first };\n }\n\n private chooseDecryptedOrRawCandidate(params: {\n raw: Buffer;\n enc: EncryptionProtocol;\n channelId: number;\n allowResync: boolean;\n encryptLen?: number;\n }): Buffer {\n const { raw, enc, channelId, allowResync, encryptLen } = params;\n\n // If encryptLen is provided, only decrypt the first encryptLen bytes\n // and keep the rest as-is. This is how Reolink partial encryption works.\n if (encryptLen !== undefined && encryptLen > 0 && encryptLen < raw.length) {\n const encryptedPart = raw.subarray(0, encryptLen);\n const clearPart = raw.subarray(encryptLen);\n const decryptedPart = this.client.tryDecryptBinary(\n encryptedPart,\n channelId,\n enc,\n );\n const chosen = Buffer.concat([decryptedPart, clearPart]);\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n\n // --- Special handling for full_aes mode ---\n // In AES-128-CFB mode, decryption behavior differs between live and replay:\n //\n // LIVE STREAM (cmdId=3): Each Baichuan frame contains complete BcMedia packets.\n // Fresh IV decryption works correctly for every frame.\n //\n // PLAYBACK/REPLAY (cmdId=5):\n // - I-frames: Entire chunk is encrypted, fresh IV works.\n // - P-frames: Only the BcMedia HEADER is encrypted, video PAYLOAD is clear!\n // Fresh IV decryption corrupts the clear payload.\n // We must use partial decryption: decrypt header, keep payload as-is.\n //\n // Detection: After fresh decrypt, check if raw bytes at video payload offset\n // have H.264 start codes. If yes, use partial decryption.\n if (enc.kind === \"full_aes\") {\n const key = enc.key;\n const isReplayMode = this.cmdId === 5; // BC_CMD_ID_FILE_INFO_LIST_REPLAY\n\n // Try decryption with fresh IV\n const freshDecrypted = aesDecrypt(raw, key);\n const freshScore = BaichuanVideoStream.scoreBcMediaLike(freshDecrypted);\n const rawScore = BaichuanVideoStream.scoreBcMediaLike(raw);\n\n // Check if fresh-decrypted data starts with a BcMedia magic\n const startsWithMagic = freshScore.first === 0 && freshScore.score > 0;\n\n // For live stream (cmdId=3), always use fresh IV decryption\n if (!isReplayMode) {\n const chosen = freshScore.score > rawScore.score ? freshDecrypted : raw;\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n\n // --- Replay mode: handle partial encryption for P-frames ---\n\n if (startsWithMagic && freshDecrypted.length >= 24) {\n // Parse BcMedia header to determine if we need partial decryption\n const magic = freshDecrypted.readUInt32LE(0);\n const isIFrame = magic >= 0x63643030 && magic <= 0x63643039;\n const isPFrame = magic >= 0x63643130 && magic <= 0x63643139;\n\n if ((isIFrame || isPFrame) && freshDecrypted.length >= 24) {\n // Video frame: magic(4) + videoType(4) + payloadSize(4) + additionalHeaderSize(4) + microseconds(4) + unknown(4) + additionalHeader + payload\n const additionalHeaderSize = freshDecrypted.readUInt32LE(12);\n const headerLen = 24 + additionalHeaderSize;\n\n if (headerLen > 0 && headerLen < raw.length) {\n // Check if raw bytes at headerLen have H.264 start codes (clear payload indicator)\n const rawPayloadStart = raw.subarray(headerLen, headerLen + 4);\n const hasRawStartCode =\n rawPayloadStart.length >= 4 &&\n rawPayloadStart[0] === 0 &&\n rawPayloadStart[1] === 0 &&\n (rawPayloadStart[2] === 1 ||\n (rawPayloadStart[2] === 0 && rawPayloadStart[3] === 1));\n\n if (hasRawStartCode) {\n // P-frame case: header is encrypted, payload is clear\n // Use partial decryption: decrypt only the header\n const headerDecrypted = aesDecrypt(\n raw.subarray(0, headerLen),\n key,\n );\n const clearPayload = raw.subarray(headerLen);\n const chosen = Buffer.concat([headerDecrypted, clearPayload]);\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n }\n }\n\n // I-frame case: Reolink encrypts only the first 1024 bytes of the BcMedia frame.\n // Bytes 0-1023 are encrypted, bytes 1024+ are clear (unencrypted).\n // This is a form of partial encryption for efficiency (64 AES blocks).\n const IFRAME_ENCRYPT_BOUNDARY = 1024;\n\n if (raw.length > IFRAME_ENCRYPT_BOUNDARY) {\n // Partial encryption: decrypt first 1024 bytes, keep rest as-is\n const encryptedPart = raw.subarray(0, IFRAME_ENCRYPT_BOUNDARY);\n const clearPart = raw.subarray(IFRAME_ENCRYPT_BOUNDARY);\n const decryptedPart = aesDecrypt(encryptedPart, key);\n const chosen = Buffer.concat([decryptedPart, clearPart]);\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n\n // Small frame (<=1024 bytes): full decryption needed\n {\n const chosen = freshDecrypted;\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n }\n\n // Check if raw data already looks like valid BcMedia (not encrypted)\n if (rawScore.first === 0 && rawScore.score > freshScore.score) {\n // Data is not encrypted - use raw\n // Note: we don't reset the decryptor here since the stream may be mixed\n const chosen = raw;\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n\n // Continuation frame - use stateful decryptor if available\n if (this.aesStreamDecryptor && this.aesStreamDecryptor.isInitialized()) {\n const statefulDecrypted = this.aesStreamDecryptor.update(raw);\n // Continuation frames don't start with magic, so we return as-is\n // (the BcMediaCodec will append to its buffer)\n return statefulDecrypted;\n }\n\n // Fallback: no stateful decryptor yet - might be first frame or stream desync\n // Try fresh decryption as last resort\n const chosen = freshScore.score > rawScore.score ? freshDecrypted : raw;\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n\n // --- Standard handling for other encryption modes ---\n const rawScore = BaichuanVideoStream.scoreBcMediaLike(raw);\n const dec =\n this.client.enc.kind === \"aes\" || this.client.enc.kind === \"bc\"\n ? this.client.tryDecryptBinary(raw, channelId, enc)\n : raw;\n const decScore = BaichuanVideoStream.scoreBcMediaLike(dec);\n const chosen = decScore.score > rawScore.score ? dec : raw;\n\n // WARNING: trimming to the first magic boundary can drop bytes that belong to a\n // fragmented BcMedia packet (spanning across frames). Only do this when we know\n // the chunk may contain non-media bytes (e.g. XML) at the start.\n if (!allowResync) return chosen;\n const best = BaichuanVideoStream.scoreBcMediaLike(chosen);\n return best.first > 0 ? chosen.subarray(best.first) : chosen;\n }\n\n constructor(options: BaichuanVideoStreamOptions) {\n super();\n this.client = options.client;\n this.api = options.api;\n this.channel = options.channel;\n this.profile = options.profile;\n this.variant = options.variant ?? \"default\";\n this.logger = options.logger;\n this.cmdId = options.cmdId ?? 3;\n this.acceptAnyStreamType = options.acceptAnyStreamType ?? false;\n // Stream type varies across firmwares:\n // - some use 0/1 for main/sub (even for tele on Hub/NVR)\n // - others use 2/3 for autotrack/telephoto variants\n // Accept both and rely on msgNum filtering when available.\n this.expectedStreamTypes =\n this.profile === \"sub\" ? new Set([1, 3]) : new Set([0, 2]);\n // Hub/NVR tele selection uses Preview v1.1 while keeping header streamType=0.\n // Without this, native_sub telephoto would discard all frames and timeout.\n if (this.variant === \"telephoto\") this.expectedStreamTypes.add(0);\n // this.logger?.log(\n // `[BaichuanVideoStream] constructor: channel=${this.channel}, profile=${this.profile}, variant=${this.variant}, expectedStreamTypes=[${[\n // ...this.expectedStreamTypes,\n // ].join(\",\")}]`\n // );\n this.bcMediaCodec = new BcMediaCodec(false, this.logger); // non-strict mode for error recovery\n // Debug is configured on the client; the library must not read env vars.\n const dbg = this.client.getDebugConfig();\n this.debugH264LogsLeft = dbg.traceNativeStream ? 200 : 0;\n this.debugSavedSamples = false;\n this.dumpChunkIdx = 0;\n this.spsById = new Map();\n this.ppsById = new Map();\n this.lastSps = null;\n this.lastPps = null;\n this.lastPrependedPpsId = null;\n // H.265 parameter sets\n this.lastVps = null;\n this.lastSpsH265 = null;\n this.lastPpsH265 = null;\n this.lastPrependedParamSetsH265 = false;\n\n // If the caller knows the msgNum (e.g. replay), lock to it immediately.\n if (options.msgNum !== undefined) {\n this.activeMsgNum = options.msgNum;\n }\n\n // Internal defaults (no external knobs): if the stream goes idle for too long,\n // best-effort restart the native stream request.\n // NOTE: UDP (battery/BCUDP) can legitimately take longer to wake and begin streaming.\n // Keep this relatively high to avoid causing reconnect storms.\n // TCP: 30s timeout (cameras may have temporary delays)\n // UDP: 60s timeout (battery cameras take longer to wake)\n const transport = this.client.getTransport?.();\n this.idleRestartMs = transport === \"udp\" ? 60_000 : 30_000;\n }\n\n private noteMediaActivity(): void {\n this.lastMediaAtMs = Date.now();\n }\n\n private startWatchdog(): void {\n // Only useful when we can re-request the stream via API.\n if (!this.api) return;\n if (this.watchdogTimer) return;\n\n this.restartWindowStartMs = Date.now();\n this.restartCountInWindow = 0;\n this.watchdogTimer = setInterval(() => {\n void this.watchdogTick();\n }, WATCHDOG_TICK_MS);\n }\n\n private stopWatchdog(): void {\n if (this.watchdogTimer) clearInterval(this.watchdogTimer);\n this.watchdogTimer = undefined;\n }\n\n private async watchdogTick(): Promise<void> {\n if (!this.active) return;\n if (!this.api) return;\n if (this.restarting) return;\n if (this.lastMediaAtMs <= 0) return;\n\n const now = Date.now();\n const idleMs = now - this.lastMediaAtMs;\n if (idleMs < this.idleRestartMs) return;\n\n // Rate-limit restarts to avoid loops.\n if (now - this.restartWindowStartMs > 60_000) {\n this.restartWindowStartMs = now;\n this.restartCountInWindow = 0;\n }\n if (this.restartCountInWindow >= WATCHDOG_MAX_RESTARTS_PER_MINUTE) {\n this.logger?.warn(\n `[BaichuanVideoStream] Watchdog: idle for ${idleMs}ms, but restart budget exceeded (${WATCHDOG_MAX_RESTARTS_PER_MINUTE}/min); leaving stream as-is`,\n );\n // Avoid spamming: reset lastMediaAt to delay the next restart attempt.\n this.lastMediaAtMs = now;\n return;\n }\n this.restartCountInWindow++;\n\n void this.restartNativeStream({ reason: `idle ${idleMs}ms` });\n }\n\n private async restartNativeStream(params: { reason: string }): Promise<void> {\n if (!this.api) return;\n if (!this.active) return;\n if (this.restarting) return;\n this.restarting = true;\n\n try {\n const transport = this.client.getTransport?.() ?? \"unknown\";\n const msgNum = this.activeMsgNum ?? \"unknown\";\n this.logger?.warn(\n `[BaichuanVideoStream] Watchdog restarting native stream (channel=${this.channel} profile=${this.profile} expectedStreamTypes=[${[\n ...this.expectedStreamTypes,\n ].join(\n \",\",\n )}] msgNum=${msgNum} transport=${transport} reason=${params.reason})`,\n );\n\n // Reset parsers to avoid carrying corrupt state across a restart.\n this.depacketizer.reset();\n this.depacketizerH265.reset();\n this.bcMediaCodec.clear();\n this.activeMsgNum = undefined;\n this.lockedChannelId = undefined;\n this.lastPrependedPpsId = null;\n this.lastPrependedParamSetsH265 = false;\n\n // Best-effort stop/start: some firmwares behave better with a full reset.\n try {\n await this.api.stopVideoStream(this.channel, this.profile, {\n variant: this.variant,\n client: this.client,\n });\n } catch {\n // ignore\n }\n\n await this.api.startVideoStream(this.channel, this.profile, {\n variant: this.variant,\n client: this.client,\n });\n\n try {\n const getMsgNum = (this.api as any).getActiveVideoMsgNumWithVariant as\n | ((\n ch: number,\n p: StreamProfile,\n v?: NativeVideoStreamVariant,\n ) => number | undefined)\n | undefined;\n const v =\n typeof getMsgNum === \"function\"\n ? getMsgNum(this.channel, this.profile, this.variant)\n : undefined;\n if (v !== undefined) this.activeMsgNum = v;\n } catch {\n // keep current activeMsgNum (may have been learned from frames)\n }\n\n // Avoid immediate re-trigger if the camera takes a moment.\n this.lastMediaAtMs = Date.now();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.emitSafeError(err);\n } finally {\n this.restarting = false;\n }\n }\n\n /**\n * Start video stream.\n * Listens to `push` events and processes cmd_id=3 frames carrying BcMedia packets.\n */\n async start(): Promise<void> {\n if (this.active) {\n throw new Error(\"Video stream already active\");\n }\n\n // Ensure depacketizers start from a clean state.\n this.depacketizer.reset();\n this.depacketizerH265.reset();\n\n // Listen to push events carrying BcMedia packets (cmd_id=3).\n // IMPORTANT: register this handler BEFORE awaiting the VIDEO start response.\n // Some NVR/Hub firmwares are slow to reply, but begin streaming earlier.\n let totalFramesReceived = 0;\n let totalMediaPackets = 0;\n this.videoFrameHandler = (frame: BaichuanFrame) => {\n // Only frames with the configured cmdId carry the media stream.\n if (frame.header.cmdId !== this.cmdId) return;\n\n // Filter by msgNum if we were able to capture it from the API.\n // Some firmwares reuse streamType but keep msgNum distinct per stream.\n // NOTE: Some cameras (e.g., TrackMix PoE) always respond with msgNum=0 regardless\n // of what msgNum we used in the request. When acceptAnyStreamType is true (replay mode),\n // we also accept msgNum=0 as a fallback.\n if (\n this.activeMsgNum !== undefined &&\n frame.header.msgNum !== this.activeMsgNum\n ) {\n // Accept msgNum=0 as fallback when in permissive mode (replay)\n const allowMsgNum0Fallback =\n this.acceptAnyStreamType && frame.header.msgNum === 0;\n if (!allowMsgNum0Fallback) {\n const frameCount = ((this as any)._msgNumMismatchCount =\n ((this as any)._msgNumMismatchCount || 0) + 1);\n // Gated behind the general socket debug flag: on shared sockets\n // (main+sub on the same TCP socket) it is by-design to receive\n // frames for the other profile and discard them — this is not a\n // bug and we don't want it polluting user logs.\n if (frameCount <= 5 && this.client.getDebugConfig().general) {\n this.logger?.log(\n `[BaichuanVideoStream] Frame msgNum mismatch: received=${frame.header.msgNum}, expected=${this.activeMsgNum}, channel=${this.channel}, profile=${this.profile}, variant=${this.variant} (frame discarded)`,\n );\n }\n return;\n }\n }\n\n // Filter by expected streamType(s). Some devices use 0/1, others 2/3 for variants.\n if (\n !this.acceptAnyStreamType &&\n !this.expectedStreamTypes.has(frame.header.streamType)\n ) {\n const frameCount = ((this as any)._streamTypeMismatchCount =\n ((this as any)._streamTypeMismatchCount || 0) + 1);\n // Gated behind the general socket debug flag — see msgNum mismatch\n // above. Expected noise during handshake on shared sockets.\n if (frameCount <= 5 && this.client.getDebugConfig().general) {\n this.logger?.log(\n `[BaichuanVideoStream] Frame streamType mismatch: received=${frame.header.streamType}, expectedAny=[${[\n ...this.expectedStreamTypes,\n ].join(\n \",\",\n )}], channel=${this.channel}, profile=${this.profile}, variant=${this.variant} (frame discarded)`,\n );\n }\n return;\n }\n\n // Additional safety net for multi-stream scenarios:\n // If multiple streams share the same BaichuanClient/socket and msgNum filtering is unavailable,\n // frames from different channelIds can interleave and manifest as stutter/picture loss.\n // Lock to the first observed channelId and discard mismatches.\n if (this.lockedChannelId === undefined) {\n this.lockedChannelId = frame.header.channelId;\n } else if (frame.header.channelId !== this.lockedChannelId) {\n const frameCount = ((this as any)._channelIdMismatchCount =\n ((this as any)._channelIdMismatchCount || 0) + 1);\n if (frameCount <= 5) {\n this.logger?.warn(\n `[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)`,\n );\n }\n return;\n }\n\n totalFramesReceived++;\n\n const dbg = this.client.getDebugConfig();\n const rtspDebug = dbg.debugRtsp;\n if (totalFramesReceived === 1) {\n // Always log first frame info for debugging variant issues\n // this.logger?.log(\n // `[BaichuanVideoStream] First frame accepted: streamType=${frame.header.streamType}, expectedAny=[${[\n // ...this.expectedStreamTypes,\n // ].join(\",\")}], msgNum=${frame.header.msgNum}, activeMsgNum=${this.activeMsgNum}, channelId=${frame.header.channelId}, bodyLen=${frame.body.length}, variant=${this.variant}`\n // );\n if (rtspDebug) {\n this.logger?.log(\n `[BaichuanVideoStream] First cmd_id=${this.cmdId} frame received (bodyLen: ${frame.body.length}, channelId: ${frame.header.channelId})`,\n );\n }\n }\n if (totalFramesReceived % 10 === 0 || totalFramesReceived <= 5) {\n if (rtspDebug) {\n this.logger?.log(\n `[BaichuanVideoStream] Received ${totalFramesReceived} Baichuan frames (cmd_id=${this.cmdId})`,\n );\n }\n }\n\n // Note: the media stream is often NOT encrypted like control messages.\n // So we first try to parse the `payload` directly (if present), and only as a fallback\n // we try stateless decryption.\n const enc = this.client.enc;\n const rawCandidate =\n frame.payload.length > 0 ? frame.payload : frame.body;\n\n // If the payload still contains XML+binary (missing payloadOffset), strip XML as a fallback.\n let dataToParse = rawCandidate;\n if (frame.payload.length === 0) {\n let searchStart = 0;\n const extensionEnd = rawCandidate.indexOf(Buffer.from(\"</Extension>\"));\n const bodyEnd = rawCandidate.indexOf(Buffer.from(\"</body>\"));\n if (extensionEnd !== -1)\n searchStart = extensionEnd + Buffer.from(\"</Extension>\").length;\n else if (bodyEnd !== -1)\n searchStart = bodyEnd + Buffer.from(\"</body>\").length;\n dataToParse = rawCandidate.subarray(searchStart);\n }\n\n // Extract encryptLen from extension XML if present.\n // This tells us how many bytes are encrypted (rest is clear).\n // Format: <encryptLen>N</encryptLen>\n let encryptLen: number | undefined;\n if (frame.extension && frame.extension.length > 0) {\n try {\n const extDec = this.client.tryDecryptXml(\n frame.extension,\n frame.header.channelId,\n enc,\n );\n const encryptLenMatch = extDec.match(\n /<encryptLen>(\\d+)<\\/encryptLen>/i,\n );\n if (encryptLenMatch && encryptLenMatch[1]) {\n encryptLen = parseInt(encryptLenMatch[1], 10);\n }\n } catch {\n // ignore decryption errors\n }\n }\n\n // If the session uses encryption, some models send an encrypted stream at the frame level.\n // If we find a BcMedia magic in the raw payload, it's not a guarantee the content is NOT encrypted\n // (it can be a false positive). So we also try stateless decryption and pick\n // the most \"BcMedia-like\" candidate (decrypt before deserializing).\n\n const dataAfterXml = this.chooseDecryptedOrRawCandidate({\n raw: dataToParse,\n enc,\n channelId: frame.header.channelId,\n // Some NVR/Hub streams appear to include non-media bytes even when payloadOffset is present.\n // Allow a one-time resync at startup to avoid delaying the first keyframe.\n allowResync:\n frame.payload.length === 0 ||\n (totalFramesReceived <= 10 && totalMediaPackets === 0),\n ...(encryptLen !== undefined ? { encryptLen } : {}),\n });\n\n // If we are currently aligned (no pending buffered bytes) and we receive a tiny chunk that\n // contains no recognizable BcMedia magic anywhere, it's very likely out-of-band data.\n // Dropping it avoids repeated recover/resync loops (commonly 528/1056-byte patterns on some NVRs).\n if (\n this.bcMediaCodec.getRemainingBuffer().length === 0 &&\n dataAfterXml.length <= 600\n ) {\n const s = BaichuanVideoStream.scoreBcMediaLike(dataAfterXml);\n if (s.first < 0) {\n return;\n }\n }\n if (totalFramesReceived === 1) {\n if (rtspDebug) {\n this.logger?.log(\n `[BaichuanVideoStream] Data after XML: ${dataAfterXml.length} bytes, first 32 bytes: ${dataAfterXml\n .subarray(0, Math.min(32, dataAfterXml.length))\n .toString(\"hex\")}`,\n );\n }\n }\n if (dbg.dumpEnabled) ensureDumpDir(dbg);\n\n // Dumps the exact chunks fed to the BcMedia decoder.\n if (dbg.dumpBcMedia && this.dumpChunkIdx < 200) {\n const outDir = dbg.dumpDir;\n const idx = String(this.dumpChunkIdx).padStart(4, \"0\");\n const chunkPath = path.join(outDir, `bcmedia_chunk_${idx}.bin`);\n const infoPath = path.join(outDir, \"bcmedia_info.json\");\n const chunk = Buffer.from(dataAfterXml);\n const writeInfo = this.dumpChunkIdx === 0;\n const infoJson = writeInfo\n ? JSON.stringify(\n {\n note: \"Chunks fed into the BcMedia decoder (after XML stripping/alignment).\",\n profile: this.profile,\n channel: this.channel,\n encKind: this.client.enc.kind,\n },\n null,\n 2,\n )\n : \"\";\n\n // Non-blocking: sync FS calls can starve BCUDP ACK/keepalive and abort the stream.\n this.dumpIo.enqueue(async () => {\n await fs.promises.writeFile(chunkPath, chunk);\n if (writeInfo) {\n await fs.promises.writeFile(infoPath, infoJson);\n }\n });\n this.dumpChunkIdx++;\n }\n\n const mediaPackets = this.bcMediaCodec.decode(dataAfterXml);\n totalMediaPackets += mediaPackets.length;\n\n // Count packet types\n const packetTypes = new Map<string, number>();\n for (const pkt of mediaPackets) {\n packetTypes.set(pkt.type, (packetTypes.get(pkt.type) || 0) + 1);\n }\n\n // Log detailed info for first few frames and periodically\n // Disabled for production - enable for debugging only\n // if (totalFramesReceived <= 10 || totalFramesReceived % 100 === 0) {\n // const remainingBuffer = this.bcMediaCodec.getRemainingBuffer();\n // const typesStr = Array.from(packetTypes.entries())\n // .map(([t, c]) => `${t}:${c}`)\n // .join(\", \");\n // console.log(\n // `[BaichuanVideoStream] Frame #${totalFramesReceived}: dataToParse=${dataAfterXml.length} bytes, parsed ${mediaPackets.length} BcMedia packets (${typesStr || \"none\"}), total: ${totalMediaPackets}, remaining buffer: ${remainingBuffer.length} bytes`,\n // );\n // }\n\n // Process complete BcMedia packets.\n // Each BcMedia::Iframe/Pframe already contains a complete frame (access unit).\n // BcMediaCodec only handles transport fragmentation, not \"half frames\".\n let videoFramesEmitted = 0;\n let audioFramesEmitted = 0;\n\n for (const media of mediaPackets) {\n const maybeCacheParamSets = (\n annexB: Buffer,\n source: \"Iframe\" | \"Pframe\",\n videoType: \"H264\" | \"H265\",\n ) => {\n // Some models send parameter sets outside of I-frames (e.g. parameter set updates),\n // so we always allow caching from both I-frames and P-frames.\n\n if (videoType === \"H264\") {\n const nals = splitAnnexBToNalPayloads(annexB);\n for (const nal of nals) {\n const t = (nal[0] ?? 0) & 0x1f;\n if (t === 7) {\n const id = parseSpsIdFromNal(nal);\n if (id != null) {\n if (!isPlausibleH264Sps(nal)) continue;\n this.spsById.set(id, nal);\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Cached H.264 SPS id=${id} len=${nal.length}`,\n );\n }\n }\n if (isPlausibleH264Sps(nal)) this.lastSps = nal;\n }\n if (t === 8) {\n const ids = parsePpsIdsFromNal(nal);\n if (ids) {\n // If the PPS points to an implausible SPS, drop it.\n const sps = this.spsById.get(ids.spsId);\n if (sps && !isPlausibleH264Sps(sps)) continue;\n this.ppsById.set(ids.ppsId, { nal, spsId: ids.spsId });\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Cached H.264 PPS id=${ids.ppsId} (spsId=${ids.spsId}) len=${nal.length}`,\n );\n }\n }\n this.lastPps = nal;\n }\n }\n } else if (videoType === \"H265\") {\n // Cache H.265 parameter sets (VPS, SPS, PPS)\n const vps = extractVpsFromAnnexB(annexB);\n if (vps) {\n this.lastVps = vps;\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Cached H.265 VPS len=${vps.length}`,\n );\n }\n }\n const sps = extractSpsFromAnnexB(annexB);\n if (sps) {\n this.lastSpsH265 = sps;\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Cached H.265 SPS len=${sps.length}`,\n );\n }\n }\n const pps = extractPpsFromAnnexB(annexB);\n if (pps) {\n this.lastPpsH265 = pps;\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Cached H.265 PPS len=${pps.length}`,\n );\n }\n }\n }\n };\n\n const prependParamSetsIfNeeded = (\n annexB: Buffer,\n videoType: \"H264\" | \"H265\",\n isPframe = false,\n ): Buffer => {\n if (videoType === \"H264\") {\n const nals = splitAnnexBToNalPayloads(annexB);\n if (nals.length === 0) return annexB;\n const types = nals.map((n) => (n[0] ?? 0) & 0x1f);\n\n // Check if there's VCL (slice data) - required for P-frames\n const hasVcl = types.some(\n (t) => t === 1 || t === 5 || t === 19 || t === 20,\n );\n\n // For P-frames without VCL (e.g., only SPS+PPS), drop the frame\n // This can happen with NVR replay when parameter sets are sent separately\n if (isPframe && !hasVcl) {\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Dropping P-frame without VCL (only param sets): types=${types.join(\",\")}`,\n );\n }\n return Buffer.alloc(0);\n }\n\n // If it already includes SPS/PPS, do not prepend but mark as \"prepended\"\n // to avoid prepending to subsequent P-frames\n if (types.includes(7) && types.includes(8)) {\n // Extract ppsId from the slice to track which PPS was used\n let ppsIdFromSlice: number | null = null;\n for (const nal of nals) {\n const t = (nal[0] ?? 0) & 0x1f;\n if (t === 1 || t === 5) {\n ppsIdFromSlice = parseSlicePpsIdFromNal(nal);\n break;\n }\n }\n // Mark that we've already seen param sets, so subsequent P-frames don't get them prepended\n if (ppsIdFromSlice != null && ppsIdFromSlice <= 255) {\n this.lastPrependedPpsId = ppsIdFromSlice;\n } else {\n // Fallback: mark as -1 to indicate \"some\" param sets were seen\n this.lastPrependedPpsId = -1;\n }\n return annexB;\n }\n // If there is no VCL, there's nothing to prepend to.\n if (!hasVcl) return annexB;\n\n // Determine pps_id referenced by the first slice (if present)\n let ppsId: number | null = null;\n for (const nal of nals) {\n const t = (nal[0] ?? 0) & 0x1f;\n if (t === 1 || t === 5) {\n ppsId = parseSlicePpsIdFromNal(nal);\n break;\n }\n }\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Slice references ppsId=${ppsId ?? \"?\"} lastPrepended=${this.lastPrependedPpsId ?? \"?\"}`,\n );\n }\n\n // If we can't parse it, be conservative: only use the last seen SPS/PPS once at the beginning.\n if (ppsId == null || ppsId > 255) {\n if (this.lastPrependedPpsId != null) return annexB;\n if (!this.lastSps || !this.lastPps) return annexB;\n this.lastPrependedPpsId = -1;\n return Buffer.concat([\n NAL_START_CODE_4B,\n this.lastSps,\n NAL_START_CODE_4B,\n this.lastPps,\n annexB,\n ]);\n }\n\n // Only prepend when ppsId changes (reduces duplication and instability)\n if (this.lastPrependedPpsId === ppsId) return annexB;\n\n const pps = this.ppsById.get(ppsId);\n if (pps) {\n const sps = this.spsById.get(pps.spsId);\n if (sps) {\n this.lastPrependedPpsId = ppsId;\n return Buffer.concat([\n NAL_START_CODE_4B,\n sps,\n NAL_START_CODE_4B,\n pps.nal,\n annexB,\n ]);\n }\n }\n // If the slice references a PPS we don't have, we cannot \"invent it\".\n // Drop the access unit until the correct PPS arrives (prevents black/garbled video).\n return Buffer.alloc(0);\n } else if (videoType === \"H265\") {\n // For H.265, prepend VPS, SPS, PPS if not already present\n const nals = splitH265AnnexBToNalPayloads(annexB);\n if (nals.length === 0) return annexB;\n\n const types = nals\n .map((n) => getH265NalType(n))\n .filter((t): t is number => t !== null);\n\n // If there is no VCL (IRAP or non-IRAP picture), there's nothing to prepend to.\n const hasVcl = types.some(\n (t) => (t >= 0 && t <= 9) || (t >= 16 && t <= 23),\n );\n\n // For P-frames without VCL (e.g., only VPS+SPS+PPS), drop the frame\n if (isPframe && !hasVcl) {\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Dropping H.265 P-frame without VCL (only param sets): types=${types.join(\",\")}`,\n );\n }\n return Buffer.alloc(0);\n }\n\n // If it already includes VPS/SPS/PPS, do not prepend\n if (types.includes(32) && types.includes(33) && types.includes(34))\n return annexB;\n\n if (!hasVcl) return annexB;\n\n // Only prepend once to avoid duplication\n if (this.lastPrependedParamSetsH265) return annexB;\n\n // Prepend VPS, SPS, PPS if we have them\n if (!this.lastVps || !this.lastSpsH265 || !this.lastPpsH265)\n return annexB;\n\n this.lastPrependedParamSetsH265 = true;\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Prepending H.265 VPS/SPS/PPS to frame`,\n );\n }\n return Buffer.concat([\n NAL_START_CODE_4B,\n this.lastVps,\n NAL_START_CODE_4B,\n this.lastSpsH265,\n NAL_START_CODE_4B,\n this.lastPpsH265,\n annexB,\n ]);\n }\n return annexB;\n };\n\n const dumpNalSummary = (\n annexB: Buffer,\n label: string,\n microseconds: number,\n ) => {\n if (!dbg.dumpNals) return;\n try {\n if (dbg.dumpEnabled) ensureDumpDir(dbg);\n const outDir = dbg.dumpDir;\n const nals = splitAnnexBToNalPayloads(annexB);\n const types = nals.map((n) => (n[0] ?? 0) & 0x1f);\n let slicePpsId: number | null = null;\n const spsIds: number[] = [];\n const ppsIds: number[] = [];\n for (const nal of nals) {\n const t = (nal[0] ?? 0) & 0x1f;\n if ((t === 1 || t === 5) && slicePpsId == null) {\n slicePpsId = parseSlicePpsIdFromNal(nal);\n }\n if (t === 7) {\n const id = parseSpsIdFromNal(nal);\n if (id != null) spsIds.push(id);\n }\n if (t === 8) {\n const ids = parsePpsIdsFromNal(nal);\n if (ids) ppsIds.push(ids.ppsId);\n }\n }\n // Keep this non-blocking: sync FS writes can starve BCUDP ACK/keepalive.\n // Also cap total dumped lines to avoid unbounded I/O.\n if (this.dumpNalLines >= 20_000) return;\n const line =\n JSON.stringify({\n label,\n microseconds,\n len: annexB.length,\n nalTypes: types,\n slicePpsId,\n auSpsIds: spsIds,\n auPpsIds: ppsIds,\n cachedSps: this.spsById.size,\n cachedPps: this.ppsById.size,\n lastPrependedPpsId: this.lastPrependedPpsId,\n }) + \"\\n\";\n this.dumpNalLines++;\n const outPath = path.join(outDir, \"nal_dump.ndjson\");\n this.dumpIo.enqueue(async () => {\n await fs.promises.appendFile(outPath, line);\n });\n } catch {\n // ignore\n }\n };\n\n // Media payloads are expected to be plaintext here because we select the best\n // raw vs decrypted candidate before running BcMedia decoding.\n const isPlausibleH264Sps = (nal: Buffer): boolean => {\n // nal is a payload without a start code. nal[0] is the NAL header (type=7)\n if (nal.length < 4) return false;\n if (((nal[0] ?? 0) & 0x1f) !== 7) return false;\n const profileIdc = nal[1] ?? 0;\n const levelIdc = nal[3] ?? 0;\n const knownProfiles = new Set([66, 77, 88, 100, 110, 122, 244]);\n if (!knownProfiles.has(profileIdc)) return false;\n if (levelIdc === 0 || levelIdc > 255) return false;\n return true;\n };\n if (media.type === \"Iframe\") {\n // Detect actual video codec from NAL data (some cameras report wrong codec in BcMedia header)\n let videoType = media.videoType;\n const detectedCodec = detectVideoCodecFromNal(media.data);\n if (detectedCodec && detectedCodec !== videoType) {\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Codec mismatch in Iframe: header says ${videoType}, NAL says ${detectedCodec} - using ${detectedCodec}`,\n );\n }\n videoType = detectedCodec;\n }\n\n if (media.additionalHeader && media.additionalHeader.length > 0) {\n this.emit(\"additionalHeader\", {\n raw: media.additionalHeader,\n frameType: \"Iframe\",\n videoType,\n microseconds: media.microseconds,\n ...(this.latestFrameWidth !== undefined\n ? { frameWidth: this.latestFrameWidth }\n : {}),\n ...(this.latestFrameHeight !== undefined\n ? { frameHeight: this.latestFrameHeight }\n : {}),\n });\n }\n\n // Convert to Annex-B format (different converters for H.264 and H.265)\n const annexBData =\n videoType === \"H265\"\n ? convertH265ToAnnexB(media.data)\n : convertToAnnexB(media.data);\n\n const isKeyframe = true;\n\n maybeCacheParamSets(annexBData, \"Iframe\", videoType);\n const outAnnex = prependParamSetsIfNeeded(annexBData, videoType);\n\n if (outAnnex.length === 0) {\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Iframe DROPPED: outAnnex is empty`,\n );\n }\n continue;\n }\n\n dumpNalSummary(outAnnex, \"Iframe\", media.microseconds);\n\n // Guard rail: do not emit invalid keyframes (prevents cascading parameter set issues)\n if (videoType === \"H264\") {\n if (\n !isValidH264AnnexBAccessUnit(outAnnex) ||\n !isH264KeyframeAnnexB(outAnnex)\n ) {\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Dropping invalid H.264 Iframe (Annex-B) len=${outAnnex.length}`,\n );\n }\n continue;\n }\n } else if (videoType === \"H265\") {\n // For H.265, validate access unit and check for keyframe (IRAP with VPS/SPS/PPS)\n if (!isValidH265AnnexBAccessUnit(outAnnex)) {\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] Dropping invalid H.265 Iframe (Annex-B) len=${outAnnex.length} first16=${outAnnex.subarray(0, 16).toString(\"hex\")}`,\n );\n }\n continue;\n }\n // Check if it's a proper keyframe (should have VPS/SPS/PPS and IRAP)\n if (!isH265KeyframeAnnexB(outAnnex)) {\n if (dbg.traceNativeStream) {\n this.logger?.warn(\n `[BaichuanVideoStream] H.265 Iframe missing VPS/SPS/PPS or IRAP, but continuing len=${outAnnex.length}`,\n );\n }\n // Continue anyway - the parameter sets might be prepended\n }\n }\n\n // Save a one-off sample for offline analysis\n if (dbg.traceNativeStream && !this.debugSavedSamples) {\n try {\n const outDir = dbg.dumpDir;\n fs.mkdirSync(outDir, { recursive: true });\n if (media.type === \"Iframe\" && hasStartCodes(annexBData)) {\n fs.writeFileSync(\n path.join(outDir, \"iframe_annexb.bin\"),\n annexBData,\n );\n }\n } catch {\n // do not block streaming for debug\n }\n }\n\n // If debug is off, still emit a single warning the first time we see non-AnnexB output.\n // This helps diagnose models that prepend headers or use nonstandard framing.\n if (!this.warnedNonAnnexBOnce && !hasStartCodes(annexBData)) {\n this.warnedNonAnnexBOnce = true;\n const b = media.data;\n const head = b.subarray(0, Math.min(24, b.length)).toString(\"hex\");\n const headAnnex = annexBData\n .subarray(0, Math.min(24, annexBData.length))\n .toString(\"hex\");\n this.logger?.warn(\n `[BaichuanVideoStream] WARNING: non-AnnexB frame after conversion (${media.type} ${media.videoType}) ` +\n `len=${b.length} head=${head} convertedLen=${annexBData.length} convertedHead=${headAnnex}`,\n );\n }\n\n this.emit(\"videoFrame\", outAnnex);\n this.emit(\"videoAccessUnit\", {\n data: outAnnex,\n isKeyframe,\n videoType, // Use the detected/corrected videoType, not media.videoType\n microseconds: media.microseconds,\n ...(media.type === \"Iframe\" && \"time\" in media\n ? media.time !== undefined\n ? { time: media.time }\n : {}\n : {}),\n });\n videoFramesEmitted++;\n\n if (totalFramesReceived <= 5 || videoFramesEmitted <= 5) {\n const sc = hasStartCodes(annexBData) ? \"yes\" : \"no\";\n if (rtspDebug) {\n this.logger?.log(\n `[BaichuanVideoStream] Emitted ${media.type} (${media.videoType}) ${media.data.length} bytes -> ${annexBData.length} bytes (Annex-B, startCode:${sc})`,\n );\n }\n }\n } else if (media.type === \"Pframe\") {\n const chunk = media.data;\n\n if (media.additionalHeader && media.additionalHeader.length > 0) {\n // Detect codec on raw NAL chunk before annex-B conversion, mirroring the\n // logic below so the emitted videoType is consistent across both events.\n const detected = detectVideoCodecFromNal(chunk);\n const videoTypeForHeader = detected ?? media.videoType;\n this.emit(\"additionalHeader\", {\n raw: media.additionalHeader,\n frameType: \"Pframe\",\n videoType: videoTypeForHeader,\n microseconds: media.microseconds,\n ...(this.latestFrameWidth !== undefined\n ? { frameWidth: this.latestFrameWidth }\n : {}),\n ...(this.latestFrameHeight !== undefined\n ? { frameHeight: this.latestFrameHeight }\n : {}),\n });\n }\n\n // Detect actual video codec from NAL data (some cameras report wrong codec in BcMedia header)\n let videoType = media.videoType;\n const detectedCodec = detectVideoCodecFromNal(chunk);\n if (detectedCodec && detectedCodec !== videoType) {\n videoType = detectedCodec;\n }\n\n // P-frame: often not a complete access unit but an \"RTP-like\" payload (FU-A/STAP)\n // which must be depacketized with state. Do NOT run AVCC heuristics before depacketizing.\n // First try AVCC/HVCC -> AnnexB (some models send P-frames length-prefixed).\n // If we still don't get start codes, try RFC6184 depacketizer (FU-A/STAP) for H.264.\n // Note: H.265 RTP depacketization is similar but uses different NAL unit types.\n const annexBOrRaw = hasStartCodes(chunk)\n ? chunk\n : videoType === \"H265\"\n ? convertH265ToAnnexB(chunk)\n : convertToAnnexB(chunk);\n\n // For H.264, use the depacketizer. For H.265, we might need a similar depacketizer in the future.\n const parts = hasStartCodes(annexBOrRaw)\n ? [annexBOrRaw]\n : videoType === \"H265\"\n ? this.depacketizerH265.push(chunk)\n : this.depacketizer.push(chunk);\n\n if (parts.length === 0) {\n // incomplete fragment (FU-A mid) or unrecognized payload: wait for more packets\n continue;\n }\n\n for (const p of parts) {\n maybeCacheParamSets(p, \"Pframe\", videoType);\n const outP0 = prependParamSetsIfNeeded(p, videoType, true); // isPframe=true\n if (outP0.length === 0) continue;\n const outP = outP0;\n dumpNalSummary(outP, \"Pframe\", media.microseconds);\n // Guard rail: drop only if the access unit is invalid (codec-specific)\n const isValid =\n videoType === \"H265\"\n ? isValidH265AnnexBAccessUnit(outP)\n : isValidH264AnnexBAccessUnit(outP);\n if (!isValid) {\n if (dbg.traceNativeStream && this.debugH264LogsLeft > 0) {\n this.debugH264LogsLeft--;\n const head = outP\n .subarray(0, Math.min(24, outP.length))\n .toString(\"hex\");\n this.logger?.warn(\n `[BaichuanVideoStream] Dropping invalid Pframe (${videoType}): len=${outP.length} head=${head}`,\n );\n }\n continue;\n }\n this.emit(\"videoFrame\", outP);\n this.emit(\"videoAccessUnit\", {\n data: outP,\n isKeyframe: false,\n videoType: videoType,\n microseconds: media.microseconds,\n });\n videoFramesEmitted++;\n }\n }\n\n // Emit audio frames\n if (media.type === \"Aac\" || media.type === \"Adpcm\") {\n audioFramesEmitted++;\n this.emit(\"audioFrame\", media.data);\n }\n\n // Emit info frames for metadata\n if (media.type === \"InfoV1\" || media.type === \"InfoV2\") {\n // Cache the most recent frame dimensions so subsequent additionalHeader\n // events can attach width/height context for normalized box coords.\n if (media.videoWidth > 0) this.latestFrameWidth = media.videoWidth;\n if (media.videoHeight > 0) this.latestFrameHeight = media.videoHeight;\n }\n }\n\n // Log frame emission stats\n if (\n totalFramesReceived <= 10 ||\n (totalFramesReceived % 20 === 0 &&\n (videoFramesEmitted > 0 || audioFramesEmitted > 0))\n ) {\n if (rtspDebug) {\n this.logger?.log(\n `[BaichuanVideoStream] Frame #${totalFramesReceived}: emitted ${videoFramesEmitted} video frames, ${audioFramesEmitted} audio frames`,\n );\n }\n }\n\n // Mark liveness only when we actually produced media (not just transport frames).\n if (videoFramesEmitted > 0 || audioFramesEmitted > 0) {\n this.noteMediaActivity();\n }\n\n // Track total video frames emitted\n if (\n videoFramesEmitted > 0 &&\n (totalFramesReceived <= 10 || totalFramesReceived % 50 === 0)\n ) {\n let totalVideoFrames = 0;\n // Count would need to be tracked separately - for now just log\n }\n };\n\n // Register the push handler BEFORE the VIDEO start command.\n // Some NVR/Hub firmwares begin streaming before replying to the start command.\n this.client.on(\"push\", this.videoFrameHandler);\n this.active = true;\n this.startWatchdog();\n\n // Register with the API so it can forward additionalHeader frames to any\n // onDetection listeners.\n if (this.api && typeof this.api._registerVideoStreamForDetection === \"function\") {\n this.detectionTeardown = this.api._registerVideoStreamForDetection(this, {\n channel: this.channel,\n profile: this.profile,\n });\n }\n\n // Seed liveness so the watchdog doesn't immediately restart while we are still negotiating.\n this.lastMediaAtMs = Date.now();\n\n // Request the video stream if the API is available.\n if (this.api) {\n try {\n // Best-effort stop: stop any existing stream on this channel/profile.\n // This ensures we start fresh when switching between variants.\n if (this.variant === \"default\") {\n // Default stream: best-effort stop default before starting.\n try {\n await this.api.stopVideoStream(this.channel, this.profile, {\n variant: \"default\",\n client: this.client,\n });\n } catch {\n // ignore\n }\n } else {\n // Variant stream: stop ONLY the same variant. Do not forcibly stop the default stream.\n // Rationale:\n // - BaichuanClient subscriptions are per msgNum, so different streams can coexist without mixing.\n // - Stopping default streams on some NVRs can add several seconds of renegotiation delay.\n try {\n await this.api.stopVideoStream(this.channel, this.profile, {\n variant: this.variant,\n client: this.client,\n });\n this.logger?.log(\n `[BaichuanVideoStream] Successfully stopped existing variant stream: ${this.variant}`,\n );\n } catch (e) {\n this.logger?.log(\n `[BaichuanVideoStream] Error stopping variant stream ${this.variant} (may not exist): ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n // Small delay to ensure the device has processed the stop commands\n await new Promise((resolve) => setTimeout(resolve, 100));\n\n // Start the video stream. startVideoStream() synchronously reserves a msgNum and\n // stores it in activeVideoMsgNums BEFORE its first await (sendFrame). Read the\n // activeMsgNum immediately after to enable per-stream frame filtering on shared sockets.\n const startPromise = this.api.startVideoStream(\n this.channel,\n this.profile,\n { variant: this.variant, client: this.client },\n );\n\n const updateActiveMsgNum = () => {\n try {\n const getMsgNum = (this.api as any)\n .getActiveVideoMsgNumWithVariant as\n | ((\n ch: number,\n p: StreamProfile,\n v?: NativeVideoStreamVariant,\n ) => number | undefined)\n | undefined;\n const v =\n typeof getMsgNum === \"function\"\n ? getMsgNum(this.channel, this.profile, this.variant)\n : undefined;\n if (v !== undefined) this.activeMsgNum = v;\n } catch {\n // keep current activeMsgNum (may have been learned from frames)\n }\n };\n\n // Read activeMsgNum immediately — it was set synchronously by startVideoStream().\n // This enables msgNum-based frame filtering even before the camera responds.\n updateActiveMsgNum();\n\n // On UDP/battery cams the stream typically will NOT start automatically; wait for the response.\n // On TCP/NVR/Hub the response can be very slow; do not block stream processing on it.\n if (this.client.getTransport?.() === \"udp\") {\n await startPromise;\n } else {\n // Give it a brief window to fail fast (bad credentials, disconnected).\n await Promise.race([\n startPromise,\n new Promise<void>((resolve) => setTimeout(resolve, 400)),\n ]);\n }\n\n updateActiveMsgNum();\n void startPromise\n .then(() => updateActiveMsgNum())\n .catch((e) => {\n const err = e instanceof Error ? e : new Error(String(e));\n this.emitSafeError(err);\n });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n if (this.client.getTransport?.() === \"udp\") {\n this.stopWatchdog();\n this.active = false;\n if (this.videoFrameHandler)\n this.client.off(\"push\", this.videoFrameHandler);\n this.videoFrameHandler = undefined;\n throw err;\n }\n this.emitSafeError(err);\n }\n }\n }\n\n // isVideoFrame, isAudioFrame, and extractVideoData are no longer needed\n // BcMediaParser handles all frame parsing and extraction\n\n /**\n * Stop video stream.\n */\n async stop(): Promise<void> {\n if (!this.active) return;\n\n this.stopWatchdog();\n\n // Ensure depacketizers don't keep FU state across restarts.\n this.depacketizer.reset();\n this.depacketizerH265.reset();\n\n if (this.videoFrameHandler) {\n this.client.removeListener(\"push\", this.videoFrameHandler);\n }\n this.videoFrameHandler = undefined;\n\n // Note: stream-level decipher is not used here; we pick decrypted/raw before BcMedia decoding.\n\n // Clear codec buffer\n this.bcMediaCodec.clear();\n\n this.activeMsgNum = undefined;\n\n // Stop the video stream if the API is available\n if (this.api) {\n try {\n await this.api.stopVideoStream(this.channel, this.profile, {\n variant: this.variant,\n client: this.client,\n });\n } catch (error) {\n // Log error but continue\n this.emitSafeError(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n\n this.active = false;\n if (this.detectionTeardown) {\n try {\n this.detectionTeardown();\n } catch {\n // Swallow: the API may already be torn down at this point.\n }\n this.detectionTeardown = undefined;\n }\n this.emit(\"close\");\n }\n\n isActive(): boolean {\n return this.active;\n }\n}\n","import { createCipheriv, createDecipheriv, createHash } from \"node:crypto\";\nimport { BC_AES_IV, BC_XML_KEY } from \"./constants\";\n\nexport type EncryptionProtocol =\n | { kind: \"none\" }\n | { kind: \"bc\" }\n | { kind: \"aes\"; key: Buffer }\n | { kind: \"full_aes\"; key: Buffer };\n\nexport function md5HexUpper(input: string): string {\n return createHash(\"md5\").update(input, \"utf8\").digest(\"hex\").toUpperCase();\n}\n\n/**\n * MD5 \"modern\" formatting used by Baichuan:\n * - MD5 hex uppercase\n * - truncate to 31 chars\n */\nexport function md5StrModern(input: string): string {\n return md5HexUpper(input).slice(0, 31);\n}\n\n/**\n * AES key derivation used by Reolink:\n * keyString = md5_str_modern(`${nonce}-${password}`).slice(0,16)\n * keyBytes = UTF-8 bytes of that string (16 bytes)\n */\nexport function deriveAesKey(nonce: string, password: string): Buffer {\n const keyStr = md5StrModern(`${nonce}-${password}`).slice(0, 16);\n return Buffer.from(keyStr, \"utf8\");\n}\n\nexport function bcEncrypt(buf: Buffer, offset: number): Buffer {\n const off = offset & 0xff;\n const out = Buffer.allocUnsafe(buf.length);\n for (let i = 0; i < buf.length; i++) {\n const key = BC_XML_KEY[(off + i) % BC_XML_KEY.length]!;\n out[i] = buf[i]! ^ key ^ off;\n }\n return out;\n}\n\nexport function bcDecrypt(buf: Buffer, offset: number): Buffer {\n // XOR is symmetric\n return bcEncrypt(buf, offset);\n}\n\nexport function aesEncrypt(buf: Buffer, key: Buffer): Buffer {\n if (buf.length === 0) return Buffer.alloc(0);\n const cipher = createCipheriv(\"aes-128-cfb\", key, BC_AES_IV);\n cipher.setAutoPadding(false);\n return Buffer.concat([cipher.update(buf), cipher.final()]);\n}\n\nexport function aesDecrypt(buf: Buffer, key: Buffer): Buffer {\n if (buf.length === 0) return Buffer.alloc(0);\n const decipher = createDecipheriv(\"aes-128-cfb\", key, BC_AES_IV);\n decipher.setAutoPadding(false);\n return Buffer.concat([decipher.update(buf), decipher.final()]);\n}\n\n/**\n * Stateful AES-128-CFB stream decryptor.\n *\n * AES-128-CFB mode requires maintaining cipher state across chunk boundaries.\n * When a large BcMedia packet (e.g., a 400KB I-frame) is fragmented into multiple\n * Baichuan frames, each frame must be decrypted using the cipher state from the\n * previous frame, NOT a fresh IV.\n *\n * The first frame of a new BcMedia packet starts with a recognizable magic\n * (e.g., \"cd00\", \"1001\", \"1002\"). This signals we should reset the cipher state.\n * Subsequent frames (continuations) should use the continued cipher state.\n *\n * Usage:\n * ```ts\n * const decryptor = new AesStreamDecryptor(key);\n *\n * for (const frame of baichuanFrames) {\n * // Try fresh IV to detect new packet\n * const freshDecrypted = aesDecrypt(frame.payload, key);\n * if (startsWithBcMediaMagic(freshDecrypted)) {\n * // New packet - reset and decrypt with fresh state\n * decryptor.reset();\n * const decrypted = decryptor.update(frame.payload);\n * } else {\n * // Continuation - use existing state\n * const decrypted = decryptor.update(frame.payload);\n * }\n * }\n * ```\n */\nexport class AesStreamDecryptor {\n private decipher: ReturnType<typeof createDecipheriv> | null = null;\n private readonly key: Buffer;\n\n constructor(key: Buffer) {\n this.key = key;\n }\n\n /**\n * Reset the cipher state (start decrypting with fresh IV).\n * Call this when a new BcMedia packet is detected.\n */\n reset(): void {\n this.decipher = createDecipheriv(\"aes-128-cfb\", this.key, BC_AES_IV);\n this.decipher.setAutoPadding(false);\n }\n\n /**\n * Decrypt a chunk using the current cipher state.\n * Automatically resets if no cipher exists.\n *\n * @param buf - Encrypted buffer to decrypt\n * @returns Decrypted buffer\n */\n update(buf: Buffer): Buffer {\n if (buf.length === 0) return Buffer.alloc(0);\n if (!this.decipher) this.reset();\n return this.decipher!.update(buf);\n }\n\n /**\n * Check if the decryptor has been initialized.\n */\n isInitialized(): boolean {\n return this.decipher !== null;\n }\n}\n","export const BC_TCP_DEFAULT_PORT = 9000;\n\n/** Magic header bytes: `f0 de bc 0a` */\nexport const BC_MAGIC = Buffer.from([0xf0, 0xde, 0xbc, 0x0a]);\n\n/**\n * Some cameras sometimes send a reversed-endian magic header for certain payloads (e.g. JPEG).\n * In Rust reference this appears as 0x0fedcba0 (LE bytes: a0 cb ed 0f).\n */\nexport const BC_MAGIC_REV = Buffer.from([0xa0, 0xcb, 0xed, 0x0f]);\n\n/** Reolink \"BCEncrypt\" XOR key for XML payloads. */\nexport const BC_XML_KEY = Uint8Array.from([\n 0x1f, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78, 0xff,\n]);\n\n/** Fixed IV used by Reolink for AES-CFB. */\nexport const BC_AES_IV = Buffer.from(\"0123456789abcdef\", \"utf8\");\n\nexport const BC_CLASS_LEGACY = 0x6514;\nexport const BC_CLASS_MODERN_20 = 0x6614;\nexport const BC_CLASS_MODERN_24 = 0x6414;\nexport const BC_CLASS_MODERN_24_ALT = 0x0000;\n// Modern (file download) message class, still uses 24-byte header with payloadOffset.\nexport const BC_CLASS_FILE_DOWNLOAD = 0x6482;\n\nexport function bcHeaderHasPayloadOffset(messageClass: number): boolean {\n return (\n messageClass === BC_CLASS_MODERN_24 ||\n messageClass === BC_CLASS_MODERN_24_ALT ||\n messageClass === BC_CLASS_FILE_DOWNLOAD\n );\n}\n\n/**\n * Baichuan command IDs for login/logout.\n *\n * Values:\n * - MSG_ID_LOGIN = 1: Login request/response\n * - MSG_ID_LOGOUT = 2: Logout request/response\n */\nexport const BC_CMD_ID_LOGIN = 1; // MSG_ID_LOGIN - Login request/response\nexport const BC_CMD_ID_LOGOUT = 2; // MSG_ID_LOGOUT - Logout request/response (PCAP-confirmed)\n\n/**\n * Baichuan command IDs for video streaming.\n *\n * Values:\n * - MSG_ID_VIDEO = 3: Video and Audio Streams messages\n * - MSG_ID_VIDEO_STOP = 4: ID used to stop the video stream\n */\nexport const BC_CMD_ID_VIDEO = 3; // MSG_ID_VIDEO - Video and Audio Streams messages\nexport const BC_CMD_ID_VIDEO_STOP = 4; // MSG_ID_VIDEO_STOP - ID used to stop the video stream\n\n// Replay / recordings / file list\nexport const BC_CMD_ID_FILE_INFO_LIST_REPLAY = 5; // <FileInfoList> (replay)\nexport const BC_CMD_ID_FILE_INFO_LIST_STOP = 7; // <FileInfoList> (stop)\nexport const BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO = 8; // <FileInfoList> (DL Video)\nexport const BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD = 13; // <FileInfoList> (download)\nexport const BC_CMD_ID_FILE_INFO_LIST_OPEN = 14; // <FileInfoList> (open/list)\nexport const BC_CMD_ID_FILE_INFO_LIST_GET = 15; // <FileInfoList> (get/list page)\nexport const BC_CMD_ID_FILE_INFO_LIST_CLOSE = 16; // <FileInfoList> (close)\n\n// Recording search (alarm video list)\nexport const BC_CMD_ID_FIND_REC_VIDEO_OPEN = 272; // <findAlarmVideo> (open)\nexport const BC_CMD_ID_FIND_REC_VIDEO_GET = 273; // <findAlarmVideo> (get)\nexport const BC_CMD_ID_FIND_REC_VIDEO_CLOSE = 274; // <findAlarmVideo> (close)\n\n// CoverPreview / Thumbnail commands\n// cmd_id=298: CoverPreview for NVR/HomeHub (XML-based, returns I-frame)\nexport const BC_CMD_ID_COVER_PREVIEW = 298; // <CoverPreview> - I-frame from recording\n// cmd_id=458-462: Cover/Thumbnail for standalone cameras (PCAP-observed)\n// These appear to be batch/paged cover requests used by the Reolink app\nexport const BC_CMD_ID_COVER_STANDALONE_458 = 458; // Standalone cover request type A\nexport const BC_CMD_ID_COVER_STANDALONE_459 = 459; // Standalone cover request type B\nexport const BC_CMD_ID_COVER_STANDALONE_460 = 460; // Standalone cover request type C (main)\nexport const BC_CMD_ID_COVER_STANDALONE_461 = 461; // Standalone cover request type D\nexport const BC_CMD_ID_COVER_STANDALONE_462 = 462; // Standalone cover request type E\n// Response cmd_id for cover data (observed in PCAP)\nexport const BC_CMD_ID_COVER_RESPONSE = 138; // Response containing cover/thumbnail data (0x8A)\n\n// Talk / two-way audio command IDs\nexport const BC_CMD_ID_TALK_ABILITY = 10; // MSG_ID_TALKABILITY - get talk ability/config\nexport const BC_CMD_ID_TALK_RESET = 11; // MSG_ID_TALKRESET - stop/reset talk session\nexport const BC_CMD_ID_TALK_CONFIG = 201; // MSG_ID_TALKCONFIG - configure talk audio format\nexport const BC_CMD_ID_TALK = 202; // MSG_ID_TALK - send talk binary (BcMedia ADPCM)\n\n// PTZ Control command IDs\nexport const BC_CMD_ID_PTZ_CONTROL = 18; // MSG_ID_PTZ_CONTROL - Pan/tilt/zoom control\nexport const BC_CMD_ID_PTZ_CONTROL_PRESET = 19; // MSG_ID_PTZ_CONTROL_PRESET - Set/move to preset\nexport const BC_CMD_ID_GET_PTZ_PRESET = 190; // MSG_ID_GET_PTZ_PRESET - Get preset list\nexport const BC_CMD_ID_GET_PTZ_POSITION = 433; // Get current PTZ position\n\n// PTZ Zoom/Focus command IDs\nexport const BC_CMD_ID_GET_ZOOM_FOCUS = 294; // MSG_ID_GET_ZOOM_FOCUS - Read zoom/focus min/max/current\nexport const BC_CMD_ID_SET_ZOOM_FOCUS = 295; // MSG_ID_SET_ZOOM_FOCUS - Write zoom/focus position\n\n// Battery Info command IDs\n// - 252: MSG_ID_BATTERY_INFO_LIST (camera-initiated status/event)\n// - 253: MSG_ID_BATTERY_INFO (client-initiated request)\nexport const BC_CMD_ID_GET_BATTERY_INFO_LIST = 252; // MSG_ID_BATTERY_INFO_LIST\nexport const BC_CMD_ID_GET_BATTERY_INFO = 253; // MSG_ID_BATTERY_INFO\n\n// UDP Keep Alive command ID\n// Battery cameras (BCUDP) periodically send this and expect a 200 response.\nexport const BC_CMD_ID_UDP_KEEP_ALIVE = 234; // MSG_ID_UDP_KEEP_ALIVE\n\n// PIR State command IDs\nexport const BC_CMD_ID_GET_PIR_INFO = 212; // MSG_ID_GET_PIR_ALARM - Get PIR settings\nexport const BC_CMD_ID_SET_PIR_INFO = 213; // MSG_ID_START_PIR_ALARM - Set PIR settings\n\n// Motion Detection command IDs\nexport const BC_CMD_ID_GET_MOTION_ALARM = 46; // GetMdAlarm - Get motion detection state\nexport const BC_CMD_ID_SET_MOTION_ALARM = 47; // SetMdAlarm - Set motion detection\n\n// Alarm Event List (push from camera when alarm state changes)\nexport const BC_CMD_ID_ALARM_EVENT_LIST = 33; // AlarmEventList push - contains motion/AI alarm status\n\n// AI Detection command IDs\nexport const BC_CMD_ID_GET_AI_ALARM = 342; // GetAiAlarm - Get AI detection state\nexport const BC_CMD_ID_SET_AI_ALARM = 343; // SetAiAlarm - Set AI detection\n\n// Siren/Audio Alarm command IDs\nexport const BC_CMD_ID_GET_AUDIO_ALARM = 547; // GetAudioAlarm - Get siren status (push event)\nexport const BC_CMD_ID_AUDIO_ALARM_PLAY = 263; // MSG_ID_PLAY_AUDIO - Play siren/audio alarm\n\n// White LED/Floodlight command IDs\nexport const BC_CMD_ID_GET_WHITE_LED = 289; // GetWhiteLed/Floodlight - Get floodlight state\nexport const BC_CMD_ID_SET_WHITE_LED_STATE = 288; // SetWhiteLed state\nexport const BC_CMD_ID_SET_WHITE_LED_TASK = 290; // SetWhiteLed task (brightness, mode, etc.)\n// Floodlight status report pushed by camera\nexport const BC_CMD_ID_FLOODLIGHT_STATUS_LIST = 291; // MSG_ID_FLOODLIGHT_STATUS_LIST\n\n// Ability Info command ID\nexport const BC_CMD_ID_ABILITY_INFO = 151; // MSG_ID_ABILITY_INFO - Get device capabilities/abilities\n\n// Support query command ID\n// Returns a <Support> XML block with ptzMode and per-channel flags (e.g. battery, ledCtrl).\nexport const BC_CMD_ID_SUPPORT = 199; // MSG_ID_SUPPORT\n\n// Ping command ID\nexport const BC_CMD_ID_PING = 93; // MSG_ID_PING - Keep connection alive / check status\n\n// Channel Info command IDs\nexport const BC_CMD_ID_CHANNEL_INFO_ALL = 145; // Get channel info for all channels in a single request\n\n// --- PCAP-derived (settings) command IDs (not yet wrapped in helpers) ---\n// These command IDs were observed in our settings PCAPs and are exported here so they can be used\n// by library callers (e.g. via ReolinkBaichuanApi.sendXml({ cmdId, ... })).\n// Naming is best-effort and derived from the first XML tag under <body> seen in responses.\n\n// client->device (request/response)\nexport const BC_CMD_ID_GET_OSD_DATETIME = 44; // <OsdDatetime>\nexport const BC_CMD_ID_SET_OSD_DATETIME = 45; // <OsdDatetime> + <OsdChannelName>\nexport const BC_CMD_ID_GET_RECORD_CFG = 54; // <RecordCfg>\nexport const BC_CMD_ID_GET_ABILITY_SUPPORT = 58; // <AbilitySuppport> (spelling as seen in XML)\nexport const BC_CMD_ID_GET_FTP_TASK = 70; // <FtpTask>\nexport const BC_CMD_ID_GET_VERSION_INFO = 80; // <VersionInfo> - model / firmware / serial number\nexport const BC_CMD_ID_GET_RECORD = 81; // <Record>\nexport const BC_CMD_ID_GET_HDD_INFO_LIST = 102; // <HddInfoList>\nexport const BC_CMD_ID_GET_WIFI_SIGNAL = 115; // <WifiSignal>\nexport const BC_CMD_ID_GET_WIFI = 116; // <Wifi>\nexport const BC_CMD_ID_GET_ONLINE_USER_LIST = 120; // <OnlineUserList> - active user sessions\nexport const BC_CMD_ID_GET_DAY_RECORDS = 142; // <DayRecords>\nexport const BC_CMD_ID_GET_STREAM_INFO_LIST = 146; // <StreamInfoList>\nexport const BC_CMD_ID_GET_LED_STATE = 208; // <LedState>\nexport const BC_CMD_ID_GET_EMAIL_TASK = 217; // <EmailTask>\nexport const BC_CMD_ID_GET_AUDIO_TASK = 232; // <AudioTask>\nexport const BC_CMD_ID_GET_AUDIO_CFG = 264; // <audioCfg>\nexport const BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD = 296; // <DayNightThreshold>\nexport const BC_CMD_ID_GET_TIMELAPSE_CFG = 319; // <timelapseCfg>\nexport const BC_CMD_ID_GET_AI_DENOISE = 439; // <aiDenoise>\nexport const BC_CMD_ID_GET_KIT_AP_CFG = 481; // <kitApCfg>\nexport const BC_CMD_ID_GET_REC_ENC_CFG = 507; // <RecEncCfg>\nexport const BC_CMD_ID_GET_ACCESS_USER_LIST = 511; // <accessUserList>\nexport const BC_CMD_ID_GET_SLEEP_STATE = 574; // <sleepState>\n\n// Additional discovered command IDs from PCAP analysis (motion_alarm.pcapng)\nexport const BC_CMD_ID_GET_VIDEO_INPUT = 26; // <VideoInput> + <InputAdvanceCfg> - Video settings/exposure\nexport const BC_CMD_ID_GET_SYSTEM_GENERAL = 104; // <SystemGeneral> + <Norm> - System time/name/language\nexport const BC_CMD_ID_GET_SUPPORT = 199; // <Support> - Device capability flags\nexport const BC_CMD_ID_GET_AI_CFG = 299; // <AiCfg> - AI tracking config\nexport const BC_CMD_ID_SET_AI_CFG = 300; // <AiCfg> - Set AI tracking config (autotracking)\nexport const BC_CMD_ID_GET_SIREN_STATUS = 547; // <SirenStatusList> - Siren status\n\n// AudioTask - Motion Alarm control (confirmed in PCAP analysis)\n// cmdId=232 GET returns <AudioTask><enable>1/0</enable>...</AudioTask>\n// cmdId=231 SET sends encrypted XML payload to toggle motion alarm\nexport const BC_CMD_ID_SET_AUDIO_TASK = 231; // SetAudioTask - Toggle motion alarm enable/disable\n\n// SetVideoInput / SetImage / SetIsp share the same write cmdId.\n// GET (cmdId=26) returns the merged blob, SET (cmdId=25) writes back the\n// patched XML. Mirrors reolink_aio's @http_cmd(\"SetImage\") / SetIsp.\nexport const BC_CMD_ID_SET_VIDEO_INPUT = 25; // SetImage / SetIsp - write patched VideoInput/InputAdvanceCfg\nexport const BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD = 297; // SetIsp dayNightThreshold writer\n\n// Encoding settings (mainStream / subStream / audio / bitrate / framerate).\n// cmdId=56 GET <Enc>, cmdId=57 SET (read-modify-write).\nexport const BC_CMD_ID_GET_ENC = 56; // GetEnc - encoding info\nexport const BC_CMD_ID_SET_ENC = 57; // SetEnc - write patched encoding\n\n// Privacy mask (Shelter). cmdId=52 GET, cmdId=53 SET (read-modify-write).\nexport const BC_CMD_ID_GET_PRIVACY_MASK = 52; // GetMask - privacy mask config\nexport const BC_CMD_ID_SET_PRIVACY_MASK = 53; // SetMask - write patched mask\n\n// Audio noise reduction (aiDenoise). cmdId=439 GET, cmdId=440 SET.\nexport const BC_CMD_ID_SET_AI_DENOISE = 440; // SetAudioNoise - write aiDenoise\n\n// LED state (status LED + IR lights). cmdId=208 GET, cmdId=209 SET.\nexport const BC_CMD_ID_SET_LED_STATE = 209; // SetIrLights / SetPowerLed - write LedState\n\n// AudioCfg (volume / talk / visitor). cmdId=264 GET, cmdId=265 SET.\nexport const BC_CMD_ID_SET_AUDIO_CFG = 265; // SetAudioCfg - write audio settings\n\n// Recording schedule. cmdId=81 GET schedule, cmdId=82 SET schedule.\n// cmdId=54 GET RecordCfg (delay/pack), cmdId=55 SET RecordCfg.\nexport const BC_CMD_ID_SET_RECORD = 82; // SetRec - write recording schedule\nexport const BC_CMD_ID_SET_RECORD_CFG = 55; // SetRecCfg - write recording delay/pack\n\n// Email task. cmdId=217 GET, cmdId=216 SET.\nexport const BC_CMD_ID_SET_EMAIL_TASK = 216; // SetEmail - write email schedule\n\n// Push task. cmdId=219 GET, cmdId=218 SET.\nexport const BC_CMD_ID_GET_PUSH_TASK = 219; // GetPush - push schedule\nexport const BC_CMD_ID_SET_PUSH_TASK = 218; // SetPush - write push schedule\n\n// Audio alarm (siren on event). GET cmdId=232 (shared with AudioTask),\n// SET cmdId=231 (shared with SetAudioTask).\n// We reuse BC_CMD_ID_GET_AUDIO_TASK / BC_CMD_ID_SET_AUDIO_TASK above.\n\n// Auto focus. cmdId=224 GET, cmdId=225 SET.\nexport const BC_CMD_ID_GET_AUTO_FOCUS = 224; // GetAutoFocus - auto focus settings\nexport const BC_CMD_ID_SET_AUTO_FOCUS = 225; // SetAutoFocus - write auto focus\n\n// Email SMTP server config. cmdId=42 GET, cmdId=43 SET, cmdId=141 TEST.\n// Schema confirmed from Reolink Client pcap (2026-05-16):\n// <Email><smtpServer/><userName/><password/><address1..3/><smtpPort/>\n// <sendNickname/><attachment/><attachmentType/><textType/><ssl/><interval/></Email>\nexport const BC_CMD_ID_GET_EMAIL = 42;\nexport const BC_CMD_ID_SET_EMAIL = 43;\nexport const BC_CMD_ID_TEST_EMAIL = 141; // Server returns 200=ok, 482=test failed\n\n// NTP. cmdId=38 GET, cmdId=39 SET.\n// <Ntp><enable/><server/><synchronizeInterval/><port/></Ntp>\nexport const BC_CMD_ID_GET_NTP = 38;\nexport const BC_CMD_ID_SET_NTP = 39;\n\n// System general (timezone, manual time, osd date format, device name, language).\n// cmdId=104 GET (already declared), cmdId=105 SET.\n// SET supports partial payloads: include only the fields to change, plus the\n// `<year>0</year>` marker meaning \"do not set manual time\". `<deviceNameOnly>1</deviceNameOnly>`\n// is required when patching only the device name.\nexport const BC_CMD_ID_SET_SYSTEM_GENERAL = 105;\n\n// Daylight Saving Time. cmdId=106 GET, cmdId=107 SET.\n// <Dst><enable/><offset/><startMonth/><startWeekIndex/><startWeekday/>\n// <startHour/><startMinute/><startSecond/><endMonth/><endWeekIndex/>\n// <endWeekday/><endHour/><endMinute/><endSecond/><version/></Dst>\nexport const BC_CMD_ID_GET_DST = 106;\nexport const BC_CMD_ID_SET_DST = 107;\n\n// Auto reboot. cmdId=101 GET, cmdId=100 SET.\n// <AutoReboot><enable/><weekDay/><hour/><minute/><second/></AutoReboot>\n// weekDay accepts \"Sunday\"..\"Saturday\" or \"everyday\".\nexport const BC_CMD_ID_GET_AUTO_REBOOT = 101;\nexport const BC_CMD_ID_SET_AUTO_REBOOT = 100;\n\n// Legacy placeholder aliases — kept for back-compat with consumers that\n// referenced the cmd_id directly. New callers should use the semantic\n// names above.\n/** @deprecated Use {@link BC_CMD_ID_SET_LED_STATE} (209). */\nexport const BC_CMD_ID_CMD_123 = 123;\n/** @deprecated Use {@link BC_CMD_ID_SET_LED_STATE} (209). */\nexport const BC_CMD_ID_CMD_209 = 209;\n/** @deprecated Use {@link BC_CMD_ID_SET_AUDIO_CFG} (265). */\nexport const BC_CMD_ID_CMD_265 = 265;\n/** @deprecated Use {@link BC_CMD_ID_SET_AI_DENOISE} (440). */\nexport const BC_CMD_ID_CMD_440 = 440;\n\n// push/device->client (camera-initiated)\nexport const BC_CMD_ID_PUSH_VIDEO_INPUT = 78; // <VideoInput>\nexport const BC_CMD_ID_PUSH_SERIAL = 79; // <Serial>\nexport const BC_CMD_ID_PUSH_NET_INFO = 464; // <NetInfo>\nexport const BC_CMD_ID_PUSH_DINGDONG_LIST = 484; // <dingdongList>\nexport const BC_CMD_ID_PUSH_SLEEP_STATUS = 623; // <sleepStatus>\nexport const BC_CMD_ID_PUSH_COORDINATE_POINT_LIST = 723; // <coordinatePointList>\n\n// Chime / DingDong command IDs\nexport const BC_CMD_ID_DING_DONG_CTRL = 483; // GetDingDongCtrl / SetDingDongCtrl (hardwired chime)\nexport const BC_CMD_ID_GET_DING_DONG_LIST = 484; // GetDingDongList (paired wireless chimes)\nexport const BC_CMD_ID_DING_DONG_OPT = 485; // DingDongOpt (get/set/ring wireless chime params)\nexport const BC_CMD_ID_GET_DING_DONG_CFG = 486; // GetDingDongCfg (chime event config)\nexport const BC_CMD_ID_SET_DING_DONG_CFG = 487; // SetDingDongCfg (set chime event config)\nexport const BC_CMD_ID_QUICK_REPLY_PLAY = 349; // QuickReplyPlay (play audio file on doorbell)\nexport const BC_CMD_ID_GET_DING_DONG_SILENT = 609; // GetDingDongSilent (get wireless chime silent mode)\nexport const BC_CMD_ID_SET_DING_DONG_SILENT = 610; // SetDingDongSilent (set wireless chime silent mode)\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport type Logger = import(\"../logging/logger\").Logger;\n\nexport type DebugOptions = {\n /** Enables generic debug logs. */\n general?: boolean;\n /** Enables extra RTSP proxy/server debug logs (quiet by default). */\n debugRtsp?: boolean;\n /**\n * Enables native stream tracing and related low-level logs.\n *\n * Includes:\n * - stream command tracing (tx/rx cmd_id 3/4 + rx stream frames)\n * - H.264/H.265 debug logs/samples\n * - SPS/PPS/VPS parameter sets debug logs\n */\n traceNativeStream?: boolean;\n /**\n * Enables detailed tracing for recording-related commands (FileInfoList, findAlarmVideo, download).\n * Useful to understand what is happening on the wire for recordings only.\n */\n traceRecordings?: boolean;\n /** Enables talkback tracing (tx/rx cmd_id 10/11/201/202). */\n traceTalk?: boolean;\n /** Enables per-event tracing for cmd_id 33 (AlarmEventList push). */\n traceEvents?: boolean;\n dump?: {\n enabled?: boolean;\n dir?: string;\n bcmedia?: boolean;\n nals?: boolean;\n };\n};\n\nexport type DebugConfig = {\n general: boolean;\n debugRtsp: boolean;\n traceNativeStream: boolean;\n traceRecordings: boolean;\n traceTalk: boolean;\n traceEvents: boolean;\n dumpEnabled: boolean;\n dumpDir: string;\n dumpBcMedia: boolean;\n dumpNals: boolean;\n};\n\nexport function normalizeDebugOptions(opts?: DebugOptions): DebugConfig {\n const general = opts?.general === true;\n const debugRtsp = opts?.debugRtsp === true;\n const traceNativeStream = opts?.traceNativeStream === true;\n const traceRecordings = opts?.traceRecordings === true;\n const traceTalk = opts?.traceTalk === true;\n const traceEvents = opts?.traceEvents === true;\n\n const dumpEnabled = opts?.dump?.enabled === true;\n const dumpDir =\n (opts?.dump?.dir && opts.dump.dir.trim()) ||\n path.join(process.cwd(), \"test\", \"frames-debug\");\n const dumpBcMedia = opts?.dump?.bcmedia ?? dumpEnabled;\n const dumpNals = opts?.dump?.nals ?? dumpEnabled;\n\n return {\n general,\n debugRtsp,\n traceNativeStream,\n traceRecordings,\n traceTalk,\n traceEvents,\n dumpEnabled,\n dumpDir,\n dumpBcMedia,\n dumpNals,\n };\n}\n\nexport function recordingsTraceLog(\n cfg: DebugConfig | undefined,\n logger: Logger | undefined,\n tag: string,\n message: string,\n): void {\n if (!cfg?.traceRecordings || !logger) return;\n logger.log(`[${tag}] ${message}`);\n}\n\nexport function ensureDumpDir(cfg: DebugConfig): void {\n if (!cfg.dumpEnabled) return;\n fs.mkdirSync(cfg.dumpDir, { recursive: true });\n}\n\nexport function debugLog(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.general) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function debugWarn(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.general) return;\n logger.warn(`[${tag}] ${message}`);\n}\n\nexport function traceLog(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.traceNativeStream) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function talkTraceLog(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.traceTalk) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function talkTraceWarn(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.traceTalk) return;\n logger.warn(`[${tag}] ${message}`);\n}\n\nexport function eventTraceLog(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.traceEvents) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function rtspLog(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.debugRtsp) return;\n logger.info(`[${tag}] ${message}`);\n}\n\nexport function rtspWarn(\n cfg: DebugConfig,\n logger: Logger,\n tag: string,\n message: string,\n): void {\n if (!cfg.debugRtsp) return;\n logger.warn(`[${tag}] ${message}`);\n}\n","/**\n * BcMedia Parser - Parses Baichuan media packets (video/audio frames)\n * \n * BcMedia packets have magic headers that identify the packet type:\n * - InfoV1: 0x31303031\n * - InfoV2: 0x32303031\n * - IFrame: 0x63643030 - 0x63643039 (includes channel number)\n * - PFrame: 0x63643130 - 0x63643139 (includes channel number)\n * - AAC: 0x62773530\n * - ADPCM: 0x62773130\n */\n\nexport type BcMediaType = \"InfoV1\" | \"InfoV2\" | \"Iframe\" | \"Pframe\" | \"Aac\" | \"Adpcm\";\n\nexport interface BcMediaIframe {\n type: \"Iframe\";\n videoType: \"H264\" | \"H265\";\n microseconds: number;\n time?: number;\n /** Raw additional header (if present) */\n additionalHeader?: Buffer;\n /** Additional header size */\n additionalHeaderSize?: number;\n /** Unknown u32 field after microseconds */\n unknown?: number;\n data: Buffer; // Raw video data (H.264/H.265 NAL units)\n}\n\nexport interface BcMediaPframe {\n type: \"Pframe\";\n videoType: \"H264\" | \"H265\";\n microseconds: number;\n /** Raw additional header (if present) */\n additionalHeader?: Buffer;\n /** Additional header size */\n additionalHeaderSize?: number;\n /** Unknown u32 field after microseconds */\n unknown?: number;\n data: Buffer; // Raw video data (H.264/H.265 NAL units)\n}\n\nexport interface BcMediaInfoV1 {\n type: \"InfoV1\";\n videoWidth: number;\n videoHeight: number;\n fps: number;\n startYear: number;\n startMonth: number;\n startDay: number;\n startHour: number;\n startMin: number;\n startSeconds: number;\n endYear: number;\n endMonth: number;\n endDay: number;\n endHour: number;\n endMin: number;\n endSeconds: number;\n}\n\nexport interface BcMediaInfoV2 {\n type: \"InfoV2\";\n videoWidth: number;\n videoHeight: number;\n fps: number;\n startYear: number;\n startMonth: number;\n startDay: number;\n startHour: number;\n startMin: number;\n startSeconds: number;\n endYear: number;\n endMonth: number;\n endDay: number;\n endHour: number;\n endMin: number;\n endSeconds: number;\n}\n\nexport interface BcMediaAac {\n type: \"Aac\";\n data: Buffer; // Raw AAC audio data\n}\n\nexport interface BcMediaAdpcm {\n type: \"Adpcm\";\n data: Buffer; // Raw ADPCM audio data\n}\n\nexport type BcMedia = BcMediaIframe | BcMediaPframe | BcMediaInfoV1 | BcMediaInfoV2 | BcMediaAac | BcMediaAdpcm;\n\n// Magic headers (u32 little-endian)\nconst MAGIC_INFO_V1 = 0x31303031;\nconst MAGIC_INFO_V2 = 0x32303031;\nconst MAGIC_IFRAME_START = 0x63643030; // \"cd00\"\nconst MAGIC_IFRAME_END = 0x63643039; // \"cd09\"\nconst MAGIC_PFRAME_START = 0x63643130; // \"cd10\"\nconst MAGIC_PFRAME_END = 0x63643139; // \"cd19\"\nconst MAGIC_AAC = 0x62773530; // \"bw50\"\nconst MAGIC_ADPCM = 0x62773130; // \"bw10\"\n\nconst PAD_SIZE = 8; // Media packets use 8 byte padding\n\n/**\n * Parse BcMedia packet from binary data.\n */\nexport function parseBcMedia(buf: Buffer): { media: BcMedia; consumed: number } | null {\n if (buf.length < 4) return null;\n\n const magic = buf.readUInt32LE(0);\n\n // Check magic header\n if (magic === MAGIC_INFO_V1) {\n return parseInfoV1(buf);\n } else if (magic === MAGIC_INFO_V2) {\n return parseInfoV2(buf);\n } else if (magic >= MAGIC_IFRAME_START && magic <= MAGIC_IFRAME_END) {\n return parseIframe(buf);\n } else if (magic >= MAGIC_PFRAME_START && magic <= MAGIC_PFRAME_END) {\n return parsePframe(buf);\n } else if (magic === MAGIC_AAC) {\n return parseAac(buf);\n } else if (magic === MAGIC_ADPCM) {\n return parseAdpcm(buf);\n }\n\n return null;\n}\n\nfunction parseInfoV1(buf: Buffer): { media: BcMediaInfoV1; consumed: number } | null {\n if (buf.length < 32) return null;\n\n const headerSize = buf.readUInt32LE(4);\n if (headerSize !== 32) return null;\n\n const media: BcMediaInfoV1 = {\n type: \"InfoV1\",\n videoWidth: buf.readUInt32LE(8),\n videoHeight: buf.readUInt32LE(12),\n fps: buf.readUInt8(17),\n startYear: buf.readUInt8(18),\n startMonth: buf.readUInt8(19),\n startDay: buf.readUInt8(20),\n startHour: buf.readUInt8(21),\n startMin: buf.readUInt8(22),\n startSeconds: buf.readUInt8(23),\n endYear: buf.readUInt8(24),\n endMonth: buf.readUInt8(25),\n endDay: buf.readUInt8(26),\n endHour: buf.readUInt8(27),\n endMin: buf.readUInt8(28),\n endSeconds: buf.readUInt8(29),\n };\n\n return { media, consumed: 32 };\n}\n\nfunction parseInfoV2(buf: Buffer): { media: BcMediaInfoV2; consumed: number } | null {\n if (buf.length < 32) return null;\n\n const headerSize = buf.readUInt32LE(4);\n if (headerSize !== 32) return null;\n\n const media: BcMediaInfoV2 = {\n type: \"InfoV2\",\n videoWidth: buf.readUInt32LE(8),\n videoHeight: buf.readUInt32LE(12),\n fps: buf.readUInt8(17),\n startYear: buf.readUInt8(18),\n startMonth: buf.readUInt8(19),\n startDay: buf.readUInt8(20),\n startHour: buf.readUInt8(21),\n startMin: buf.readUInt8(22),\n startSeconds: buf.readUInt8(23),\n endYear: buf.readUInt8(24),\n endMonth: buf.readUInt8(25),\n endDay: buf.readUInt8(26),\n endHour: buf.readUInt8(27),\n endMin: buf.readUInt8(28),\n endSeconds: buf.readUInt8(29),\n };\n\n return { media, consumed: 32 };\n}\n\nfunction parseIframe(buf: Buffer): { media: BcMediaIframe; consumed: number } | null {\n if (buf.length < 20) return null;\n\n // Magic (4) + \"H264\"/\"H265\" (4) = 8 bytes minimum\n const videoTypeStr = buf.toString(\"utf8\", 4, 8);\n if (videoTypeStr !== \"H264\" && videoTypeStr !== \"H265\") return null;\n\n const videoType = videoTypeStr as \"H264\" | \"H265\";\n const payloadSize = buf.readUInt32LE(8);\n const additionalHeaderSize = buf.readUInt32LE(12);\n const microseconds = buf.readUInt32LE(16);\n\n // Calculate total size: magic(4) + videoType(4) + payloadSize(4) + additionalHeaderSize(4) + microseconds(4) + unknown(4) + additionalHeader + payload + padding\n let offset = 20; // magic(4) + videoType(4) + payloadSize(4) + additionalHeaderSize(4) + microseconds(4)\n const unknown = buf.readUInt32LE(offset);\n offset += 4;\n\n let time: number | undefined;\n // I-frame has time (u32) in the additional header, but for some models\n // the entire additional header might be relevant (e.g. IV/flags). So we preserve it COMPLETELY.\n if (buf.length < offset + additionalHeaderSize) return null;\n const additionalHeader = buf.subarray(offset, offset + additionalHeaderSize);\n if (additionalHeaderSize >= 4) {\n time = additionalHeader.readUInt32LE(0);\n }\n offset += additionalHeaderSize;\n\n // Read payload data\n if (buf.length < offset + payloadSize) return null;\n const data = buf.subarray(offset, offset + payloadSize);\n offset += payloadSize;\n\n // Skip padding (8-byte aligned)\n const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - (payloadSize % PAD_SIZE);\n if (buf.length < offset + padSize) return null;\n offset += padSize;\n\n const media: BcMediaIframe = {\n type: \"Iframe\",\n videoType,\n microseconds,\n ...(time !== undefined ? { time } : {}),\n additionalHeader,\n additionalHeaderSize,\n unknown,\n data,\n };\n\n return { media, consumed: offset };\n}\n\nfunction parsePframe(buf: Buffer): { media: BcMediaPframe; consumed: number } | null {\n if (buf.length < 20) return null;\n\n // Magic (4) + \"H264\"/\"H265\" (4) = 8 bytes minimum\n const videoTypeStr = buf.toString(\"utf8\", 4, 8);\n if (videoTypeStr !== \"H264\" && videoTypeStr !== \"H265\") return null;\n\n const videoType = videoTypeStr as \"H264\" | \"H265\";\n const payloadSize = buf.readUInt32LE(8);\n const additionalHeaderSize = buf.readUInt32LE(12);\n const microseconds = buf.readUInt32LE(16);\n\n // Calculate total size\n let offset = 20; // magic(4) + videoType(4) + payloadSize(4) + additionalHeaderSize(4) + microseconds(4)\n const unknown = buf.readUInt32LE(offset);\n offset += 4;\n\n // Skip additional header\n if (buf.length < offset + additionalHeaderSize) return null;\n const additionalHeader = buf.subarray(offset, offset + additionalHeaderSize);\n offset += additionalHeaderSize;\n\n // Read payload data\n if (buf.length < offset + payloadSize) return null;\n const data = buf.subarray(offset, offset + payloadSize);\n offset += payloadSize;\n\n // Skip padding (8-byte aligned)\n const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - (payloadSize % PAD_SIZE);\n if (buf.length < offset + padSize) return null;\n offset += padSize;\n\n const media: BcMediaPframe = {\n type: \"Pframe\",\n videoType,\n microseconds,\n additionalHeader,\n additionalHeaderSize,\n unknown,\n data,\n };\n\n return { media, consumed: offset };\n}\n\nfunction parseAac(buf: Buffer): { media: BcMediaAac; consumed: number } | null {\n if (buf.length < 12) return null;\n\n const payloadSize = buf.readUInt16LE(4);\n const payloadSizeB = buf.readUInt16LE(6);\n\n if (payloadSize !== payloadSizeB) return null;\n\n // After the payload there is 8-byte alignment padding (based on payloadSize)\n const headerLen = 8; // magic(4) + size(2) + sizeB(2)\n const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - (payloadSize % PAD_SIZE);\n const totalLen = headerLen + payloadSize + padSize;\n if (buf.length < totalLen) return null;\n const data = buf.subarray(headerLen, headerLen + payloadSize);\n\n const media: BcMediaAac = {\n type: \"Aac\",\n data,\n };\n\n return { media, consumed: totalLen };\n}\n\nfunction parseAdpcm(buf: Buffer): { media: BcMediaAdpcm; consumed: number } | null {\n // Structure:\n // magic(4) + payload_size(u16) + payload_size_b(u16) + magic_data(u16=0x0100) + half_block_size(u16) + data(block_size) + padding\n if (buf.length < 12) return null;\n\n const payloadSize = buf.readUInt16LE(4);\n const payloadSizeB = buf.readUInt16LE(6);\n\n if (payloadSize !== payloadSizeB) return null;\n\n // Check for MAGIC_HEADER_BCMEDIA_ADPCM_DATA (0x0100)\n const magicData = buf.readUInt16LE(8);\n if (magicData !== 0x0100) return null;\n\n // half_block_size (read but not used to compute the length)\n const halfBlockSize = buf.readUInt16LE(10);\n void halfBlockSize;\n\n // payloadSize include SUB_HEADER_SIZE (4 bytes: magicData + halfBlockSize)\n const subHeaderSize = 4;\n if (payloadSize < subHeaderSize) return null;\n const blockSize = payloadSize - subHeaderSize;\n\n const headerLen = 12; // magic+sizes+magicData+halfBlockSize\n const padSize = payloadSize % PAD_SIZE === 0 ? 0 : PAD_SIZE - (payloadSize % PAD_SIZE);\n const totalLen = headerLen + blockSize + padSize;\n if (buf.length < totalLen) return null;\n\n const data = buf.subarray(headerLen, headerLen + blockSize);\n\n const media: BcMediaAdpcm = {\n type: \"Adpcm\",\n data,\n };\n\n return { media, consumed: totalLen };\n}\n\n","/**\n * BcMedia Codec - Assembles fragmented BcMedia packets from stream\n * \n * BcMedia packets can be fragmented across multiple Baichuan frames.\n * This codec buffers incomplete packets and assembles them when complete.\n */\n\nimport { parseBcMedia, type BcMedia } from \"./BcMediaParser\";\nimport type { Logger } from \"../../debug/DebugConfig\";\n\n/**\n * Optional listener invoked whenever the codec encounters a 4-byte sequence\n * at the buffer head that does NOT match any known BcMedia magic and is about\n * to be skipped as \"recovery\". Use it to discover undocumented sub-packet\n * shapes (e.g. AI overlay metadata) without altering the codec's behaviour.\n */\nexport type UnknownChunkListener = (info: {\n magic: number;\n /** Up to 256 bytes starting at the unknown chunk's first byte. */\n preview: Buffer;\n /** Number of bytes the codec is about to skip before resyncing. */\n skipped: number;\n}) => void;\n\nexport class BcMediaCodec {\n private buffer: Buffer = Buffer.alloc(0);\n private strict: boolean;\n private amountSkipped: number = 0;\n private logger: Logger | undefined;\n private onUnknownChunk: UnknownChunkListener | undefined;\n\n constructor(strict: boolean = false, logger?: Logger) {\n this.strict = strict;\n this.logger = logger;\n }\n\n /** Register a listener that fires for every unknown chunk before recovery. */\n setUnknownChunkListener(listener: UnknownChunkListener | undefined): void {\n this.onUnknownChunk = listener;\n }\n\n /**\n * Push data into the codec buffer and try to parse complete BcMedia packets.\n * Returns an array of complete BcMedia packets found.\n * \n * @param chunk - New data chunk to add to buffer\n * @returns Array of complete BcMedia packets (empty if none complete yet)\n */\n decode(chunk: Buffer): BcMedia[] {\n // Append new chunk to buffer\n this.buffer = this.buffer.length === 0 ? chunk : Buffer.concat([this.buffer, chunk]);\n const results: BcMedia[] = [];\n\n // Try to parse packets from buffer\n while (this.buffer.length >= 4) { // Need at least 4 bytes for magic\n const result = parseBcMedia(this.buffer);\n \n if (result) {\n // Complete packet found\n if (this.amountSkipped > 0) {\n // Log recovery if we had to skip data\n if (this.strict) {\n this.logger?.warn(`[BcMediaCodec] Recovered stream after skipping ${this.amountSkipped} bytes`);\n } else {\n this.logger?.warn(`[BcMediaCodec] Recovered stream after skipping ${this.amountSkipped} bytes`);\n }\n this.amountSkipped = 0;\n }\n \n results.push(result.media);\n this.buffer = this.buffer.subarray(result.consumed);\n } else {\n // No complete packet yet.\n // If the buffer does NOT start with a known magic,\n // in non-strict mode we drop the whole buffer (prevents desync and \"fake\" packets).\n const isKnownMagic = (magic: number): boolean => {\n const isInfoV1 = magic === 0x31303031;\n const isInfoV2 = magic === 0x32303031;\n const isIFrame = magic >= 0x63643030 && magic <= 0x63643039;\n const isPFrame = magic >= 0x63643130 && magic <= 0x63643139;\n const isAac = magic === 0x62773530;\n const isAdpcm = magic === 0x62773130;\n return isInfoV1 || isInfoV2 || isIFrame || isPFrame || isAac || isAdpcm;\n };\n\n const magic = this.buffer.readUInt32LE(0);\n const startsWithKnownMagic = isKnownMagic(magic);\n\n if (startsWithKnownMagic) {\n // Likely incomplete: wait for more data.\n break;\n }\n\n // Doesn't start with a valid magic: corrupted or misaligned stream.\n if (this.strict) {\n throw new Error(`[BcMediaCodec] Invalid data in stream (no valid magic at buffer start, len=${this.buffer.length})`);\n }\n\n if (this.amountSkipped === 0) {\n this.logger?.warn(`[BcMediaCodec] Error in stream, attempting to recover...`);\n }\n\n // Non-strict recovery: find the next known magic.\n // On some Hub/NVR tele streams we observe repeated fixed-size padding blocks\n // (commonly 528 bytes, sometimes 1056). A fast-path avoids O(n) scans for every packet.\n let next = -1;\n for (const off of [528, 1056, 1584]) {\n if (this.buffer.length >= off + 4 && isKnownMagic(this.buffer.readUInt32LE(off))) {\n next = off;\n break;\n }\n }\n\n // Fallback: linear scan for the next magic.\n if (next < 0) {\n for (let i = 1; i <= this.buffer.length - 4; i++) {\n if (isKnownMagic(this.buffer.readUInt32LE(i))) {\n next = i;\n break;\n }\n }\n }\n\n if (next > 0) {\n if (this.onUnknownChunk) {\n this.onUnknownChunk({\n magic,\n preview: Buffer.from(this.buffer.subarray(0, Math.min(256, next))),\n skipped: next,\n });\n }\n this.amountSkipped += next;\n this.buffer = this.buffer.subarray(next);\n continue;\n }\n\n // No magic found: keep a short tail so that a 4-byte magic split across chunks can be reconstructed.\n if (this.buffer.length > 3) {\n const keep = 3;\n this.amountSkipped += this.buffer.length - keep;\n this.buffer = this.buffer.subarray(this.buffer.length - keep);\n }\n break;\n }\n }\n\n return results;\n }\n\n /**\n * Get remaining buffer (for debugging)\n */\n getRemainingBuffer(): Buffer {\n return this.buffer;\n }\n\n /**\n * Clear the buffer (useful for resetting the codec)\n */\n clear(): void {\n this.buffer = Buffer.alloc(0);\n this.amountSkipped = 0;\n }\n}\n\n","/**\n * H.264 Format Converter\n * Converts H.264 data from length-prefixed (AVCC) to Annex-B (start codes).\n *\n * BcMedia payloads can be length-prefixed and must be converted\n * to Annex-B for ffmpeg/RTSP streaming.\n */\n\n// Annex-B start codes:\n// - 4 bytes: 0x00 00 00 01\n// - 3 bytes: 0x00 00 01\nconst NAL_START_CODE_4B = Buffer.from([0x00, 0x00, 0x00, 0x01]);\nconst NAL_START_CODE_3B = Buffer.from([0x00, 0x00, 0x01]);\n\n/** Returns true if the buffer starts with an Annex-B start code. */\nexport function hasStartCodes(data: Buffer): boolean {\n if (data.length < 4) return false;\n \n // Important: to distinguish Annex-B vs AVCC, check ONLY the beginning.\n // Searching for a start code \"in the middle\" can cause false positives and prevent conversion.\n if (data.subarray(0, 4).equals(NAL_START_CODE_4B)) return true; // 0x00000001\n if (data.subarray(0, 3).equals(NAL_START_CODE_3B)) return true; // 0x000001\n return false;\n}\n\nfunction tryConvertWithLengthReader(data: Buffer, readLen: (buf: Buffer, offset: number) => number): Buffer | null {\n const result: Buffer[] = [];\n let offset = 0;\n let nalCount = 0;\n\n while (offset < data.length) {\n if (offset + 4 > data.length) return null;\n const nalLength = readLen(data, offset);\n offset += 4;\n if (nalLength <= 0) return null;\n if (nalLength > data.length - offset) return null;\n\n result.push(NAL_START_CODE_4B);\n result.push(data.subarray(offset, offset + nalLength));\n offset += nalLength;\n nalCount++;\n }\n\n // Require at least 1 NAL to consider the conversion valid.\n if (nalCount === 0) return null;\n return Buffer.concat(result);\n}\n\nfunction tryConvertWithLengthReader16(data: Buffer, readLen: (buf: Buffer, offset: number) => number): Buffer | null {\n const result: Buffer[] = [];\n let offset = 0;\n let nalCount = 0;\n\n while (offset < data.length) {\n if (offset + 2 > data.length) return null;\n const nalLength = readLen(data, offset);\n offset += 2;\n if (nalLength <= 0) return null;\n if (nalLength > data.length - offset) return null;\n\n result.push(NAL_START_CODE_4B);\n result.push(data.subarray(offset, offset + nalLength));\n offset += nalLength;\n nalCount++;\n }\n\n if (nalCount === 0) return null;\n return Buffer.concat(result);\n}\n\nfunction tryConvertWithLengthReader24(data: Buffer, endian: \"be\" | \"le\"): Buffer | null {\n const result: Buffer[] = [];\n let offset = 0;\n let nalCount = 0;\n\n const readLen24 = (buf: Buffer, at: number): number => {\n if (at + 3 > buf.length) return 0;\n const b0 = buf[at]!;\n const b1 = buf[at + 1]!;\n const b2 = buf[at + 2]!;\n return endian === \"be\"\n ? ((b0 << 16) | (b1 << 8) | b2) >>> 0\n : ((b2 << 16) | (b1 << 8) | b0) >>> 0;\n };\n\n while (offset < data.length) {\n if (offset + 3 > data.length) return null;\n const nalLength = readLen24(data, offset);\n offset += 3;\n if (nalLength <= 0) return null;\n if (nalLength > data.length - offset) return null;\n\n result.push(NAL_START_CODE_4B);\n result.push(data.subarray(offset, offset + nalLength));\n offset += nalLength;\n nalCount++;\n }\n\n if (nalCount === 0) return null;\n return Buffer.concat(result);\n}\n\nfunction looksLikeSingleH264Nal(nalPayload: Buffer): boolean {\n if (nalPayload.length < 1) return false;\n const b0 = nalPayload[0];\n if (b0 === undefined) return false;\n if ((b0 & 0x80) !== 0) return false; // forbidden_zero_bit must be 0\n const nalType = b0 & 0x1f;\n return nalType >= 1 && nalType <= 23;\n}\n\nfunction depacketizeRtpAggregationToAnnexB(payload: Buffer): Buffer | null {\n // Supports some H.264 \"RTP-style\" aggregation payloads (STAP/MTAP) that can appear in Baichuan streams.\n // Reference: RFC 6184\n if (payload.length < 1) return null;\n const nalHeader = payload[0]!;\n const nalType = nalHeader & 0x1f;\n\n const out: Buffer[] = [];\n const pushNal = (nal: Buffer) => {\n if (nal.length === 0) return;\n out.push(NAL_START_CODE_4B, nal);\n };\n\n // STAP-A (24): [1B header][2B size][NAL]...\n if (nalType === 24) {\n let off = 1;\n while (off + 2 <= payload.length) {\n const size = payload.readUInt16BE(off);\n off += 2;\n if (size <= 0 || off + size > payload.length) return null;\n pushNal(payload.subarray(off, off + size));\n off += size;\n }\n return out.length ? Buffer.concat(out) : null;\n }\n\n // STAP-B (25): [1B header][2B DON][2B size][NAL]...\n if (nalType === 25) {\n let off = 1 + 2; // skip DON\n if (off > payload.length) return null;\n while (off + 2 <= payload.length) {\n const size = payload.readUInt16BE(off);\n off += 2;\n if (size <= 0 || off + size > payload.length) return null;\n pushNal(payload.subarray(off, off + size));\n off += size;\n }\n return out.length ? Buffer.concat(out) : null;\n }\n\n // MTAP16 (26): [1B header][2B DON][2B size][1B DOND][2B TS offset][NAL]...\n if (nalType === 26) {\n let off = 1 + 2; // skip DON\n if (off > payload.length) return null;\n while (off + 2 <= payload.length) {\n const size = payload.readUInt16BE(off);\n off += 2;\n if (off + 1 + 2 > payload.length) return null; // need DOND + TS offset\n off += 1; // skip DOND\n off += 2; // skip TS offset (16-bit)\n if (size <= 0 || off + size > payload.length) return null;\n pushNal(payload.subarray(off, off + size));\n off += size;\n }\n return out.length ? Buffer.concat(out) : null;\n }\n\n // MTAP24 (27): [1B header][2B DON][2B size][1B DOND][3B TS offset][NAL]...\n if (nalType === 27) {\n let off = 1 + 2; // skip DON\n if (off > payload.length) return null;\n while (off + 2 <= payload.length) {\n const size = payload.readUInt16BE(off);\n off += 2;\n if (off + 1 + 3 > payload.length) return null; // need DOND + TS offset\n off += 1; // skip DOND\n off += 3; // skip TS offset (24-bit)\n if (size <= 0 || off + size > payload.length) return null;\n pushNal(payload.subarray(off, off + size));\n off += size;\n }\n return out.length ? Buffer.concat(out) : null;\n }\n\n return null;\n}\n\n/**\n * Converts H.264 data from length-prefixed (AVCC) to Annex-B (start codes).\n *\n * The length-prefixed format uses a 4-byte big-endian integer to indicate the size\n * of each NAL unit, followed by the NAL unit bytes.\n *\n * Annex-B uses start codes (0x00000001 or 0x000001) before each NAL unit.\n */\nexport function convertToAnnexB(data: Buffer): Buffer {\n // If the data already has start codes, return it as-is.\n if (hasStartCodes(data)) {\n return data;\n }\n\n // Some models prepend a small header (e.g. size/timestamp) before an Annex-B access unit.\n // If we can find a start code very early, resync to it.\n const sc4 = Buffer.from([0x00, 0x00, 0x00, 0x01]);\n const sc3 = Buffer.from([0x00, 0x00, 0x01]);\n const maxScan = Math.min(64, data.length);\n const idx4 = data.subarray(0, maxScan).indexOf(sc4);\n if (idx4 > 0) return data.subarray(idx4);\n const idx3 = data.subarray(0, maxScan).indexOf(sc3);\n if (idx3 > 0) return data.subarray(idx3);\n\n // Otherwise, try AVCC -> AnnexB conversion.\n // In practice most sources use 4-byte big-endian, but some streams can be little-endian:\n // try both and take the first valid conversion.\n const be = tryConvertWithLengthReader(data, (b, o) => b.readUInt32BE(o));\n if (be) return be;\n const le = tryConvertWithLengthReader(data, (b, o) => b.readUInt32LE(o));\n if (le) return le;\n\n // Some devices use 3-byte (24-bit) NAL lengths.\n const be24 = tryConvertWithLengthReader24(data, \"be\");\n if (be24) return be24;\n const le24 = tryConvertWithLengthReader24(data, \"le\");\n if (le24) return le24;\n\n // Also try 2-byte length-prefixed (some streams do this on P-frames).\n const be16 = tryConvertWithLengthReader16(data, (b, o) => b.readUInt16BE(o));\n if (be16) return be16;\n const le16 = tryConvertWithLengthReader16(data, (b, o) => b.readUInt16LE(o));\n if (le16) return le16;\n\n // Fallback: unrecognized.\n // If it looks like an RTP aggregation payload (STAP/MTAP), depacketize into Annex-B NAL units.\n const agg = depacketizeRtpAggregationToAnnexB(data);\n if (agg) return agg;\n\n // If it looks like a single H.264 NAL without start codes, prepend a start code.\n if (looksLikeSingleH264Nal(data)) {\n return Buffer.concat([NAL_START_CODE_4B, data]);\n }\n\n // Last-ditch: if we couldn't convert, return original bytes.\n // Callers that validate Annex-B will drop invalid frames.\n return data;\n}\n\nexport function splitAnnexBToNalPayloads(annexB: Buffer): Buffer[] {\n // Returns NAL payloads without start codes.\n const starts: Array<{ idx: number; len: number }> = [];\n for (let i = 0; i < annexB.length - 3; i++) {\n if (annexB[i] === 0x00 && annexB[i + 1] === 0x00) {\n if (annexB[i + 2] === 0x01) {\n starts.push({ idx: i, len: 3 });\n i += 2;\n } else if (annexB[i + 2] === 0x00 && annexB[i + 3] === 0x01) {\n starts.push({ idx: i, len: 4 });\n i += 3;\n }\n }\n }\n if (starts.length === 0) return [];\n const out: Buffer[] = [];\n for (let s = 0; s < starts.length; s++) {\n const st = starts[s]!;\n const start = st.idx + st.len;\n const end = starts[s + 1] ? starts[s + 1]!.idx : annexB.length;\n if (end > start) out.push(annexB.subarray(start, end));\n }\n return out;\n}\n\nexport function isValidH264AnnexBAccessUnit(annexB: Buffer): boolean {\n if (!hasStartCodes(annexB)) return false;\n const nals = splitAnnexBToNalPayloads(annexB);\n if (nals.length === 0) return false;\n for (const nal of nals) {\n if (nal.length < 1) return false;\n const b0 = nal[0];\n if (b0 === undefined) return false;\n if ((b0 & 0x80) !== 0) return false; // forbidden_zero_bit\n const nalType = b0 & 0x1f;\n // In Annex-B we expect types 1..23 (VCL + SPS/PPS/SEI/AUD etc).\n // Types 24..29 are RTP packetization payloads, not a NAL unit byte stream.\n if (nalType === 0 || nalType >= 24) return false;\n }\n return true;\n}\n\nexport function isH264KeyframeAnnexB(annexB: Buffer): boolean {\n const nals = splitAnnexBToNalPayloads(annexB);\n let hasSps = false;\n let hasPps = false;\n let hasIdr = false;\n for (const nal of nals) {\n const t = (nal[0] ?? 0) & 0x1f;\n if (t === 7) hasSps = true;\n if (t === 8) hasPps = true;\n if (t === 5) hasIdr = true;\n }\n return hasIdr && hasSps && hasPps;\n}\n\n/**\n * Depacketizer for H.264 \"RTP-like\" payloads (RFC 6184) when the camera sends single NAL units,\n * STAP/MTAP aggregation or FU-A/FU-B fragmentation inside a BcMedia frame payload.\n *\n * Returns NAL units already in Annex-B (with start codes).\n */\nexport class H264RtpDepacketizer {\n private fuNalHeader: number | null = null;\n private fuParts: Buffer[] = [];\n\n private static parseRtpPayload(packet: Buffer): Buffer | null {\n // Accept full RTP packets (12B header + optional CSRC/ext/padding) and return the payload.\n if (!packet || packet.length < 12) return null;\n const version = (packet[0]! >> 6) & 0x03;\n if (version !== 2) return null;\n\n const padding = (packet[0]! & 0x20) !== 0;\n const extension = (packet[0]! & 0x10) !== 0;\n const csrcCount = packet[0]! & 0x0f;\n\n let offset = 12 + csrcCount * 4;\n if (offset > packet.length) return null;\n\n if (extension) {\n if (offset + 4 > packet.length) return null;\n const extLenWords = packet.readUInt16BE(offset + 2);\n offset += 4 + extLenWords * 4;\n if (offset > packet.length) return null;\n }\n\n let end = packet.length;\n if (padding) {\n const padLen = packet[packet.length - 1]!;\n if (padLen <= 0 || padLen > packet.length) return null;\n end = packet.length - padLen;\n if (end < offset) return null;\n }\n\n if (end <= offset) return null;\n return packet.subarray(offset, end);\n }\n\n reset(): void {\n this.fuNalHeader = null;\n this.fuParts = [];\n }\n\n push(payload: Buffer): Buffer[] {\n if (payload.length === 0) return [];\n\n // Some models embed full RTP packets in the BcMedia payload.\n // If this looks like RTP, strip the header and depacketize the RTP payload.\n const rtpPayload = H264RtpDepacketizer.parseRtpPayload(payload);\n if (rtpPayload) payload = rtpPayload;\n\n // Important: behave like an RFC 6184 \"RTP-like\" depacketizer here, so do NOT run\n // AVCC/heuristic conversions that could produce false positives.\n // If it's already Annex-B, return as-is.\n if (hasStartCodes(payload)) return [payload];\n\n const b0 = payload[0]!;\n if ((b0 & 0x80) !== 0) return []; // forbidden_zero_bit must be 0 for H.264\n const nalType = b0 & 0x1f;\n\n // Single NAL unit\n if (nalType >= 1 && nalType <= 23) {\n return [Buffer.concat([NAL_START_CODE_4B, payload])];\n }\n\n // STAP-A (24): multiple NAL in one payload\n if (nalType === 24) {\n if (payload.length < 1 + 2) return [];\n let off = 1;\n const out: Buffer[] = [];\n while (off + 2 <= payload.length) {\n const size = payload.readUInt16BE(off);\n off += 2;\n if (size <= 0 || off + size > payload.length) return [];\n const nal = payload.subarray(off, off + size);\n off += size;\n if (nal.length < 1) return [];\n if ((nal[0]! & 0x80) !== 0) return [];\n const t = nal[0]! & 0x1f;\n if (t === 0 || t >= 24) return [];\n out.push(Buffer.concat([NAL_START_CODE_4B, nal]));\n }\n return out;\n }\n\n // FU-A / FU-B\n if (nalType === 28 || nalType === 29) {\n if (payload.length < 2) return [];\n const fuIndicator = payload[0]!;\n const fuHeader = payload[1]!;\n const start = (fuHeader & 0x80) !== 0;\n const end = (fuHeader & 0x40) !== 0;\n const origType = fuHeader & 0x1f;\n const reconstructedHeader = (fuIndicator & 0xe0) | origType; // F+NRI from indicator, type from header\n\n let off = 2;\n if (nalType === 29) {\n // FU-B has DON (2 bytes)\n if (payload.length < off + 2) return [];\n off += 2;\n }\n const frag = payload.subarray(off);\n\n if (start) {\n this.fuNalHeader = reconstructedHeader;\n this.fuParts = [frag];\n } else if (this.fuNalHeader != null) {\n this.fuParts.push(frag);\n } else {\n // fragment without start: discard\n return [];\n }\n\n if (end && this.fuNalHeader != null) {\n const nal = Buffer.concat([Buffer.from([this.fuNalHeader]), ...this.fuParts]);\n this.reset();\n return [Buffer.concat([NAL_START_CODE_4B, nal])];\n }\n return [];\n }\n\n // Other types (24-27) should already be handled by convertToAnnexB; if we are here, ignore.\n return [];\n }\n}\n\n/**\n * Converte dati H.264 da Annex-B a length-prefixed (se necessario)\n * Not used for ffmpeg, but useful for other purposes\n */\nexport function convertToLengthPrefixed(data: Buffer): Buffer {\n const result: Buffer[] = [];\n let offset = 0;\n \n while (offset < data.length) {\n // Search for start code\n let startCodeOffset = -1;\n let startCodeLength = 0;\n \n // Search for 4-byte start code\n if (offset + 4 <= data.length && data.subarray(offset, offset + 4).equals(NAL_START_CODE_4B)) {\n startCodeOffset = offset;\n startCodeLength = 4;\n }\n // Search for 3-byte start code\n else if (offset + 3 <= data.length && data.subarray(offset, offset + 3).equals(NAL_START_CODE_3B)) {\n startCodeOffset = offset;\n startCodeLength = 3;\n }\n \n if (startCodeOffset === -1) {\n // No start code found, append the remainder as-is\n result.push(data.subarray(offset));\n break;\n }\n \n // Skip the start code\n offset = startCodeOffset + startCodeLength;\n \n // Find the next start code or end of data\n let nextStartCode = -1;\n let nextStartCodeLength = 0;\n \n for (let i = offset; i < data.length - 3; i++) {\n if (i + 4 <= data.length && data.subarray(i, i + 4).equals(NAL_START_CODE_4B)) {\n nextStartCode = i;\n nextStartCodeLength = 4;\n break;\n }\n if (i + 3 <= data.length && data.subarray(i, i + 3).equals(NAL_START_CODE_3B)) {\n nextStartCode = i;\n nextStartCodeLength = 3;\n break;\n }\n }\n \n // Estrai il NAL unit\n const nalEnd = nextStartCode !== -1 ? nextStartCode : data.length;\n const nalData = data.subarray(offset, nalEnd);\n \n // Aggiungi length prefix (4 bytes, big-endian)\n const lengthBuf = Buffer.alloc(4);\n lengthBuf.writeUInt32BE(nalData.length, 0);\n result.push(lengthBuf);\n \n // Aggiungi dati NAL\n result.push(nalData);\n \n offset = nalEnd;\n }\n \n return Buffer.concat(result);\n}\n\n","/**\n * H.265/HEVC Format Converter\n * Converts H.265 data from length-prefixed (HVCC) to Annex-B (start codes).\n *\n * Similar to H.264 converter, but handles H.265/HEVC specific NAL unit types.\n * H.265 uses VPS (Video Parameter Set), SPS (Sequence Parameter Set), and PPS (Picture Parameter Set).\n */\n\n// Annex-B start codes (same as H.264):\nconst NAL_START_CODE_4B = Buffer.from([0x00, 0x00, 0x00, 0x01]);\nconst NAL_START_CODE_3B = Buffer.from([0x00, 0x00, 0x01]);\n\n/** Returns true if the buffer starts with an Annex-B start code. */\nexport function hasStartCodes(data: Buffer): boolean {\n if (data.length < 4) return false;\n \n // Important: to distinguish Annex-B vs HVCC, check ONLY the beginning.\n if (data.subarray(0, 4).equals(NAL_START_CODE_4B)) return true; // 0x00000001\n if (data.subarray(0, 3).equals(NAL_START_CODE_3B)) return true; // 0x000001\n return false;\n}\n\nfunction tryConvertWithLengthReader(data: Buffer, readLen: (buf: Buffer, offset: number) => number): Buffer | null {\n const result: Buffer[] = [];\n let offset = 0;\n let nalCount = 0;\n\n while (offset < data.length) {\n if (offset + 4 > data.length) return null;\n const nalLength = readLen(data, offset);\n offset += 4;\n if (nalLength <= 0) return null;\n if (nalLength > data.length - offset) return null;\n\n result.push(NAL_START_CODE_4B);\n result.push(data.subarray(offset, offset + nalLength));\n offset += nalLength;\n nalCount++;\n }\n\n // Require at least 1 NAL to consider the conversion valid.\n if (nalCount === 0) return null;\n return Buffer.concat(result);\n}\n\nfunction tryConvertWithLengthReader16(data: Buffer, readLen: (buf: Buffer, offset: number) => number): Buffer | null {\n const result: Buffer[] = [];\n let offset = 0;\n let nalCount = 0;\n\n while (offset < data.length) {\n if (offset + 2 > data.length) return null;\n const nalLength = readLen(data, offset);\n offset += 2;\n if (nalLength <= 0) return null;\n if (nalLength > data.length - offset) return null;\n\n result.push(NAL_START_CODE_4B);\n result.push(data.subarray(offset, offset + nalLength));\n offset += nalLength;\n nalCount++;\n }\n\n if (nalCount === 0) return null;\n return Buffer.concat(result);\n}\n\nfunction tryConvertWithLengthReader24(data: Buffer, endian: \"be\" | \"le\"): Buffer | null {\n const result: Buffer[] = [];\n let offset = 0;\n let nalCount = 0;\n\n const readLen24 = (buf: Buffer, at: number): number => {\n if (at + 3 > buf.length) return 0;\n const b0 = buf[at]!;\n const b1 = buf[at + 1]!;\n const b2 = buf[at + 2]!;\n return endian === \"be\"\n ? ((b0 << 16) | (b1 << 8) | b2) >>> 0\n : ((b2 << 16) | (b1 << 8) | b0) >>> 0;\n };\n\n while (offset < data.length) {\n if (offset + 3 > data.length) return null;\n const nalLength = readLen24(data, offset);\n offset += 3;\n if (nalLength <= 0) return null;\n if (nalLength > data.length - offset) return null;\n\n result.push(NAL_START_CODE_4B);\n result.push(data.subarray(offset, offset + nalLength));\n offset += nalLength;\n nalCount++;\n }\n\n if (nalCount === 0) return null;\n return Buffer.concat(result);\n}\n\nfunction looksLikeSingleH265Nal(nalPayload: Buffer): boolean {\n if (nalPayload.length < 2) return false;\n const b0 = nalPayload[0];\n const b1 = nalPayload[1];\n if (b0 === undefined || b1 === undefined) return false;\n \n // H.265 NAL unit header: first byte has forbidden_zero_bit (bit 7) and nal_unit_type (bits 0-6)\n // Second byte has nuh_layer_id (bits 0-5) and nuh_temporal_id_plus1 (bits 6-7)\n if ((b0 & 0x80) !== 0) return false; // forbidden_zero_bit must be 0\n const nalType = (b0 >> 1) & 0x3f; // Extract NAL unit type (6 bits)\n \n // Valid H.265 NAL unit types: 0-40 (some are reserved)\n // Common types: VPS (32), SPS (33), PPS (34), IDR (19-20), CRA (21), etc.\n return nalType <= 40;\n}\n\n/**\n * Converts H.265 data from length-prefixed (HVCC) to Annex-B (start codes).\n *\n * The length-prefixed format uses a 4-byte big-endian integer to indicate the size\n * of each NAL unit, followed by the NAL unit bytes.\n *\n * Annex-B uses start codes (0x00000001 or 0x000001) before each NAL unit.\n */\nexport function convertToAnnexB(data: Buffer): Buffer {\n // If the data already has start codes, return it as-is.\n if (hasStartCodes(data)) {\n return data;\n }\n\n // Some models prepend a small header (e.g. size/timestamp) before an Annex-B access unit.\n // If we can find a start code very early, resync to it.\n const sc4 = Buffer.from([0x00, 0x00, 0x00, 0x01]);\n const sc3 = Buffer.from([0x00, 0x00, 0x01]);\n const maxScan = Math.min(64, data.length);\n const idx4 = data.subarray(0, maxScan).indexOf(sc4);\n if (idx4 > 0) return data.subarray(idx4);\n const idx3 = data.subarray(0, maxScan).indexOf(sc3);\n if (idx3 > 0) return data.subarray(idx3);\n\n // Otherwise, try HVCC -> AnnexB conversion.\n // In practice most sources use 4-byte big-endian, but some streams can be little-endian:\n // try both and take the first valid conversion.\n const be = tryConvertWithLengthReader(data, (b, o) => b.readUInt32BE(o));\n if (be) return be;\n const le = tryConvertWithLengthReader(data, (b, o) => b.readUInt32LE(o));\n if (le) return le;\n\n // Some devices use 3-byte (24-bit) NAL lengths.\n const be24 = tryConvertWithLengthReader24(data, \"be\");\n if (be24) return be24;\n const le24 = tryConvertWithLengthReader24(data, \"le\");\n if (le24) return le24;\n\n // Also try 2-byte length-prefixed.\n const be16 = tryConvertWithLengthReader16(data, (b, o) => b.readUInt16BE(o));\n if (be16) return be16;\n const le16 = tryConvertWithLengthReader16(data, (b, o) => b.readUInt16LE(o));\n if (le16) return le16;\n\n // If it looks like a single H.265 NAL without start codes, prepend a start code.\n if (looksLikeSingleH265Nal(data)) {\n return Buffer.concat([NAL_START_CODE_4B, data]);\n }\n return data;\n}\n\n/**\n * Depacketizer for H.265 \"RTP-like\" payloads (RFC 7798) when the camera sends\n * single NAL units, AP aggregation, or FU fragmentation inside a BcMedia frame payload.\n *\n * Returns complete access units already in Annex-B (with start codes).\n */\nexport class H265RtpDepacketizer {\n private fuParts: Buffer[] | null = null;\n\n reset(): void {\n this.fuParts = null;\n }\n\n private static parseRtpPayload(packet: Buffer): Buffer | null {\n if (!packet || packet.length < 12) return null;\n const version = (packet[0]! >> 6) & 0x03;\n if (version !== 2) return null;\n\n const padding = (packet[0]! & 0x20) !== 0;\n const extension = (packet[0]! & 0x10) !== 0;\n const csrcCount = packet[0]! & 0x0f;\n\n let offset = 12 + csrcCount * 4;\n if (offset > packet.length) return null;\n\n if (extension) {\n if (offset + 4 > packet.length) return null;\n const extLenWords = packet.readUInt16BE(offset + 2);\n offset += 4 + extLenWords * 4;\n if (offset > packet.length) return null;\n }\n\n let end = packet.length;\n if (padding) {\n const padLen = packet[packet.length - 1]!;\n if (padLen <= 0 || padLen > packet.length) return null;\n end = packet.length - padLen;\n if (end < offset) return null;\n }\n\n if (end <= offset) return null;\n return packet.subarray(offset, end);\n }\n\n push(payload: Buffer): Buffer[] {\n if (!payload || payload.length < 2) return [];\n\n // Some models embed full RTP packets in the BcMedia payload.\n // If this looks like RTP, strip the header and depacketize the RTP payload.\n const rtpPayload = H265RtpDepacketizer.parseRtpPayload(payload);\n if (rtpPayload) payload = rtpPayload;\n\n const h0 = payload[0]!;\n const h1 = payload[1]!;\n\n // forbidden_zero_bit must be 0\n if ((h0 & 0x80) !== 0) return [];\n\n const nalType = (h0 >> 1) & 0x3f;\n\n // AP (48): [2B header][2B size][NAL (incl 2B hdr)]...\n if (nalType === 48) {\n let off = 2;\n const out: Buffer[] = [];\n while (off + 2 <= payload.length) {\n const size = payload.readUInt16BE(off);\n off += 2;\n if (size <= 0 || off + size > payload.length) return [];\n const nal = payload.subarray(off, off + size);\n off += size;\n if (nal.length) out.push(NAL_START_CODE_4B, nal);\n }\n return out.length ? [Buffer.concat(out)] : [];\n }\n\n // FU (49): [2B FU indicator][1B FU header][fragment]\n if (nalType === 49) {\n if (payload.length < 3) return [];\n const fuHeader = payload[2]!;\n const start = (fuHeader & 0x80) !== 0;\n const end = (fuHeader & 0x40) !== 0;\n const origType = fuHeader & 0x3f;\n\n // Reconstruct original NAL header: keep F bit and nuh_layer_id msb (bit0) from FU indicator,\n // replace type.\n const orig0 = (h0 & 0x81) | ((origType & 0x3f) << 1);\n const orig1 = h1;\n const frag = payload.subarray(3);\n\n if (start) {\n this.fuParts = [NAL_START_CODE_4B, Buffer.from([orig0, orig1]), frag];\n }\n else {\n if (!this.fuParts) return [];\n this.fuParts.push(frag);\n }\n\n if (end) {\n if (!this.fuParts) return [];\n const out = Buffer.concat(this.fuParts);\n this.fuParts = null;\n return [out];\n }\n\n return [];\n }\n\n // Single NAL unit (no start code)\n return [Buffer.concat([NAL_START_CODE_4B, payload])];\n }\n}\n\n/**\n * Splits Annex-B formatted H.265 data into individual NAL unit payloads (without start codes).\n */\nexport function splitAnnexBToNalPayloads(annexB: Buffer): Buffer[] {\n // Returns NAL payloads without start codes.\n const starts: Array<{ idx: number; len: number }> = [];\n for (let i = 0; i < annexB.length - 3; i++) {\n if (annexB[i] === 0x00 && annexB[i + 1] === 0x00) {\n if (annexB[i + 2] === 0x01) {\n starts.push({ idx: i, len: 3 });\n i += 2;\n } else if (annexB[i + 2] === 0x00 && annexB[i + 3] === 0x01) {\n starts.push({ idx: i, len: 4 });\n i += 3;\n }\n }\n }\n if (starts.length === 0) return [];\n const out: Buffer[] = [];\n for (let s = 0; s < starts.length; s++) {\n const st = starts[s]!;\n const start = st.idx + st.len;\n const end = starts[s + 1] ? starts[s + 1]!.idx : annexB.length;\n if (end > start) out.push(annexB.subarray(start, end));\n }\n return out;\n}\n\n/**\n * Gets H.265 NAL unit type from a NAL payload (without start code).\n * Returns null if invalid.\n */\nexport function getH265NalType(nalPayload: Buffer): number | null {\n if (nalPayload.length < 1) return null;\n const b0 = nalPayload[0];\n if (b0 === undefined) return null;\n if ((b0 & 0x80) !== 0) return null; // forbidden_zero_bit must be 0\n return (b0 >> 1) & 0x3f; // Extract NAL unit type (6 bits)\n}\n\n/**\n * Checks if an H.265 NAL unit is an IRAP (Intra Random Access Point) picture.\n * IRAP pictures include IDR, CRA, and BLA types.\n */\nexport function isH265Irap(nalType: number): boolean {\n // H.265 IRAP types: 16-23\n // 16: BLA_W_LP, 17: BLA_W_RADL, 18: BLA_N_LP\n // 19: IDR_W_RADL, 20: IDR_N_LP\n // 21: CRA_NUT\n // 22-23: Reserved\n return nalType >= 16 && nalType <= 23;\n}\n\n/**\n * Validates an H.265 Annex-B access unit.\n * Checks that it has start codes and contains valid NAL unit types.\n */\nexport function isValidH265AnnexBAccessUnit(annexB: Buffer): boolean {\n if (!hasStartCodes(annexB)) return false;\n const nals = splitAnnexBToNalPayloads(annexB);\n if (nals.length === 0) return false;\n for (const nal of nals) {\n if (nal.length < 2) return false; // H.265 NAL header is at least 2 bytes\n const b0 = nal[0];\n if (b0 === undefined) return false;\n if ((b0 & 0x80) !== 0) return false; // forbidden_zero_bit\n const nalType = getH265NalType(nal);\n if (nalType === null) return false;\n // Valid H.265 NAL unit types: 0-40 (some are reserved)\n if (nalType > 40) return false;\n }\n return true;\n}\n\n/**\n * Checks if an H.265 Annex-B access unit is a keyframe (IRAP picture).\n * A keyframe should contain VPS, SPS, PPS, and an IRAP picture.\n */\nexport function isH265KeyframeAnnexB(annexB: Buffer): boolean {\n const nals = splitAnnexBToNalPayloads(annexB);\n let hasVps = false;\n let hasSps = false;\n let hasPps = false;\n let hasIrap = false;\n \n for (const nal of nals) {\n const nalType = getH265NalType(nal);\n if (nalType === null) continue;\n \n if (nalType === 32) hasVps = true; // VPS\n if (nalType === 33) hasSps = true; // SPS\n if (nalType === 34) hasPps = true; // PPS\n if (isH265Irap(nalType)) hasIrap = true; // IRAP picture\n }\n \n // A keyframe should have at least VPS, SPS, PPS, and an IRAP picture\n return hasIrap && hasVps && hasSps && hasPps;\n}\n\n/**\n * Extracts VPS (Video Parameter Set) from H.265 Annex-B data.\n * Returns the VPS NAL payload (without start code) or null if not found.\n */\nexport function extractVpsFromAnnexB(annexB: Buffer): Buffer | null {\n const nals = splitAnnexBToNalPayloads(annexB);\n for (const nal of nals) {\n const nalType = getH265NalType(nal);\n if (nalType === 32) { // VPS\n return nal;\n }\n }\n return null;\n}\n\n/**\n * Extracts SPS (Sequence Parameter Set) from H.265 Annex-B data.\n * Returns the SPS NAL payload (without start code) or null if not found.\n */\nexport function extractSpsFromAnnexB(annexB: Buffer): Buffer | null {\n const nals = splitAnnexBToNalPayloads(annexB);\n for (const nal of nals) {\n const nalType = getH265NalType(nal);\n if (nalType === 33) { // SPS\n return nal;\n }\n }\n return null;\n}\n\n/**\n * Extracts PPS (Picture Parameter Set) from H.265 Annex-B data.\n * Returns the PPS NAL payload (without start code) or null if not found.\n */\nexport function extractPpsFromAnnexB(annexB: Buffer): Buffer | null {\n const nals = splitAnnexBToNalPayloads(annexB);\n for (const nal of nals) {\n const nalType = getH265NalType(nal);\n if (nalType === 34) { // PPS\n return nal;\n }\n }\n return null;\n}\n\n","import type { Logger } from \"../../debug/DebugConfig\";\nimport { BcMediaCodec } from \"./BcMediaCodec\";\nimport type {\n BcMedia,\n BcMediaAac,\n BcMediaAdpcm,\n BcMediaInfoV1,\n BcMediaInfoV2,\n} from \"./BcMediaParser\";\nimport {\n convertToAnnexB as convertH264ToAnnexB,\n isH264KeyframeAnnexB,\n splitAnnexBToNalPayloads as splitH264AnnexBToNalPayloads,\n hasStartCodes as hasH264StartCodes,\n} from \"./H264Converter\";\nimport {\n convertToAnnexB as convertH265ToAnnexB,\n extractPpsFromAnnexB,\n extractSpsFromAnnexB,\n extractVpsFromAnnexB,\n isH265KeyframeAnnexB,\n} from \"./H265Converter\";\n\nconst ANNEXB_START_CODE_4B = Buffer.from([0x00, 0x00, 0x00, 0x01]);\n\nexport type BcMediaVideoType = \"H264\" | \"H265\";\nexport type BcMediaAudioType = \"Aac\" | \"Adpcm\";\n\n/**\n * Detect the actual video codec from raw NAL data.\n * Some cameras report wrong codec (e.g. \"H264\" but send H.265 data).\n * This function analyzes the NAL header to determine the real codec.\n *\n * @param data - Raw video data (either Annex-B or length-prefixed)\n * @returns Detected codec type or null if detection fails\n */\nexport function detectVideoCodecFromNal(data: Buffer): BcMediaVideoType | null {\n if (!data || data.length < 5) return null;\n\n // Find start code (00 00 00 01 or 00 00 01)\n let nalStart = -1;\n for (let i = 0; i < Math.min(data.length - 4, 100); i++) {\n if (data[i] === 0 && data[i + 1] === 0) {\n if (data[i + 2] === 0 && data[i + 3] === 1) {\n nalStart = i + 4;\n break;\n }\n if (data[i + 2] === 1) {\n nalStart = i + 3;\n break;\n }\n }\n }\n\n // If no start code, try length-prefixed (AVCC/HVCC)\n if (nalStart < 0 && data.length >= 5) {\n // Try 4-byte length prefix\n const len = data.readUInt32BE(0);\n if (len > 0 && len < data.length - 4) {\n nalStart = 4;\n }\n }\n\n if (nalStart < 0 || nalStart >= data.length) return null;\n\n const nalByte = data[nalStart];\n if (nalByte === undefined) return null;\n\n // IMPORTANT: Check H.264 FIRST because H.264 NAL bytes can be misinterpreted as H.265.\n // Example: H.264 NAL byte 0x41 (non-IDR slice, nal_ref_idc=2) would be interpreted as\n // H.265 VPS (type 32) if we check H.265 first. Similarly, 0x21 would appear as H.265 BLA.\n\n // H.264/AVC: forbidden_zero_bit (bit 7) = 0, nal_ref_idc in bits 5-6, nal_unit_type in bits 0-4\n // NAL types: SPS=7, PPS=8, IDR=5, non-IDR=1\n const forbiddenBit264 = (nalByte >> 7) & 1;\n const h264Type = nalByte & 0x1f;\n\n if (forbiddenBit264 === 0 && h264Type > 0 && h264Type <= 12) {\n // Strong H.264 indicators: SPS, PPS (these types don't exist in H.265 in the same form)\n if (h264Type === 7 || h264Type === 8) {\n return \"H264\";\n }\n // IDR slice (type 5) - H.265 type 5 is RADL_R which is rare without VPS/SPS/PPS\n if (h264Type === 5) {\n return \"H264\";\n }\n // Non-IDR slice (type 1) - need to be more careful here as it could be H.265 TRAIL_R\n // For H.264 non-IDR, nal_ref_idc (bits 5-6) is typically non-zero for reference frames\n // For H.265, the second byte's bits 0-2 contain temporal_id_plus1 which must be 1-7\n if (h264Type === 1) {\n // Additional heuristic: if nal_ref_idc is 2 or 3, strongly prefer H.264\n // H.265 TRAIL_R (type 1) with forbidden=0 would have nalByte values 0x02 or 0x03\n // H.264 non-IDR with nal_ref_idc=2 has nalByte 0x41, with nal_ref_idc=1 has 0x21\n const nalRefIdc = (nalByte >> 5) & 0x03;\n if (nalRefIdc >= 1) {\n return \"H264\";\n }\n }\n }\n\n // H.265/HEVC: forbidden_zero_bit (bit 7) = 0, nal_unit_type in bits 1-6\n // NAL types: VPS=32, SPS=33, PPS=34, IDR=19/20, CRA=21, TRAIL=0/1\n // The second byte contains temporal_id_plus1 in bits 0-2 (must be non-zero)\n if (nalStart + 1 < data.length) {\n const nalByte2 = data[nalStart + 1];\n if (nalByte2 !== undefined) {\n const forbiddenBit = (nalByte >> 7) & 1;\n const hevcType = (nalByte >> 1) & 0x3f;\n const temporalId = nalByte2 & 0x07;\n\n // Valid H.265 NAL: forbidden=0, temporal_id_plus1 > 0, type in valid range\n if (forbiddenBit === 0 && temporalId > 0 && hevcType <= 40) {\n // Strong H.265 indicators: VPS, SPS, PPS (unique to H.265)\n if (hevcType === 32 || hevcType === 33 || hevcType === 34) {\n return \"H265\";\n }\n // IDR_W_RADL, IDR_N_LP, CRA_NUT\n if (hevcType === 19 || hevcType === 20 || hevcType === 21) {\n return \"H265\";\n }\n // TRAIL_N (0), TRAIL_R (1) - but only if we didn't already match H.264\n // These are weak indicators since H.264 type 1 is common\n if (hevcType <= 1 && nalByte <= 0x03) {\n // H.265 TRAIL with low NAL byte values (0x00-0x03) are more likely H.265\n return \"H265\";\n }\n }\n }\n }\n\n return null;\n}\n\nexport type BcMediaAnnexBInfo = {\n type: \"InfoV1\" | \"InfoV2\";\n videoWidth: number;\n videoHeight: number;\n fps: number;\n startYear: number;\n startMonth: number;\n startDay: number;\n startHour: number;\n startMin: number;\n startSeconds: number;\n endYear: number;\n endMonth: number;\n endDay: number;\n endHour: number;\n endMin: number;\n endSeconds: number;\n};\n\n/**\n * Audio frame emitted by the decoder\n */\nexport type BcMediaAudioFrame = {\n /** Audio codec type */\n audioType: BcMediaAudioType;\n /** Raw audio data (AAC ADTS frames or ADPCM samples) */\n data: Buffer;\n};\n\n/**\n * Video frame emitted by the decoder\n */\nexport type BcMediaVideoFrame = {\n videoType: BcMediaVideoType;\n annexB: Buffer;\n microseconds: number;\n isKeyframe: boolean;\n};\n\nexport type BcMediaAnnexBDecoderStats = {\n bytesIn: number;\n bytesOut: number;\n audioBytesOut: number;\n packets: number;\n videoPackets: number;\n audioPackets: number;\n aacPackets: number;\n adpcmPackets: number;\n keyframes: number;\n videoType: BcMediaVideoType | null;\n audioType: BcMediaAudioType | null;\n infos: BcMediaAnnexBInfo[];\n recoveredSkips: number;\n};\n\nexport class BcMediaAnnexBDecoder {\n private readonly codec: BcMediaCodec;\n private readonly logger: Logger | undefined;\n private readonly onVideoAccessUnit:\n | ((p: BcMediaVideoFrame) => void)\n | undefined;\n private readonly onAudioFrame: ((p: BcMediaAudioFrame) => void) | undefined;\n\n private stats: BcMediaAnnexBDecoderStats = {\n bytesIn: 0,\n bytesOut: 0,\n audioBytesOut: 0,\n packets: 0,\n videoPackets: 0,\n audioPackets: 0,\n aacPackets: 0,\n adpcmPackets: 0,\n keyframes: 0,\n videoType: null,\n audioType: null,\n infos: [],\n recoveredSkips: 0,\n };\n\n private lastH264Sps: Buffer | null = null;\n private lastH264Pps: Buffer | null = null;\n\n private lastH265Vps: Buffer | null = null;\n private lastH265Sps: Buffer | null = null;\n private lastH265Pps: Buffer | null = null;\n\n constructor(params?: {\n strict?: boolean;\n logger?: Logger;\n onVideoAccessUnit?: (p: BcMediaVideoFrame) => void;\n onAudioFrame?: (p: BcMediaAudioFrame) => void;\n }) {\n this.logger = params?.logger;\n this.onVideoAccessUnit = params?.onVideoAccessUnit;\n this.onAudioFrame = params?.onAudioFrame;\n // Default non-strict: cmd143 often has preamble/junk before first magic.\n this.codec = new BcMediaCodec(params?.strict ?? false, this.logger);\n }\n\n getStats(): BcMediaAnnexBDecoderStats {\n return { ...this.stats, infos: [...this.stats.infos] };\n }\n\n /**\n * Push arbitrary bytes from a Baichuan/BcMedia transport into the decoder.\n * Emits complete Annex-B access units via callback.\n */\n push(chunk: Buffer): void {\n if (!chunk || chunk.length === 0) return;\n this.stats.bytesIn += chunk.length;\n\n const packets = this.codec.decode(chunk);\n this.stats.packets += packets.length;\n\n for (const media of packets) {\n this.handleMedia(media);\n }\n }\n\n private handleMedia(media: BcMedia): void {\n if (media.type === \"InfoV1\" || media.type === \"InfoV2\") {\n const info = media as BcMediaInfoV1 | BcMediaInfoV2;\n this.stats.infos.push({\n type: media.type,\n videoWidth: info.videoWidth,\n videoHeight: info.videoHeight,\n fps: info.fps,\n startYear: info.startYear,\n startMonth: info.startMonth,\n startDay: info.startDay,\n startHour: info.startHour,\n startMin: info.startMin,\n startSeconds: info.startSeconds,\n endYear: info.endYear,\n endMonth: info.endMonth,\n endDay: info.endDay,\n endHour: info.endHour,\n endMin: info.endMin,\n endSeconds: info.endSeconds,\n });\n return;\n }\n\n // Handle audio packets\n if (media.type === \"Aac\" || media.type === \"Adpcm\") {\n const audioMedia = media as BcMediaAac | BcMediaAdpcm;\n this.stats.audioPackets++;\n\n if (media.type === \"Aac\") {\n this.stats.aacPackets++;\n } else {\n this.stats.adpcmPackets++;\n }\n\n // Track first audio type\n if (this.stats.audioType == null) {\n this.stats.audioType = media.type;\n }\n\n // Emit audio frame if callback is registered\n if (this.onAudioFrame) {\n this.stats.audioBytesOut += audioMedia.data.length;\n this.onAudioFrame({\n audioType: media.type,\n data: audioMedia.data,\n });\n }\n return;\n }\n\n if (media.type !== \"Iframe\" && media.type !== \"Pframe\") return;\n\n this.stats.videoPackets++;\n\n const microseconds = media.microseconds;\n const raw = media.data;\n\n // Detect actual video type from NAL data (some cameras report wrong codec)\n let videoType: BcMediaVideoType = media.videoType;\n const detectedType = detectVideoCodecFromNal(raw);\n if (detectedType != null && detectedType !== videoType) {\n // Camera reported wrong codec - use detected one\n this.logger?.debug?.(\n `[BcMediaAnnexBDecoder] Codec mismatch: reported ${videoType}, detected ${detectedType}`,\n );\n videoType = detectedType;\n }\n\n if (this.stats.videoType == null) this.stats.videoType = videoType;\n\n let annexB =\n videoType === \"H265\"\n ? convertH265ToAnnexB(raw)\n : convertH264ToAnnexB(raw);\n\n const isKeyframe =\n media.type === \"Iframe\" ||\n (videoType === \"H265\"\n ? isH265KeyframeAnnexB(annexB)\n : isH264KeyframeAnnexB(annexB));\n\n // Update caches from the current access unit.\n if (videoType === \"H264\") {\n const nals = splitH264AnnexBToNalPayloads(annexB);\n for (const nal of nals) {\n const t = (nal[0] ?? 0) & 0x1f;\n if (t === 7) this.lastH264Sps = nal;\n if (t === 8) this.lastH264Pps = nal;\n }\n\n // If this looks like a keyframe but is missing SPS/PPS, prepend cached ones.\n if (isKeyframe) {\n const hasSps = nals.some((nal) => ((nal[0] ?? 0) & 0x1f) === 7);\n const hasPps = nals.some((nal) => ((nal[0] ?? 0) & 0x1f) === 8);\n const toPrepend: Buffer[] = [];\n if (!hasSps && this.lastH264Sps)\n toPrepend.push(ANNEXB_START_CODE_4B, this.lastH264Sps);\n if (!hasPps && this.lastH264Pps)\n toPrepend.push(ANNEXB_START_CODE_4B, this.lastH264Pps);\n if (toPrepend.length > 0)\n annexB = Buffer.concat([...toPrepend, annexB]);\n }\n } else {\n const vps = extractVpsFromAnnexB(annexB);\n const sps = extractSpsFromAnnexB(annexB);\n const pps = extractPpsFromAnnexB(annexB);\n if (vps) this.lastH265Vps = vps;\n if (sps) this.lastH265Sps = sps;\n if (pps) this.lastH265Pps = pps;\n\n if (isKeyframe) {\n const hasVps = vps != null;\n const hasSps = sps != null;\n const hasPps = pps != null;\n const toPrepend: Buffer[] = [];\n if (!hasVps && this.lastH265Vps)\n toPrepend.push(ANNEXB_START_CODE_4B, this.lastH265Vps);\n if (!hasSps && this.lastH265Sps)\n toPrepend.push(ANNEXB_START_CODE_4B, this.lastH265Sps);\n if (!hasPps && this.lastH265Pps)\n toPrepend.push(ANNEXB_START_CODE_4B, this.lastH265Pps);\n if (toPrepend.length > 0)\n annexB = Buffer.concat([...toPrepend, annexB]);\n }\n }\n\n if (isKeyframe) this.stats.keyframes++;\n\n this.stats.bytesOut += annexB.length;\n this.onVideoAccessUnit?.({ videoType, annexB, microseconds, isKeyframe });\n }\n}\n"],"mappings":"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","names":["fs","path","magic","NAL_START_CODE_4B","NAL_START_CODE_3B","hasStartCodes","tryConvertWithLengthReader","tryConvertWithLengthReader16","tryConvertWithLengthReader24","convertToAnnexB","splitAnnexBToNalPayloads","convertToAnnexB","NAL_START_CODE_4B","chosen","best","rawScore","splitAnnexBToNalPayloads","convertToAnnexB"]}
|