{"version":3,"sources":["../src/util/debug.ts","../src/strategies/fallback/libav-loader.ts"],"names":[],"mappings":"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","file":"chunk-NNVOHKXJ.cjs","sourcesContent":["/**\n * Debug + self-diagnosis helper.\n *\n * avbridge has a lot of async stages (probe → classify → libav load →\n * strategy.execute → decoder pump → cold-start gate → first paint) and\n * when something's slow or wrong the symptom — \"it hangs\", \"it stutters\",\n * \"it plays audio without video\" — is usually nowhere near the actual\n * cause. This module gives us two things:\n *\n * 1. **Gated verbose logging.** `dbg.info(tag, ...)` etc. are no-ops\n *    unless the consumer sets `globalThis.AVBRIDGE_DEBUG = true` (or\n *    uses the matching `?avbridge_debug` URL search param at dev time).\n *    When enabled, every log is prefixed with `[avbridge:<tag>]` so the\n *    console is filterable.\n *\n * 2. **Unconditional self-diagnosis.** `dbg.warnIf(cond, tag, ...)`\n *    always fires when a suspicious condition is detected, even with\n *    debug off. These are the things we *know* mean something is\n *    broken or degraded and the user would want to know about — e.g.\n *    the cold-start gate timing out, the decoder running slower than\n *    realtime, a libav variant taking longer to load than any network\n *    should take, >20% of packets getting rejected.\n *\n * The guiding principle: **if a symptom caused more than 10 minutes of\n * human debugging once, add a targeted warning so the next instance\n * self-identifies in the console.** This module is where those\n * warnings live.\n */\n\n/** Read the debug flag fresh on every call so it's runtime-toggleable. */\nfunction isDebugEnabled(): boolean {\n  if (typeof globalThis === \"undefined\") return false;\n  const g = globalThis as { AVBRIDGE_DEBUG?: unknown };\n  if (g.AVBRIDGE_DEBUG === true) return true;\n  // Convenience: if running in a browser with a `?avbridge_debug` search\n  // param, flip the flag on automatically. Useful for demos and quick\n  // user reproduction without editing code.\n  if (typeof location !== \"undefined\" && typeof URLSearchParams !== \"undefined\") {\n    try {\n      const p = new URLSearchParams(location.search);\n      if (p.has(\"avbridge_debug\")) {\n        g.AVBRIDGE_DEBUG = true;\n        return true;\n      }\n    } catch { /* ignore */ }\n  }\n  return false;\n}\n\nfunction fmt(tag: string): string {\n  return `[avbridge:${tag}]`;\n}\n\n/* eslint-disable no-console */\n\nexport const dbg = {\n  /** Verbose — only when debug is enabled. The hot-path normal case. */\n  info(tag: string, ...args: unknown[]): void {\n    if (isDebugEnabled()) console.info(fmt(tag), ...args);\n  },\n\n  /** Warning — only when debug is enabled. Non-fatal oddities. */\n  warn(tag: string, ...args: unknown[]): void {\n    if (isDebugEnabled()) console.warn(fmt(tag), ...args);\n  },\n\n  /**\n   * Self-diagnosis warning. **Always** emits regardless of debug flag.\n   * Use this only for conditions that mean something is actually wrong\n   * or degraded — not for routine chatter.\n   */\n  diag(tag: string, ...args: unknown[]): void {\n    console.warn(fmt(tag), ...args);\n  },\n\n  /**\n   * Timing helper: wraps an async call and logs its elapsed time when\n   * debug is on. The callback runs whether debug is on or off — this is\n   * just for the `dbg.info` at the end.\n   *\n   * Also unconditionally fires `dbg.diag` if the elapsed time exceeds\n   * `slowMs`, so \"the bootstrap took 8 seconds\" shows up even without\n   * debug mode enabled.\n   */\n  async timed<T>(\n    tag: string,\n    label: string,\n    slowMs: number,\n    fn: () => Promise<T>,\n  ): Promise<T> {\n    const start = performance.now();\n    try {\n      const result = await fn();\n      const elapsed = performance.now() - start;\n      if (isDebugEnabled()) {\n        console.info(fmt(tag), `${label} ${elapsed.toFixed(0)}ms`);\n      }\n      if (elapsed > slowMs) {\n        console.warn(\n          fmt(tag),\n          `${label} took ${elapsed.toFixed(0)}ms (>${slowMs}ms expected) — ` +\n          `this is unusually slow; possible causes: ${hintForTag(tag)}`,\n        );\n      }\n      return result;\n    } catch (err) {\n      const elapsed = performance.now() - start;\n      console.warn(\n        fmt(tag),\n        `${label} FAILED after ${elapsed.toFixed(0)}ms:`,\n        err,\n      );\n      throw err;\n    }\n  },\n};\n\nfunction hintForTag(tag: string): string {\n  switch (tag) {\n    case \"probe\":\n      return \"slow network (range request), large sniff window, or libav cold-start\";\n    case \"libav-load\":\n      return \"large .wasm download, misconfigured AVBRIDGE_LIBAV_BASE, or server-side MIME type\";\n    case \"bootstrap\":\n      return \"probe+classify+strategy-init chain; enable AVBRIDGE_DEBUG for a phase breakdown\";\n    case \"cold-start\":\n      return \"decoder is producing output slower than realtime — check framesDecoded in getDiagnostics()\";\n    default:\n      return \"unknown stage — enable globalThis.AVBRIDGE_DEBUG for more detail\";\n  }\n}\n","/**\n * Lazy libav.js loader supporting multiple variants.\n *\n * avbridge recognises three libav variants:\n *\n * - **webcodecs** — npm `@libav.js/variant-webcodecs`, ~5 MB. Modern formats\n *   only (mp4/mkv/webm/ogg/wav/...) — designed to bridge to WebCodecs.\n *\n * - **default** — npm `@libav.js/variant-default`, ~12 MB. Audio-only build\n *   (Opus, FLAC, WAV) despite the name. Useful for audio fallback.\n *\n * - **avbridge** — a custom build produced by `scripts/build-libav.sh` and\n *   landing in `vendor/libav/`. Includes the AVI/ASF/FLV/MKV demuxers plus\n *   the legacy decoders (WMV3, MPEG-4 Part 2, MS-MPEG4 v1/2/3, VC-1, MPEG-1/2,\n *   AC-3/E-AC-3, WMAv1/v2/Pro). This is the only variant that can read AVI;\n *   the npm variants are intentionally minimal and ship none of the legacy\n *   demuxers.\n *\n * Variant resolution always goes through a runtime URL + `/* @vite-ignore *\\/`\n * dynamic import. Static imports trigger Vite's optimized-deps pipeline,\n * which rewrites `import.meta.url` away from the real `dist/` directory and\n * breaks libav's sibling-binary loading.\n */\n\nimport { dbg } from \"../../util/debug.js\";\n\nexport type LibavVariant = \"webcodecs\" | \"default\" | \"avbridge\";\n\nexport interface LoadLibavOptions {\n  /**\n   * Force threading on/off for this load. If unspecified, defaults to\n   * \"true if `crossOriginIsolated`, otherwise false\". Some libav.js code\n   * paths (notably the cross-thread reader-device protocol used during\n   * `avformat_find_stream_info` for AVI) are unreliable in threaded mode,\n   * so probing forces this to `false` while decode keeps it default.\n   */\n  threads?: boolean;\n}\n\n// Cache key includes both variant and threading mode so probe and decode\n// can run different libav instances of the same variant.\nconst cache: Map<string, Promise<LibavInstance>> = new Map();\n\nfunction cacheKey(variant: LibavVariant, threads: boolean): string {\n  return `${variant}:${threads ? \"thr\" : \"wasm\"}`;\n}\n\n/**\n * Load (and cache) a libav.js variant. Pass `\"webcodecs\"` for the small\n * default; pass `\"default\"` for the audio fallback; pass `\"avbridge\"` for the\n * custom build that supports AVI/WMV/legacy codecs.\n */\nexport function loadLibav(\n  variant: LibavVariant = \"webcodecs\",\n  opts: LoadLibavOptions = {},\n): Promise<LibavInstance> {\n  // Threading is OFF by default. The threaded libav.js variant is too\n  // fragile in practice for our usage:\n  //   - Probe (`avformat_find_stream_info` for AVI) throws an `undefined`\n  //     exception out of `ff_init_demuxer_file`, apparently due to the\n  //     cross-thread reader-device protocol racing with the main thread.\n  //   - Decode hits a `TypeError: Cannot read properties of undefined\n  //     (reading 'apply')` inside libav.js's own worker message handler\n  //     within seconds of starting — a bug in libav.js's threaded message\n  //     dispatch that we can't fix from outside.\n  //\n  // Performance work for the fallback strategy needs to come from elsewhere\n  // (WASM SIMD, OffscreenCanvas, larger decode batches) instead of libav's\n  // pthreads. Threading can still be force-enabled with\n  // `globalThis.AVBRIDGE_LIBAV_THREADS = true` for testing if libav.js fixes\n  // those bugs in a future release.\n  const env = globalThis as { AVBRIDGE_LIBAV_THREADS?: boolean };\n  const wantThreads =\n    opts.threads !== undefined\n      ? opts.threads\n      : env.AVBRIDGE_LIBAV_THREADS === true;\n\n  const key = cacheKey(variant, wantThreads);\n  let entry = cache.get(key);\n  if (!entry) {\n    entry = loadVariant(variant, wantThreads);\n    cache.set(key, entry);\n  }\n  return entry;\n}\n\nasync function loadVariant(\n  variant: LibavVariant,\n  wantThreads: boolean,\n): Promise<LibavInstance> {\n  return dbg.timed(\"libav-load\", `load \"${variant}\" (threads=${wantThreads})`, 5000, () =>\n    loadVariantInner(variant, wantThreads),\n  );\n}\n\nasync function loadVariantInner(\n  variant: LibavVariant,\n  wantThreads: boolean,\n): Promise<LibavInstance> {\n  const key = cacheKey(variant, wantThreads);\n  const base = `${libavBaseUrl()}/${variant}`;\n  // The custom variant is named `libav-avbridge.mjs`; the npm variants follow\n  // the same convention (`libav-webcodecs.mjs`, `libav-default.mjs`).\n  const variantUrl = `${base}/libav-${variant}.mjs`;\n  dbg.info(\"libav-load\", `fetching ${variantUrl}`);\n\n  // Preflight HEAD-ish check: issue a bytes=0-0 range request so a missing\n  // file fails fast with a clear error instead of hanging deep inside the\n  // dynamic import or inside libav's own WASM instantiation. Surfaces the\n  // most common mistake (\"libav base path is wrong\") in <100 ms instead of\n  // an indeterminate stall.\n  if (typeof fetch === \"function\") {\n    try {\n      const head = await fetch(variantUrl, { method: \"GET\", headers: { Range: \"bytes=0-0\" } });\n      if (!head.ok && head.status !== 206) {\n        throw new Error(\n          `HTTP ${head.status} ${head.statusText} — check that libav files are served ` +\n          `at ${base}/ (override via globalThis.AVBRIDGE_LIBAV_BASE)`,\n        );\n      }\n      // Drain the tiny response so the connection can be reused.\n      try { await head.arrayBuffer(); } catch { /* ignore */ }\n    } catch (err) {\n      cache.delete(key);\n      throw chain(\n        `libav.js \"${variant}\" variant not reachable at ${variantUrl}`,\n        err,\n      );\n    }\n  }\n\n  let mod: LoadedVariant;\n  try {\n    // @ts-ignore runtime URL\n    const imported: unknown = await import(/* @vite-ignore */ variantUrl);\n    if (!imported || typeof (imported as { LibAV?: unknown }).LibAV !== \"function\") {\n      throw new Error(`module at ${variantUrl} did not export LibAV`);\n    }\n    mod = imported as LoadedVariant;\n  } catch (err) {\n    cache.delete(key);\n    const hint =\n      variant === \"avbridge\"\n        ? `The \"avbridge\" variant is a custom local build. Run \\`./scripts/build-libav.sh\\` ` +\n          `to produce it (requires Emscripten; ~15-30 min the first time).`\n        : `Make sure the variant files are present at ${base}/ (set ` +\n          `globalThis.AVBRIDGE_LIBAV_BASE to override the default lookup path).`;\n    throw new Error(\n      `failed to load libav.js \"${variant}\" variant from ${variantUrl}. ${hint} ` +\n        `Original error: ${(err as Error).message || String(err)}`,\n    );\n  }\n\n  try {\n    const inst = (await mod.LibAV(buildOpts(base, wantThreads))) as LibavInstance;\n    await silenceLibavLogs(inst);\n    return inst;\n  } catch (err) {\n    cache.delete(key);\n    throw chain(`LibAV() factory failed for \"${variant}\" variant (threads=${wantThreads})`, err);\n  }\n}\n\n/**\n * Lower libav's internal log level so the console doesn't get flooded with\n * `[mp3 @ ...] Header missing` and `Video uses a non-standard and wasteful\n * way to store B-frames` warnings on every legacy file. We still get any\n * actual JS-level errors via the normal Error path; this only affects\n * libav's own ffmpeg log channel.\n *\n * AV_LOG_QUIET = -8 (no output at all). If you want to keep fatal errors,\n * use AV_LOG_FATAL = 8 instead.\n */\nasync function silenceLibavLogs(inst: LibavInstance): Promise<void> {\n  try {\n    const setLevel = (inst as { av_log_set_level?: (n: number) => Promise<void> })\n      .av_log_set_level;\n    if (typeof setLevel === \"function\") {\n      const quiet = (inst as { AV_LOG_QUIET?: number }).AV_LOG_QUIET ?? -8;\n      await setLevel(quiet);\n    }\n  } catch {\n    /* not fatal — verbose logs are noise, not an error */\n  }\n}\n\nfunction buildOpts(base: string, wantThreads: boolean): Record<string, unknown> {\n  // The wantThreads decision is made by `loadLibav()` so callers (probe,\n  // decoder) can override per-load. Decode wants pthreads for speed; probe\n  // forces them off because libav.js's cross-thread reader-device protocol\n  // is unreliable mid-`avformat_find_stream_info` for some AVI files.\n  return {\n    base,\n    nothreads: !wantThreads,\n    yesthreads: wantThreads,\n  };\n}\n\nfunction libavBaseUrl(): string {\n  // Consumer override — the documented \"LGPL replaceability\" hook.\n  // Setting `globalThis.AVBRIDGE_LIBAV_BASE = \"/my/path\"` lets anyone swap\n  // in a different libav build (custom fragments, security patches, etc.)\n  // without rebuilding avbridge.\n  const override =\n    typeof globalThis !== \"undefined\"\n      ? (globalThis as { AVBRIDGE_LIBAV_BASE?: string }).AVBRIDGE_LIBAV_BASE\n      : undefined;\n  if (override) return override;\n\n  // Default: resolve relative to this module's URL. When avbridge is installed\n  // under `node_modules/avbridge/`, this module lives at `dist/chunk-*.js` (or\n  // `dist/element-browser.js` for the browser entry) and `../vendor/libav`\n  // resolves to `node_modules/avbridge/vendor/libav`, where the build step\n  // vendored every variant's binaries. That's the zero-config path.\n  //\n  // `import.meta.url` throws in some synthetic environments (CJS tests, some\n  // SSR evaluators). If it fails, fall back to the legacy `/libav` path so\n  // consumers who relied on the pre-2.1 behavior still work.\n  try {\n    return new URL(\"../vendor/libav\", import.meta.url).href;\n  } catch {\n    if (typeof location !== \"undefined\" && location.protocol.startsWith(\"http\")) {\n      return `${location.origin}/libav`;\n    }\n    return \"/libav\";\n  }\n}\n\nfunction chain(message: string, err: unknown): Error {\n  const inner = err instanceof Error ? err.message : String(err);\n  // eslint-disable-next-line no-console\n  console.error(`[avbridge] ${message}:`, err);\n  return new Error(`${message}: ${inner || \"(no message — see browser console)\"}`);\n}\n\ninterface LoadedVariant {\n  LibAV: (opts?: Record<string, unknown>) => Promise<Record<string, unknown>>;\n}\n\n/** Loose structural type — the AVI probe and the fallback decoder add fields. */\nexport type LibavInstance = Record<string, unknown> & {\n  mkreadaheadfile(name: string, blob: Blob): Promise<void>;\n  unlinkreadaheadfile(name: string): Promise<void>;\n};\n"]}