@apocaliss92/nodelink-js 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/dist/{DiagnosticsTools-MTXG65O3.js → DiagnosticsTools-EC7DADEQ.js} +2 -2
- package/dist/{chunk-MC2BRLLE.js → chunk-TZFZ5WJX.js} +71 -9
- package/dist/chunk-TZFZ5WJX.js.map +1 -0
- package/dist/{chunk-JMT75JNG.js → chunk-YUBYINJF.js} +674 -64
- package/dist/chunk-YUBYINJF.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +740 -68
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.d.cts +1 -0
- package/dist/cli/rtsp-server.d.ts +1 -0
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +3293 -248
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8187 -0
- package/dist/index.d.ts +761 -1
- package/dist/index.js +2359 -5
- package/dist/index.js.map +1 -1
- package/package.json +14 -3
- package/dist/chunk-JMT75JNG.js.map +0 -1
- package/dist/chunk-MC2BRLLE.js.map +0 -1
- /package/dist/{DiagnosticsTools-MTXG65O3.js.map → DiagnosticsTools-EC7DADEQ.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/debug/DiagnosticsTools.ts","../src/debug/DebugConfig.ts","../src/reolink/baichuan/recordingFileName.ts","../src/reolink/http/ReolinkHttpClient.ts","../src/reolink/cgi/ReolinkCgiApi.ts","../src/debug/zip.ts","../src/baichuan/stream/BaichuanVideoStream.ts","../src/protocol/crypto.ts","../src/protocol/constants.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","../src/rtsp/urls.ts","../src/protocol/xml.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nimport type { ReolinkBaichuanApi } from \"../reolink/baichuan/ReolinkBaichuanApi\";\nimport type { NativeVideoStreamVariant } from \"../reolink/baichuan/types\";\nimport type {\n ReolinkCgiApi,\n DeviceInputData,\n} from \"../reolink/cgi/ReolinkCgiApi\";\nimport type { ReolinkHttpClientOptions } from \"../reolink/http/ReolinkHttpClient\";\nimport { ReolinkCgiApi as ReolinkCgiApiImpl } from \"../reolink/cgi/ReolinkCgiApi\";\nimport { join } from \"node:path\";\nimport { zipDirectory } from \"./zip\";\nimport { BaichuanVideoStream } from \"../baichuan/stream/BaichuanVideoStream\";\nimport type { StreamProfile } from \"../reolink/baichuan/types\";\nimport { buildRtspUrl } from \"../rtsp/urls\";\nimport { splitAnnexBToNalPayloads } from \"../baichuan/stream/H264Converter\";\nimport {\n getH265NalType,\n splitAnnexBToNalPayloads as splitH265AnnexBToNalPayloads,\n} from \"../baichuan/stream/H265Converter\";\nimport type { Logger } from \"./DebugConfig\";\nimport {\n BC_CLASS_MODERN_24,\n BC_CLASS_MODERN_24_ALT,\n BC_CMD_ID_VIDEO,\n} from \"../protocol/constants\";\nimport type { BaichuanFrame } from \"../protocol/framing\";\nimport {\n buildChannelExtensionXml,\n buildPreviewStopXml,\n buildPreviewStopXmlV11,\n buildPreviewXml,\n buildPreviewXmlV11,\n} from \"../protocol/xml\";\n\nexport type DiagnosticsStreamKind = \"native\" | \"rtsp\" | \"rtmp\";\n\nexport type DiagnosticsCollectorResult<T> =\n | { ok: true; value: T }\n | { ok: false; error: string };\n\nfunction safeStringifyError(error: unknown): string {\n if (error instanceof Error) return error.stack || error.message;\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n\nasync function tryCall<T>(\n fn: () => Promise<T>,\n): Promise<DiagnosticsCollectorResult<T>> {\n try {\n return { ok: true, value: await fn() };\n } catch (e) {\n return { ok: false, error: safeStringifyError(e) };\n }\n}\n\nfunction mkdirp(dir: string): void {\n fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction writeJson(filePath: string, obj: unknown): void {\n mkdirp(path.dirname(filePath));\n fs.writeFileSync(filePath, JSON.stringify(obj, null, 2));\n}\n\nfunction appendNdjson(filePath: string, obj: unknown): void {\n mkdirp(path.dirname(filePath));\n fs.appendFileSync(filePath, JSON.stringify(obj) + \"\\n\");\n}\n\nfunction sleepMs(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction nowIsoCompact(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nfunction nalTypesSummary(\n videoType: \"H264\" | \"H265\",\n accessUnitAnnexB: Buffer,\n): number[] {\n if (videoType === \"H264\") {\n const nals = splitAnnexBToNalPayloads(accessUnitAnnexB);\n return nals.map((n) => (n[0] ?? 0) & 0x1f);\n }\n const nals = splitH265AnnexBToNalPayloads(accessUnitAnnexB);\n return nals\n .map((nal: Buffer) => getH265NalType(nal))\n .filter((t: number | null): t is number => typeof t === \"number\");\n}\n\nfunction normalizeProfiles(\n p: Array<string | undefined | null> | undefined,\n): StreamProfile[] {\n const out: StreamProfile[] = [];\n for (const v of p ?? []) {\n if (v === \"main\" || v === \"sub\" || v === \"ext\") {\n if (!out.includes(v)) out.push(v);\n }\n }\n return out;\n}\n\nexport async function collectNativeDiagnostics(params: {\n api: ReolinkBaichuanApi;\n channel?: number;\n}): Promise<Record<string, unknown>> {\n const { api } = params;\n const channel = params.channel ?? 0;\n\n const result: Record<string, unknown> = {\n kind: \"native\",\n channel,\n transport: api.client.getTransport?.(),\n encKind: api.client.enc?.kind,\n collectedAt: new Date().toISOString(),\n };\n\n const [\n info,\n ports,\n streamMetadata,\n abilities,\n capabilities,\n talkAbility,\n twoWayAudio,\n ] = await Promise.all([\n tryCall(() => api.getInfo(channel)),\n tryCall(() => api.getPorts()),\n tryCall(() => api.getStreamMetadata(channel)),\n tryCall(() => api.getAbilityInfo()),\n tryCall(() => api.getDeviceCapabilities(channel)),\n tryCall(() => api.getTalkAbility(channel)),\n tryCall(() => api.getTwoWayAudioConfig(channel)),\n ]);\n\n result.info = info;\n result.ports = ports;\n result.streamMetadata = streamMetadata;\n result.abilityInfo = abilities;\n result.deviceCapabilities = capabilities;\n result.talkAbility = talkAbility;\n result.twoWayAudio = twoWayAudio;\n\n // Convenience: derive a \"recommended\" config (no side effects) from TalkAbility.\n if (talkAbility?.ok && talkAbility.value) {\n const v: any = talkAbility.value as any;\n const recommended = {\n duplex: Array.isArray(v.duplexList) ? v.duplexList[0] : undefined,\n audioStreamMode: Array.isArray(v.audioStreamModeList)\n ? v.audioStreamModeList[0]\n : undefined,\n audioConfig: Array.isArray(v.audioConfigList)\n ? v.audioConfigList[0]\n : undefined,\n };\n result.intercomRecommended = recommended;\n }\n\n return result;\n}\n\nexport async function collectCgiDiagnostics(params: {\n cgi: ReolinkCgiApi;\n channel?: number;\n}): Promise<Record<string, unknown>> {\n const { cgi } = params;\n const channel = params.channel ?? 0;\n\n const result: Record<string, unknown> = {\n kind: \"cgi\",\n channel,\n collectedAt: new Date().toISOString(),\n };\n\n const [info, netPort, ability, enc, chStatus, chnType, aiState] =\n await Promise.all([\n tryCall(() => cgi.getInfo(channel)),\n tryCall(() => cgi.GetNetPort()),\n tryCall(() => cgi.GetAbility()),\n tryCall(() => cgi.GetEnc(channel)),\n tryCall(() => cgi.GetChannelstatus()),\n tryCall(() => cgi.GetChnTypeInfo(channel)),\n tryCall(() => cgi.GetAiState(channel)),\n ]);\n\n result.info = info;\n result.netPort = netPort;\n result.ability = ability;\n result.enc = enc;\n result.channelStatus = chStatus;\n result.channelTypeInfo = chnType;\n result.aiState = aiState;\n\n // Batch helper includes raw requestBody/response: super useful for user diagnostics.\n result.devicesInfo = await tryCall(() => cgi.getDevicesInfo());\n\n return result;\n}\n\nexport async function createDiagnosticsBundle(params: {\n outDir: string;\n native?: { api: ReolinkBaichuanApi; channel?: number };\n cgi?: { cgi: ReolinkCgiApi; channel?: number };\n extra?: Record<string, unknown>;\n}): Promise<{ outDir: string; diagnosticsPath: string }> {\n const outDir = params.outDir;\n mkdirp(outDir);\n\n const [native, cgi] = await Promise.all([\n params.native\n ? tryCall(() => collectNativeDiagnostics(params.native!))\n : Promise.resolve(undefined),\n params.cgi\n ? tryCall(() => collectCgiDiagnostics(params.cgi!))\n : Promise.resolve(undefined),\n ]);\n\n const diagnostics = {\n createdAt: new Date().toISOString(),\n native,\n cgi,\n ...(params.extra ? { extra: params.extra } : {}),\n };\n\n const diagnosticsPath = path.join(outDir, \"diagnostics.json\");\n writeJson(diagnosticsPath, diagnostics);\n\n return { outDir, diagnosticsPath };\n}\n\nexport type StreamSamplingSelection = {\n kinds: DiagnosticsStreamKind[];\n profiles?: StreamProfile[];\n};\n\nexport type StreamSamplingOptions = {\n outDir: string;\n durationSeconds: number;\n snapshotIntervalSeconds?: number;\n channel?: number;\n selection: StreamSamplingSelection;\n\n /** Optional logger for human-readable progress logs. */\n logger?: Logger;\n\n // RTSP: if enabled, build the direct-camera RTSP URL.\n rtsp?: {\n host: string;\n username: string;\n password: string;\n port?: number;\n };\n\n // RTMP: optional explicit URLs per profile.\n rtmp?: {\n urlsByProfile: Partial<Record<StreamProfile, string>>;\n };\n\n // Native: required when \"native\" is selected.\n native?: {\n api: ReolinkBaichuanApi;\n };\n\n // Limits to avoid runaway dumps.\n limits?: {\n maxNativeRawFrames?: number;\n maxNativeRawBytes?: number;\n };\n};\n\ntype FfmpegResult = { ok: true } | { ok: false; error: string };\n\n/**\n * Sanitize error messages by removing credentials from URLs.\n * Replaces patterns like \"rtsp://username:password@host\" with \"rtsp://***:***@host\"\n */\nfunction sanitizeFfmpegError(error: string): string {\n // Replace credentials in URLs (rtsp://, rtmp://, http://, https://)\n return error.replace(\n /([a-z]+:\\/\\/)([^:@/\\s]+):([^@/\\s]+)@/gi,\n (match, protocol, username, password) => {\n // Keep the protocol, but hide username and password\n return `${protocol}***:***@`;\n },\n );\n}\n\nfunction spawnFfmpeg(args: string[], logPath: string): Promise<FfmpegResult> {\n return new Promise((resolve) => {\n mkdirp(path.dirname(logPath));\n const logStream = fs.createWriteStream(logPath, { flags: \"a\" });\n\n const p = spawn(\"ffmpeg\", args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n p.on(\"error\", (e) => {\n logStream.write(\n `ffmpeg spawn error: ${e instanceof Error ? e.message : String(e)}\\n`,\n );\n logStream.end();\n resolve({ ok: false, error: e instanceof Error ? e.message : String(e) });\n });\n\n let stderr = \"\";\n p.stderr.on(\"data\", (d: Buffer) => {\n const s = d.toString();\n stderr += s;\n // Sanitize credentials before writing to log file\n logStream.write(sanitizeFfmpegError(s));\n });\n\n p.on(\"close\", (code) => {\n logStream.end();\n if (code === 0) {\n resolve({ ok: true });\n return;\n }\n const errorMsg = `ffmpeg exited with code ${code}\\n${stderr.slice(-4000)}`;\n resolve({ ok: false, error: sanitizeFfmpegError(errorMsg) });\n });\n });\n}\n\ntype FfprobeVideoInfo = {\n codecName?: string;\n codecLongName?: string;\n width?: number;\n height?: number;\n avgFrameRate?: string;\n rFrameRate?: string;\n pixFmt?: string;\n};\n\nfunction spawnFfprobeJson(\n args: string[],\n logPath: string,\n): Promise<{ ok: true; json: any } | { ok: false; error: string }> {\n return new Promise((resolve) => {\n mkdirp(path.dirname(logPath));\n const logStream = fs.createWriteStream(logPath, { flags: \"a\" });\n\n const p = spawn(\"ffprobe\", args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n p.on(\"error\", (e) => {\n const msg = e instanceof Error ? e.message : String(e);\n logStream.write(`ffprobe spawn error: ${msg}\\n`);\n logStream.end();\n resolve({ ok: false, error: msg });\n });\n\n let stdout = \"\";\n let stderr = \"\";\n p.stdout.on(\"data\", (d: Buffer) => {\n stdout += d.toString();\n });\n p.stderr.on(\"data\", (d: Buffer) => {\n const s = d.toString();\n stderr += s;\n logStream.write(sanitizeFfmpegError(s));\n });\n\n p.on(\"close\", (code) => {\n logStream.end();\n if (code === 0) {\n try {\n const json = JSON.parse(stdout || \"{}\");\n resolve({ ok: true, json });\n } catch (e) {\n resolve({\n ok: false,\n error: `ffprobe JSON parse failed: ${e instanceof Error ? e.message : String(e)}`,\n });\n }\n return;\n }\n\n resolve({\n ok: false,\n error: sanitizeFfmpegError(\n `ffprobe exited with code ${code}\\n${stderr.slice(-2000)}`,\n ),\n });\n });\n });\n}\n\nasync function probeVideoInfo(params: {\n url: string;\n kind: \"rtsp\" | \"rtmp\";\n logPath: string;\n}): Promise<\n { ok: true; info: FfprobeVideoInfo } | { ok: false; error: string }\n> {\n const args = [\n \"-v\",\n \"error\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-print_format\",\n \"json\",\n \"-show_streams\",\n \"-select_streams\",\n \"v:0\",\n params.url,\n ];\n\n const res = await spawnFfprobeJson(args, params.logPath);\n if (!res.ok) return res;\n\n const streams = Array.isArray(res.json?.streams) ? res.json.streams : [];\n const s0 = streams[0] ?? undefined;\n const info: FfprobeVideoInfo = {\n codecName: typeof s0?.codec_name === \"string\" ? s0.codec_name : undefined,\n codecLongName:\n typeof s0?.codec_long_name === \"string\" ? s0.codec_long_name : undefined,\n width: typeof s0?.width === \"number\" ? s0.width : undefined,\n height: typeof s0?.height === \"number\" ? s0.height : undefined,\n avgFrameRate:\n typeof s0?.avg_frame_rate === \"string\" ? s0.avg_frame_rate : undefined,\n rFrameRate:\n typeof s0?.r_frame_rate === \"string\" ? s0.r_frame_rate : undefined,\n pixFmt: typeof s0?.pix_fmt === \"string\" ? s0.pix_fmt : undefined,\n };\n\n return { ok: true, info };\n}\n\nasync function recordRtspOrRtmpToFile(params: {\n kind: \"rtsp\" | \"rtmp\";\n url: string;\n outputPath: string;\n durationSeconds: number;\n logPath: string;\n}): Promise<FfmpegResult> {\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(params.durationSeconds),\n \"-map\",\n \"0:v:0\",\n \"-map\",\n \"0:a:0?\",\n \"-c\",\n \"copy\",\n \"-y\",\n params.outputPath,\n ];\n\n return await spawnFfmpeg(args, params.logPath);\n}\n\n/**\n * Test stream availability with ffmpeg (short session).\n * Returns success if ffmpeg can connect and receive data.\n */\nasync function testStreamWithFfmpeg(params: {\n url: string;\n kind: \"rtsp\" | \"rtmp\";\n durationSeconds?: number;\n}): Promise<DiagnosticsCollectorResult<{ duration: number }>> {\n const duration = params.durationSeconds ?? 2;\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(duration),\n \"-f\",\n \"null\",\n \"-\", // Output to null (we just want to test connection)\n ];\n\n return new Promise((resolve) => {\n const p = spawn(\"ffmpeg\", args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n let hasData = false;\n\n p.stderr.on(\"data\", (d: Buffer) => {\n const s = d.toString();\n stderr += s;\n // Check for successful connection indicators\n if (\n s.includes(\"Stream #0\") ||\n s.includes(\"Video:\") ||\n s.includes(\"Audio:\") ||\n s.includes(\"Duration:\")\n ) {\n hasData = true;\n }\n });\n\n p.on(\"close\", (code) => {\n if (code === 0 || hasData) {\n resolve({ ok: true, value: { duration } });\n } else {\n const errorMsg = `ffmpeg exited with code ${code}\\n${stderr.slice(-1000)}`;\n resolve({ ok: false, error: sanitizeFfmpegError(errorMsg) });\n }\n });\n\n p.on(\"error\", (e) => {\n const errorMsg = e instanceof Error ? e.message : String(e);\n resolve({ ok: false, error: sanitizeFfmpegError(errorMsg) });\n });\n });\n}\n\nasync function recordRtspOrRtmp(params: {\n kind: \"rtsp\" | \"rtmp\";\n url: string;\n outputMp4: string;\n durationSeconds: number;\n logPath: string;\n}): Promise<FfmpegResult> {\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(params.durationSeconds),\n \"-c\",\n \"copy\",\n \"-y\",\n params.outputMp4,\n ];\n\n return await spawnFfmpeg(args, params.logPath);\n}\n\nasync function snapshotsRtspOrRtmp(params: {\n kind: \"rtsp\" | \"rtmp\";\n url: string;\n snapshotsPattern: string;\n durationSeconds: number;\n snapshotIntervalSeconds: number;\n logPath: string;\n}): Promise<FfmpegResult> {\n const fpsExpr = `fps=1/${Math.max(0.25, params.snapshotIntervalSeconds)}`;\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(params.durationSeconds),\n \"-vf\",\n fpsExpr,\n \"-q:v\",\n \"2\",\n \"-y\",\n params.snapshotsPattern,\n ];\n\n return await spawnFfmpeg(args, params.logPath);\n}\n\nasync function tryJpegFromAnnexB(params: {\n videoType: \"H264\" | \"H265\";\n snapshotAnnexBPath: string;\n outputJpegPath: string;\n logPath: string;\n}): Promise<void> {\n // Best-effort: this is optional for user diagnostics.\n // ffmpeg can decode raw Annex-B elementary streams with -f h264/-f hevc.\n const fmt = params.videoType === \"H265\" ? \"hevc\" : \"h264\";\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n \"-f\",\n fmt,\n \"-i\",\n params.snapshotAnnexBPath,\n \"-frames:v\",\n \"1\",\n \"-q:v\",\n \"2\",\n \"-y\",\n params.outputJpegPath,\n ];\n\n const res = await spawnFfmpeg(args, params.logPath);\n void res;\n}\n\nexport async function sampleStreams(\n opts: StreamSamplingOptions,\n): Promise<void> {\n const channel = opts.channel ?? 0;\n const durationMs = Math.max(250, Math.round(opts.durationSeconds * 1000));\n const snapshotIntervalSeconds = opts.snapshotIntervalSeconds ?? 2;\n\n const logger = opts.logger;\n const log = (\n level: \"log\" | \"warn\" | \"error\",\n msg: string,\n extra?: unknown,\n ) => {\n const fn = (logger?.[level] ?? logger?.log) as\n | ((...args: any[]) => void)\n | undefined;\n if (!fn) return;\n if (extra !== undefined) fn.call(logger, msg, extra);\n else fn.call(logger, msg);\n };\n\n const profiles = normalizeProfiles(\n opts.selection.profiles,\n ) as StreamProfile[];\n const selectedProfiles: StreamProfile[] = profiles.length\n ? profiles\n : [\"main\", \"sub\", \"ext\"];\n\n const outDir = opts.outDir;\n mkdirp(outDir);\n\n log(\"log\", \"[Diagnostics] stream sampling start\", {\n outDir,\n channel,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n selection: opts.selection,\n });\n\n const eventsPath = path.join(outDir, \"events.ndjson\");\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"sampling_start\",\n channel,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n selection: opts.selection,\n });\n\n for (const profile of selectedProfiles) {\n for (const kind of opts.selection.kinds) {\n const tag = `${kind}_${profile}`;\n const baseDir = path.join(outDir, tag);\n mkdirp(baseDir);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_begin\",\n kind,\n profile,\n channel,\n });\n log(\"log\", \"[Diagnostics] stream begin\", { kind, profile, channel });\n\n if (kind === \"native\") {\n const api = opts.native?.api;\n if (!api) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_skip\",\n kind,\n profile,\n reason: \"native api missing\",\n });\n log(\"warn\", \"[Diagnostics] stream skip (native api missing)\", {\n kind,\n profile,\n channel,\n });\n continue;\n }\n\n const rawDir = path.join(baseDir, \"raw_frames\");\n const snapsDir = path.join(baseDir, \"snapshots\");\n mkdirp(rawDir);\n mkdirp(snapsDir);\n\n const maxRawFrames = opts.limits?.maxNativeRawFrames ?? 400;\n const maxRawBytes = opts.limits?.maxNativeRawBytes ?? 50 * 1024 * 1024;\n let rawFrames = 0;\n let rawBytes = 0;\n\n const expectedStreamType = profile === \"sub\" ? 1 : 0;\n\n const onFrame = (frame: any) => {\n if (!frame?.header) return;\n if (frame.header.cmdId !== 3) return;\n if (frame.header.streamType !== expectedStreamType) return;\n if (rawFrames >= maxRawFrames) return;\n const payload: Buffer =\n Buffer.isBuffer(frame.payload) && frame.payload.length\n ? frame.payload\n : frame.body;\n if (!Buffer.isBuffer(payload) || payload.length === 0) return;\n if (rawBytes + payload.length > maxRawBytes) return;\n\n rawFrames++;\n rawBytes += payload.length;\n\n const idx = String(rawFrames).padStart(6, \"0\");\n const binPath = path.join(rawDir, `frame_${idx}.bin`);\n try {\n fs.writeFileSync(binPath, payload);\n } catch {\n // ignore\n }\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_raw_frame\",\n kind,\n profile,\n idx: rawFrames,\n bytes: payload.length,\n header: frame.header,\n bodyLen: frame.body?.length ?? 0,\n payloadLen: frame.payload?.length ?? 0,\n });\n };\n\n api.client.on(\"frame\", onFrame);\n\n const videoStream = new BaichuanVideoStream({\n client: api.client as any,\n api: api as any,\n channel,\n profile,\n });\n\n const clipBase = path.join(baseDir, `clip_${nowIsoCompact()}`);\n const clipAnnexBPath = clipBase + \".annexb\";\n const clipAudioPath = clipBase + \".audio.bin\";\n const clipInfoPath = clipBase + \".json\";\n\n const videoOut = fs.createWriteStream(clipAnnexBPath, { flags: \"w\" });\n const audioOut = fs.createWriteStream(clipAudioPath, { flags: \"w\" });\n\n let firstVideoType: \"H264\" | \"H265\" | undefined;\n let firstKeyframeAtMs: number | null = null;\n let videoAUs = 0;\n let audioFrames = 0;\n let lastSnapshotAtMs = 0;\n let startedAtMs: number | null = null;\n\n const onAU = (u: any) => {\n if (!u || !Buffer.isBuffer(u.data)) return;\n if (!firstVideoType) firstVideoType = u.videoType;\n\n videoAUs++;\n try {\n videoOut.write(u.data);\n } catch {\n // ignore\n }\n\n if (u.isKeyframe && firstKeyframeAtMs == null) {\n firstKeyframeAtMs = Date.now();\n }\n\n const nalTypes = nalTypesSummary(u.videoType, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_access_unit\",\n kind,\n profile,\n isKeyframe: !!u.isKeyframe,\n videoType: u.videoType,\n microseconds: u.microseconds,\n bytes: u.data.length,\n nalTypes,\n });\n\n const now = Date.now();\n if (\n u.isKeyframe &&\n now - lastSnapshotAtMs >= snapshotIntervalSeconds * 1000\n ) {\n lastSnapshotAtMs = now;\n const snapId = nowIsoCompact();\n const snapAnnex = path.join(\n snapsDir,\n `snap_${snapId}.${u.videoType === \"H265\" ? \"h265\" : \"h264\"}`,\n );\n try {\n fs.writeFileSync(snapAnnex, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_snapshot_saved\",\n kind,\n profile,\n path: snapAnnex,\n });\n\n // Optional: try producing a jpeg (best-effort).\n const snapJpeg = path.join(snapsDir, `snap_${snapId}.jpg`);\n void tryJpegFromAnnexB({\n videoType: u.videoType,\n snapshotAnnexBPath: snapAnnex,\n outputJpegPath: snapJpeg,\n logPath: path.join(baseDir, \"ffmpeg_snapshot.log\"),\n });\n } catch {\n // ignore\n }\n }\n };\n\n const onAudio = (buf: Buffer) => {\n if (!Buffer.isBuffer(buf) || buf.length === 0) return;\n audioFrames++;\n try {\n audioOut.write(buf);\n } catch {\n // ignore\n }\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_audio\",\n kind,\n profile,\n bytes: buf.length,\n });\n };\n\n videoStream.on(\"videoAccessUnit\" as any, onAU as any);\n videoStream.on(\"audioFrame\" as any, onAudio as any);\n videoStream.on(\"error\", (e: any) => {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_error\",\n kind,\n profile,\n error: safeStringifyError(e),\n });\n });\n\n try {\n await videoStream.start();\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_started\",\n kind,\n profile,\n });\n\n startedAtMs = Date.now();\n while (Date.now() - startedAtMs < durationMs) {\n await sleepMs(200);\n }\n } finally {\n try {\n await videoStream.stop();\n } catch {\n // ignore\n }\n\n api.client.removeListener(\"frame\", onFrame);\n\n try {\n videoOut.end();\n } catch {\n // ignore\n }\n try {\n audioOut.end();\n } catch {\n // ignore\n }\n\n const clipInfo = {\n kind,\n profile,\n channel,\n durationSeconds: opts.durationSeconds,\n videoAccessUnits: videoAUs,\n audioFrames,\n videoType: firstVideoType,\n firstKeyframeLatencyMs:\n firstKeyframeAtMs == null || startedAtMs == null\n ? null\n : Math.max(0, firstKeyframeAtMs - startedAtMs),\n rawFrames,\n rawBytes,\n };\n writeJson(clipInfoPath, clipInfo);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_done\",\n ...clipInfo,\n });\n log(\"log\", \"[Diagnostics] stream done\", clipInfo);\n }\n\n continue;\n }\n\n if (kind === \"rtsp\") {\n if (!opts.rtsp) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_skip\",\n kind,\n profile,\n reason: \"rtsp config missing\",\n });\n log(\"warn\", \"[Diagnostics] stream skip (rtsp config missing)\", {\n kind,\n profile,\n channel,\n });\n continue;\n }\n\n const url = buildRtspUrl({\n host: opts.rtsp.host,\n username: opts.rtsp.username,\n password: opts.rtsp.password,\n channel,\n stream: profile,\n ...(opts.rtsp.port != null ? { port: opts.rtsp.port } : {}),\n });\n\n const mp4Path = path.join(baseDir, `clip_${nowIsoCompact()}.mp4`);\n const logPath = path.join(baseDir, \"ffmpeg_record.log\");\n const snapsDir = path.join(baseDir, \"snapshots\");\n mkdirp(snapsDir);\n const snapsPattern = path.join(snapsDir, \"snap_%05d.jpg\");\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtsp_url\",\n kind,\n profile,\n url,\n });\n\n const [recordRes, snapsRes] = await Promise.all([\n recordRtspOrRtmp({\n kind: \"rtsp\",\n url,\n outputMp4: mp4Path,\n durationSeconds: opts.durationSeconds,\n logPath,\n }),\n snapshotsRtspOrRtmp({\n kind: \"rtsp\",\n url,\n snapshotsPattern: snapsPattern,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n logPath: path.join(baseDir, \"ffmpeg_snapshots.log\"),\n }),\n ]);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtsp_done\",\n kind,\n profile,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n log(\"log\", \"[Diagnostics] stream done\", {\n kind,\n profile,\n channel,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n continue;\n }\n\n if (kind === \"rtmp\") {\n const url = opts.rtmp?.urlsByProfile?.[profile];\n if (!url) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_skip\",\n kind,\n profile,\n reason: \"rtmp url missing\",\n });\n log(\"warn\", \"[Diagnostics] stream skip (rtmp url missing)\", {\n kind,\n profile,\n channel,\n });\n continue;\n }\n\n const mp4Path = path.join(baseDir, `clip_${nowIsoCompact()}.mp4`);\n const logPath = path.join(baseDir, \"ffmpeg_record.log\");\n const snapsDir = path.join(baseDir, \"snapshots\");\n mkdirp(snapsDir);\n const snapsPattern = path.join(snapsDir, \"snap_%05d.jpg\");\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtmp_url\",\n kind,\n profile,\n url,\n });\n\n const [recordRes, snapsRes] = await Promise.all([\n recordRtspOrRtmp({\n kind: \"rtmp\",\n url,\n outputMp4: mp4Path,\n durationSeconds: opts.durationSeconds,\n logPath,\n }),\n snapshotsRtspOrRtmp({\n kind: \"rtmp\",\n url,\n snapshotsPattern: snapsPattern,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n logPath: path.join(baseDir, \"ffmpeg_snapshots.log\"),\n }),\n ]);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtmp_done\",\n kind,\n profile,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n log(\"log\", \"[Diagnostics] stream done\", {\n kind,\n profile,\n channel,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n continue;\n }\n }\n }\n\n appendNdjson(eventsPath, { t: Date.now(), type: \"sampling_done\" });\n}\n\n/**\n * Comprehensive NVR/HUB diagnostics function.\n * Collects and prints all available information about the NVR/HUB device and all its channels.\n */\nexport async function collectNvrDiagnostics(params: {\n cgi: ReolinkCgiApi;\n logger?: Logger;\n}): Promise<Record<string, unknown>> {\n const { cgi, logger } = params;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const result: Record<string, unknown> = {\n kind: \"nvr_diagnostics\",\n collectedAt: new Date().toISOString(),\n };\n\n log(\"=\".repeat(80));\n log(\"NVR/HUB DIAGNOSTICS - Starting comprehensive data collection\");\n log(\"=\".repeat(80));\n\n // 1. NVR/HUB Device Information\n log(\"\\n[1/7] Collecting NVR/HUB device information...\");\n const nvrInfo = await tryCall(() => cgi.getNvrInfo());\n result.nvrInfo = nvrInfo;\n if (nvrInfo.ok) {\n log(\"✓ NVR/HUB info collected\", {\n model: nvrInfo.value.devInfo?.type,\n name: nvrInfo.value.devInfo?.name,\n firmware: nvrInfo.value.devInfo?.firmVer,\n hardware: nvrInfo.value.devInfo?.hardVer,\n serial: nvrInfo.value.devInfo?.serial,\n });\n } else {\n log(\"✗ Failed to collect NVR/HUB info\", nvrInfo.error);\n }\n\n // 2. Get all channels\n log(\"\\n[2/7] Discovering channels...\");\n // Use channelNum fallback for multi-focal cameras\n const channelsResult = await tryCall(() =>\n cgi.getChannels({ useChannelNumFallback: true }),\n );\n result.channels = channelsResult;\n let channels: number[] = [];\n if (channelsResult.ok) {\n channels = channelsResult.value.channels;\n log(`✓ Found ${channels.length} channel(s): ${channels.join(\", \")}`);\n result.channelList = channels;\n } else {\n log(\"✗ Failed to discover channels\", channelsResult.error);\n result.channelList = [];\n }\n\n // 3. Devices Information (per-channel: type, AI, encoding)\n log(\"\\n[3/7] Collecting devices information for all channels...\");\n const devicesInfo = await tryCall(() =>\n cgi.getDevicesInfo({ useChannelNumFallback: true }),\n );\n result.devicesInfo = devicesInfo;\n if (devicesInfo.ok) {\n log(\n `✓ Devices info collected for ${Object.keys(devicesInfo.value.devicesData).length} channel(s)`,\n );\n for (const [channel, device] of Object.entries(\n devicesInfo.value.devicesData,\n )) {\n const ch = Number(channel);\n const info = device as any;\n log(` Channel ${ch}:`, {\n model: info.channelInfo?.typeInfo || info.channelStatus?.typeInfo,\n name: info.channelStatus?.name,\n online: info.channelStatus?.online === 1,\n sleeping: info.channelStatus?.sleep === 1,\n uid: info.channelStatus?.uid,\n firmware: info.channelInfo?.firmVer,\n boardInfo: info.channelInfo?.boardInfo,\n });\n }\n } else {\n log(\"✗ Failed to collect devices info\", devicesInfo.error);\n }\n\n // 4. Events and Detection (motion, AI)\n log(\"\\n[4/7] Collecting events and detection states for all channels...\");\n const eventsInfo = await tryCall(() =>\n cgi.getAllChannelsEvents({ useChannelNumFallback: true }),\n );\n result.eventsInfo = eventsInfo;\n if (eventsInfo.ok) {\n log(\n `✓ Events info collected for ${Object.keys(eventsInfo.value.parsed).length} channel(s)`,\n );\n for (const [channel, events] of Object.entries(eventsInfo.value.parsed)) {\n const ch = Number(channel);\n const evt = events as any;\n log(` Channel ${ch}:`, {\n motion: evt.motion,\n detectedObjects: evt.objects,\n });\n }\n } else {\n log(\"✗ Failed to collect events info\", eventsInfo.error);\n }\n\n // 5. Battery Information\n log(\"\\n[5/7] Collecting battery information for all channels...\");\n const batteryInfo = await tryCall(() =>\n cgi.getAllChannelsBatteryInfo({ useChannelNumFallback: true }),\n );\n result.batteryInfo = batteryInfo;\n if (batteryInfo.ok) {\n log(\n `✓ Battery info collected for ${Object.keys(batteryInfo.value.batteryInfoData).length} channel(s)`,\n );\n for (const [channel, battery] of Object.entries(\n batteryInfo.value.batteryInfoData,\n )) {\n const ch = Number(channel);\n const bat = battery as any;\n log(` Channel ${ch}:`, {\n batteryLevel: bat.batteryLevel,\n sleeping: bat.sleeping,\n });\n }\n } else {\n log(\"✗ Failed to collect battery info\", batteryInfo.error);\n }\n\n // 6. Build DeviceInputData map for status info\n log(\"\\n[6/7] Building device capabilities map...\");\n const channelsMap = new Map<number, DeviceInputData>();\n if (devicesInfo.ok && batteryInfo.ok) {\n for (const channel of channels) {\n const device = devicesInfo.value.devicesData[channel];\n const battery = batteryInfo.value.batteryInfoData[channel];\n const abilities = device?.abilities;\n\n channelsMap.set(channel, {\n hasBattery:\n battery?.batteryLevel !== undefined && battery.batteryLevel > 0,\n hasPirEvents: !!(abilities as any)?.pirAlarm,\n hasFloodlight: !!(abilities as any)?.ledCtrl,\n hasPtz: !!(abilities as any)?.ptz,\n sleeping: battery?.sleeping ?? false,\n });\n }\n log(`✓ Built capabilities map for ${channelsMap.size} channel(s)`);\n }\n\n // 7. Status Information (OSD, Floodlight, PIR, PTZ Presets)\n log(\n \"\\n[7/7] Collecting status information (OSD, Floodlight, PIR, PTZ) for all channels...\",\n );\n const statusInfo = await tryCall(() => cgi.getStatusInfo(channelsMap));\n result.statusInfo = statusInfo;\n if (statusInfo.ok) {\n log(\n `✓ Status info collected for ${Object.keys(statusInfo.value.deviceStatusData).length} channel(s)`,\n );\n for (const [channel, status] of Object.entries(\n statusInfo.value.deviceStatusData,\n )) {\n const ch = Number(channel);\n const st = status as any;\n log(` Channel ${ch}:`, {\n osd: st.osd ? \"configured\" : \"not available\",\n floodlightEnabled: st.floodlightEnabled,\n pirEnabled: st.pirEnabled,\n ptzPresets: st.ptzPresets?.length ?? 0,\n });\n }\n } else {\n log(\"✗ Failed to collect status info\", statusInfo.error);\n }\n\n // 8. Additional per-channel details\n log(\"\\n[8/8] Collecting additional per-channel details...\");\n const perChannelDetails: Record<number, Record<string, unknown>> = {};\n\n for (const channel of channels) {\n const channelDetails: Record<string, unknown> = {};\n\n // OSD\n const osd = await tryCall(() => cgi.GetOsd(channel));\n if (osd.ok) channelDetails.osd = osd.value;\n\n // Local Link / WiFi\n const localLink = await tryCall(() => cgi.getLocalLink(channel));\n if (localLink.ok) channelDetails.localLink = localLink.value;\n\n // PIR State\n const pirState = await tryCall(() => cgi.getPirState(channel));\n if (pirState.ok) channelDetails.pirState = pirState.value;\n\n // Siren/Audio Alarm\n const siren = await tryCall(() => cgi.getSiren(channel));\n if (siren.ok) channelDetails.siren = siren.value;\n\n // PTZ Presets\n const ptzPresets = await tryCall(() => cgi.GetPtzPreset(channel));\n if (ptzPresets.ok) channelDetails.ptzPresets = ptzPresets.value;\n\n // White LED / Floodlight\n const whiteLed = await tryCall(() => cgi.GetWhiteLed(channel));\n if (whiteLed.ok) channelDetails.whiteLed = whiteLed.value;\n\n // Encoding Configuration\n const enc = await tryCall(() => cgi.GetEnc(channel));\n if (enc.ok) channelDetails.encoding = enc.value;\n\n // AI State\n const aiState = await tryCall(() => cgi.GetAiState(channel));\n if (aiState.ok) channelDetails.aiState = aiState.value;\n\n // Motion Detection State\n const mdState = await tryCall(() => cgi.GetMdState(channel));\n if (mdState.ok) channelDetails.motionDetection = mdState.value;\n\n // Battery Info\n const battery = await tryCall(() => cgi.GetBatteryInfo(channel));\n if (battery.ok) channelDetails.battery = battery.value;\n\n // Channel Type Info\n const chnType = await tryCall(() => cgi.GetChnTypeInfo(channel));\n if (chnType.ok) channelDetails.channelType = chnType.value;\n\n // WiFi Signal\n const wifiSignal = await tryCall(() => cgi.GetWifiSignal(channel));\n if (wifiSignal.ok) channelDetails.wifiSignal = wifiSignal.value;\n\n perChannelDetails[channel] = channelDetails;\n log(\n ` Channel ${channel}: collected ${Object.keys(channelDetails).length} detail(s)`,\n );\n }\n\n result.perChannelDetails = perChannelDetails;\n log(\n `✓ Additional details collected for ${Object.keys(perChannelDetails).length} channel(s)`,\n );\n\n // Summary\n log(\"\\n\" + \"=\".repeat(80));\n log(\"NVR/HUB DIAGNOSTICS - Summary\");\n log(\"=\".repeat(80));\n log(`Total Channels: ${channels.length}`);\n log(`NVR Info: ${nvrInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Devices Info: ${devicesInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Events Info: ${eventsInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Battery Info: ${batteryInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Status Info: ${statusInfo.ok ? \"✓\" : \"✗\"}`);\n log(\n `Per-Channel Details: ✓ (${Object.keys(perChannelDetails).length} channels)`,\n );\n log(\"=\".repeat(80));\n\n return result;\n}\n\n/**\n * Print NVR/HUB diagnostics in a human-readable format.\n */\nexport function printNvrDiagnostics(\n diagnostics: Record<string, unknown>,\n logger?: Logger,\n): void {\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n log(\"\\n\" + \"=\".repeat(80));\n log(\"NVR/HUB DIAGNOSTICS REPORT\");\n log(\"=\".repeat(80));\n\n // NVR/HUB Info\n const nvrInfo = diagnostics.nvrInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (nvrInfo?.ok) {\n log(\"\\n📡 NVR/HUB DEVICE:\");\n const devInfo = nvrInfo.value.devInfo;\n if (devInfo) {\n log(` Model: ${devInfo.type || \"N/A\"}`);\n log(` Name: ${devInfo.name || \"N/A\"}`);\n log(` Firmware: ${devInfo.firmVer || \"N/A\"}`);\n log(` Hardware: ${devInfo.hardVer || \"N/A\"}`);\n log(` Serial: ${devInfo.serial || \"N/A\"}`);\n log(` Item No: ${devInfo.exactType || devInfo.model || \"N/A\"}`);\n }\n }\n\n // Channels\n const channels = (diagnostics.channelList as number[]) || [];\n log(`\\n📺 CHANNELS (${channels.length}):`);\n\n // Devices Info\n const devicesInfo = diagnostics.devicesInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (devicesInfo?.ok) {\n for (const channel of channels) {\n const device = devicesInfo.value.devicesData?.[channel];\n if (!device) continue;\n\n log(\n `\\n ┌─ Channel ${channel} ────────────────────────────────────────────────────`,\n );\n\n const status = device.channelStatus;\n if (status) {\n log(` │ Status:`);\n log(` │ Online: ${status.online === 1 ? \"✓\" : \"✗\"}`);\n log(` │ Sleeping: ${status.sleep === 1 ? \"Yes\" : \"No\"}`);\n log(` │ UID: ${status.uid || \"N/A\"}`);\n log(` │ Name: ${status.name || \"N/A\"}`);\n }\n\n const channelInfo = device.channelInfo;\n if (channelInfo) {\n log(` │ Device Info:`);\n log(` │ Model: ${channelInfo.typeInfo || \"N/A\"}`);\n log(` │ Firmware: ${channelInfo.firmVer || \"N/A\"}`);\n log(` │ Board Info: ${channelInfo.boardInfo || \"N/A\"}`);\n }\n\n const enc = device.enc;\n if (enc) {\n const encData = enc.Enc;\n log(` │ Encoding:`);\n if (encData) {\n log(\n ` │ Main Stream: ${encData.mainStream?.vType || \"N/A\"} ${encData.mainStream?.vSize || \"\"}`,\n );\n log(\n ` │ Sub Stream: ${encData.subStream?.vType || \"N/A\"} ${encData.subStream?.vSize || \"\"}`,\n );\n }\n }\n\n const ai = device.ai;\n if (ai) {\n log(` │ AI Detection:`);\n const aiKeys = Object.keys(ai).filter((k) => k !== \"channel\");\n if (aiKeys.length > 0) {\n for (const key of aiKeys) {\n const state = (ai as any)[key];\n if (state?.support === 1) {\n log(\n ` │ ${key}: ${state.alarm_state === 1 ? \"Enabled\" : \"Disabled\"}`,\n );\n }\n }\n } else {\n log(` │ No AI detection available`);\n }\n }\n\n const abilities = device.abilities;\n if (abilities) {\n log(` │ Capabilities:`);\n log(` │ Battery: ${(abilities as any).battery ? \"Yes\" : \"No\"}`);\n log(` │ PTZ: ${(abilities as any).ptz ? \"Yes\" : \"No\"}`);\n log(` │ Floodlight: ${(abilities as any).ledCtrl ? \"Yes\" : \"No\"}`);\n log(` │ PIR: ${(abilities as any).pirAlarm ? \"Yes\" : \"No\"}`);\n }\n\n // Events\n const eventsInfo = diagnostics.eventsInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (eventsInfo?.ok) {\n const events = eventsInfo.value.parsed?.[channel];\n if (events) {\n log(` │ Events:`);\n log(` │ Motion: ${events.motion ? \"Yes\" : \"No\"}`);\n log(\n ` │ Detected Objects: ${events.objects?.join(\", \") || \"None\"}`,\n );\n }\n }\n\n // Battery\n const batteryInfo = diagnostics.batteryInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (batteryInfo?.ok) {\n const battery = batteryInfo.value.batteryInfoData?.[channel];\n if (battery) {\n log(` │ Battery:`);\n log(` │ Level: ${battery.batteryLevel}%`);\n log(` │ Sleeping: ${battery.sleeping ? \"Yes\" : \"No\"}`);\n }\n }\n\n // Status\n const statusInfo = diagnostics.statusInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (statusInfo?.ok) {\n const status = statusInfo.value.deviceStatusData?.[channel];\n if (status) {\n log(` │ Status:`);\n if (status.osd) log(` │ OSD: Configured`);\n if (status.floodlightEnabled !== undefined) {\n log(` │ Floodlight: ${status.floodlightEnabled ? \"On\" : \"Off\"}`);\n }\n if (status.pirEnabled !== undefined) {\n log(` │ PIR: ${status.pirEnabled ? \"Enabled\" : \"Disabled\"}`);\n }\n if (status.ptzPresets) {\n log(` │ PTZ Presets: ${status.ptzPresets.length}`);\n }\n }\n }\n\n // Per-channel details\n const perChannelDetails = diagnostics.perChannelDetails as\n | Record<number, Record<string, unknown>>\n | undefined;\n if (perChannelDetails?.[channel]) {\n const details = perChannelDetails[channel];\n log(` │ Additional Details:`);\n if (details.localLink) {\n const ll = details.localLink as any;\n log(` │ Connection: ${ll.activeLink || \"N/A\"}`);\n log(\n ` │ WiFi Signal: ${ll.wifiSignal !== undefined ? `${ll.wifiSignal}/4` : \"N/A\"}`,\n );\n }\n if (details.siren) {\n const siren = details.siren as any;\n log(` │ Siren: ${siren.enabled ? \"Enabled\" : \"Disabled\"}`);\n }\n }\n\n log(\n ` └────────────────────────────────────────────────────────────────────`,\n );\n }\n }\n\n log(\"\\n\" + \"=\".repeat(80));\n}\n\n/**\n * Test all available streams for a specific channel.\n * Tests RTSP, RTMP, and native Baichuan streams with all profiles (main, sub, ext).\n *\n * @param params - Parameters for stream testing\n * @returns Test results for all stream types and profiles\n */\nexport async function testChannelStreams(params: {\n api: ReolinkBaichuanApi;\n channel: number;\n logger?: Logger;\n}): Promise<Record<string, unknown>> {\n const { api, channel, logger } = params;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const result: Record<string, unknown> = {\n kind: \"channel_stream_test\",\n channel,\n collectedAt: new Date().toISOString(),\n streams: {},\n };\n\n log(\"=\".repeat(80));\n log(`STREAM TEST - Channel ${channel}`);\n log(\"=\".repeat(80));\n\n // Get all available stream options\n log(`\\n[1/3] Getting available stream options for channel ${channel}...`);\n\n // Get raw XML from GetEnc to inspect what streams are actually available\n const encXmlResult = await tryCall(() => api.getEncXml(channel));\n if (encXmlResult.ok) {\n // Check what stream tags are present in the XML\n const xml = encXmlResult.value;\n const tagsPresent = {\n mainStream: /<mainStream\\b/.test(xml),\n subStream: /<subStream\\b/.test(xml),\n extStream: /<extStream\\b/.test(xml),\n thirdStream: /<thirdStream\\b/.test(xml),\n externStream: /<externStream\\b/.test(xml),\n extraStream: /<extraStream\\b/.test(xml),\n };\n log(` Raw GetEnc XML stream tags present: ${JSON.stringify(tagsPresent)}`);\n\n // Log a preview of the XML (first 2000 chars)\n const xmlPreview =\n xml.length > 2000\n ? xml.substring(0, 2000) +\n `\\n... (truncated, total length: ${xml.length})`\n : xml;\n log(` GetEnc XML preview:\\n${xmlPreview}`);\n\n // Also get parsed metadata to see what streams were detected\n const metadataResult = await tryCall(() => api.getStreamMetadata(channel));\n if (metadataResult.ok) {\n const detectedProfiles = metadataResult.value.streams.map(\n (s) => s.profile,\n );\n log(\n ` Parsed streams from metadata: ${detectedProfiles.join(\", \")} (${detectedProfiles.length} total)`,\n );\n result.rawEncXml = xml;\n result.encXmlTagsPresent = tagsPresent;\n result.parsedStreamProfiles = detectedProfiles;\n }\n }\n\n const streamOptionsResult = await tryCall(() =>\n api.buildVideoStreamOptions({ channel }),\n );\n if (!streamOptionsResult.ok) {\n log(`✗ Failed to get stream options: ${streamOptionsResult.error}`);\n result.error = streamOptionsResult.error;\n return result;\n }\n\n const { nativeStreams, rtspStreams, rtmpStreams } = streamOptionsResult.value;\n log(\n `✓ Found ${nativeStreams.length} native, ${rtspStreams.length} RTSP, ${rtmpStreams.length} RTMP stream(s)`,\n );\n\n // Log detailed breakdown of profiles found\n const nativeProfiles = nativeStreams.map((s) => s.profile).join(\", \");\n const rtspProfiles = rtspStreams.map((s) => s.profile).join(\", \");\n const rtmpProfiles = rtmpStreams.map((s) => s.profile).join(\", \");\n log(` Native profiles: [${nativeProfiles || \"none\"}]`);\n log(` RTSP profiles: [${rtspProfiles || \"none\"}]`);\n log(` RTMP profiles: [${rtmpProfiles || \"none\"}]`);\n\n // Test each stream type\n const streamTests: Record<string, Record<string, unknown>> = {};\n\n // Test RTSP streams with ffmpeg\n log(`\\n[2/3] Testing RTSP streams with ffmpeg...`);\n for (const stream of rtspStreams) {\n const key = `rtsp_${stream.profile}`;\n log(` Testing RTSP ${stream.profile}...`);\n\n const testResult: Record<string, unknown> = {\n available: false,\n profile: stream.profile,\n container: stream.container,\n url: stream.url,\n urlWithAuth: stream.urlWithAuth,\n metadata: stream.metadata,\n };\n\n // Test with ffmpeg - short session (2 seconds)\n const testUrl = stream.urlWithAuth;\n const ffmpegTest = await testStreamWithFfmpeg({\n url: testUrl,\n kind: \"rtsp\",\n durationSeconds: 2,\n });\n\n if (ffmpegTest.ok) {\n testResult.available = true;\n testResult.ffmpegSuccess = true;\n if (stream.metadata) {\n testResult.codec = stream.metadata.videoEncType;\n testResult.width = stream.metadata.width;\n testResult.height = stream.metadata.height;\n testResult.fps = stream.metadata.frameRate;\n testResult.bitRate = stream.metadata.bitRate;\n testResult.audio = stream.metadata.audio === 1;\n log(\n ` ✓ Available: ${stream.metadata.width}x${stream.metadata.height} @ ${stream.metadata.frameRate}fps, ${stream.metadata.videoEncType}`,\n );\n } else {\n log(` ✓ Available (ffmpeg test passed)`);\n }\n } else {\n testResult.available = false;\n testResult.error = ffmpegTest.error;\n log(` ✗ Not available: ${ffmpegTest.error}`);\n }\n\n streamTests[key] = testResult;\n }\n\n // Test RTMP streams with ffmpeg\n log(`\\n[3/4] Testing RTMP streams with ffmpeg...`);\n for (const stream of rtmpStreams) {\n const key = `rtmp_${stream.profile}`;\n log(` Testing RTMP ${stream.profile}...`);\n\n const testResult: Record<string, unknown> = {\n available: false,\n profile: stream.profile,\n container: stream.container,\n url: stream.url,\n urlWithAuth: stream.urlWithAuth,\n metadata: stream.metadata,\n };\n\n // Test with ffmpeg - short session (2 seconds)\n const testUrl = stream.urlWithAuth;\n const ffmpegTest = await testStreamWithFfmpeg({\n url: testUrl,\n kind: \"rtmp\",\n durationSeconds: 2,\n });\n\n if (ffmpegTest.ok) {\n testResult.available = true;\n testResult.ffmpegSuccess = true;\n if (stream.metadata) {\n testResult.codec = stream.metadata.videoEncType;\n testResult.width = stream.metadata.width;\n testResult.height = stream.metadata.height;\n testResult.fps = stream.metadata.frameRate;\n testResult.bitRate = stream.metadata.bitRate;\n testResult.audio = stream.metadata.audio === 1;\n log(\n ` ✓ Available: ${stream.metadata.width}x${stream.metadata.height} @ ${stream.metadata.frameRate}fps, ${stream.metadata.videoEncType}`,\n );\n } else {\n log(` ✓ Available (ffmpeg test passed)`);\n }\n } else {\n testResult.available = false;\n testResult.error = ffmpegTest.error;\n log(` ✗ Not available: ${ffmpegTest.error}`);\n }\n\n streamTests[key] = testResult;\n }\n\n // Test native Baichuan streams with short session\n log(`\\n[4/4] Testing native Baichuan streams...`);\n for (const stream of nativeStreams) {\n const variant = (stream.nativeVariant ?? \"default\") as any;\n const key =\n variant === \"default\"\n ? `native_${stream.profile}`\n : `native_${stream.profile}_${variant}`;\n log(\n ` Testing native ${stream.profile}${variant === \"default\" ? \"\" : ` (${variant})`}...`,\n );\n\n const testResult: Record<string, unknown> = {\n available: false,\n profile: stream.profile,\n container: stream.container,\n metadata: stream.metadata,\n };\n\n // Test with short session - subscribe and wait for at least one frame\n try {\n await api.startVideoStream(\n channel,\n stream.profile,\n variant === \"default\" ? undefined : { variant },\n );\n\n // Wait for at least one video frame (max 5 seconds)\n const framePromise = new Promise<boolean>((resolve) => {\n let frameReceived = false;\n const timeout = setTimeout(() => {\n api.client.off(\"push\", onFrame);\n resolve(frameReceived);\n }, 5000);\n\n const onFrame = (frame: BaichuanFrame) => {\n // Check if it's a video frame (cmd_id 3)\n if (frame?.header?.cmdId === BC_CMD_ID_VIDEO) {\n frameReceived = true;\n clearTimeout(timeout);\n api.client.off(\"push\", onFrame);\n resolve(true);\n }\n };\n\n api.client.on(\"push\", onFrame);\n });\n\n const frameReceived = await framePromise;\n\n if (frameReceived) {\n testResult.available = true;\n testResult.frameReceived = true;\n\n if (stream.metadata) {\n testResult.codec = stream.metadata.videoEncType;\n testResult.width = stream.metadata.width;\n testResult.height = stream.metadata.height;\n testResult.fps = stream.metadata.frameRate;\n testResult.bitRate = stream.metadata.bitRate;\n testResult.audio = stream.metadata.audio === 1;\n log(\n ` ✓ Available: ${stream.metadata.width}x${stream.metadata.height} @ ${stream.metadata.frameRate}fps, ${stream.metadata.videoEncType}`,\n );\n } else {\n log(` ✓ Available (frame received)`);\n }\n } else {\n testResult.available = false;\n testResult.error = \"Timeout waiting for video frame\";\n log(` ✗ Not available: timeout waiting for video frame`);\n }\n\n // Stop the stream\n await api.stopVideoStream(\n channel,\n stream.profile,\n variant === \"default\" ? undefined : { variant },\n );\n } catch (error) {\n testResult.available = false;\n testResult.error = safeStringifyError(error);\n log(` ✗ Not available: ${testResult.error}`);\n }\n\n streamTests[key] = testResult;\n }\n\n result.streams = streamTests;\n\n // Summary\n log(\"\\n\" + \"=\".repeat(80));\n log(\"STREAM TEST SUMMARY\");\n log(\"=\".repeat(80));\n const available = Object.values(streamTests).filter(\n (s: any) => s.available === true,\n ).length;\n const total = Object.keys(streamTests).length;\n log(`Available streams: ${available}/${total}`);\n for (const [key, test] of Object.entries(streamTests)) {\n const t = test as any;\n log(\n ` ${key}: ${t.available ? \"✓\" : \"✗\"} ${t.codec || \"N/A\"} ${t.width || \"\"}x${t.height || \"\"}`,\n );\n }\n log(\"=\".repeat(80));\n\n return result;\n}\n\n/**\n * Comprehensive diagnostics for multi-focal devices.\n * Tests all channels and all available streams for each channel.\n *\n * @param params - Parameters for multi-focal diagnostics (see inline types for property descriptions)\n * @returns Complete diagnostics for all channels and streams\n */\nexport async function collectMultifocalDiagnostics(params: {\n /** ReolinkBaichuanApi instance */\n api: ReolinkBaichuanApi;\n /** Optional logger for output */\n logger: Logger;\n}): Promise<Record<string, unknown>> {\n const { api, logger } = params;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const result: Record<string, unknown> = {\n kind: \"multifocal_diagnostics\",\n collectedAt: new Date().toISOString(),\n };\n\n log(\"=\".repeat(80));\n log(\"MULTI-FOCAL DEVICE DIAGNOSTICS - Starting comprehensive analysis\");\n log(\"=\".repeat(80));\n\n // Check if device is multi-focal\n log(\"\\n[1/4] Checking device capabilities...\");\n const capabilitiesResult = await tryCall(() => api.getDeviceCapabilities());\n if (!capabilitiesResult.ok) {\n log(`✗ Failed to get device capabilities: ${capabilitiesResult.error}`);\n result.error = capabilitiesResult.error;\n return result;\n }\n\n const support = capabilitiesResult.value.support;\n const channelNum = support?.channelNum ?? 1;\n\n log(`✓ Device channelNum: ${channelNum}`);\n\n if (channelNum !== 2 && channelNum !== 3) {\n log(\n `⚠ Warning: channelNum is ${channelNum}, expected 2 or 3 for multi-focal device`,\n );\n result.warning = `channelNum is ${channelNum}, expected 2 or 3`;\n }\n\n result.channelNum = channelNum;\n result.deviceInfo = await tryCall(() => api.getInfo());\n\n // Get device info\n log(\"\\n[2/4] Getting device information...\");\n const deviceInfoResult = await tryCall(() => api.getInfo());\n if (deviceInfoResult.ok) {\n log(`✓ Device: ${deviceInfoResult.value.type || \"N/A\"}`);\n result.deviceInfo = deviceInfoResult.value;\n }\n\n // Test all channels\n log(`\\n[3/4] Testing all ${channelNum} channel(s)...`);\n const channelResults: Record<number, Record<string, unknown>> = {};\n\n for (let ch = 0; ch < channelNum; ch++) {\n log(`\\n--- Channel ${ch} ---`);\n const channelTest = await testChannelStreams({\n api,\n channel: ch,\n logger,\n });\n channelResults[ch] = channelTest;\n }\n\n result.channels = channelResults;\n\n // Summary\n log(\"\\n\" + \"=\".repeat(80));\n log(\"MULTI-FOCAL DIAGNOSTICS SUMMARY\");\n log(\"=\".repeat(80));\n log(`Total Channels: ${channelNum}`);\n for (let ch = 0; ch < channelNum; ch++) {\n const channelData = channelResults[ch];\n if (channelData?.streams) {\n const streams = channelData.streams as Record<string, any>;\n const available = Object.values(streams).filter(\n (s: any) => s.available === true,\n ).length;\n const total = Object.keys(streams).length;\n log(` Channel ${ch}: ${available}/${total} streams available`);\n }\n }\n log(\"=\".repeat(80));\n\n return result;\n}\n\nexport interface RunMultifocalDiagnosticsConsecutivelyParams {\n api: ReolinkBaichuanApi;\n /** Base output directory. A timestamped subfolder will be created for each run. */\n outDir: string;\n host: string;\n username: string;\n password: string;\n /** NVR/HUB channel (0-based). */\n channel: number;\n /** Clip length to record per OK stream. */\n durationSeconds: number;\n /** Which RTMP apps to probe. Default: [\"bcs\",\"live\",\"vod\"]. */\n rtmpApps?: Array<\"bcs\" | \"live\" | \"vod\">;\n /** If true, probes a wider set of RTSP/RTMP candidates (may take longer). Default: false */\n probeFull?: boolean;\n /** Treat the target as NVR/Hub channel mapping (0-based). Default: true */\n onNvr?: boolean;\n logger?: Logger;\n}\n\nexport async function runMultifocalDiagnosticsConsecutively(\n params: RunMultifocalDiagnosticsConsecutivelyParams,\n): Promise<{ runDir: string; resultsPath: string; streamsDir: string }> {\n const logger = params.logger;\n const log = (\n level: \"log\" | \"warn\" | \"error\",\n msg: string,\n extra?: unknown,\n ) => {\n const fn = (logger?.[level] ?? logger?.log) as\n | ((...args: any[]) => void)\n | undefined;\n if (fn) {\n if (extra !== undefined) fn.call(logger, msg, extra);\n else fn.call(logger, msg);\n return;\n }\n\n // Fallback for non-logger callers.\n // Keep output minimal but informative.\n if (extra !== undefined) console.log(msg, extra);\n else console.log(msg);\n };\n\n // NOTE: user requested diagnostics to be written directly in the provided root folder.\n // We still keep `streams/` and `logs/` subfolders inside that root.\n const runDir = params.outDir;\n const streamsDir = join(runDir, \"streams\");\n const logsDir = join(runDir, \"logs\");\n mkdirp(runDir);\n mkdirp(streamsDir);\n mkdirp(logsDir);\n\n const redact = (s: string) =>\n s\n .replaceAll(encodeURIComponent(params.password), \"***\")\n .replaceAll(params.password, \"***\");\n\n log(\"log\", \"[MultifocalDiagnostics] starting run\", {\n outDir: params.outDir,\n runDir,\n host: params.host,\n channel: params.channel,\n durationSeconds: params.durationSeconds,\n rtmpApps: params.rtmpApps ?? [\"bcs\", \"live\", \"vod\"],\n probeFull: params.probeFull === true,\n onNvr: params.onNvr !== false,\n });\n\n const results: any = {\n kind: \"multifocal_diagnostics_run\",\n collectedAt: new Date().toISOString(),\n host: params.host,\n channel: params.channel,\n durationSeconds: params.durationSeconds,\n ok: [] as any[],\n failed: [] as any[],\n };\n\n const channelStr2 = String(params.channel + 1).padStart(2, \"0\");\n const userEnc = encodeURIComponent(params.username);\n const passEnc = encodeURIComponent(params.password);\n\n const rtspCandidates = (() => {\n const base = [\n `/Preview_${channelStr2}_main`,\n `/Preview_${channelStr2}_sub`,\n `/Preview_${channelStr2}_autotrack`,\n `/h264Preview_${channelStr2}_main`,\n `/h264Preview_${channelStr2}_sub`,\n `/h264Preview_${channelStr2}_autotrack`,\n `/h265Preview_${channelStr2}_main`,\n `/h265Preview_${channelStr2}_sub`,\n `/h265Preview_${channelStr2}_autotrack`,\n ];\n\n if (params.probeFull) {\n base.unshift(\n `/rtsp/Preview_${channelStr2}_main`,\n `/rtsp/Preview_${channelStr2}_sub`,\n `/rtsp/Preview_${channelStr2}_mobile`,\n `/rtsp/Preview_${channelStr2}_autotrack`,\n );\n base.push(\n `/Preview_${channelStr2}_mobile`,\n `/Preview_${channelStr2}_autotrack_main`,\n `/Preview_${channelStr2}_autotrack_sub`,\n );\n }\n\n return [...new Set(base)];\n })();\n\n const rtmpApps = params.rtmpApps ?? [\"bcs\", \"live\", \"vod\"];\n const rtmpCandidates = (() => {\n const minimalStreams = [\"sub\", \"mobile\", \"autotrack_sub\", \"telephoto_sub\"];\n const fullStreams = [\n \"main\",\n \"sub\",\n \"mobile\",\n \"autotrack\",\n \"autotrack_main\",\n \"autotrack_sub\",\n \"telephoto_main\",\n \"telephoto_sub\",\n ];\n const streams = params.probeFull ? fullStreams : minimalStreams;\n const out: Array<{ app: string; streamName: string; url: string }> = [];\n for (const app of rtmpApps) {\n for (const streamName of streams) {\n const streamType =\n streamName.includes(\"sub\") ||\n streamName === \"sub\" ||\n streamName === \"mobile\"\n ? 1\n : 0;\n const path = `/${app}/channel${params.channel}_${streamName}.bcs`;\n const u = new URL(`rtmp://${params.host}:1935${path}`);\n u.searchParams.set(\"channel\", params.channel.toString());\n u.searchParams.set(\"stream\", streamType.toString());\n u.searchParams.set(\"user\", params.username);\n u.searchParams.set(\"password\", params.password);\n out.push({ app, streamName, url: u.toString() });\n }\n }\n return out;\n })();\n\n const okLine = (entry: any) => {\n const wh =\n entry.width && entry.height ? `${entry.width}x${entry.height}` : \"?\";\n const codec = entry.codec ?? entry.codecName ?? \"\";\n return `${entry.kind} ${entry.id} ${wh} ${codec}`.trim();\n };\n\n log(\"log\", \"[MultifocalDiagnostics] probing RTSP\", {\n candidates: rtspCandidates.length,\n });\n for (const pathCandidate of rtspCandidates) {\n const urlWithAuth = `rtsp://${userEnc}:${passEnc}@${params.host}:554${pathCandidate}`;\n const id = `rtsp:${pathCandidate}`;\n const baseName = `${nowIsoCompact()}_${id}`.replace(\n /[^a-zA-Z0-9._-]+/g,\n \"_\",\n );\n const outPath = join(streamsDir, `${baseName}.mkv`);\n const probeLog = join(logsDir, `${baseName}.ffprobe.log`);\n const recLog = join(logsDir, `${baseName}.ffmpeg.log`);\n\n const probe = await probeVideoInfo({\n url: urlWithAuth,\n kind: \"rtsp\",\n logPath: probeLog,\n });\n if (!probe.ok) {\n results.failed.push({\n kind: \"rtsp\",\n id,\n url: redact(urlWithAuth),\n error: probe.error,\n });\n continue;\n }\n\n const rec = await recordRtspOrRtmpToFile({\n kind: \"rtsp\",\n url: urlWithAuth,\n outputPath: outPath,\n durationSeconds: params.durationSeconds,\n logPath: recLog,\n });\n if (!rec.ok) {\n results.failed.push({\n kind: \"rtsp\",\n id,\n url: redact(urlWithAuth),\n error: rec.error,\n });\n continue;\n }\n\n const entry = {\n kind: \"rtsp\",\n id: pathCandidate,\n url: redact(urlWithAuth),\n clipPath: outPath,\n ...probe.info,\n };\n results.ok.push(entry);\n log(\"log\", \"[MultifocalDiagnostics] clip saved\", {\n kind: \"rtsp\",\n id: pathCandidate,\n clipPath: outPath,\n width: probe.info.width,\n height: probe.info.height,\n codec: probe.info.codecName,\n });\n log(\"log\", `[MultifocalDiagnostics] OK ${okLine(entry)}`);\n }\n\n log(\"log\", \"[MultifocalDiagnostics] probing RTMP\", {\n candidates: rtmpCandidates.length,\n });\n for (const cand of rtmpCandidates) {\n const id = `rtmp:${cand.app}:${cand.streamName}`;\n const baseName = `${nowIsoCompact()}_${id}`.replace(\n /[^a-zA-Z0-9._-]+/g,\n \"_\",\n );\n const outPath = join(streamsDir, `${baseName}.mkv`);\n const probeLog = join(logsDir, `${baseName}.ffprobe.log`);\n const recLog = join(logsDir, `${baseName}.ffmpeg.log`);\n\n const probe = await probeVideoInfo({\n url: cand.url,\n kind: \"rtmp\",\n logPath: probeLog,\n });\n if (!probe.ok) {\n results.failed.push({\n kind: \"rtmp\",\n id,\n url: redact(cand.url),\n error: probe.error,\n });\n continue;\n }\n\n const rec = await recordRtspOrRtmpToFile({\n kind: \"rtmp\",\n url: cand.url,\n outputPath: outPath,\n durationSeconds: params.durationSeconds,\n logPath: recLog,\n });\n if (!rec.ok) {\n results.failed.push({\n kind: \"rtmp\",\n id,\n url: redact(cand.url),\n error: rec.error,\n });\n continue;\n }\n\n const entry = {\n kind: \"rtmp\",\n id,\n url: redact(cand.url),\n clipPath: outPath,\n app: cand.app,\n streamName: cand.streamName,\n ...probe.info,\n };\n results.ok.push(entry);\n log(\"log\", \"[MultifocalDiagnostics] clip saved\", {\n kind: \"rtmp\",\n id,\n clipPath: outPath,\n width: probe.info.width,\n height: probe.info.height,\n codec: probe.info.codecName,\n });\n log(\"log\", `[MultifocalDiagnostics] OK ${okLine(entry)}`);\n }\n\n // Native probing: do NOT rely solely on buildVideoStreamOptions.\n // Probe a matrix of (mode nvr/standalone, channel, profile, variant) and save rich artifacts.\n log(\"log\", \"[MultifocalDiagnostics] probing native streams\", {\n channel: params.channel,\n onNvr: params.onNvr !== false,\n probeFull: params.probeFull === true,\n });\n\n // Keep buildVideoStreamOptions output for reference (debugging what the API thinks).\n results.nativeStreamOptions = await tryCall(() =>\n params.api.buildVideoStreamOptions({\n channel: params.channel,\n onNvr: params.onNvr !== false,\n }),\n );\n\n const nativeModes: Array<\"nvr\" | \"standalone\"> =\n params.onNvr === false && !params.probeFull\n ? [\"standalone\"]\n : [\"nvr\", \"standalone\"];\n\n const uniqNums = (arr: number[]) =>\n [...new Set(arr)].filter((n) => Number.isFinite(n) && n >= 0);\n const channelsForMode = (mode: \"nvr\" | \"standalone\"): number[] => {\n if (mode === \"nvr\") return [params.channel];\n // Standalone TrackMix usually uses channels 0 (wide) and 1 (tele). Keep current channel if it matches.\n return uniqNums([0, 1, params.channel].filter((n) => n === 0 || n === 1));\n };\n\n const nativeProfiles: StreamProfile[] = params.probeFull\n ? [\"main\", \"sub\", \"ext\"]\n : [\"main\", \"sub\"];\n const nativeVariants: NativeVideoStreamVariant[] = params.probeFull\n ? [\"default\", \"autotrack\", \"telephoto\"]\n : [\"default\"];\n\n const expectedStreamTypesFor = (\n profile: StreamProfile,\n variant: NativeVideoStreamVariant,\n ): Set<number> => {\n if (profile === \"sub\") {\n return new Set([variant === \"default\" ? 1 : 3]);\n }\n if (profile === \"main\") {\n return new Set([variant === \"default\" ? 0 : 2]);\n }\n // ext\n return new Set([0]);\n };\n\n for (const mode of nativeModes) {\n for (const chNative of channelsForMode(mode)) {\n for (const profile of nativeProfiles) {\n for (const variant of nativeVariants) {\n if (profile === \"ext\" && variant !== \"default\") {\n results.failed.push({\n kind: \"native\",\n id: `native:${mode}:ch${chNative}:${profile}:${variant}`,\n error: \"invalid (ext does not support variant)\",\n });\n continue;\n }\n\n const id = `native:${mode}:ch${chNative}:${profile}:${variant}`;\n const baseName = `${nowIsoCompact()}_${id}`.replace(\n /[^a-zA-Z0-9._-]+/g,\n \"_\",\n );\n const baseDir = join(\n streamsDir,\n \"native\",\n mode,\n `ch${chNative}`,\n profile,\n variant,\n );\n const rawDir = join(baseDir, \"raw_frames\");\n const snapsDir = join(baseDir, \"snapshots\");\n const logsBase = join(logsDir, baseName);\n mkdirp(rawDir);\n mkdirp(snapsDir);\n\n const eventsPath = join(baseDir, \"events.ndjson\");\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_begin\",\n id,\n mode,\n channel: chNative,\n profile,\n variant,\n });\n log(\"log\", \"[MultifocalDiagnostics] native begin\", { id });\n\n const expectedStreamTypes = expectedStreamTypesFor(profile, variant);\n const maxRawFrames = 400;\n const maxRawBytes = 50 * 1024 * 1024;\n let rawFrames = 0;\n let rawBytes = 0;\n let lockedChannelId: number | undefined;\n let lockedMsgNum: number | undefined;\n\n const onPush = (frame: BaichuanFrame) => {\n if (!frame?.header) return;\n if (frame.header.cmdId !== 3) return;\n if (!expectedStreamTypes.has(frame.header.streamType)) return;\n\n // Lock to first observed channelId/msgNum for this combination.\n if (lockedChannelId === undefined)\n lockedChannelId = frame.header.channelId;\n if (lockedMsgNum === undefined) lockedMsgNum = frame.header.msgNum;\n if (frame.header.channelId !== lockedChannelId) return;\n if (frame.header.msgNum !== lockedMsgNum) return;\n\n if (rawFrames >= maxRawFrames) return;\n const payload: Buffer =\n Buffer.isBuffer(frame.payload) && frame.payload.length\n ? frame.payload\n : frame.body;\n if (!Buffer.isBuffer(payload) || payload.length === 0) return;\n if (rawBytes + payload.length > maxRawBytes) return;\n\n rawFrames++;\n rawBytes += payload.length;\n const idx = String(rawFrames).padStart(6, \"0\");\n const binPath = join(rawDir, `frame_${idx}.bin`);\n try {\n fs.writeFileSync(binPath, payload);\n } catch {\n // ignore\n }\n\n if (rawFrames === 1) {\n try {\n writeJson(\n join(baseDir, \"first_frame_header.json\"),\n frame.header,\n );\n } catch {\n // ignore\n }\n }\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_raw_frame\",\n id,\n bytes: payload.length,\n header: frame.header,\n bodyLen: frame.body?.length ?? 0,\n payloadLen: frame.payload?.length ?? 0,\n });\n };\n\n const clipBase = join(baseDir, `clip_${nowIsoCompact()}`);\n const clipAnnexBPath = clipBase + \".annexb\";\n const clipAudioPath = clipBase + \".audio.bin\";\n const clipInfoPath = clipBase + \".json\";\n const ffmpegMuxLog = logsBase + \".ffmpeg_mux.log\";\n\n const videoOut = fs.createWriteStream(clipAnnexBPath, { flags: \"w\" });\n const audioOut = fs.createWriteStream(clipAudioPath, { flags: \"w\" });\n\n let firstVideoType: \"H264\" | \"H265\" | undefined;\n let firstKeyframeAtMs: number | null = null;\n let startedAtMs: number | null = null;\n let videoAUs = 0;\n let audioFrames = 0;\n let lastSnapshotAtMs = 0;\n\n const client: any = params.api.client as any;\n\n const videoStream = new BaichuanVideoStream({\n client,\n // Intentionally omit `api`: we will manually issue start/stop VIDEO requests\n // to probe different streamType + Preview XML combinations.\n channel: chNative,\n profile,\n ...(variant !== \"default\" ? { variant } : {}),\n ...(logger ? { logger } : {}),\n } as any);\n\n const onAU = (u: any) => {\n if (!u || !Buffer.isBuffer(u.data)) return;\n if (!firstVideoType) firstVideoType = u.videoType;\n videoAUs++;\n try {\n videoOut.write(u.data);\n } catch {\n // ignore\n }\n\n if (u.isKeyframe && firstKeyframeAtMs == null)\n firstKeyframeAtMs = Date.now();\n\n const nalTypes = nalTypesSummary(u.videoType, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_access_unit\",\n id,\n isKeyframe: !!u.isKeyframe,\n videoType: u.videoType,\n microseconds: u.microseconds,\n bytes: u.data.length,\n nalTypes,\n });\n\n const now = Date.now();\n if (u.isKeyframe && now - lastSnapshotAtMs >= 2_000) {\n lastSnapshotAtMs = now;\n const snapId = nowIsoCompact();\n const snapAnnex = join(\n snapsDir,\n `snap_${snapId}.${u.videoType === \"H265\" ? \"h265\" : \"h264\"}`,\n );\n try {\n fs.writeFileSync(snapAnnex, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_snapshot_saved\",\n id,\n path: snapAnnex,\n });\n const snapJpeg = join(snapsDir, `snap_${snapId}.jpg`);\n void tryJpegFromAnnexB({\n videoType: u.videoType,\n snapshotAnnexBPath: snapAnnex,\n outputJpegPath: snapJpeg,\n logPath: join(baseDir, \"ffmpeg_snapshot.log\"),\n });\n } catch {\n // ignore\n }\n }\n };\n\n const onAudio = (buf: Buffer) => {\n if (!Buffer.isBuffer(buf) || buf.length === 0) return;\n audioFrames++;\n try {\n audioOut.write(buf);\n } catch {\n // ignore\n }\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_audio\",\n id,\n bytes: buf.length,\n });\n };\n\n videoStream.on(\"videoAccessUnit\" as any, onAU as any);\n videoStream.on(\"audioFrame\" as any, onAudio as any);\n videoStream.on(\"error\", (e: any) => {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_error\",\n id,\n error: safeStringifyError(e),\n });\n log(\"warn\", \"[MultifocalDiagnostics] native stream error\", {\n id,\n error: safeStringifyError(e),\n });\n });\n\n const uniq = <T>(arr: T[]) => [...new Set(arr)];\n\n const baseStreamName =\n profile === \"main\"\n ? \"mainStream\"\n : profile === \"sub\"\n ? \"subStream\"\n : \"externStream\";\n const headerStreamTypeCandidates =\n profile === \"sub\" ? [1, 3] : profile === \"main\" ? [0, 2] : [0];\n\n // Candidate channelId tags (some firmwares use 0-based, others 1-based).\n const channelIdTagCandidates = uniq(\n [chNative, chNative + 1].filter(\n (n) => Number.isFinite(n) && n >= 0,\n ),\n );\n\n const messageClassCandidates = params.probeFull\n ? [BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT]\n : [BC_CLASS_MODERN_24];\n const extensionXmlCandidates = params.probeFull\n ? [\"\", buildChannelExtensionXml(chNative)]\n : [buildChannelExtensionXml(chNative)];\n\n type NativeStartAttempt = {\n attemptId: string;\n messageClass: number;\n streamTypeHeader: number;\n payloadXml: string;\n payloadVersion: \"v10\" | \"v11\";\n channelIdTag: number | undefined;\n handle: number;\n previewStreamType: string;\n extensionXml: string;\n };\n\n const attempts: NativeStartAttempt[] = [];\n\n for (const messageClass of messageClassCandidates) {\n for (const streamTypeHeader of headerStreamTypeCandidates) {\n for (const extensionXml of extensionXmlCandidates) {\n // Preview v1.0: try both with and without channelId.\n const v10ChannelIdTags = params.probeFull\n ? [undefined, ...channelIdTagCandidates]\n : [undefined, chNative];\n for (const channelIdTag of uniq(v10ChannelIdTags)) {\n const handle =\n profile === \"main\" ? 0 : profile === \"sub\" ? 256 : 1024;\n attempts.push({\n attemptId: `v10:${baseStreamName}:h${handle}:cid${channelIdTag ?? \"none\"}:st${streamTypeHeader}:ext${extensionXml ? \"1\" : \"0\"}:mc${messageClass}`,\n messageClass,\n streamTypeHeader,\n payloadXml: buildPreviewXml(\n handle,\n baseStreamName,\n channelIdTag,\n ),\n payloadVersion: \"v10\",\n channelIdTag,\n handle,\n previewStreamType: baseStreamName,\n extensionXml,\n });\n }\n\n // Preview v1.1: always includes channelId; also try tele PCAP variants.\n for (const channelIdTag of channelIdTagCandidates) {\n const handle =\n profile === \"main\" ? 0 : profile === \"sub\" ? 256 : 1024;\n attempts.push({\n attemptId: `v11:${baseStreamName}:h${handle}:cid${channelIdTag}:st${streamTypeHeader}:ext${extensionXml ? \"1\" : \"0\"}:mc${messageClass}`,\n messageClass,\n streamTypeHeader,\n payloadXml: buildPreviewXmlV11({\n channelId: channelIdTag,\n handle,\n streamType: baseStreamName,\n }),\n payloadVersion: \"v11\",\n channelIdTag,\n handle,\n previewStreamType: baseStreamName,\n extensionXml,\n });\n\n if (variant === \"telephoto\") {\n const telePreviewStreamType =\n profile === \"main\"\n ? \"externStream\"\n : profile === \"sub\"\n ? \"mobileStream\"\n : undefined;\n const teleHandleBase =\n profile === \"main\"\n ? 1024\n : profile === \"sub\"\n ? 512\n : undefined;\n if (telePreviewStreamType && teleHandleBase !== undefined) {\n const teleHandle = teleHandleBase + channelIdTag;\n // Empirically: Hub tele often requires header streamType=0.\n attempts.push({\n attemptId: `v11tele:${telePreviewStreamType}:h${teleHandle}:cid${channelIdTag}:st0:ext${extensionXml ? \"1\" : \"0\"}:mc${messageClass}`,\n messageClass,\n streamTypeHeader: 0,\n payloadXml: buildPreviewXmlV11({\n channelId: channelIdTag,\n handle: teleHandle,\n streamType: telePreviewStreamType,\n }),\n payloadVersion: \"v11\",\n channelIdTag,\n handle: teleHandle,\n previewStreamType: telePreviewStreamType,\n extensionXml,\n });\n }\n }\n }\n }\n }\n }\n\n // Capture raw frames (cmd_id=3) concurrently.\n client.on(\"push\", onPush);\n\n let lastAttemptOk: NativeStartAttempt | undefined;\n let lastStartMsgNum: number | undefined;\n\n try {\n for (const attempt of attempts) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_begin\",\n id,\n attemptId: attempt.attemptId,\n });\n videoAUs = 0;\n audioFrames = 0;\n firstVideoType = undefined;\n firstKeyframeAtMs = null;\n startedAtMs = null;\n lockedChannelId = undefined;\n lockedMsgNum = undefined;\n rawFrames = 0;\n rawBytes = 0;\n\n const msgNum = client.reserveNextMsgNum();\n lastStartMsgNum = msgNum;\n try {\n client.subscribeVideoStream(BC_CMD_ID_VIDEO, msgNum);\n } catch {\n // ignore\n }\n\n // Force msgNum filtering in BaichuanVideoStream.\n try {\n (videoStream as any).activeMsgNum = msgNum;\n } catch {\n // ignore\n }\n\n try {\n await videoStream.start();\n const resp = await client.sendFrame({\n cmdId: BC_CMD_ID_VIDEO,\n channel: chNative,\n channelIdOverride: chNative,\n msgNumOverride: msgNum,\n extensionXml: attempt.extensionXml,\n payloadXml: attempt.payloadXml,\n messageClass: attempt.messageClass,\n streamType: attempt.streamTypeHeader,\n timeoutMs: 20_000,\n });\n\n if (resp?.header?.responseCode !== 200) {\n throw new Error(\n `response_code ${resp?.header?.responseCode}`,\n );\n }\n\n startedAtMs = Date.now();\n while (\n Date.now() - startedAtMs <\n Math.max(250, Math.round(params.durationSeconds * 1000))\n ) {\n await sleepMs(200);\n }\n\n // Consider it OK only if we got at least some media.\n if (videoAUs > 0) {\n lastAttemptOk = attempt;\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_ok\",\n id,\n attemptId: attempt.attemptId,\n msgNum,\n });\n break;\n }\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_no_media\",\n id,\n attemptId: attempt.attemptId,\n msgNum,\n });\n } catch (e) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_failed\",\n id,\n attemptId: attempt.attemptId,\n error: safeStringifyError(e),\n });\n } finally {\n try {\n // Best-effort stop: some firmwares keep streaming unless a stop is sent.\n const stopXml =\n attempt.payloadVersion === \"v11\" &&\n attempt.channelIdTag !== undefined\n ? buildPreviewStopXmlV11({\n channelId: attempt.channelIdTag,\n handle: attempt.handle,\n })\n : buildPreviewStopXml(\n attempt.handle,\n attempt.channelIdTag,\n );\n await client.sendFrame({\n cmdId: BC_CMD_ID_VIDEO,\n channel: chNative,\n channelIdOverride: chNative,\n msgNumOverride: msgNum,\n extensionXml: attempt.extensionXml,\n payloadXml: stopXml,\n messageClass: attempt.messageClass,\n streamType: attempt.streamTypeHeader,\n timeoutMs: 5_000,\n });\n } catch {\n // ignore\n }\n try {\n client.unsubscribeVideoStream(BC_CMD_ID_VIDEO, msgNum);\n } catch {\n // ignore\n }\n try {\n await videoStream.stop();\n } catch {\n // ignore\n }\n }\n }\n\n if (!lastAttemptOk) {\n throw new Error(\"no working native start attempt produced media\");\n }\n } catch (e) {\n results.failed.push({\n kind: \"native\",\n id,\n error: safeStringifyError(e),\n });\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_failed\",\n id,\n error: safeStringifyError(e),\n });\n continue;\n } finally {\n client.removeListener(\"push\", onPush);\n try {\n videoOut.end();\n } catch {\n // ignore\n }\n try {\n audioOut.end();\n } catch {\n // ignore\n }\n }\n\n const mkvPath = clipBase + \".mkv\";\n if (firstVideoType) {\n const fmt = firstVideoType === \"H265\" ? \"hevc\" : \"h264\";\n const muxArgs = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n \"-f\",\n fmt,\n \"-i\",\n clipAnnexBPath,\n \"-c\",\n \"copy\",\n \"-y\",\n mkvPath,\n ];\n const muxRes = await spawnFfmpeg(muxArgs, ffmpegMuxLog);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_mux\",\n id,\n ok: muxRes.ok,\n });\n }\n\n const info: any = {\n kind: \"native\",\n id,\n mode,\n channel: chNative,\n profile,\n variant,\n durationSeconds: params.durationSeconds,\n nativeAttempt: lastAttemptOk,\n nativeMsgNum: lastStartMsgNum,\n videoAccessUnits: videoAUs,\n audioFrames,\n videoType: firstVideoType,\n firstKeyframeLatencyMs:\n firstKeyframeAtMs == null || startedAtMs == null\n ? null\n : Math.max(0, firstKeyframeAtMs - startedAtMs),\n rawFrames,\n rawBytes,\n lockedChannelId,\n lockedMsgNum,\n annexbPath: clipAnnexBPath,\n audioPath: clipAudioPath,\n mkvPath: fs.existsSync(mkvPath) ? mkvPath : undefined,\n };\n\n // Derive resolution/codec from the saved MKV if available.\n if (info.mkvPath) {\n const p = await spawnFfprobeJson(\n [\n \"-v\",\n \"error\",\n \"-print_format\",\n \"json\",\n \"-show_streams\",\n \"-select_streams\",\n \"v:0\",\n info.mkvPath,\n ],\n logsBase + \".ffprobe_file.log\",\n );\n if (p.ok) {\n const streams = Array.isArray(p.json?.streams)\n ? p.json.streams\n : [];\n const s0 = streams[0] ?? undefined;\n info.width = typeof s0?.width === \"number\" ? s0.width : undefined;\n info.height =\n typeof s0?.height === \"number\" ? s0.height : undefined;\n info.codecName =\n typeof s0?.codec_name === \"string\" ? s0.codec_name : undefined;\n }\n }\n\n writeJson(clipInfoPath, info);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_done\",\n ...info,\n });\n\n const entry = {\n kind: \"native\",\n id,\n clipPath: info.mkvPath ?? clipAnnexBPath,\n width: info.width,\n height: info.height,\n codec:\n info.codecName ??\n (firstVideoType === \"H265\"\n ? \"hevc\"\n : firstVideoType === \"H264\"\n ? \"h264\"\n : undefined),\n profile,\n nativeVariant: variant,\n mode,\n channel: chNative,\n };\n results.ok.push(entry);\n log(\"log\", \"[MultifocalDiagnostics] clip saved\", {\n kind: \"native\",\n id,\n clipPath: entry.clipPath,\n width: entry.width,\n height: entry.height,\n codec: entry.codec,\n rawFrames,\n rawBytes,\n videoAccessUnits: videoAUs,\n });\n log(\"log\", `[MultifocalDiagnostics] OK ${okLine(entry)}`);\n }\n }\n }\n }\n\n const okIds = (results.ok as any[]).map((x) => ({\n kind: x.kind,\n id: x.id,\n clipPath: x.clipPath,\n width: x.width,\n height: x.height,\n }));\n log(\"log\", \"[MultifocalDiagnostics] summary\", {\n ok: results.ok.length,\n failed: results.failed.length,\n streamsDir,\n okStreams: okIds,\n });\n const resultsPath = join(runDir, \"multifocal_diagnostics.json\");\n writeJson(resultsPath, results);\n return { runDir, resultsPath, streamsDir };\n}\n\n/**\n * Parameters for running all diagnostics consecutively.\n */\nexport interface RunAllDiagnosticsConsecutivelyParams {\n /** ReolinkBaichuanApi instance */\n api: ReolinkBaichuanApi;\n /** Base output directory. A timestamped subfolder will be created for each run. */\n outDir: string;\n channel?: number;\n /** Stream sampling duration. */\n durationSeconds: number;\n /** Snapshot interval used for RTSP/RTMP snapshots. Default: 2 seconds. */\n snapshotIntervalSeconds?: number;\n /** Which kinds/profiles to sample. */\n selection: StreamSamplingSelection;\n\n /** Enable CGI diagnostics. `true` uses the same host/creds as the Baichuan client. */\n cgi?: boolean | Partial<ReolinkHttpClientOptions>;\n /** Enable RTSP sampling. `true` uses the same host/creds as the Baichuan client. */\n rtsp?: boolean | Partial<NonNullable<StreamSamplingOptions[\"rtsp\"]>>;\n /** Optional RTMP URLs for each profile. */\n rtmp?: StreamSamplingOptions[\"rtmp\"];\n /** Optional dump limits (native raw frames). */\n limits?: StreamSamplingOptions[\"limits\"];\n\n /** Extra user-provided metadata written into diagnostics.json */\n extra?: Record<string, unknown>;\n /** Logger for progress messages */\n logger?: Logger;\n /** Host for CGI/RTSP (if not provided in cgi/rtsp options) */\n host: string;\n /** Username for CGI/RTSP (if not provided in cgi/rtsp options) */\n username: string;\n /** Password for CGI/RTSP (if not provided in cgi/rtsp options) */\n password: string;\n}\n\n/**\n * Run all diagnostics consecutively: collect diagnostics bundle, sample streams, and create zip archive.\n *\n * @param params - Configuration parameters\n * @returns Results including run directory, zip path, diagnostics path, and streams directory\n */\nexport async function runAllDiagnosticsConsecutively(\n params: RunAllDiagnosticsConsecutivelyParams,\n): Promise<{\n runDir: string;\n zipPath: string;\n diagnosticsPath: string;\n streamsDir: string;\n}> {\n const { api, logger, host, username, password } = params;\n const channel = params.channel ?? 0;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const baseOutDir = params.outDir;\n const runDirName = new Date().toISOString().replace(/[:.]/g, \"-\");\n const runDir = join(baseOutDir, runDirName);\n\n log(\"[Diagnostics] starting run\", {\n outDir: baseOutDir,\n runDir,\n channel,\n durationSeconds: params.durationSeconds,\n selection: params.selection,\n });\n\n const cgiEnabled =\n params.cgi === true ||\n (typeof params.cgi === \"object\" && params.cgi != null);\n const cgiApi = cgiEnabled\n ? new ReolinkCgiApiImpl({\n host:\n (typeof params.cgi === \"object\" ? params.cgi.host : undefined) ??\n host,\n username:\n (typeof params.cgi === \"object\" ? params.cgi.username : undefined) ??\n username,\n password:\n (typeof params.cgi === \"object\" ? params.cgi.password : undefined) ??\n password,\n ...(logger ? { logger } : {}),\n ...(api.client.getDebugConfig?.()\n ? { debugConfig: api.client.getDebugConfig?.() }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.port != null\n ? { port: params.cgi.port }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.useHttps != null\n ? { useHttps: params.cgi.useHttps }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.insecureTLS != null\n ? { insecureTLS: params.cgi.insecureTLS }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.timeoutMs != null\n ? { timeoutMs: params.cgi.timeoutMs }\n : {}),\n })\n : undefined;\n\n const diagnosticsRes = await createDiagnosticsBundle({\n outDir: runDir,\n native: { api, channel },\n ...(cgiApi ? { cgi: { cgi: cgiApi, channel } } : {}),\n ...(params.extra ? { extra: params.extra } : {}),\n });\n\n log(\"[Diagnostics] diagnostics bundle collected\", {\n diagnosticsPath: diagnosticsRes.diagnosticsPath,\n runDir: diagnosticsRes.outDir,\n cgiEnabled,\n });\n\n const streamsDir = join(runDir, \"streams\");\n const rtspEnabled =\n params.rtsp === true ||\n (typeof params.rtsp === \"object\" && params.rtsp != null);\n const rtspCfg: StreamSamplingOptions[\"rtsp\"] | undefined = rtspEnabled\n ? {\n host:\n (typeof params.rtsp === \"object\" ? params.rtsp.host : undefined) ??\n host,\n username:\n (typeof params.rtsp === \"object\"\n ? params.rtsp.username\n : undefined) ?? username,\n password:\n (typeof params.rtsp === \"object\"\n ? params.rtsp.password\n : undefined) ?? password,\n ...(typeof params.rtsp === \"object\" && params.rtsp.port != null\n ? { port: params.rtsp.port }\n : {}),\n }\n : undefined;\n\n await sampleStreams({\n outDir: streamsDir,\n durationSeconds: params.durationSeconds,\n ...(params.snapshotIntervalSeconds != null\n ? { snapshotIntervalSeconds: params.snapshotIntervalSeconds }\n : {}),\n channel,\n selection: params.selection,\n ...(rtspCfg ? { rtsp: rtspCfg } : {}),\n ...(params.rtmp ? { rtmp: params.rtmp } : {}),\n native: { api },\n ...(params.limits ? { limits: params.limits } : {}),\n ...(logger ? { logger } : {}),\n });\n\n log(\"[Diagnostics] stream sampling completed\", { streamsDir });\n\n const zipPath = join(baseOutDir, `${runDirName}.zip`);\n\n log(\"[Diagnostics] creating zip bundle\", { zipPath });\n await zipDirectory({ sourceDir: runDir, zipPath });\n log(\"[Diagnostics] zip bundle created\", { zipPath });\n\n return {\n runDir: diagnosticsRes.outDir,\n zipPath,\n diagnosticsPath: diagnosticsRes.diagnosticsPath,\n streamsDir,\n };\n}\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 = (opts?.dump?.dir && opts.dump.dir.trim()) || 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(cfg: DebugConfig | undefined, logger: Logger | undefined, tag: string, message: string): 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(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.general) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function debugWarn(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.general) return;\n logger.warn(`[${tag}] ${message}`);\n}\n\nexport function traceLog(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.traceNativeStream) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function talkTraceLog(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.traceTalk) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function talkTraceWarn(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.traceTalk) return;\n logger.warn(`[${tag}] ${message}`);\n}\n\nexport function eventTraceLog(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.traceEvents) return;\n logger.debug(`[${tag}] ${message}`);\n}\n\nexport function rtspLog(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.debugRtsp) return;\n logger.info(`[${tag}] ${message}`);\n}\n\nexport function rtspWarn(cfg: DebugConfig, logger: Logger, tag: string, message: string): void {\n if (!cfg.debugRtsp) return;\n logger.warn(`[${tag}] ${message}`);\n}\n\n\n","import type {\n ParsedRecordingFileName,\n RecordingDevType,\n RecordingVodFlags,\n RecordingVodStreamHint,\n} from \"./types\";\n\ntype FlagSpec = Record<string, readonly [number, number]>;\n\nconst FLAGS_CAM_V2: FlagSpec = {\n resolution_index: [0, 7],\n tv_system: [7, 1],\n framerate: [8, 7],\n audio_index: [15, 2],\n ai_pd: [17, 1],\n ai_fd: [18, 1],\n ai_vd: [19, 1],\n ai_ad: [20, 1],\n encoder_type_index: [21, 2],\n is_schedule_record: [23, 1],\n is_motion_record: [24, 1],\n is_rf_record: [25, 1],\n is_doorbell_record: [26, 1],\n ai_other: [27, 1],\n};\n\nconst FLAGS_HUB_V0: FlagSpec = {\n resolution_index: [0, 7],\n tv_system: [7, 1],\n framerate: [8, 7],\n audio_index: [15, 2],\n ai_pd: [17, 1],\n ai_fd: [18, 1],\n ai_vd: [19, 1],\n ai_ad: [20, 1],\n encoder_type_index: [21, 2],\n is_schedule_record: [23, 1],\n is_motion_record: [24, 1],\n is_rf_record: [25, 1],\n is_doorbell_record: [26, 1],\n is_ai_other_record: [27, 1],\n picture_layout_index: [28, 7],\n package_delivered: [35, 1],\n package_takenaway: [36, 1],\n};\n\nconst FLAGS_HUB_V1: FlagSpec = {\n ...FLAGS_HUB_V0,\n package_event: [37, 1],\n};\n\nconst FLAGS_HUB_V2: FlagSpec = {\n resolution_index: [0, 7],\n tv_system: [7, 1],\n framerate: [8, 7],\n audio_index: [15, 2],\n ai_pd: [17, 1],\n ai_fd: [18, 1],\n ai_vd: [19, 1],\n ai_ad: [20, 1],\n ai_other: [21, 2],\n encoder_type_index: [23, 1],\n is_schedule_record: [24, 1],\n is_motion_record: [25, 1],\n is_rf_record: [26, 1],\n is_doorbell_record: [27, 1],\n picture_layout_index: [28, 7],\n package_delivered: [35, 1],\n package_takenaway: [36, 1],\n package_event: [37, 1],\n upload_flag: [38, 1],\n};\n\nconst FLAGS_MAPPING: Record<RecordingDevType, Record<number, FlagSpec>> = {\n cam: {\n 2: FLAGS_CAM_V2,\n 3: FLAGS_CAM_V2,\n 4: FLAGS_CAM_V2,\n 7: FLAGS_CAM_V2,\n 9: FLAGS_CAM_V2,\n 10: FLAGS_CAM_V2,\n },\n hub: {\n 0: FLAGS_HUB_V0,\n 1: FLAGS_HUB_V1,\n 2: FLAGS_HUB_V2,\n // NOTE: Home Hub / NVR firmware may emit hub filenames with higher versions (e.g. RecM04)\n // and longer hex payloads. We fall back to the highest known hub mapping (v2)\n // by not hardcoding a guessed v4 mapping.\n },\n};\n\nfunction decodeHexToFlags(\n hexValue: string,\n mapping: FlagSpec,\n): Record<string, number> | undefined {\n if (!hexValue) return undefined;\n if (!/^[0-9a-fA-F]+$/.test(hexValue)) return undefined;\n\n const bitLen = hexValue.length * 4;\n const hexInt = BigInt(`0x${hexValue}`);\n const bin = hexInt.toString(2).padStart(bitLen, \"0\");\n const revBin = bin.split(\"\").reverse().join(\"\");\n const revInt = BigInt(`0b${revBin}`);\n\n const out: Record<string, number> = {};\n for (const [flag, [bitPosition, bitSize]] of Object.entries(mapping)) {\n const pos = BigInt(bitPosition);\n const size = BigInt(bitSize);\n const mask = ((1n << size) - 1n) << pos;\n const segRev = (revInt & mask) >> pos;\n const segBin = segRev\n .toString(2)\n .padStart(bitSize, \"0\")\n .split(\"\")\n .reverse()\n .join(\"\");\n out[flag] = Number.parseInt(segBin, 2);\n }\n return out;\n}\n\nfunction decodeKnownFlags(\n devType: RecordingDevType,\n version: number,\n hexValue: string,\n): { raw: Record<string, number>; flags: RecordingVodFlags } | undefined {\n const versions = FLAGS_MAPPING[devType];\n const mapping =\n versions[version] ??\n versions[Math.max(...Object.keys(versions).map((k) => Number(k)))];\n if (!mapping) return undefined;\n const raw = decodeHexToFlags(hexValue, mapping);\n if (!raw) return undefined;\n\n const flags: RecordingVodFlags = {\n aiPerson: raw.ai_pd === 1,\n aiVehicle: raw.ai_vd === 1,\n aiAnimal: raw.ai_ad === 1,\n aiFace: raw.ai_fd === 1,\n aiOther:\n raw.is_ai_other_record === 1 ||\n raw.ai_other === 1 ||\n (raw.ai_other ?? 0) > 0,\n schedule: raw.is_schedule_record === 1,\n motion: raw.is_motion_record === 1,\n rf: raw.is_rf_record === 1,\n doorbell: raw.is_doorbell_record === 1,\n package:\n raw.package_event === 1 ||\n raw.package_delivered === 1 ||\n raw.package_takenaway === 1,\n };\n\n return { raw, flags };\n}\n\nfunction parseDateTimeLocal(\n yyyymmdd: string,\n hhmmss: string,\n): Date | undefined {\n if (!/^\\d{8}$/.test(yyyymmdd)) return undefined;\n if (!/^\\d{6}$/.test(hhmmss)) return undefined;\n const year = Number.parseInt(yyyymmdd.slice(0, 4), 10);\n const month = Number.parseInt(yyyymmdd.slice(4, 6), 10);\n const day = Number.parseInt(yyyymmdd.slice(6, 8), 10);\n const hour = Number.parseInt(hhmmss.slice(0, 2), 10);\n const minute = Number.parseInt(hhmmss.slice(2, 4), 10);\n const second = Number.parseInt(hhmmss.slice(4, 6), 10);\n if (![year, month, day, hour, minute, second].every(Number.isFinite))\n return undefined;\n // IMPORTANT: Parse as LOCAL TIME because the camera stores timestamps in local time in the filename.\n // When we create a Date object with new Date(year, month, day, hour, minute, second), JavaScript\n // interprets these values as local time and stores the date internally as UTC timestamp.\n // This means getTime() will return the correct UTC timestamp that represents that local time moment.\n // This is correct because the filename values represent the actual local time shown on the camera.\n return new Date(year, month - 1, day, hour, minute, second);\n}\n\n/**\n * Best-effort parse of Reolink VOD recording filenames.\n *\n * Formats observed:\n * - Mp4Record/2020-12-22/RecM01_20201222_075939_080140_<HEX>_<SIZE>.mp4\n * - Mp4Record/2023-04-26/RecS02_DST20230426_145918_150032_<HEX>_<SIZE>.mp4\n * - .../RecS07_20250219_111146_111238_0_<HEX>_<SIZE>.mp4\n * - .../RecM02_DST20240827_090302_090334_0_800_800_<HEX>_<SIZE>.mp4\n * - 0120260107000000 (numeric identifier format: [channel][YYYYMMDD][HHMMSS])\n * - 829_0_900_510_033C8200000000_184C8B5.mp4 (NVR compact format)\n */\nexport function parseRecordingFileName(\n fileName: string,\n): ParsedRecordingFileName | undefined {\n // Try numeric identifier format first (e.g., \"0120260107000000\")\n // Format: [channel][YYYYMMDD][HHMMSS] where channel is 2 digits, date is 8 digits, time is 6 digits\n const numericMatch = /^(\\d{2})(\\d{8})(\\d{6})$/.exec(fileName);\n if (numericMatch) {\n const channelStr = numericMatch[1];\n const dateStr = numericMatch[2];\n const timeStr = numericMatch[3];\n if (channelStr && dateStr && timeStr) {\n const start = parseDateTimeLocal(dateStr, timeStr);\n if (start) {\n // For numeric format, assume 20 second duration (common for motion detection clips)\n const end = new Date(start.getTime() + 20_000);\n return {\n baseName: fileName,\n ext: \"\",\n streamHint: \"main\",\n version: 0,\n devType: \"cam\",\n start,\n end,\n durationMs: 20_000,\n };\n }\n }\n }\n\n // Try NVR compact format: {timestamp}_{channel}_{duration?}_{flags1}_{hexFlags}_{hash}.mp4\n // Example: 829_0_900_510_033C8200000000_184C8B5.mp4\n // The hexFlags field contains video metadata including framerate\n const nvrCompactMatch =\n /^(\\d+)_(\\d+)_(\\d+)_(\\d+)_([0-9A-Fa-f]+)_[0-9A-Fa-f]+\\.mp4$/i.exec(\n fileName,\n );\n if (nvrCompactMatch) {\n const [, , channelStr, durationField, , hexFlags] = nvrCompactMatch;\n if (hexFlags && hexFlags.length >= 8) {\n // Duration field appears to be in deciseconds (900 = 90 seconds)\n const durationDs = Number.parseInt(durationField ?? \"0\", 10);\n const durationMs = durationDs > 0 ? durationDs * 100 : 60_000; // Default 60s if unknown\n\n // Try to decode flags using hub format (NVR uses similar structure)\n const decoded = decodeKnownFlags(\"hub\", 2, hexFlags);\n const parsed: ParsedRecordingFileName = {\n baseName: fileName.replace(/\\.mp4$/i, \"\"),\n ext: \"mp4\",\n streamHint: \"main\",\n version: 2,\n devType: \"hub\",\n start: new Date(), // Unknown start time in compact format\n end: new Date(Date.now() + durationMs),\n durationMs,\n };\n\n if (decoded) {\n parsed.flags = decoded.flags;\n parsed.rawFlags = decoded.raw;\n if (decoded.raw.framerate != null && decoded.raw.framerate > 0) {\n parsed.framerate = decoded.raw.framerate;\n }\n }\n\n return parsed;\n }\n }\n\n const dot = fileName.lastIndexOf(\".\");\n if (dot <= 0 || dot === fileName.length - 1) return undefined;\n const ext = fileName.slice(dot + 1);\n const pathNoExt = fileName.slice(0, dot);\n const baseName = pathNoExt.split(\"/\").filter(Boolean).at(-1);\n if (!baseName) return undefined;\n\n const parts = baseName.split(\"_\");\n const prefix = parts[0] ?? \"\";\n if (!prefix.startsWith(\"Rec\") || prefix.length !== 6) return undefined;\n\n const streamChar = prefix[3];\n const streamHint: RecordingVodStreamHint =\n streamChar === \"M\" ? \"main\" : streamChar === \"S\" ? \"sub\" : \"unknown\";\n\n const version = Number.parseInt(prefix.slice(4, 6), 16);\n if (!Number.isFinite(version)) return undefined;\n\n let devType: RecordingDevType = \"cam\";\n let startDate = \"\";\n let startTime = \"\";\n let endTime = \"\";\n let animalTypeRaw: string | undefined;\n let widthRaw: string | undefined;\n let heightRaw: string | undefined;\n let hexValue = \"\";\n let sizeHex: string | undefined;\n\n if (parts.length === 6) {\n // RecM01_20201222_075939_080140_<HEX>_<SIZE>\n startDate = parts[1] ?? \"\";\n startTime = parts[2] ?? \"\";\n endTime = parts[3] ?? \"\";\n hexValue = parts[4] ?? \"\";\n sizeHex = parts[5];\n } else if (parts.length === 7) {\n // RecS07_20250219_111146_111238_0_<HEX>_<SIZE>\n startDate = parts[1] ?? \"\";\n startTime = parts[2] ?? \"\";\n endTime = parts[3] ?? \"\";\n animalTypeRaw = parts[4];\n hexValue = parts[5] ?? \"\";\n sizeHex = parts[6];\n } else if (parts.length === 9) {\n // RecM02_DST20240827_090302_090334_0_800_800_<HEX>_<SIZE>\n devType = \"hub\";\n startDate = parts[1] ?? \"\";\n startTime = parts[2] ?? \"\";\n endTime = parts[3] ?? \"\";\n animalTypeRaw = parts[4];\n widthRaw = parts[5];\n heightRaw = parts[6];\n hexValue = parts[7] ?? \"\";\n sizeHex = parts[8];\n } else {\n return undefined;\n }\n\n startDate = startDate.toLowerCase().replace(\"dst\", \"\");\n const start = parseDateTimeLocal(startDate, startTime);\n if (!start) return undefined;\n const end =\n endTime === \"000000\" ? start : parseDateTimeLocal(startDate, endTime);\n if (!end) return undefined;\n\n const durationMs = Math.max(0, end.getTime() - start.getTime());\n\n const parsed: ParsedRecordingFileName = {\n baseName,\n ext,\n streamHint,\n version,\n devType,\n start,\n end,\n durationMs,\n };\n\n const decoded = decodeKnownFlags(devType, version, hexValue);\n if (decoded) {\n parsed.flags = decoded.flags;\n parsed.rawFlags = decoded.raw;\n // Extract framerate if available in rawFlags\n if (decoded.raw.framerate != null && decoded.raw.framerate > 0) {\n parsed.framerate = decoded.raw.framerate;\n }\n }\n\n if (animalTypeRaw != null) parsed.animalTypeRaw = animalTypeRaw;\n if (widthRaw != null) parsed.widthRaw = widthRaw;\n if (heightRaw != null) parsed.heightRaw = heightRaw;\n\n // Parse file size from hex (last field before extension)\n if (sizeHex && /^[0-9a-fA-F]+$/.test(sizeHex)) {\n const sizeBytes = parseInt(sizeHex, 16);\n if (Number.isFinite(sizeBytes) && sizeBytes > 0) {\n parsed.sizeBytes = sizeBytes;\n }\n }\n\n return parsed;\n}\n","import { Agent } from \"undici\";\nimport {\n ReolinkCmdRequest,\n type LoginResponseValue,\n type ReolinkCmdResponse,\n} from \"./types\";\n\nexport type ReolinkHttpClientOptions = {\n host: string;\n username: string;\n password: string;\n port?: number;\n useHttps?: boolean;\n /** Disable TLS verification for HTTPS (default: true). */\n insecureTLS?: boolean;\n timeoutMs?: number;\n};\n\nexport class ReolinkHttpClient {\n private static readonly sessionPool = new Map<\n string,\n {\n token?: string;\n tokenExpiresAt?: number;\n loginInFlight?: Promise<void>;\n }\n >();\n\n private readonly host: string;\n private readonly username: string;\n private readonly password: string;\n private port: number | undefined;\n private useHttps: boolean | undefined;\n private readonly insecureTLS: boolean;\n private readonly timeoutMs: number;\n\n private token: string | undefined;\n private tokenExpiresAt: number | undefined; // epoch ms\n\n // `fetch` in Node uses `undici` and accepts `dispatcher`, but typings may differ\n // between `undici` and `undici-types` (Node). We keep a permissive type.\n private httpsAgent: unknown;\n\n constructor(opts: ReolinkHttpClientOptions) {\n this.host = opts.host;\n this.username = opts.username;\n this.password = opts.password;\n this.port = opts.port;\n this.useHttps = opts.useHttps;\n this.insecureTLS = opts.insecureTLS ?? true;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n\n if (this.insecureTLS) {\n this.httpsAgent = new Agent({ connect: { rejectUnauthorized: false } });\n }\n }\n\n getUsername(): string {\n return this.username;\n }\n\n getToken(): string | undefined {\n return this.token;\n }\n\n clearToken(): void {\n this.token = undefined;\n this.tokenExpiresAt = undefined;\n }\n\n private sessionKey(): string {\n const scheme = this.useHttps ? \"https\" : \"http\";\n const port = this.port ?? (this.useHttps ? 443 : 80);\n // Token is bound to host + user session. We intentionally do NOT include password here;\n // a password change should be handled by consumer recreating the client or by logout.\n return `${scheme}://${this.host}:${port}|${this.username}`;\n }\n\n private getSharedSession(): {\n token?: string;\n tokenExpiresAt?: number;\n loginInFlight?: Promise<void>;\n } {\n const key = this.sessionKey();\n const existing = ReolinkHttpClient.sessionPool.get(key);\n if (existing) return existing;\n const created: {\n token?: string;\n tokenExpiresAt?: number;\n loginInFlight?: Promise<void>;\n } = {};\n ReolinkHttpClient.sessionPool.set(key, created);\n return created;\n }\n\n private baseUrl(): string {\n const scheme = this.useHttps ? \"https\" : \"http\";\n const port = this.port ?? (this.useHttps ? 443 : 80);\n return `${scheme}://${this.host}:${port}`;\n }\n\n private apiUrl(params: Record<string, string | number | undefined>): string {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v == null) continue;\n qs.set(k, String(v));\n }\n return `${this.baseUrl()}/cgi-bin/api.cgi?${qs.toString()}`;\n }\n\n private isTokenValid(): boolean {\n if (!this.token || !this.tokenExpiresAt) return false;\n // margine 10s\n return Date.now() + 10_000 < this.tokenExpiresAt;\n }\n\n private isSharedTokenValid(shared: {\n token?: string;\n tokenExpiresAt?: number;\n }): boolean {\n if (!shared.token || !shared.tokenExpiresAt) return false;\n return Date.now() + 10_000 < shared.tokenExpiresAt;\n }\n\n private async performLogin(): Promise<void> {\n const body: ReolinkCmdRequest[] = [\n {\n cmd: \"Login\",\n action: 0,\n param: {\n User: {\n userName: this.username,\n password: this.password,\n },\n },\n },\n ];\n\n const rsp = await this.sendJson<LoginResponseValue>(\n body,\n { cmd: \"Login\" },\n { includeToken: false },\n );\n const first = rsp[0];\n if (!first || first.code !== 0 || !first.value?.Token?.name) {\n throw new Error(`Login failed: ${JSON.stringify(rsp)}`);\n }\n\n const lease = Number(first.value.Token.leaseTime);\n const token = first.value.Token.name;\n const expiresAt = Date.now() + (Number.isFinite(lease) ? lease * 1000 : 0);\n\n this.token = token;\n this.tokenExpiresAt = expiresAt;\n\n const shared = this.getSharedSession();\n shared.token = token;\n shared.tokenExpiresAt = expiresAt;\n }\n\n async login(): Promise<void> {\n if (this.isTokenValid()) return;\n\n const shared = this.getSharedSession();\n if (this.isSharedTokenValid(shared)) {\n this.token = shared.token;\n this.tokenExpiresAt = shared.tokenExpiresAt;\n return;\n }\n\n if (shared.loginInFlight) {\n await shared.loginInFlight;\n if (this.isSharedTokenValid(shared)) {\n this.token = shared.token;\n this.tokenExpiresAt = shared.tokenExpiresAt;\n }\n return;\n }\n\n shared.loginInFlight = (async () => {\n try {\n await this.performLogin();\n } finally {\n delete shared.loginInFlight;\n }\n })();\n\n await shared.loginInFlight;\n }\n\n async logout(): Promise<void> {\n if (!this.token) return;\n try {\n await this.call(\"Logout\", { action: 0, param: {} });\n } finally {\n this.clearToken();\n\n const shared = this.getSharedSession();\n delete shared.token;\n delete shared.tokenExpiresAt;\n }\n }\n\n async call<TValue = unknown, TParam = unknown>(\n cmd: string,\n opts?: {\n action?: number;\n param?: TParam; /** Some commands are host-level and do not require a param */\n },\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n await this.login();\n const body: ReolinkCmdRequest<TParam>[] = [\n {\n cmd,\n action: opts?.action ?? 0,\n ...(opts && \"param\" in opts && opts.param !== undefined\n ? { param: opts.param }\n : {}),\n },\n ];\n return await this.sendJson<TValue>(\n body,\n { cmd, token: this.token },\n { includeToken: true },\n );\n }\n\n async callMany<TValue = unknown>(\n cmds: ReolinkCmdRequest[],\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n await this.login();\n // Extract cmd from first request for query string\n const cmd = cmds[0]?.cmd;\n const query: Record<string, string | number | undefined> = {\n token: this.token,\n };\n if (cmd) query.cmd = cmd;\n return await this.sendJson<TValue>(cmds, query, { includeToken: true });\n }\n\n private async sendJson<TValue>(\n body: ReolinkCmdRequest[],\n query: Record<string, string | number | undefined>,\n _opts: { includeToken: boolean },\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n const url = this.apiUrl(query);\n const ac = new AbortController();\n const t = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const init: any = {\n method: \"POST\",\n body: JSON.stringify(body),\n headers: { \"Content-Type\": \"application/json\" },\n signal: ac.signal,\n };\n if (this.useHttps && this.httpsAgent)\n init.dispatcher = this.httpsAgent as any;\n\n const res = await fetch(url, init as RequestInit);\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n const json = JSON.parse(text) as ReolinkCmdResponse<TValue>[];\n return json;\n } finally {\n clearTimeout(t);\n }\n }\n\n /**\n * Take a snapshot via CGI `cmd=Snap` and return the raw JPEG bytes.\n *\n * This is a binary endpoint (not JSON). Requires a valid token.\n */\n async snap(\n channel: number,\n opts?: {\n timeoutMs?: number;\n rs?: string;\n /** Optional snapshot quality selector used by some firmwares (\"main\" | \"sub\"). */\n snapType?: \"main\" | \"sub\";\n /** Optional logic channel for dual-lens models behind NVR/Hub (e.g. TrackMix tele lens). */\n iLogicChannel?: number;\n },\n ): Promise<Buffer> {\n await this.login();\n if (!this.token) throw new Error(\"Missing token after login\");\n\n const rs = opts?.rs ?? Date.now().toString();\n const url = this.apiUrl({\n cmd: \"Snap\",\n channel,\n rs,\n snapType: opts?.snapType,\n iLogicChannel: opts?.iLogicChannel,\n token: this.token,\n });\n\n const ac = new AbortController();\n const t = setTimeout(() => ac.abort(), opts?.timeoutMs ?? this.timeoutMs);\n try {\n const init: any = {\n method: \"GET\",\n signal: ac.signal,\n };\n if (this.useHttps && this.httpsAgent)\n init.dispatcher = this.httpsAgent as any;\n\n const res = await fetch(url, init as RequestInit);\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n const ab = await res.arrayBuffer();\n return Buffer.from(ab);\n } finally {\n clearTimeout(t);\n }\n }\n\n /**\n * Download a VOD/recording via CGI `cmd=Download`.\n *\n * Uses POST with a dummy JSON body and query params.\n * Returns the raw binary payload (often mp4).\n */\n async downloadVod(\n source: string,\n output?: string,\n start?: string,\n ): Promise<Buffer> {\n await this.login();\n if (!this.token) throw new Error(\"Missing token after login\");\n\n // NOTE: Do NOT URL-encode '/' in `source`. Many Reolink firmwares expect raw paths\n // like \"/mnt/sda/...\" and return 404 when `%2F` is used. Only encode spaces.\n const scheme = this.useHttps ? \"https\" : \"http\";\n const port = this.port ?? (this.useHttps ? 443 : 80);\n const sourceForQuery = source.replaceAll(\" \", \"%20\");\n const outputForQuery = output ? encodeURIComponent(output) : undefined;\n const startForQuery = start ? encodeURIComponent(start) : undefined;\n const tokenForQuery = encodeURIComponent(this.token);\n const url = `${scheme}://${this.host}:${port}/cgi-bin/api.cgi?cmd=Download&source=${sourceForQuery}${outputForQuery ? `&output=${outputForQuery}` : \"\"}${startForQuery ? `&start=${startForQuery}` : \"\"}&token=${tokenForQuery}`;\n const ac = new AbortController();\n const t = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const init: any = {\n method: \"POST\",\n body: \"[{}]\",\n headers: { \"Content-Type\": \"application/json\" },\n signal: ac.signal,\n };\n if (this.useHttps && this.httpsAgent)\n init.dispatcher = this.httpsAgent as any;\n\n const res = await fetch(url, init as RequestInit);\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n const ab = await res.arrayBuffer();\n return Buffer.from(ab);\n } finally {\n clearTimeout(t);\n }\n }\n}\n","import type { DebugConfig, Logger } from \"../../debug/DebugConfig\";\nimport { recordingsTraceLog } from \"../../debug/DebugConfig\";\nimport { collectNvrDiagnostics } from \"../../debug/DiagnosticsTools\";\nimport { parseRecordingFileName } from \"../baichuan/recordingFileName\";\nimport type { RecordingDetectionClass, RecordingFile } from \"../baichuan/types\";\nimport {\n ReolinkHttpClient,\n type ReolinkHttpClientOptions,\n} from \"../http/ReolinkHttpClient\";\nimport type { ReolinkCmdRequest, ReolinkCmdResponse } from \"../http/types\";\nimport type { ReolinkDeviceInfo, ReolinkDeviceInfoTag } from \"../types\";\n\nexport type JsonPrimitive = string | number | boolean | null;\nexport type JsonObject = { [key: string]: JsonValue };\nexport type JsonValue = JsonPrimitive | JsonObject | JsonValue[];\n\nexport type ReolinkCmdResponseExt<TValue = JsonValue> =\n ReolinkCmdResponse<TValue> & {\n /** Some CGI commands (notably GetEnc) return additional metadata fields. */\n initial?: JsonValue;\n range?: JsonValue;\n };\n\nexport type CgiChannelStatusEntry = {\n channel: number;\n name?: string;\n online?: number;\n sleep?: number;\n uid?: string;\n typeInfo?: string;\n};\n\nexport type CgiGetChannelstatusValue = {\n status?: CgiChannelStatusEntry[];\n};\n\nexport type CgiChnTypeInfoValue = {\n boardInfo?: string;\n firmVer?: string;\n pakSuffix?: string;\n typeInfo?: string;\n};\n\nexport type CgiDetectionState = {\n alarm_state: number;\n support: number;\n};\n\nexport type CgiAiKey =\n | \"dog_cat\"\n | \"face\"\n | \"other\"\n | \"package\"\n | \"people\"\n | \"vehicle\";\n\nexport type CgiAiStateValue = Partial<Record<CgiAiKey, CgiDetectionState>> & {\n channel: number;\n};\n\nexport type CgiEncStream = {\n bitRate: number;\n frameRate: number;\n gop: number;\n height: number;\n profile: string;\n size: string;\n vType: string;\n width: number;\n};\n\nexport type CgiEnc = {\n audio: number;\n channel: number;\n mainStream: CgiEncStream;\n subStream: CgiEncStream;\n};\n\nexport type CgiEncValue = {\n Enc: CgiEnc;\n};\n\nexport type CgiGetChannelstatusResponse =\n ReolinkCmdResponseExt<CgiGetChannelstatusValue> & {\n cmd: \"GetChannelstatus\";\n };\n\nexport type CgiGetChnTypeInfoResponse =\n ReolinkCmdResponseExt<CgiChnTypeInfoValue> & {\n cmd: \"GetChnTypeInfo\";\n };\n\nexport type CgiGetAiStateResponse = ReolinkCmdResponseExt<CgiAiStateValue> & {\n cmd: \"GetAiState\";\n};\n\nexport type CgiGetEncResponse = ReolinkCmdResponseExt<CgiEncValue> & {\n cmd: \"GetEnc\";\n initial?: CgiEncValue;\n range?: JsonValue;\n};\n\nexport type CgiGetRtspUrlValue = {\n rtspUrl?: string;\n url?: string;\n RtspUrl?: string;\n rtsp?: string;\n} & Record<string, JsonValue>;\n\nexport type CgiGetRtspUrlResponse =\n ReolinkCmdResponseExt<CgiGetRtspUrlValue> & {\n cmd: \"GetRtspUrl\";\n };\n\nexport type CgiAbilityLeaf = {\n permit: number;\n ver: number;\n};\n\nexport type CgiAbilityChn = {\n aiTrack?: CgiAbilityLeaf;\n aiTrackDogCat?: CgiAbilityLeaf;\n alarmAudio?: CgiAbilityLeaf;\n alarmIoIn?: CgiAbilityLeaf;\n alarmIoOut?: CgiAbilityLeaf;\n alarmMd?: CgiAbilityLeaf;\n alarmRf?: CgiAbilityLeaf;\n batAnalysis?: CgiAbilityLeaf;\n battery?: CgiAbilityLeaf;\n cameraMode?: CgiAbilityLeaf;\n channelType?: CgiAbilityLeaf;\n customAudio?: CgiAbilityLeaf;\n disableAutoFocus?: CgiAbilityLeaf;\n enc?: CgiAbilityLeaf;\n floodLight?: CgiAbilityLeaf;\n ftp?: CgiAbilityLeaf;\n image?: CgiAbilityLeaf;\n indicatorLight?: CgiAbilityLeaf;\n isp?: CgiAbilityLeaf;\n isp3Dnr?: CgiAbilityLeaf;\n ispAntiFlick?: CgiAbilityLeaf;\n ispBackLight?: CgiAbilityLeaf;\n ispBright?: CgiAbilityLeaf;\n ispContrast?: CgiAbilityLeaf;\n ispDayNight?: CgiAbilityLeaf;\n ispExposureMode?: CgiAbilityLeaf;\n ispFlip?: CgiAbilityLeaf;\n ispHue?: CgiAbilityLeaf;\n ispMirror?: CgiAbilityLeaf;\n ispSatruation?: CgiAbilityLeaf;\n ispSharpen?: CgiAbilityLeaf;\n ispWhiteBalance?: CgiAbilityLeaf;\n ledControl?: CgiAbilityLeaf;\n lightType?: CgiAbilityLeaf;\n live?: CgiAbilityLeaf;\n mainEncType?: CgiAbilityLeaf;\n mask?: CgiAbilityLeaf;\n mdTriggerAudio?: CgiAbilityLeaf;\n mdTriggerRecord?: CgiAbilityLeaf;\n mdWithPir?: CgiAbilityLeaf;\n osd?: CgiAbilityLeaf;\n powerLed?: CgiAbilityLeaf;\n ptzCtrl?: CgiAbilityLeaf;\n ptzDirection?: CgiAbilityLeaf;\n ptzPatrol?: CgiAbilityLeaf;\n ptzPreset?: CgiAbilityLeaf;\n ptzTattern?: CgiAbilityLeaf;\n ptzType?: CgiAbilityLeaf;\n recCfg?: CgiAbilityLeaf;\n recDownload?: CgiAbilityLeaf;\n recReplay?: CgiAbilityLeaf;\n recSchedule?: CgiAbilityLeaf;\n shelterCfg?: CgiAbilityLeaf;\n snap?: CgiAbilityLeaf;\n supportAIDenoise?: CgiAbilityLeaf;\n supportAITrackLimit?: CgiAbilityLeaf;\n supportAITrackSchedule?: CgiAbilityLeaf;\n supportAfAlgorithmSwitch?: CgiAbilityLeaf;\n supportAi?: CgiAbilityLeaf;\n supportAiAnimal?: CgiAbilityLeaf;\n supportAiDetectConfig?: CgiAbilityLeaf;\n supportAiDogCat?: CgiAbilityLeaf;\n supportAiFace?: CgiAbilityLeaf;\n supportAiPackage?: CgiAbilityLeaf;\n supportAiPeople?: CgiAbilityLeaf;\n supportAiSensitivity?: CgiAbilityLeaf;\n supportAiStayTime?: CgiAbilityLeaf;\n supportAiTargetSize?: CgiAbilityLeaf;\n supportAiTrackClassify?: CgiAbilityLeaf;\n supportAiVehicle?: CgiAbilityLeaf;\n supportAllColors?: CgiAbilityLeaf;\n supportAoAdjust?: CgiAbilityLeaf;\n supportAudioAlarm?: CgiAbilityLeaf;\n supportAudioFileList?: CgiAbilityLeaf;\n supportAutoPt?: CgiAbilityLeaf;\n supportAutoReply?: CgiAbilityLeaf;\n supportAutoTrackStream?: CgiAbilityLeaf;\n supportBinoStitch?: CgiAbilityLeaf;\n supportDigitalZoom?: CgiAbilityLeaf;\n supportDingDongCtrl?: CgiAbilityLeaf;\n supportDoorbellLight?: CgiAbilityLeaf;\n supportDoorbellLightKeepOff?: CgiAbilityLeaf;\n supportEncoderSelect?: CgiAbilityLeaf;\n supportFLBrightness?: CgiAbilityLeaf;\n supportFLIntelligent?: CgiAbilityLeaf;\n supportFLKeepOn?: CgiAbilityLeaf;\n supportFLSchedule?: CgiAbilityLeaf;\n supportFLswitch?: CgiAbilityLeaf;\n supportFishEyeCfg?: CgiAbilityLeaf;\n supportFocus?: CgiAbilityLeaf;\n supportGop?: CgiAbilityLeaf;\n supportGuardPointImage?: CgiAbilityLeaf;\n supportImportExportImage?: CgiAbilityLeaf;\n supportIspBinningModeCfg?: CgiAbilityLeaf;\n supportLightAutoBrightness?: CgiAbilityLeaf;\n supportMd?: CgiAbilityLeaf;\n supportPt?: CgiAbilityLeaf;\n supportPtz3DLocation?: CgiAbilityLeaf;\n supportPtzCalibration?: CgiAbilityLeaf;\n supportPtzPresetImage?: CgiAbilityLeaf;\n supportPtzSpeed?: CgiAbilityLeaf;\n supportQuickReplyPlay?: CgiAbilityLeaf;\n supportThresholdAdjust?: CgiAbilityLeaf;\n supportVisitorLoudspeaker?: CgiAbilityLeaf;\n supportWLLightAlarm?: CgiAbilityLeaf;\n supportWebhook?: CgiAbilityLeaf;\n supportWhiteDark?: CgiAbilityLeaf;\n supportWiFi?: CgiAbilityLeaf;\n supportWiFiSdb?: CgiAbilityLeaf;\n supportZoom?: CgiAbilityLeaf;\n supportZoomAndFocusSliderCfg?: CgiAbilityLeaf;\n talk?: CgiAbilityLeaf;\n videoClip?: CgiAbilityLeaf;\n waterMark?: CgiAbilityLeaf;\n white_balance?: CgiAbilityLeaf;\n\n [key: string]: CgiAbilityLeaf | undefined;\n};\n\nexport type CgiAbility = {\n Ability: { abilityChn?: CgiAbilityChn[] } & Record<string, JsonValue>;\n};\n\nexport type CgiGetAbilityValue = CgiAbility;\n\nexport type CgiGetAbilityResponse =\n ReolinkCmdResponseExt<CgiGetAbilityValue> & {\n cmd: \"GetAbility\";\n };\n\nexport type CgiDevInfo = {\n B485?: number;\n IOInputNum?: number;\n IOOutputNum?: number;\n audioNum?: number;\n buildDay?: string;\n cfgVer?: string;\n channelNum?: number;\n detail?: string;\n diskNum?: number;\n exactType?: string;\n firmVer?: string;\n frameworkVer?: number;\n hardVer?: string;\n model?: string;\n name?: string;\n pakSuffix?: string;\n serial?: string;\n type?: string;\n wifi?: number;\n};\n\nexport type CgiGetDevInfoValue = {\n DevInfo: CgiDevInfo;\n};\n\nexport type CgiGetDevInfoResponse =\n ReolinkCmdResponseExt<CgiGetDevInfoValue> & {\n cmd: \"GetDevInfo\";\n };\n\nexport type CgiOsd = {\n channel: number;\n osdChannel?: number;\n osdTime?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetOsdValue = {\n Osd?: CgiOsd;\n} & Record<string, JsonValue>;\n\nexport type CgiSetOsdParam = {\n Osd: {\n channel: number;\n osdChannel?: number;\n osdTime?: number;\n };\n};\n\nexport type CgiWhiteLed = {\n channel: number;\n state?: number;\n bright?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiSetWhiteLedParam = {\n WhiteLed: CgiWhiteLed;\n};\n\nexport type CgiPirInfo = {\n channel: number;\n enable: number;\n} & Record<string, JsonValue>;\n\nexport type CgiSetPirInfoParam = {\n pirInfo: CgiPirInfo;\n};\n\nexport type CgiPtzPreset = {\n enable?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiAudioAlarmPlayParam =\n | ({ channel: number } & { alarm_mode: \"times\"; times: number })\n | ({ channel: number } & { alarm_mode: \"manul\"; manual_switch: number });\n\nexport type CgiNetPort = Record<string, JsonValue>;\n\nexport type CgiBattery = {\n batteryPercent?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiDeviceInfoEntries = [\n CgiGetChnTypeInfoResponse | undefined,\n CgiGetAiStateResponse | undefined,\n CgiGetEncResponse | undefined,\n];\n\nexport type DeviceInputData = {\n hasBattery: boolean;\n hasPirEvents: boolean;\n hasFloodlight: boolean;\n hasPtz: boolean;\n sleeping: boolean;\n};\n\nexport type EventsResponse = {\n motion: boolean;\n objects: string[];\n entries: Array<ReolinkCmdResponseExt<JsonValue> | undefined>;\n};\n\nexport type DeviceInfoResponse = {\n channelStatus?: CgiChannelStatusEntry;\n abilities?: CgiAbilityChn;\n ai?: CgiAiStateValue;\n channelInfo?: CgiChnTypeInfoValue;\n enc?: CgiEncValue;\n entries: CgiDeviceInfoEntries;\n};\n\nexport type BatteryInfoResponse = {\n batteryLevel: number;\n sleeping: boolean;\n entries: [CgiBattery | undefined, CgiChannelStatusEntry | undefined];\n};\n\nexport type DeviceStatusResponse = {\n floodlightEnabled?: boolean;\n pirEnabled?: boolean;\n ptzPresets?: CgiPtzPreset[];\n osd?: ReolinkCmdResponseExt<CgiGetOsdValue>;\n entries: Array<ReolinkCmdResponseExt<JsonValue>>;\n};\n\n// VOD (Video On Demand) types for hub/NVR recordings\nexport type VodSearchStatus = {\n year: number;\n mon: number;\n /** Bitmap string indicating which days of the month have recordings (e.g., \"1111111100000000000000000000000\") */\n table?: string;\n /** Legacy field - may not be present */\n day?: number;\n /** Legacy field - may not be present */\n month?: number;\n};\n\nexport type VodFile = {\n type: string;\n StartTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n EndTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n PlaybackTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n name: string;\n /** File size in bytes - API may return as string or number */\n size: number | string;\n};\n\nexport type VodSearchResult = {\n SearchResult?: {\n Status?: VodSearchStatus[];\n File?: VodFile[];\n };\n};\n\nexport type VodSearchResponse = ReolinkCmdResponseExt<VodSearchResult> & {\n cmd: \"Search\";\n};\n\ntype RecordingsCacheKey = string;\n\ntype RecordingsCacheEntry = {\n data: Array<RecordingFile>;\n expiresAt: number;\n};\n\n/**\n * Parameters for getVideoclips() recording search (CGI API).\n * Note: CGI API uses different parameters than Baichuan API.\n */\nexport interface CgiGetVideoclipsParams {\n /** Channel number (0-based). Required for NVR, optional for standalone cameras. */\n channel: number;\n /** Start date/time for search */\n start: Date;\n /** End date/time for search */\n end: Date;\n /** Stream type: \"main\" (default), \"sub\", \"autotrack_main\", \"autotrack_sub\", \"telephoto_main\", \"telephoto_sub\" */\n streamType?: string;\n /** For multifocal cameras: logical channel (0 or 1) */\n iLogicChannel?: number;\n /** If true, automatically search day-by-day when Status table is available (default: false) */\n autoSearchByDay?: boolean;\n /** If true, bypass cache and fetch fresh data (default: false) */\n bypassCache?: boolean;\n /**\n * If true, fetch streaming URLs for each recording.\n * This adds latency as it requires additional API calls.\n * Default: false.\n */\n fetchStreamUrls?: boolean;\n /** Stream URL type (only when fetchStreamUrls=true): \"FLV\" (default), \"RTMP\", \"Playback\" */\n streamUrlType?: \"FLV\" | \"RTMP\" | \"Playback\";\n}\n\n/**\n * Options for collecting NVR diagnostics.\n */\nexport interface CollectNvrDiagnosticsOptions {\n /** Logger for progress messages */\n logger: Logger;\n}\n\n/**\n * Parameters for getting VOD URL for playback, download, or streaming.\n */\nexport interface GetVodUrlParams {\n /** Request type: \"Playback\" (default), \"Download\", \"FLV\", \"RTMP\", \"NVR_DOWNLOAD\" */\n requestType?: \"Playback\" | \"Download\" | \"FLV\" | \"RTMP\" | \"NVR_DOWNLOAD\";\n /** Stream type for FLV/RTMP: \"main\" (default), \"sub\" */\n streamType?: string;\n /** Video stream type (alias for streamType, used when preparing from VodFile) */\n videoStreamType?: string;\n /** Start time string for Playback/Download */\n startTime?: string;\n /** Start time as JavaScript Date (for NVR preparation, will be converted to Reolink format) */\n startTimeObj?: Date;\n /** End time as JavaScript Date (for NVR preparation, will be converted to Reolink format) */\n endTimeObj?: Date;\n /** If true, automatically prepare file for NVR/Hub when possible (default: true for Playback/Download/NVR_DOWNLOAD) */\n prepare?: boolean;\n /** Seek position in seconds (for FLV) */\n seek?: number;\n}\n\nexport class ReolinkCgiApi {\n readonly client: ReolinkHttpClient;\n private logger: Logger = console;\n private debugConfig: DebugConfig = {\n general: false,\n debugRtsp: false,\n traceNativeStream: false,\n traceRecordings: false,\n traceEvents: false,\n traceTalk: false,\n dumpEnabled: false,\n dumpDir: \"\",\n dumpBcMedia: false,\n dumpNals: false,\n };\n\n // Recordings cache: key -> { data, expiresAt }\n // Unified cache for both NVR and Device recordings (always enriched)\n private recordingsCache = new Map<RecordingsCacheKey, RecordingsCacheEntry>();\n\n // Some devices (notably some Hub firmwares) do not support the `NvrDownload` prepare command.\n // Cache the capability to avoid repeated failing calls.\n private nvrDownloadPrepareSupport: \"unknown\" | \"supported\" | \"unsupported\" =\n \"unknown\";\n\n // Default cache TTL: 5 minutes\n private recordingsCacheTtlMs = 5 * 60 * 1000;\n\n constructor(\n opts: ReolinkHttpClientOptions & {\n logger?: Logger;\n debugConfig?: DebugConfig;\n },\n ) {\n this.client = new ReolinkHttpClient(opts);\n if (opts.logger) {\n this.logger = opts.logger;\n }\n if (opts.debugConfig) {\n this.debugConfig = opts.debugConfig;\n }\n }\n\n /**\n * Set logger for debug output\n */\n setLogger(logger: Logger): void {\n this.logger = logger;\n }\n\n /**\n * Set debug config for trace logging\n */\n setDebugConfig(debugConfig: DebugConfig): void {\n this.debugConfig = debugConfig;\n }\n\n /**\n * Set recordings cache TTL (time to live) in milliseconds.\n * Default: 5 minutes (300000 ms)\n */\n setRecordingsCacheTtl(ttlMs: number): void {\n this.recordingsCacheTtlMs = Math.max(0, ttlMs);\n }\n\n /**\n * Clear all recordings cache entries.\n */\n clearRecordingsCache(): void {\n this.recordingsCache.clear();\n }\n\n /**\n * Get recordings cache statistics.\n */\n getRecordingsCacheStats(): {\n size: number;\n ttlMs: number;\n entries: Array<{ key: string; expiresAt: number; expired: boolean }>;\n } {\n const now = Date.now();\n const entries: Array<{ key: string; expiresAt: number; expired: boolean }> =\n [];\n for (const [key, entry] of this.recordingsCache.entries()) {\n entries.push({\n key,\n expiresAt: entry.expiresAt,\n expired: entry.expiresAt < now,\n });\n }\n return {\n size: this.recordingsCache.size,\n ttlMs: this.recordingsCacheTtlMs,\n entries,\n };\n }\n\n /**\n * Clear expired recordings cache entries.\n */\n private cleanRecordingsCache(): void {\n const now = Date.now();\n for (const [key, entry] of this.recordingsCache.entries()) {\n if (entry.expiresAt < now) {\n this.recordingsCache.delete(key);\n }\n }\n }\n\n /**\n * Generate cache key for NVR recordings search.\n */\n private getNvrRecordingsCacheKey(\n channel: number,\n startTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n endTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n streamType: string,\n iLogicChannel: number,\n autoSearchByDay: boolean,\n ): RecordingsCacheKey {\n return `nvr:${channel}:${startTime.year}-${startTime.mon}-${startTime.day}-${startTime.hour}-${startTime.min}-${startTime.sec}:${endTime.year}-${endTime.mon}-${endTime.day}-${endTime.hour}-${endTime.min}-${endTime.sec}:${streamType}:${iLogicChannel}:${autoSearchByDay ? 1 : 0}`;\n }\n\n async login(): Promise<void> {\n await this.client.login();\n }\n\n async logout(): Promise<void> {\n await this.client.logout();\n }\n\n async call<TValue = JsonValue, TParam = JsonValue>(\n cmd: string,\n param?: TParam,\n action = 0,\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n // Avoid `param: undefined` with exactOptionalPropertyTypes\n if (param === undefined)\n return await this.client.call<TValue, TParam>(cmd, { action });\n return await this.client.call<TValue, TParam>(cmd, { action, param });\n }\n\n async callMany<TValue = JsonValue>(\n cmds: ReolinkCmdRequest[],\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n return await this.client.callMany<TValue>(cmds);\n }\n\n private static findFirstRtspUrl(v: unknown, depth = 0): string | undefined {\n if (depth > 6) return undefined;\n if (typeof v === \"string\") {\n const s = v.trim();\n return s.startsWith(\"rtsp://\") ? s : undefined;\n }\n if (!v || typeof v !== \"object\") return undefined;\n if (Array.isArray(v)) {\n for (const item of v) {\n const found = ReolinkCgiApi.findFirstRtspUrl(item, depth + 1);\n if (found) return found;\n }\n return undefined;\n }\n\n const obj = v as Record<string, unknown>;\n const directKeys = [\"rtspUrl\", \"RtspUrl\", \"rtsp\", \"url\"];\n for (const k of directKeys) {\n const found = ReolinkCgiApi.findFirstRtspUrl(obj[k], depth + 1);\n if (found) return found;\n }\n for (const k of Object.keys(obj)) {\n const found = ReolinkCgiApi.findFirstRtspUrl(obj[k], depth + 1);\n if (found) return found;\n }\n return undefined;\n }\n\n // Common wrappers\n async GetDevInfo(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetDevInfoValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetDevInfo\", param);\n }\n\n /**\n * CGI equivalent of Baichuan `getInfo()`.\n *\n * Uses `GetDevInfo` and returns a minimal normalized map compatible with the Baichuan helper:\n * - type\n * - hardwareVersion\n * - firmwareVersion\n * - itemNo\n * - serialNumber\n * - name\n */\n async getInfo(\n channel?: number,\n options?: {\n /** List of normalized fields to return. Defaults to the canonical minimal set used by Baichuan getInfo(). */\n tags?: ReolinkDeviceInfoTag[];\n },\n ): Promise<Partial<ReolinkDeviceInfo>> {\n const rsp = await this.GetDevInfo(channel);\n const devInfo = (rsp as any)?.[0]?.value?.DevInfo as CgiDevInfo | undefined;\n\n const normalized: Partial<ReolinkDeviceInfo> = {};\n const type = (devInfo?.type ?? devInfo?.model ?? devInfo?.exactType) as\n | string\n | undefined;\n const itemNo = (devInfo?.exactType ?? devInfo?.model ?? devInfo?.detail) as\n | string\n | undefined;\n if (typeof type === \"string\") normalized.type = type;\n if (typeof devInfo?.hardVer === \"string\")\n normalized.hardwareVersion = devInfo.hardVer;\n if (typeof devInfo?.firmVer === \"string\")\n normalized.firmwareVersion = devInfo.firmVer;\n if (typeof itemNo === \"string\") normalized.itemNo = itemNo;\n if (typeof devInfo?.serial === \"string\")\n normalized.serialNumber = devInfo.serial;\n if (typeof devInfo?.name === \"string\") normalized.name = devInfo.name;\n\n const tags: ReolinkDeviceInfoTag[] = options?.tags?.length\n ? options.tags\n : ([\n \"type\",\n \"hardwareVersion\",\n \"firmwareVersion\",\n \"itemNo\",\n \"serialNumber\",\n \"name\",\n ] satisfies ReolinkDeviceInfoTag[]);\n\n const out: Partial<ReolinkDeviceInfo> = {};\n for (const t of tags) {\n const v = normalized[t];\n if (typeof v === \"string\") out[t] = v;\n }\n return out;\n }\n\n async GetChnTypeInfo(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetChnTypeInfo\", param);\n }\n\n async GetChannelstatus(): Promise<\n Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>\n > {\n return await this.call(\"GetChannelstatus\", undefined, 0);\n }\n\n async GetLocalLink(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetLocalLink\", param);\n }\n\n async GetWifiSignal(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetWifiSignal\", param);\n }\n\n async GetOsd(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetOsdValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetOsd\", param, 1);\n }\n\n async SetOsd(\n osd: CgiSetOsdParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetOsd\", osd, 0);\n }\n\n async GetEnc(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiEncValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetEnc\", param, 1);\n }\n\n /**\n * Return an RTSP URL for the given channel (NVR-side).\n * Uses the exact request body shape:\n * `[{\"cmd\":\"GetRtspUrl\",\"action\":0,\"param\":{\"channel\":<channel>}}]`.\n */\n async GetRtspUrl(channel: number): Promise<Array<CgiGetRtspUrlResponse>> {\n const body: ReolinkCmdRequest[] = [\n { cmd: \"GetRtspUrl\", action: 0, param: { channel } },\n ];\n return (await this.callMany(body)) as Array<CgiGetRtspUrlResponse>;\n }\n\n /** Convenience helper: extracts the first `rtsp://...` from GetRtspUrl response. */\n async getRtspUrl(channel: number): Promise<string> {\n const rsp = await this.GetRtspUrl(channel);\n const value = rsp?.[0]?.value;\n const url = ReolinkCgiApi.findFirstRtspUrl(value);\n if (!url) {\n throw new Error(\n `GetRtspUrl: RTSP URL not found in response (channel=${channel})`,\n );\n }\n return url;\n }\n\n async GetAiState(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiAiStateValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetAiState\", param, 0);\n }\n\n async GetMdState(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetMdState\", param, 0);\n }\n\n async GetEvents(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetEvents\", param, 0);\n }\n\n async GetBatteryInfo(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetBatteryInfo\", param, 0);\n }\n\n async GetWhiteLed(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetWhiteLed\", param, 0);\n }\n\n async SetWhiteLed(\n whiteLed: CgiSetWhiteLedParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetWhiteLed\", whiteLed, 0);\n }\n\n async GetPirInfo(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetPirInfo\", param, 0);\n }\n\n async SetPirInfo(\n pirInfo: CgiSetPirInfoParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetPirInfo\", pirInfo, 0);\n }\n\n async GetPtzPreset(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetPtzPreset\", param, 1);\n }\n\n async GetAudioAlarmV20(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetAudioAlarmV20\", param, 0);\n }\n\n async AudioAlarmPlay(\n params: CgiAudioAlarmPlayParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"AudioAlarmPlay\", params, 0);\n }\n\n async GetNetPort(): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"GetNetPort\", {});\n }\n\n async SetNetPort(\n netPort: CgiNetPort,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetNetPort\", { NetPort: netPort });\n }\n\n async Reboot(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"Reboot\", param);\n }\n\n async GetAbility(): Promise<Array<ReolinkCmdResponseExt<CgiAbility>>> {\n const username = this.client.getUsername();\n return await this.call(\"GetAbility\", {\n User: {\n userName: username,\n },\n });\n }\n\n // --------------------\n // High-level helpers (batch-oriented)\n // --------------------\n\n /** Returns the list of channels that have a non-empty UID (typically the connected cameras on NVR/Home Hub). */\n async getChannels(options?: { useChannelNumFallback?: boolean }): Promise<{\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n }> {\n const channelsResponse = await this.GetChannelstatus();\n const status = channelsResponse?.[0]?.value?.status;\n let channels = (status ?? [])\n .filter((s) => !!s?.uid)\n .map((s) => Number(s?.channel))\n .filter((n) => Number.isFinite(n));\n\n // Fallback for multi-focal cameras: if no channels found and fallback is enabled, use channelNum from GetDevInfo\n if (channels.length === 0 && options?.useChannelNumFallback) {\n try {\n const devInfoRsp = await this.GetDevInfo();\n const devInfo = (devInfoRsp as any)?.[0]?.value?.DevInfo as\n | CgiDevInfo\n | undefined;\n const channelNum = devInfo?.channelNum;\n if (channelNum != null && channelNum > 0) {\n channels = Array.from({ length: channelNum }, (_, i) => i);\n }\n } catch (error) {\n // Ignore errors when trying to get channelNum fallback\n }\n }\n\n return { channels, channelsResponse };\n }\n\n async getNvrInfo(): Promise<{\n abilities: CgiAbility | undefined;\n nvrData: CgiGetDevInfoValue | undefined;\n devInfo: CgiDevInfo | undefined;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n }> {\n const username = this.client.getUsername();\n const body: ReolinkCmdRequest[] = [\n { cmd: \"GetAbility\", action: 0, param: { User: { userName: username } } },\n { cmd: \"GetDevInfo\", action: 0, param: {} },\n ];\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n const abilities = response.find((item: any) => item?.cmd === \"GetAbility\")\n ?.value as CgiAbility | undefined;\n const nvrData = response.find((item: any) => item?.cmd === \"GetDevInfo\")\n ?.value as CgiGetDevInfoValue | undefined;\n const devInfo = nvrData?.DevInfo;\n\n return { abilities, nvrData, devInfo, response };\n }\n\n async getDevicesInfo(options?: { useChannelNumFallback?: boolean }): Promise<{\n devicesData: Record<number, DeviceInfoResponse>;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n requestBody: ReolinkCmdRequest[];\n }> {\n const { channels, channelsResponse } = await this.getChannels(options);\n\n const username = this.client.getUsername();\n\n const body: ReolinkCmdRequest[] = [];\n\n body.push({\n cmd: \"GetAbility\",\n action: 0,\n param: { User: { userName: username } },\n });\n\n for (const channel of channels) {\n body.push(\n { cmd: \"GetChnTypeInfo\", action: 0, param: { channel } },\n { cmd: \"GetAiState\", action: 0, param: { channel } },\n { cmd: \"GetEnc\", action: 1, param: { channel } },\n );\n }\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n\n const abilities = (response[0] as CgiGetAbilityResponse | undefined)?.value;\n const abilitiesChn = abilities?.Ability?.abilityChn;\n\n const ret: Record<number, DeviceInfoResponse> = {};\n for (let i = 0; i < channels.length; i++) {\n const channel = channels[i]!;\n const base = 1 + i * 3;\n const chnInfoItem = response[base] as\n | CgiGetChnTypeInfoResponse\n | undefined;\n const aiItem = response[base + 1] as CgiGetAiStateResponse | undefined;\n const encItem = response[base + 2] as CgiGetEncResponse | undefined;\n\n const channelStatus = channelsResponse?.[0]?.value?.status?.find(\n (item) => item?.channel === channel,\n );\n\n const device: DeviceInfoResponse = {\n entries: [chnInfoItem, aiItem, encItem],\n };\n if (channelStatus) device.channelStatus = channelStatus;\n const perChannelAbilities = abilitiesChn?.[channel];\n if (perChannelAbilities) device.abilities = perChannelAbilities;\n\n if (!(chnInfoItem as any)?.error)\n device.channelInfo = (chnInfoItem as any)?.value as CgiChnTypeInfoValue;\n if (!(aiItem as any)?.error)\n device.ai = (aiItem as any)?.value as CgiAiStateValue;\n if (!(encItem as any)?.error)\n device.enc = (encItem as any)?.value as CgiEncValue;\n\n ret[channel] = device;\n }\n\n return {\n devicesData: ret,\n response,\n channels,\n channelsResponse,\n requestBody: body,\n };\n }\n\n async getAllChannelsEvents(options?: {\n useChannelNumFallback?: boolean;\n }): Promise<{\n parsed: Record<number, EventsResponse>;\n response: ReolinkCmdResponse[];\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n requestBody: ReolinkCmdRequest[];\n }> {\n const { channels, channelsResponse } = await this.getChannels(options);\n\n // Always call all relevant endpoints per channel and merge.\n const body: ReolinkCmdRequest[] = [];\n const index: Record<\n number,\n { events?: number; motion?: number; ai?: number }\n > = {};\n\n for (const channel of channels) {\n index[channel] = {};\n body.push({ cmd: \"GetEvents\", action: 0, param: { channel } });\n index[channel].events = body.length - 1;\n body.push({ cmd: \"GetMdState\", action: 0, param: { channel } });\n index[channel].motion = body.length - 1;\n body.push({ cmd: \"GetAiState\", action: 0, param: { channel } });\n index[channel].ai = body.length - 1;\n }\n\n const response = await this.callMany(body);\n\n const processDetections = (aiResponse: any): string[] => {\n const classes: string[] = [];\n for (const key of Object.keys(aiResponse ?? {})) {\n if (key === \"channel\") continue;\n const alarmState = aiResponse?.[key]?.alarm_state;\n if (alarmState) classes.push(key);\n }\n return classes;\n };\n\n const parsed: Record<number, EventsResponse> = {};\n for (const channel of channels) {\n const { events, motion, ai } = index[channel] ?? {};\n const eventsEntry = events != null ? response[events] : undefined;\n const motionEntry = motion != null ? response[motion] : undefined;\n const aiEntry = ai != null ? response[ai] : undefined;\n\n const classes = new Set<string>();\n for (const c of processDetections((aiEntry as any)?.value))\n classes.add(c);\n for (const c of processDetections(\n (eventsEntry as any)?.value?.ai ?? (eventsEntry as any)?.value,\n ))\n classes.add(c);\n\n const list = Array.from(classes);\n const objects = list.filter((cl) => cl !== \"other\");\n const hasMotion = !!(motionEntry as any)?.value?.state || list.length > 0;\n\n parsed[channel] = {\n motion: hasMotion,\n objects,\n entries: [eventsEntry as any, motionEntry as any, aiEntry as any],\n };\n }\n\n return { parsed, response, channels, channelsResponse, requestBody: body };\n }\n\n async getAllChannelsBatteryInfo(options?: {\n useChannelNumFallback?: boolean;\n }): Promise<{\n batteryInfoData: Record<number, BatteryInfoResponse>;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n requestBody: ReolinkCmdRequest[];\n }> {\n const { channels, channelsResponse } = await this.getChannels(options);\n\n // Always call battery info for every channel and merge with Channelstatus.\n const body: ReolinkCmdRequest[] = [{ cmd: \"GetChannelstatus\" }];\n const index: Record<number, number> = {};\n for (const channel of channels) {\n body.push({ cmd: \"GetBatteryInfo\", action: 0, param: { channel } });\n index[channel] = body.length - 1;\n }\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n const channelStatusData = response[0];\n\n const batteryInfoData: Record<number, BatteryInfoResponse> = {};\n for (const channel of channels) {\n const batteryInfoEntry = ((response[index[channel]!] as any)?.value\n ?.Battery ?? undefined) as CgiBattery | undefined;\n const channelStatusEntry = (\n channelStatusData as any\n )?.value?.status?.find((elem: any) => elem?.channel === channel) as\n | CgiChannelStatusEntry\n | undefined;\n batteryInfoData[channel] = {\n entries: [batteryInfoEntry, channelStatusEntry],\n batteryLevel: Number(batteryInfoEntry?.batteryPercent ?? 0),\n sleeping: channelStatusEntry?.sleep === 1,\n };\n }\n\n return {\n batteryInfoData,\n response,\n channels,\n channelsResponse,\n requestBody: body,\n };\n }\n\n async getStatusInfo(channelsMap: Map<number, DeviceInputData>): Promise<{\n deviceStatusData: Record<number, DeviceStatusResponse>;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n }> {\n const body: ReolinkCmdRequest[] = [];\n const index: Record<\n number,\n { osd?: number; floodlight?: number; pir?: number; presets?: number }\n > = {};\n\n for (const [channel, info] of channelsMap.entries()) {\n index[channel] = {};\n if (info.sleeping) continue;\n\n body.push({ cmd: \"GetOsd\", action: 1, param: { channel } });\n index[channel].osd = body.length - 1;\n\n if (info.hasFloodlight) {\n body.push({ cmd: \"GetWhiteLed\", action: 0, param: { channel } });\n index[channel].floodlight = body.length - 1;\n }\n\n if (info.hasPirEvents) {\n body.push({ cmd: \"GetPirInfo\", action: 0, param: { channel } });\n index[channel].pir = body.length - 1;\n }\n\n if (info.hasPtz) {\n body.push({ cmd: \"GetPtzPreset\", action: 1, param: { channel } });\n index[channel].presets = body.length - 1;\n }\n }\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n\n const deviceStatusData: Record<number, DeviceStatusResponse> = {};\n for (const [channel, info] of channelsMap.entries()) {\n const { osd, floodlight, pir, presets } = index[channel] ?? {};\n deviceStatusData[channel] = { entries: [] };\n\n if (osd != null) {\n const osdEntry = response[osd]!;\n deviceStatusData[channel].osd =\n osdEntry as ReolinkCmdResponseExt<CgiGetOsdValue>;\n deviceStatusData[channel].entries.push(osdEntry);\n }\n\n if (info.hasFloodlight && floodlight != null) {\n const floodlightEntry = response[floodlight]!;\n deviceStatusData[channel].floodlightEnabled =\n (floodlightEntry as any)?.value?.WhiteLed?.state === 1;\n deviceStatusData[channel].entries.push(floodlightEntry);\n }\n\n if (info.hasPirEvents && pir != null) {\n const pirEntry = response[pir]!;\n deviceStatusData[channel].pirEnabled =\n (pirEntry as any)?.value?.pirInfo?.enable === 1;\n deviceStatusData[channel].entries.push(pirEntry);\n }\n\n if (info.hasPtz && presets != null) {\n const ptzPresetsEntry = response[presets]!;\n const list = (ptzPresetsEntry as any)?.value?.PtzPreset;\n deviceStatusData[channel].ptzPresets = Array.isArray(list)\n ? (list.filter((p: any) => p?.enable === 1) as CgiPtzPreset[])\n : [];\n deviceStatusData[channel].entries.push(ptzPresetsEntry);\n }\n }\n\n return { deviceStatusData, response };\n }\n\n /** Convenience wrapper returning raw OSD response for a channel. */\n async getOsd(\n channel: number,\n ): Promise<ReolinkCmdResponseExt<CgiGetOsdValue> | undefined> {\n const rsp = await this.GetOsd(channel);\n return rsp?.[0];\n }\n\n /** Set channel OSD. Accepts either a full `Osd` object or a minimal `{ Osd: ... }` payload. */\n async setOsd(channel: number, osd: any): Promise<void> {\n const valueOsd = osd?.value?.Osd ?? osd?.Osd;\n const osdChannel = valueOsd?.osdChannel ?? osd?.osdChannel;\n const osdTime = valueOsd?.osdTime ?? osd?.osdTime;\n\n const payload = {\n Osd: {\n channel,\n osdChannel,\n osdTime,\n },\n };\n\n await this.call(\"SetOsd\", payload, 0);\n }\n\n async getEncoderConfiguration(channel: number): Promise<CgiEnc | undefined> {\n const rsp = await this.GetEnc(channel);\n return (rsp as any)?.[0]?.value?.Enc as CgiEnc | undefined;\n }\n\n /** CGI snapshot via `cmd=Snap` (binary JPEG). */\n async jpegSnapshot(\n channel: number,\n opts?: {\n timeoutMs?: number;\n snapType?: \"main\" | \"sub\";\n iLogicChannel?: number;\n },\n ): Promise<Buffer> {\n return await this.client.snap(channel, {\n ...(opts?.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n ...(opts?.snapType !== undefined ? { snapType: opts.snapType } : {}),\n ...(opts?.iLogicChannel !== undefined\n ? { iLogicChannel: opts.iLogicChannel }\n : {}),\n });\n }\n\n async getSiren(channel: number): Promise<{ enabled: boolean }> {\n const rsp = await this.GetAudioAlarmV20(channel);\n return { enabled: (rsp as any)?.[0]?.value?.Audio?.enable === 1 };\n }\n\n async setSiren(\n channel: number,\n on: boolean,\n duration?: number,\n ): Promise<{\n value: JsonValue | undefined;\n data: Array<ReolinkCmdResponseExt<JsonValue>>;\n }> {\n const params: CgiAudioAlarmPlayParam = duration\n ? { channel, alarm_mode: \"times\", times: duration }\n : { channel, alarm_mode: \"manul\", manual_switch: on ? 1 : 0 };\n\n const rsp = await this.AudioAlarmPlay(params);\n return {\n value: (rsp as any)?.[0]?.value ?? (rsp as any)?.value,\n data: rsp,\n };\n }\n\n async setWhiteLedState(\n channel: number,\n on?: boolean,\n brightness?: number,\n ): Promise<void> {\n const settings: any = { channel };\n if (on !== undefined) settings.state = on ? 1 : 0;\n if (brightness !== undefined) settings.bright = brightness;\n await this.SetWhiteLed({ WhiteLed: settings });\n }\n\n async getPirState(\n channel: number,\n ): Promise<{ enabled: boolean; state: CgiPirInfo | undefined }> {\n const rsp = await this.GetPirInfo(channel);\n const state = (rsp as any)?.[0]?.value?.pirInfo as CgiPirInfo | undefined;\n return { enabled: state?.enable === 1, state };\n }\n\n async setPirState(channel: number, on: boolean): Promise<void> {\n const current = await this.getPirState(channel);\n const newState = on ? 1 : 0;\n const currentEnable = (current?.state as any)?.enable;\n if (currentEnable === newState) return;\n\n const pirInfo = {\n ...(current?.state && typeof current.state === \"object\"\n ? current.state\n : {}),\n channel,\n enable: newState,\n };\n await this.SetPirInfo({ pirInfo });\n }\n\n async getLocalLink(channel: number): Promise<{\n activeLink: string | undefined;\n wifiSignal: number | undefined;\n isWifi: boolean;\n }> {\n const body: ReolinkCmdRequest[] = [\n { cmd: \"GetLocalLink\", action: 0, param: {} },\n { cmd: \"GetWifiSignal\", action: 0, param: { channel } },\n ];\n const rsp = await this.callMany(body);\n const activeLink = (rsp as any).find((e: any) => e?.cmd === \"GetLocalLink\")\n ?.value?.LocalLink?.activeLink as string | undefined;\n const wifiSignal = (rsp as any).find((e: any) => e?.cmd === \"GetWifiSignal\")\n ?.value?.wifiSignal as number | undefined;\n\n let isWifi = false;\n if (wifiSignal !== undefined) {\n isWifi = wifiSignal >= 0 && wifiSignal <= 4;\n }\n if (!isWifi && activeLink) {\n isWifi = activeLink !== \"LAN\";\n }\n\n return { activeLink, wifiSignal, isWifi };\n }\n\n /**\n * Comprehensive NVR/HUB diagnostics.\n * Collects and returns all available information about the NVR/HUB device and all its channels.\n * Automatically prints diagnostics after collection using the provided logger.\n *\n * @param options - Configuration object with logger property for progress messages\n * @returns Complete diagnostics data including NVR info, channels, and per-channel details\n */\n async collectNvrDiagnostics(\n options: CollectNvrDiagnosticsOptions,\n ): Promise<Record<string, unknown>> {\n const diagnostics = await collectNvrDiagnostics({\n cgi: this,\n logger: options.logger,\n });\n return diagnostics;\n }\n\n // --------------------\n // VOD (Video On Demand) methods for hub/NVR\n // --------------------\n\n /**\n * Get videoclips (recordings) via CGI Search API.\n * This command does NOT wake up battery cameras connected to the hub.\n *\n * Always returns enriched recording files with parsed metadata, detection flags, and timestamps.\n * Note: For best results, use autoSearchByDay=true to automatically search day-by-day when Status table is available.\n *\n * @example\n * ```typescript\n * // Search yesterday's recordings on channel 0\n * const yesterday = new Date();\n * yesterday.setDate(yesterday.getDate() - 1);\n * yesterday.setHours(0, 0, 0, 0);\n * const endOfDay = new Date(yesterday);\n * endOfDay.setHours(23, 59, 59, 999);\n *\n * const clips = await cgiApi.getVideoclips({\n * channel: 0,\n * start: yesterday,\n * end: endOfDay,\n * streamType: \"main\",\n * });\n * ```\n *\n * @param params - Search parameters\n * @returns Array of enriched recording files (RecordingFile[])\n */\n async getVideoclips(\n params: CgiGetVideoclipsParams,\n ): Promise<Array<RecordingFile>> {\n const { channel, start, end } = params;\n const streamType = params.streamType ?? \"main\";\n const iLogicChannel = params.iLogicChannel ?? 0;\n const autoSearchByDay = params.autoSearchByDay ?? false;\n const bypassCache = params.bypassCache ?? false;\n\n // Convert Date to Reolink time format\n let startTime = this.dateToReolinkTime(start);\n let endTime = this.dateToReolinkTime(end);\n\n // Sanitize end date: if it's midnight (00:00:00) of the next day, cap it to 23:59:59 of the same day as start\n // This ensures we search until the end of the requested day, not the beginning of the next day\n if (endTime.hour === 0 && endTime.min === 0 && endTime.sec === 0) {\n // Check if end is on a different day than start\n const startDateKey = `${startTime.year}-${startTime.mon}-${startTime.day}`;\n const endDateKey = `${endTime.year}-${endTime.mon}-${endTime.day}`;\n\n if (endDateKey !== startDateKey) {\n // End is on the next day at midnight, so cap it to the end of start day (23:59:59)\n endTime = {\n year: startTime.year,\n mon: startTime.mon,\n day: startTime.day,\n hour: 23,\n min: 59,\n sec: 59,\n };\n }\n }\n\n // Log date conversion for debugging (using both general debug and recordings trace)\n // debugLog(this.debugConfig, this.logger, \"getVideoclips\",\n // `Date conversion: start=${start.toISOString()} (local: ${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}-${String(start.getDate()).padStart(2, '0')} ${String(start.getHours()).padStart(2, '0')}:${String(start.getMinutes()).padStart(2, '0')}:${String(start.getSeconds()).padStart(2, '0')}) -> ReolinkTime=${JSON.stringify(startTime)}, ` +\n // `end=${end.toISOString()} (local: ${end.getFullYear()}-${String(end.getMonth() + 1).padStart(2, '0')}-${String(end.getDate()).padStart(2, '0')} ${String(end.getHours()).padStart(2, '0')}:${String(end.getMinutes()).padStart(2, '0')}:${String(end.getSeconds()).padStart(2, '0')}) -> ReolinkTime=${JSON.stringify(endTime)}`\n // );\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Date conversion: start=${start.toISOString()} -> ReolinkTime=${JSON.stringify(startTime)}, end=${end.toISOString()} -> ReolinkTime=${JSON.stringify(endTime)}`,\n );\n\n // Check cache first (unless bypassing)\n if (!bypassCache) {\n this.cleanRecordingsCache();\n const cacheKey = this.getNvrRecordingsCacheKey(\n channel,\n startTime,\n endTime,\n streamType,\n iLogicChannel,\n autoSearchByDay,\n );\n const cached = this.recordingsCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now()) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Cache hit: returning ${cached.data.length} cached enriched recordings`,\n );\n return cached.data;\n }\n }\n\n // If autoSearchByDay is enabled, first get status to find days with recordings\n if (autoSearchByDay) {\n const startMs = start.getTime();\n const endMs = end.getTime();\n\n const clampDateToWindow = (d: Date): Date => {\n const t = d.getTime();\n if (!Number.isFinite(t)) return d;\n if (t < startMs) return new Date(startMs);\n if (t > endMs) return new Date(endMs);\n return d;\n };\n\n const statusParam: any = {\n Search: {\n channel,\n onlyStatus: 1,\n streamType,\n StartTime: startTime,\n EndTime: endTime,\n },\n };\n if (iLogicChannel > 0) {\n statusParam.Search.iLogicChannel = iLogicChannel;\n }\n\n const statusResponse = await this.call<VodSearchResult>(\n \"Search\",\n statusParam,\n 0,\n );\n const allResults: Array<VodSearchResponse> = [];\n\n // Parse Status table to find days with recordings\n for (const statusResult of statusResponse) {\n if (\n statusResult.code === 0 &&\n statusResult.value?.SearchResult?.Status\n ) {\n for (const status of statusResult.value.SearchResult.Status) {\n if (status.table) {\n // Find days with recordings from bitmap\n const daysWithRecordings: number[] = [];\n for (let i = 0; i < status.table.length; i++) {\n if (status.table[i] === \"1\") {\n daysWithRecordings.push(i + 1); // Days are 1-indexed\n }\n }\n\n // Search each day individually\n for (const day of daysWithRecordings) {\n const year = status.year;\n const month = status.mon || status.month || 1;\n\n // IMPORTANT: Status table is typically per-month, and may include days outside the\n // requested window. Restrict queries to days that overlap [start,end], and clamp\n // the per-day query range to the window.\n const dayStartDate = new Date(year, month - 1, day, 0, 0, 0, 0);\n const dayEndDate = new Date(\n year,\n month - 1,\n day,\n 23,\n 59,\n 59,\n 999,\n );\n\n // Skip days completely outside the requested window.\n if (Number.isFinite(startMs) && Number.isFinite(endMs)) {\n if (\n dayEndDate.getTime() < startMs ||\n dayStartDate.getTime() > endMs\n )\n continue;\n }\n\n const queryStartDate = clampDateToWindow(\n dayStartDate.getTime() < startMs\n ? new Date(startMs)\n : dayStartDate,\n );\n const queryEndDate = clampDateToWindow(\n dayEndDate.getTime() > endMs ? new Date(endMs) : dayEndDate,\n );\n if (queryStartDate.getTime() > queryEndDate.getTime()) continue;\n\n const dayStart = this.dateToReolinkTime(queryStartDate);\n const dayEnd = this.dateToReolinkTime(queryEndDate);\n\n const dayParam: any = {\n Search: {\n channel,\n onlyStatus: 0,\n streamType,\n StartTime: dayStart,\n EndTime: dayEnd,\n },\n };\n if (iLogicChannel > 0) {\n dayParam.Search.iLogicChannel = iLogicChannel;\n }\n\n const dayResponse = await this.call<VodSearchResult>(\n \"Search\",\n dayParam,\n 0,\n );\n // Log raw API response for debugging\n if (dayResponse && dayResponse.length > 0) {\n const firstResult = dayResponse[0];\n if (\n firstResult &&\n firstResult.code === 0 &&\n firstResult.value?.SearchResult?.File\n ) {\n const files = firstResult.value.SearchResult.File;\n if (files.length > 0 && files[0]) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Raw API response for day ${day}/${month}/${year}: ${JSON.stringify(\n {\n fileCount: files.length,\n sampleFile: {\n name: files[0].name,\n type: files[0].type,\n size: files[0].size,\n StartTime: files[0].StartTime,\n EndTime: files[0].EndTime,\n },\n },\n )}`,\n );\n }\n }\n }\n allResults.push(...(dayResponse as Array<VodSearchResponse>));\n }\n }\n }\n }\n }\n\n if (allResults.length > 0) {\n // Collect all files from search results and enrich them\n const allFiles: VodFile[] = [];\n for (const res of allResults) {\n if (res.code === 0) {\n const searchResult = res.value?.SearchResult;\n if (searchResult?.File) {\n allFiles.push(...searchResult.File);\n }\n }\n }\n\n // Enrich all files\n const enriched: RecordingFile[] = [];\n const fetchStreamUrls = params.fetchStreamUrls === true;\n const streamUrlType = params.streamUrlType ?? \"FLV\";\n\n for (const vodFile of allFiles) {\n let streamUrl: string | undefined;\n\n // Optionally fetch streaming URL\n if (fetchStreamUrls) {\n try {\n const url = await this.getVodUrl(vodFile, channel, {\n requestType: streamUrlType,\n videoStreamType: streamType,\n prepare: true,\n });\n streamUrl = url;\n } catch (e) {\n // Silently ignore - not all recordings may have streaming URLs available\n }\n }\n\n enriched.push(this.enrichVodFile(vodFile, channel, streamUrl));\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Returning ${enriched.length} enriched recording files from autoSearchByDay`,\n );\n\n // Cache enriched results\n if (!bypassCache) {\n const cacheKey = this.getNvrRecordingsCacheKey(\n channel,\n startTime,\n endTime,\n streamType,\n iLogicChannel,\n autoSearchByDay,\n );\n this.recordingsCache.set(cacheKey, {\n data: enriched,\n expiresAt: Date.now() + this.recordingsCacheTtlMs,\n });\n }\n\n return enriched;\n }\n // Fall through to normal search if no status found\n }\n\n // Normal search: if range spans multiple days, split into day-by-day queries\n const allResults: Array<VodSearchResponse> = [];\n const dayRanges = this.generateDayRanges(startTime, endTime);\n\n if (dayRanges.length === 0) {\n // No valid range, return empty\n return [];\n }\n\n if (dayRanges.length === 1) {\n // Single day range, use original start/end times\n const range = dayRanges[0];\n if (!range) {\n return [];\n }\n const param: any = {\n Search: {\n channel,\n onlyStatus: 0,\n streamType,\n StartTime: range.start,\n EndTime: range.end,\n },\n };\n\n if (iLogicChannel > 0) {\n param.Search.iLogicChannel = iLogicChannel;\n }\n\n const response = await this.call<VodSearchResult>(\"Search\", param, 0);\n allResults.push(...(response as Array<VodSearchResponse>));\n } else {\n // Multiple days: query each day separately\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Range spans ${dayRanges.length} days, splitting into separate day queries`,\n );\n\n for (const range of dayRanges) {\n const param: any = {\n Search: {\n channel,\n onlyStatus: 0,\n streamType,\n StartTime: range.start,\n EndTime: range.end,\n },\n };\n\n if (iLogicChannel > 0) {\n param.Search.iLogicChannel = iLogicChannel;\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Querying day: ${range.start.year}-${range.start.mon}-${range.start.day} (${range.start.hour}:${range.start.min}:${range.start.sec} to ${range.end.hour}:${range.end.min}:${range.end.sec})`,\n );\n\n const response = await this.call<VodSearchResult>(\"Search\", param, 0);\n allResults.push(...(response as Array<VodSearchResponse>));\n }\n }\n\n const result = allResults;\n\n // Log raw API response for debugging\n if (result && result.length > 0) {\n const firstResult = result[0];\n if (\n firstResult &&\n firstResult.code === 0 &&\n firstResult.value?.SearchResult?.File\n ) {\n const files = firstResult.value.SearchResult.File;\n if (files.length > 0 && files[0]) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Raw API response (normal search): ${JSON.stringify({\n fileCount: files.length,\n sampleFile: {\n name: files[0].name,\n type: files[0].type,\n size: files[0].size,\n StartTime: files[0].StartTime,\n EndTime: files[0].EndTime,\n },\n })}`,\n );\n }\n }\n }\n\n // Collect all files from search results\n const allFiles: VodFile[] = [];\n for (const res of result) {\n if (res.code === 0) {\n const searchResult = res.value?.SearchResult;\n if (searchResult?.File) {\n allFiles.push(...searchResult.File);\n }\n }\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Collected ${allFiles.length} raw VodFiles from API search results`,\n );\n if (allFiles.length > 0) {\n // Log first few files as sample\n const sampleSize = Math.min(3, allFiles.length);\n for (let i = 0; i < sampleSize; i++) {\n const file = allFiles[i];\n if (file) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Sample file ${i + 1}/${allFiles.length}: ${JSON.stringify({\n name: file.name,\n type: file.type,\n size: file.size,\n StartTime: file.StartTime,\n EndTime: file.EndTime,\n })}`,\n );\n }\n }\n }\n\n // Enrich each file\n const enriched: RecordingFile[] = [];\n const fetchStreamUrls = params.fetchStreamUrls === true;\n const streamUrlType = params.streamUrlType ?? \"FLV\";\n\n for (const vodFile of allFiles) {\n let streamUrl: string | undefined;\n\n // Optionally fetch streaming URL\n if (fetchStreamUrls) {\n try {\n const url = await this.getVodUrl(vodFile, channel, {\n requestType: streamUrlType,\n videoStreamType: streamType,\n prepare: true,\n });\n streamUrl = url;\n } catch (e) {\n // Silently ignore - not all recordings may have streaming URLs available\n }\n }\n\n enriched.push(this.enrichVodFile(vodFile, channel, streamUrl));\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Returning ${enriched.length} enriched recording files`,\n );\n\n // Cache enriched results\n if (!bypassCache) {\n const cacheKey = this.getNvrRecordingsCacheKey(\n channel,\n startTime,\n endTime,\n streamType,\n iLogicChannel,\n autoSearchByDay,\n );\n this.recordingsCache.set(cacheKey, {\n data: enriched,\n expiresAt: Date.now() + this.recordingsCacheTtlMs,\n });\n }\n\n return enriched;\n }\n\n /**\n * Get a JPEG thumbnail from a VOD recording using ffmpeg.\n *\n * This method fetches the VOD URL (with Download type for better ffmpeg compatibility)\n * and extracts the first valid frame using ffmpeg.\n *\n * @param params - Parameters for thumbnail extraction\n * @returns JPEG buffer\n *\n * @example\n * ```typescript\n * const thumbnail = await cgiApi.getVideoclipThumbnailJpeg({\n * channel: 0,\n * filename: \"/mnt/sda/...\",\n * ffmpegPath: \"/usr/bin/ffmpeg\",\n * });\n * fs.writeFileSync(\"thumbnail.jpg\", thumbnail);\n * ```\n */\n async getVideoclipThumbnailJpeg(params: {\n /** Channel number (0-based) */\n channel: number;\n /** Recording filename or VodFile object from getVideoclips() */\n filename: string | VodFile;\n /** Path to ffmpeg executable */\n ffmpegPath: string;\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n /** Seek position in seconds (default: 0, extracts first frame) */\n seekSeconds?: number;\n }): Promise<Buffer> {\n const {\n channel,\n filename,\n ffmpegPath,\n timeoutMs = 30000,\n seekSeconds = 0,\n } = params;\n\n // Get VOD URL with FLV type (includes authentication in URL)\n // FLV is more reliable than Download for ffmpeg since it embeds credentials\n const vodUrl = await this.getVodUrl(filename, channel, {\n requestType: \"FLV\",\n streamType: \"main\",\n prepare: true,\n seek: seekSeconds,\n });\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclipThumbnailJpeg\",\n `Extracting thumbnail from VOD URL (FLV): ${vodUrl.substring(0, 100)}... (seek=${seekSeconds}s)`,\n );\n\n // Use ffmpeg to extract first frame as JPEG\n const { spawn } = await import(\"child_process\");\n\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let stderr = \"\";\n let timedOut = false;\n\n // ffmpeg args optimized for HTTP FLV streams:\n // - analyzeduration/probesize: allow more time to find stream info\n // - fflags: handle streams with gaps/errors gracefully\n // - rw_timeout: prevent hanging on slow/broken connections\n const ffmpeg = spawn(ffmpegPath, [\n \"-y\",\n \"-analyzeduration\",\n \"10000000\",\n \"-probesize\",\n \"10000000\",\n \"-fflags\",\n \"+genpts+discardcorrupt+igndts\",\n \"-rw_timeout\",\n String(timeoutMs * 1000), // microseconds\n \"-i\",\n vodUrl,\n \"-vframes\",\n \"1\",\n \"-q:v\",\n \"2\",\n \"-f\",\n \"image2pipe\",\n \"-vcodec\",\n \"mjpeg\",\n \"pipe:1\",\n ]);\n\n const timer = setTimeout(() => {\n timedOut = true;\n ffmpeg.kill(\"SIGKILL\");\n reject(new Error(`ffmpeg timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n ffmpeg.stdout.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n ffmpeg.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n ffmpeg.on(\"close\", (code) => {\n clearTimeout(timer);\n if (timedOut) return;\n\n if (code === 0 && chunks.length > 0) {\n const jpeg = Buffer.concat(chunks);\n if (jpeg.length < 100) {\n reject(\n new Error(\n `ffmpeg produced too small output: ${jpeg.length} bytes`,\n ),\n );\n return;\n }\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclipThumbnailJpeg\",\n `Successfully extracted thumbnail: ${jpeg.length} bytes`,\n );\n resolve(jpeg);\n } else {\n reject(\n new Error(`ffmpeg exited with code ${code}: ${stderr.slice(-500)}`),\n );\n }\n });\n\n ffmpeg.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(new Error(`ffmpeg spawn error: ${err.message}`));\n });\n });\n }\n\n /**\n * Prepare a VOD file for download on NVR/Hub.\n * This is required before downloading VOD files from NVR/Hub.\n * This command does NOT wake up battery cameras connected to the hub.\n *\n * @param channel - Channel number (0-based)\n * @param startTime - Start time for the recording\n * @param endTime - End time for the recording\n * @param streamType - Stream type: \"main\" (default), \"sub\", etc.\n * @param options - Optional parameters\n * @returns Prepared filename for download\n */\n async prepareNvrVodDownload(\n channel: number,\n startTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n endTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n streamType: string = \"main\",\n options?: {\n /** For multifocal cameras: logical channel (0 or 1) */\n iLogicChannel?: number;\n },\n ): Promise<string> {\n if (this.nvrDownloadPrepareSupport === \"unsupported\") {\n throw new Error(\"NvrDownload is not supported on this device\");\n }\n\n const iLogicChannel = options?.iLogicChannel ?? 0;\n\n // Ensure time format matches exactly (year, mon, day, hour, min, sec)\n const param = {\n NvrDownload: {\n channel,\n iLogicChannel,\n streamType,\n StartTime: {\n year: startTime.year,\n mon: startTime.mon,\n day: startTime.day,\n hour: startTime.hour,\n min: startTime.min,\n sec: startTime.sec,\n },\n EndTime: {\n year: endTime.year,\n mon: endTime.mon,\n day: endTime.day,\n hour: endTime.hour,\n min: endTime.min,\n sec: endTime.sec,\n },\n },\n };\n\n const body = [\n {\n cmd: \"NvrDownload\",\n action: 1,\n param: param,\n },\n ];\n\n // Log the request for debugging\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"prepareNvrVodDownload\",\n `Request body: ${JSON.stringify(body)}`,\n );\n\n try {\n const response = await this.callMany<{\n fileList: Array<{ fileName: string; fileSize: number }>;\n }>(body);\n\n // Log the response for debugging\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"prepareNvrVodDownload\",\n `Response: ${JSON.stringify(response)}`,\n );\n\n const first = response[0];\n if (!first || first.code !== 0 || !first.value?.fileList) {\n const rspCode = (first as any)?.error?.rspCode;\n const detail = String(\n (first as any)?.error?.detail ?? \"\",\n ).toLowerCase();\n // Some firmwares report rspCode=-17 for multiple causes (including invalid params).\n // Only mark as unsupported when the device explicitly says it's not supported.\n if (\n rspCode === -17 &&\n (detail.includes(\"not support\") ||\n detail.includes(\"unsupported\") ||\n detail.includes(\"not supported\"))\n ) {\n this.nvrDownloadPrepareSupport = \"unsupported\";\n }\n throw new Error(`NvrDownload failed: ${JSON.stringify(response)}`);\n }\n\n this.nvrDownloadPrepareSupport = \"supported\";\n\n // Return the filename of the largest file\n let maxFilesize = 0;\n let filename = \"\";\n for (const file of first.value.fileList) {\n const filesize = Number(file.fileSize);\n if (filesize > maxFilesize) {\n maxFilesize = filesize;\n filename = file.fileName;\n }\n }\n\n if (!filename) {\n throw new Error(`NvrDownload: no files found in response`);\n }\n\n return filename;\n } catch (error) {\n // Log error for debugging\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"prepareNvrVodDownload\",\n `Error: ${error instanceof Error ? error.message : String(error)}`,\n );\n throw error;\n }\n }\n\n /**\n * Get URL for VOD playback, download, or streaming.\n *\n * @param filenameOrVodFile - Filename string or VodFile object from getVideoclips\n * @param channel - Channel number (0-based)\n * @param options - Optional parameters\n * @returns URL string\n */\n async getVodUrl(\n filenameOrVodFile: string | VodFile,\n channel: number,\n options?: GetVodUrlParams,\n ): Promise<string> {\n await this.login();\n const requestType = options?.requestType ?? \"Playback\";\n const streamType =\n options?.streamType ?? options?.videoStreamType ?? \"main\";\n const videoStreamType = options?.videoStreamType ?? streamType;\n const seek = options?.seek ?? 0;\n\n // Extract filename from VodFile or use string directly\n const filename =\n typeof filenameOrVodFile === \"string\"\n ? filenameOrVodFile\n : filenameOrVodFile.name;\n\n // Get base URL from client\n const clientAny = this.client as any;\n const scheme = clientAny.useHttps ? \"https\" : \"http\";\n const port = clientAny.port ?? (clientAny.useHttps ? 443 : 80);\n const host = clientAny.host;\n const rtmpPort = 1935;\n\n // Encode filename: replace spaces with %20, but keep '/' as-is\n const encodedFilename = filename.replaceAll(\" \", \"%20\");\n\n // Map stream type to numeric value for FLV/RTMP\n let streamTypeNum = 0; // main\n if (videoStreamType === \"sub\") {\n streamTypeNum = 1;\n }\n\n let url: string;\n\n if (requestType === \"FLV\") {\n // FLV streaming URL - uses username/password authentication\n const username = encodeURIComponent(clientAny.username ?? \"\");\n const password = encodeURIComponent(clientAny.password ?? \"\");\n url = `${scheme}://${host}:${port}/flv?port=${rtmpPort}&app=bcs&stream=playback.bcs&channel=${channel}&type=${streamTypeNum}&start=${encodedFilename}&seek=${seek}&user=${username}&password=${password}`;\n } else if (requestType === \"RTMP\") {\n // RTMP streaming URL - uses username/password authentication\n const username = encodeURIComponent(clientAny.username ?? \"\");\n const password = encodeURIComponent(clientAny.password ?? \"\");\n url = `rtmp://${host}:${rtmpPort}/vod/${encodedFilename}?channel=${channel}&stream=${streamTypeNum}&user=${username}&password=${password}`;\n } else {\n // Playback or Download - use token authentication\n const token = this.client.getToken();\n if (!token) {\n throw new Error(\"Not logged in. Call login() first.\");\n }\n const cmd = requestType === \"NVR_DOWNLOAD\" ? \"Download\" : requestType;\n\n // Extract time_start from filename\n let startTimeParam = \"\";\n const timeMatch = filename.match(/Rec\\w{3}(?:_|_DST)?(\\d{8})_(\\d{6})_/);\n if (timeMatch) {\n startTimeParam = `&start=${timeMatch[1]}${timeMatch[2]}`;\n }\n\n const outputFilename = `playback_${Date.now()}.mp4`;\n url = `${scheme}://${host}:${port}/cgi-bin/api.cgi?cmd=${cmd}&source=${encodedFilename}&output=${outputFilename}${startTimeParam}&token=${encodeURIComponent(token)}`;\n }\n\n return url;\n }\n\n /**\n * Download a VOD file.\n * For NVR/Hub, use prepareNvrVodDownload first to get the filename.\n *\n * @param filename - Filename from getVideoclips or prepareNvrVodDownload\n * @param options - Optional download parameters\n * @returns Buffer containing the video file\n */\n async downloadVod(\n filename: string,\n options?: {\n /** Output filename */\n output?: string;\n /** Start time string */\n start?: string;\n },\n ): Promise<Buffer> {\n return await this.client.downloadVod(\n filename,\n options?.output,\n options?.start,\n );\n }\n\n /**\n * Parse recordType string to extract detection flags.\n */\n private parseRecordTypeFlags(recordType?: string): {\n hasPerson: boolean;\n hasVehicle: boolean;\n hasAnimal: boolean;\n hasFace: boolean;\n hasMotion: boolean;\n hasSchedule: boolean;\n hasDoorbell: boolean;\n hasPackage: boolean;\n hasRf: boolean;\n hasOther: boolean;\n } {\n const flags = {\n hasPerson: false,\n hasVehicle: false,\n hasAnimal: false,\n hasFace: false,\n hasMotion: false,\n hasSchedule: false,\n hasDoorbell: false,\n hasPackage: false,\n hasRf: false,\n hasOther: false,\n };\n\n if (!recordType) return flags;\n\n const types = recordType\n .toLowerCase()\n .split(/[,\\s]+/)\n .map((s) => s.trim())\n .filter(Boolean);\n\n for (const t of types) {\n if (t === \"people\" || t === \"person\") flags.hasPerson = true;\n else if (t === \"vehicle\" || t === \"car\") flags.hasVehicle = true;\n else if (t === \"dog_cat\" || t === \"animal\" || t === \"pet\")\n flags.hasAnimal = true;\n else if (t === \"face\") flags.hasFace = true;\n else if (t === \"md\" || t === \"motion\") flags.hasMotion = true;\n else if (t === \"sched\" || t === \"schedule\" || t === \"timer\")\n flags.hasSchedule = true;\n else if (t === \"visitor\" || t === \"doorbell\") flags.hasDoorbell = true;\n else if (t === \"package\") flags.hasPackage = true;\n else if (t === \"rf\" || t === \"io\" || t === \"pir\") flags.hasRf = true;\n else if (t === \"other\" || t === \"manual\") flags.hasOther = true;\n }\n\n return flags;\n }\n\n /**\n * Generate day-by-day ranges from startTime to endTime.\n * Each range covers exactly one day (00:00:00 to 23:59:59), except the first and last day\n * which use the original start/end times.\n *\n * @param startTime - Start time in Reolink format\n * @param endTime - End time in Reolink format\n * @returns Array of day ranges, each with \\{start, end\\} in Reolink time format\n */\n private generateDayRanges(\n startTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n endTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n ): Array<{\n start: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n end: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n }> {\n const ranges: Array<{\n start: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n end: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n }> = [];\n\n // Convert to Date objects to calculate day differences\n const startDate = new Date(\n startTime.year,\n startTime.mon - 1,\n startTime.day,\n startTime.hour,\n startTime.min,\n startTime.sec,\n );\n const endDate = new Date(\n endTime.year,\n endTime.mon - 1,\n endTime.day,\n endTime.hour,\n endTime.min,\n endTime.sec,\n );\n\n if (endDate < startDate) {\n // Invalid range, return empty\n return [];\n }\n\n // Check if same day\n const startDayKey = `${startTime.year}-${startTime.mon}-${startTime.day}`;\n const endDayKey = `${endTime.year}-${endTime.mon}-${endTime.day}`;\n\n if (startDayKey === endDayKey) {\n // Same day: single range with original times\n return [{ start: startTime, end: endTime }];\n }\n\n // Multiple days: generate ranges for each day\n let currentDate = new Date(startDate);\n\n while (currentDate <= endDate) {\n const currentYear = currentDate.getFullYear();\n const currentMonth = currentDate.getMonth() + 1;\n const currentDay = currentDate.getDate();\n const currentDayKey = `${currentYear}-${currentMonth}-${currentDay}`;\n\n if (currentDayKey === startDayKey) {\n // First day: use original start time, end at 23:59:59\n ranges.push({\n start: startTime,\n end: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 23,\n min: 59,\n sec: 59,\n },\n });\n } else if (currentDayKey === endDayKey) {\n // Last day: start at 00:00:00, use original end time\n ranges.push({\n start: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 0,\n min: 0,\n sec: 0,\n },\n end: endTime,\n });\n } else {\n // Middle day: full day (00:00:00 to 23:59:59)\n ranges.push({\n start: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 0,\n min: 0,\n sec: 0,\n },\n end: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 23,\n min: 59,\n sec: 59,\n },\n });\n }\n\n // Move to next day\n currentDate.setDate(currentDate.getDate() + 1);\n currentDate.setHours(0, 0, 0, 0);\n }\n\n return ranges;\n }\n\n /**\n * Convert Reolink time object to Date.\n */\n private reolinkTimeToDate(time: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n }): Date {\n return new Date(\n time.year,\n time.mon - 1,\n time.day,\n time.hour,\n time.min,\n time.sec,\n );\n }\n\n /**\n * Convert Date to Reolink time object.\n * IMPORTANT: Uses LOCAL TIME values because the Reolink API interprets time values as local time\n * (matching the timezone of the camera/NVR). This ensures that when we pass time values to the API,\n * they match the values stored in filenames (e.g., \"20260106_072650\" means 6 January 2026, 07:26:50 local time).\n *\n * To ensure correct extraction of local time values, we create a new Date object from the local time\n * components, similar to how it's done in test files: `new Date().setHours(0, 0, 0, 0)`.\n * This normalizes the Date to represent the exact local time moment, avoiding any UTC conversion issues.\n */\n private dateToReolinkTime(date: Date): {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n } {\n // Extract local time components first (these are what we want to pass to the API)\n const year = date.getFullYear();\n const month = date.getMonth();\n const day = date.getDate();\n const hour = date.getHours();\n const minute = date.getMinutes();\n const second = date.getSeconds();\n\n // Create a new Date object from these local time values to normalize it\n // This ensures we're working with a Date that represents the exact local time moment\n const normalizedDate = new Date(year, month, day, hour, minute, second);\n\n // Extract again from normalized date to be absolutely sure we have local time values\n // (This should be the same, but ensures consistency)\n return {\n year: normalizedDate.getFullYear(),\n mon: normalizedDate.getMonth() + 1,\n day: normalizedDate.getDate(),\n hour: normalizedDate.getHours(),\n min: normalizedDate.getMinutes(),\n sec: normalizedDate.getSeconds(),\n };\n }\n\n /**\n * Convert a VodFile to RecordingFile with parsed metadata.\n */\n private enrichVodFile(\n vodFile: VodFile,\n channel: number,\n streamUrl?: string,\n ): RecordingFile {\n // Log raw VodFile data from API (log entire object to see if there are hidden fields)\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `RAW VodFile from API: ${JSON.stringify({\n name: vodFile.name,\n type: vodFile.type,\n size: vodFile.size,\n StartTime: vodFile.StartTime,\n EndTime: vodFile.EndTime,\n PlaybackTime: vodFile.PlaybackTime,\n fullVodFile: JSON.parse(JSON.stringify(vodFile)),\n })}`,\n );\n\n // Parse filename\n const parsed = parseRecordingFileName(vodFile.name);\n\n // Extract hex value from filename for debugging\n const filenameParts = vodFile.name.split(\"_\");\n const hexValueFromFilename =\n filenameParts.length >= 8\n ? filenameParts[filenameParts.length - 2]\n : \"unknown\";\n // For version 4, the last part (hash) might contain detection info\n const hashPart =\n filenameParts.length >= 9\n ? filenameParts[filenameParts.length - 1]?.replace(\".mp4\", \"\")\n : null;\n\n // Debug: analyze hex value bit by bit\n let hexAnalysis: any = null;\n if (\n hexValueFromFilename &&\n hexValueFromFilename !== \"unknown\" &&\n /^[0-9a-fA-F]+$/.test(hexValueFromFilename)\n ) {\n const bitLen = hexValueFromFilename.length * 4;\n const hexInt = BigInt(`0x${hexValueFromFilename}`);\n const bin = hexInt.toString(2).padStart(bitLen, \"0\");\n const revBin = bin.split(\"\").reverse().join(\"\");\n\n // For Hub v4, analyze all bit ranges to find where detection flags might be\n const bitRanges: Record<string, string> = {};\n for (let start = 0; start < Math.min(bitLen, 64); start += 8) {\n const end = Math.min(start + 8, bitLen);\n bitRanges[`bits${start}to${end}`] = revBin.slice(start, end);\n }\n\n hexAnalysis = {\n hexValue: hexValueFromFilename,\n hexInt: hexInt.toString(),\n binary: bin,\n reversedBinary: revBin,\n bitLength: bitLen,\n // Show bits 17-27 which should contain detection flags (for older versions)\n bits17to27: revBin.slice(17, 28),\n // Show all 8-bit ranges for comprehensive analysis\n allBitRanges: bitRanges,\n // Show specific ranges that might contain detection flags for v4\n bits0to16: revBin.slice(0, 17),\n bits17to32: revBin.slice(17, 33),\n bits33to48: revBin.slice(33, 49),\n bits49to56: revBin.slice(49, 56),\n };\n }\n\n // Debug: analyze hash part (might contain detection info for version 4)\n let hashAnalysis: any = null;\n if (hashPart && /^[0-9a-fA-F]+$/i.test(hashPart)) {\n const hashBitLen = hashPart.length * 4;\n const hashHexInt = BigInt(`0x${hashPart}`);\n const hashBin = hashHexInt.toString(2).padStart(hashBitLen, \"0\");\n const hashRevBin = hashBin.split(\"\").reverse().join(\"\");\n hashAnalysis = {\n hashValue: hashPart,\n hashHexInt: hashHexInt.toString(),\n hashBinary: hashBin,\n hashReversedBinary: hashRevBin,\n hashBitLength: hashBitLen,\n // Show first 16 bits which might contain detection flags\n hashBits0to15: hashRevBin.slice(0, 16),\n };\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Parsed filename: ${JSON.stringify({\n fileName: vodFile.name,\n filenameParts: filenameParts,\n hexValueFromFilename: hexValueFromFilename,\n hashPart: hashPart,\n hexAnalysis: hexAnalysis,\n hashAnalysis: hashAnalysis,\n parsed: parsed\n ? {\n start: parsed.start?.toISOString(),\n end: parsed.end?.toISOString(),\n durationMs: parsed.durationMs,\n flags: parsed.flags,\n rawFlags: parsed.rawFlags,\n streamHint: parsed.streamHint,\n devType: parsed.devType,\n version: parsed.version,\n }\n : null,\n })}`,\n );\n\n // Get times from various sources\n const startTime =\n parsed?.start ?? this.reolinkTimeToDate(vodFile.StartTime);\n const endTime = parsed?.end ?? this.reolinkTimeToDate(vodFile.EndTime);\n\n const startTimeMs = startTime.getTime();\n const endTimeMs = endTime.getTime();\n\n // Calculate duration\n let durationMs = parsed?.durationMs ?? 0;\n if (durationMs === 0 && endTimeMs > startTimeMs) {\n durationMs = endTimeMs - startTimeMs;\n }\n\n // Get flags from hex decoding (from parsed filename)\n const hexFlags = parsed?.flags;\n\n // For Hub v4, check if hex value is constant (suggests detection info might not be in filename)\n if (parsed?.devType === \"hub\" && parsed?.version === 4) {\n if (hexValueFromFilename && hexValueFromFilename !== \"unknown\") {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `WARNING: Hub v4 hex value \"${hexValueFromFilename}\" appears to be constant across files. Detection flags are likely NOT encoded in filename for this version. Detection information may need to be retrieved via a separate API call or may not be available.`,\n );\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `NOTE: For Hub v4, the API response only provides 'type' field which is \"${vodFile.type}\". No detection-specific fields are available in the Search API response.`,\n );\n }\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Hex flags from filename: ${JSON.stringify(hexFlags)}`,\n );\n\n // Get flags from recordType string (if available in VodFile)\n // Note: VodFile from HTTP API doesn't have recordType, but we can try to infer from triggers\n const typeFlags = this.parseRecordTypeFlags(vodFile.type);\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Type flags from recordType \"${vodFile.type}\": ${JSON.stringify(typeFlags)}`,\n );\n\n // Merge flags: OR them together\n const hasPerson = (hexFlags?.aiPerson ?? false) || typeFlags.hasPerson;\n const hasVehicle = (hexFlags?.aiVehicle ?? false) || typeFlags.hasVehicle;\n const hasAnimal = (hexFlags?.aiAnimal ?? false) || typeFlags.hasAnimal;\n const hasFace = (hexFlags?.aiFace ?? false) || typeFlags.hasFace;\n const hasMotion = (hexFlags?.motion ?? false) || typeFlags.hasMotion;\n const hasSchedule = (hexFlags?.schedule ?? false) || typeFlags.hasSchedule;\n const hasDoorbell = (hexFlags?.doorbell ?? false) || typeFlags.hasDoorbell;\n const hasPackage = (hexFlags?.package ?? false) || typeFlags.hasPackage;\n const hasRf = (hexFlags?.rf ?? false) || typeFlags.hasRf;\n const hasOther = (hexFlags?.aiOther ?? false) || typeFlags.hasOther;\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Final merged flags: ${JSON.stringify({\n hasPerson,\n hasVehicle,\n hasAnimal,\n hasFace,\n hasMotion,\n hasSchedule,\n hasDoorbell,\n hasPackage,\n hasRf,\n hasOther,\n })}`,\n );\n\n // Build detectionClasses array from merged flags\n const detectionClasses: RecordingDetectionClass[] = [];\n if (hasPerson) detectionClasses.push(\"person\");\n if (hasVehicle) detectionClasses.push(\"vehicle\");\n if (hasAnimal) detectionClasses.push(\"animal\");\n if (hasFace) detectionClasses.push(\"face\");\n if (hasMotion) detectionClasses.push(\"motion\");\n if (hasSchedule) detectionClasses.push(\"schedule\");\n if (hasDoorbell) detectionClasses.push(\"doorbell\");\n if (hasPackage) detectionClasses.push(\"package\");\n if (hasRf) detectionClasses.push(\"rf\");\n if (hasOther) detectionClasses.push(\"other\");\n\n // Default to motion if no detection classes found\n if (detectionClasses.length === 0) {\n detectionClasses.push(\"motion\");\n }\n\n // Create RecordingFile with all available metadata\n // Note: vodFile.size might come as a string from JSON, ensure it's a number\n const sizeBytes =\n typeof vodFile.size === \"string\"\n ? parseInt(vodFile.size, 10)\n : vodFile.size;\n\n const result: RecordingFile = {\n fileName: vodFile.name,\n id: vodFile.name,\n startTime,\n endTime,\n recordType: vodFile.type,\n detectionClasses,\n };\n\n // Only add sizeBytes if it's a valid number (due to exactOptionalPropertyTypes)\n if (Number.isFinite(sizeBytes)) {\n result.sizeBytes = sizeBytes;\n }\n\n if (parsed) {\n result.parsedFileName = parsed;\n }\n\n return result;\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport yazl from \"yazl\";\n\nasync function walkDir(rootDir: string, currentDir: string): Promise<Array<{ absPath: string; relPath: string; isDir: boolean }>> {\n const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });\n const out: Array<{ absPath: string; relPath: string; isDir: boolean }> = [];\n\n for (const ent of entries) {\n const absPath = path.join(currentDir, ent.name);\n const relPath = path.relative(rootDir, absPath).replace(/\\\\/g, \"/\");\n\n if (ent.isDirectory()) {\n out.push({ absPath, relPath, isDir: true });\n out.push(...(await walkDir(rootDir, absPath)));\n continue;\n }\n\n if (ent.isFile()) {\n out.push({ absPath, relPath, isDir: false });\n continue;\n }\n\n // Ignore symlinks/sockets/etc for diagnostics bundles.\n }\n\n return out;\n}\n\nexport async function zipDirectory(params: { sourceDir: string; zipPath: string }): Promise<void> {\n const { sourceDir, zipPath } = params;\n\n await fs.promises.mkdir(path.dirname(zipPath), { recursive: true });\n\n const zipFile = new yazl.ZipFile();\n\n const entries = await walkDir(sourceDir, sourceDir);\n const dirs = entries.filter((e) => e.isDir);\n const files = entries.filter((e) => !e.isDir);\n\n // Add directories first (best-effort; zip doesn't strictly require it).\n for (const d of dirs) {\n // yazl expects directories with trailing slash\n const name = d.relPath.endsWith(\"/\") ? d.relPath : d.relPath + \"/\";\n zipFile.addEmptyDirectory(name);\n }\n\n for (const f of files) {\n zipFile.addFile(f.absPath, f.relPath);\n }\n\n const out = fs.createWriteStream(zipPath);\n\n await new Promise<void>((resolve, reject) => {\n out.on(\"error\", reject);\n zipFile.outputStream.on(\"error\", reject);\n out.on(\"close\", resolve);\n\n zipFile.outputStream.pipe(out);\n zipFile.end();\n });\n}\n","/**\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 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 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 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 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 if (frameCount <= 5) {\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 if (frameCount <= 5) {\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 // 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 // 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 // Could emit metadata event if needed\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 this.client.on(\"push\", this.videoFrameHandler);\n this.active = true;\n this.startWatchdog();\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 // this.logger?.log(\n // `[BaichuanVideoStream] start() calling startVideoStream: channel=${this.channel}, profile=${this.profile}, variant=${this.variant}`\n // );\n\n const startPromise = this.api.startVideoStream(\n this.channel,\n this.profile,\n { variant: this.variant, client: this.client },\n );\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 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 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 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 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_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_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// Unknown / no XML samples captured in current PCAP corpus (still observed cmdIds)\nexport const BC_CMD_ID_CMD_123 = 123;\nexport const BC_CMD_ID_CMD_209 = 209;\nexport const BC_CMD_ID_CMD_265 = 265;\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 * 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\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\n constructor(strict: boolean = false, logger?: Logger) {\n this.strict = strict;\n this.logger = logger;\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 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 // 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\n if (hevcType === 32 || hevcType === 33 || hevcType === 34) {\n return \"H265\";\n }\n // IDR, CRA frames\n if (hevcType === 19 || hevcType === 20 || hevcType === 21) {\n return \"H265\";\n }\n // TRAIL_N, TRAIL_R (common P/B frames)\n if (hevcType <= 9) {\n return \"H265\";\n }\n }\n }\n }\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\n if (h264Type === 7 || h264Type === 8) {\n return \"H264\";\n }\n // IDR, non-IDR slice\n if (h264Type === 5 || h264Type === 1) {\n return \"H264\";\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","export type RtspStreamProfile = \"main\" | \"sub\" | \"ext\";\n\nexport function buildRtspPath(channel: number, stream: RtspStreamProfile): string {\n // Standard Reolink path:\n // - channel is 0-based in most APIs, but RTSP path is 1-based and 2-digit.\n const ch = String(channel + 1).padStart(2, \"0\");\n let suffix: string;\n if (stream === \"main\") {\n suffix = \"main\";\n } else if (stream === \"sub\") {\n suffix = \"sub\";\n } else {\n // ext stream\n suffix = \"ext\";\n }\n return `/h264Preview_${ch}_${suffix}`;\n}\n\nexport function buildRtspUrl(params: {\n host: string;\n port?: number;\n username: string;\n password: string;\n channel: number;\n stream: RtspStreamProfile;\n}): string {\n const port = params.port ?? 554;\n const path = buildRtspPath(params.channel, params.stream);\n const user = encodeURIComponent(params.username);\n const pass = encodeURIComponent(params.password);\n return `rtsp://${user}:${pass}@${params.host}:${port}${path}`;\n}\n\n","export function xmlEscape(text: string | undefined | null): string {\n if (text === undefined || text === null || typeof text !== \"string\") {\n const error = new Error(`xmlEscape: expected string but got ${typeof text}: ${text}`);\n throw error;\n }\n return text\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nexport function buildLoginXml(userNameHash: string, passwordHash: string): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<LoginUser version=\"1.1\">\n<userName>${xmlEscape(userNameHash)}</userName>\n<password>${xmlEscape(passwordHash)}</password>\n<userVer>1</userVer>\n</LoginUser>\n<LoginNet version=\"1.1\">\n<type>LAN</type>\n<udpPort>0</udpPort>\n</LoginNet>\n</body>`;\n}\n\nexport function buildChannelExtensionXml(channelId: number | string | undefined | null): string {\n if (channelId === undefined || channelId === null) {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Extension version=\"1.1\"></Extension>`;\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<Extension version=\"1.1\">\n<channelId>${channelId}</channelId>\n</Extension>`;\n}\n\nexport function buildBinaryExtensionXml(channelId: number | string | undefined | null): string {\n if (channelId === undefined || channelId === null) {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Extension version=\"1.1\"><binaryData>1</binaryData></Extension>`;\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<Extension version=\"1.1\">\n<binaryData>1</binaryData>\n<channelId>${channelId}</channelId>\n</Extension>`;\n}\n\n/**\n * Build Preview XML for video stream request.\n * Uses Preview element with handle and stream_type.\n * \n * @param handle - Handle value: 0 for main, 256 for sub, 1024 for extern\n * @param streamType - Stream type name: \"mainStream\", \"subStream\", or \"externStream\"\n * @param channelId - Channel ID (optional, not used in working format)\n * @returns XML string for Preview element\n */\nexport function buildPreviewXml(handle: number, streamType: string | undefined | null, channelId?: number): string {\n // Preview includes channelId + handle + streamType.\n if (!streamType || typeof streamType !== \"string\") {\n throw new Error(`buildPreviewXml: streamType is required (string) but got: ${typeof streamType} = ${streamType}`);\n }\n const channelIdXml = channelId !== undefined ? `<channelId>${channelId}</channelId>\\n` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.0\">\n${channelIdXml}<handle>${handle}</handle>\n<streamType>${xmlEscape(streamType)}</streamType>\n</Preview>\n</body>`;\n}\n\n/**\n * Build Preview XML for video stream request (v1.1).\n *\n * This format is observed in Reolink client traffic to NVR/Hub where the Preview element uses\n * version=\"1.1\" and always includes channelId + handle + streamType.\n */\nexport function buildPreviewXmlV11(params: { channelId: number; handle: number; streamType: string }): string {\n if (!Number.isFinite(params.channelId)) {\n throw new Error(`buildPreviewXmlV11: channelId must be finite, got: ${params.channelId}`);\n }\n if (!Number.isFinite(params.handle)) {\n throw new Error(`buildPreviewXmlV11: handle must be finite, got: ${params.handle}`);\n }\n if (!params.streamType || typeof params.streamType !== \"string\") {\n throw new Error(`buildPreviewXmlV11: streamType is required (string) but got: ${typeof params.streamType} = ${params.streamType}`);\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.1\">\n<channelId>${params.channelId}</channelId>\n<handle>${params.handle}</handle>\n<streamType>${xmlEscape(params.streamType)}</streamType>\n</Preview>\n</body>`;\n}\n\n/**\n * Build Preview XML for video stream stop request.\n * Uses Preview element without stream_type.\n * \n * @param handle - Handle value: 0 for main, 256 for sub, 1024 for extern\n * @param channelId - Channel ID (optional, not used in working format)\n * @returns XML string for Preview element (stop)\n */\nexport function buildPreviewStopXml(handle: number, channelId?: number): string {\n const channelIdXml = channelId !== undefined ? `<channelId>${channelId}</channelId>\\n` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.0\">\n${channelIdXml}<handle>${handle}</handle>\n</Preview>\n</body>`;\n}\n\n/**\n * Build Preview XML for video stream stop request (v1.1).\n *\n * This format is observed/needed on some Hub/NVR firmwares where the VIDEO start\n * request uses Preview version=\"1.1\" with explicit channelId and handle.\n */\nexport function buildPreviewStopXmlV11(params: { channelId: number; handle: number }): string {\n if (!Number.isFinite(params.channelId)) {\n throw new Error(`buildPreviewStopXmlV11: channelId must be finite, got: ${params.channelId}`);\n }\n if (!Number.isFinite(params.handle)) {\n throw new Error(`buildPreviewStopXmlV11: handle must be finite, got: ${params.handle}`);\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.1\">\n<channelId>${params.channelId}</channelId>\n<handle>${params.handle}</handle>\n</Preview>\n</body>`;\n}\n\n/**\n * Very small helper for extracting simple `<tag>value</tag>` text from Baichuan XML.\n * This is intentionally dependency-free; it's enough for nonce/login flows.\n */\nexport function getXmlText(xml: string, tagName: string): string | undefined {\n const re = new RegExp(`<${tagName}>([^<]*)</${tagName}>`);\n const m = re.exec(xml);\n return m?.[1];\n}\n\n/**\n * Build PTZ Control XML for pan/tilt/zoom commands.\n * \n * @param channelId - Channel ID (1-based)\n * @param command - PTZ command: \"up\", \"down\", \"left\", \"right\", \"stop\"\n * @param speed - Movement speed (0.0 to 1.0, typically converted to 1-10)\n * @returns XML string for PtzControl element\n */\nexport function buildPtzControlXml(channelId: number, command: string, speed: number): string {\n // PtzControl structure: channel_id, speed, command\n // Version is \"1.1\"\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<PtzControl version=\"1.1\">\n<channelId>${channelId}</channelId>\n<command>${xmlEscape(command)}</command>\n<speed>${speed}</speed>\n</PtzControl>\n</body>`;\n}\n\n/**\n * Build PTZ Preset XML for setting or moving to preset.\n * \n * @param channelId - Channel ID (1-based)\n * @param presetId - Preset ID (1-255)\n * @param command - \"setPos\" to save current position, \"toPos\" to move to preset\n * @param name - Preset name (optional, required for setPos)\n * @returns XML string for PtzPreset element\n */\nexport function buildPtzPresetXml(channelId: number, presetId: number, command: \"setPos\" | \"toPos\", name?: string): string {\n return buildPtzPresetXmlV2(channelId, presetId, command, name === undefined ? undefined : { name });\n}\n\nexport function buildPtzPresetXmlV2(\n channelId: number,\n presetId: number,\n command: \"setPos\" | \"toPos\" | \"delPos\",\n options?: {\n /** Preset name. For setPos many firmwares require it; empty string will emit an empty <name></name>. */\n name?: string;\n /** Best-effort enable/disable support. Some firmwares include this in the preset list response. */\n enable?: 0 | 1;\n }\n): string {\n let nameXml = \"\";\n const name = options?.name;\n if (command === \"setPos\" && name !== undefined) {\n nameXml = `<name>${xmlEscape(name)}</name>`;\n }\n\n let enableXml = \"\";\n if (options?.enable !== undefined) {\n enableXml = `<enable>${options.enable}</enable>`;\n }\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<PtzPreset version=\"1.1\">\n<channelId>${channelId}</channelId>\n<presetList>\n<preset>\n<id>${presetId}</id>\n<command>${command}</command>\n${nameXml}\n${enableXml}\n</preset>\n</presetList>\n</PtzPreset>\n</body>`;\n}\n\n/**\n * Build StartZoomFocus XML for zooming to an absolute position.\n *\n * cmd_id: 295 (MSG_ID_SET_ZOOM_FOCUS)\n *\n * @param channelId - Channel ID (0/1-based depending on camera; should match header/extension)\n * @param movePos - Absolute zoom position (typically 1000 == 1.0x)\n */\nexport function buildStartZoomFocusXml(channelId: number, movePos: number): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<StartZoomFocus version=\"1.1\">\n<channelId>${channelId}</channelId>\n<command>zoomPos</command>\n<movePos>${Math.trunc(movePos)}</movePos>\n</StartZoomFocus>\n</body>`;\n}\n\n/**\n * Build Siren/Audio Alarm XML for manual control.\n * \n * @param channelId - Channel ID (1-based, optional for hub-level)\n * @param enable - Enable/disable siren (1 or 0)\n * @returns XML string for audioPlayInfo element\n */\nexport function buildSirenManualXml(channelId: number | undefined, enable: number): string {\n const channelXml = channelId !== undefined ? `<channelId>${channelId}</channelId>` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<audioPlayInfo version=\"1.1\">\n${channelXml}\n<playMode>2</playMode>\n<playDuration>10</playDuration>\n<playTimes>1</playTimes>\n<onOff>${enable}</onOff>\n</audioPlayInfo>\n</body>`;\n}\n\n/**\n * Build Siren/Audio Alarm XML for times-based control.\n * \n * @param channelId - Channel ID (1-based, optional for hub-level)\n * @param times - Number of times to play\n * @returns XML string for audioPlayInfo element\n */\nexport function buildSirenTimesXml(channelId: number | undefined, times: number): string {\n const channelXml = channelId !== undefined ? `<channelId>${channelId}</channelId>` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<audioPlayInfo version=\"1.1\">\n${channelXml}\n<playMode>0</playMode>\n<playDuration>10</playDuration>\n<playTimes>${times}</playTimes>\n<onOff>1</onOff>\n</audioPlayInfo>\n</body>`;\n}\n\n/**\n * Build FloodlightManual XML.\n * Used for cmd_id 288.\n *\n * Notes:\n * - channelId is 0-based (same as Baichuan channel id)\n * - status: 1 = on, 0 = off\n * - duration is in seconds\n */\nexport function buildFloodlightManualXml(channelId: number, status: number, durationSeconds = 180): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FloodlightManual version=\"1.1\">\n<channelId>${channelId}</channelId>\n<status>${status}</status>\n<duration>${durationSeconds}</duration>\n</FloodlightManual>\n</body>`;\n}\n\n/**\n * Back-compat alias: historically this project called floodlight control \"WhiteLed\".\n * In practice, many cameras expect FloodlightManual payload for cmd 288.\n */\nexport function buildWhiteLedStateXml(channelId: number, state: number): string {\n return buildFloodlightManualXml(channelId, state ? 1 : 0);\n}\n\n/**\n * Build AbilityInfo extension XML for requesting device capabilities.\n * Requests all available tokens: \"system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image\"\n * \n * @param username - Username for the request\n * @returns XML string for Extension element with AbilityInfo request\n */\nexport function buildAbilityInfoExtensionXml(username: string): string {\n // Request all available ability tokens to get comprehensive ability information\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<Extension version=\"1.1\">\n<userName>${xmlEscape(username)}</userName>\n<token>system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image</token>\n</Extension>`;\n}\n\n"],"mappings":"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","names":["fs","path","start","end","durationMs","decoded","parsed","allResults","allFiles","enriched","fetchStreamUrls","streamUrlType","spawn","join","fs","path","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","path","nals","splitAnnexBToNalPayloads","status","frameReceived","join","path"]}
|