@meframe/core 0.5.2 → 0.5.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"video-decoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/video-decoder.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,EAAE,EAC3B,MAAM,EAAE,kBAAkB,EAC1B,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAuKD;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,iBAAiB,EAAE,EAC3B,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAavB"}
1
+ {"version":3,"file":"video-decoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/video-decoder.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,EAAE,EAC3B,MAAM,EAAE,kBAAkB,EAC1B,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAsB5B;AAuKD;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,iBAAiB,EAAE,EAC3B,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAsBvB"}
@@ -1,13 +1,21 @@
1
- import { getRecommendedHardwareAcceleration } from "../../utils/platform-utils.js";
1
+ import { getRecommendedHardwareAcceleration, detectPlatform } from "../../utils/platform-utils.js";
2
2
  function isDecodeError(error) {
3
3
  return error instanceof Error && error.message === "Decoding error.";
4
4
  }
5
5
  async function decodeChunksForScrub(chunks, config, targetTimeUs, options = {}) {
6
6
  const hwAccel = config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();
7
+ const platformInfo = detectPlatform();
7
8
  try {
8
9
  return await _decodeForScrub(chunks, config, targetTimeUs, options, hwAccel);
9
10
  } catch (error) {
10
- console.warn("[decodeChunksForScrub] decode error:", error, " hwAccel:", hwAccel);
11
+ console.warn(
12
+ "[decodeChunksForScrub] decode error:",
13
+ error,
14
+ "hwAccel:",
15
+ hwAccel,
16
+ "platform:",
17
+ platformInfo.platform
18
+ );
11
19
  if (hwAccel !== "prefer-software" && isDecodeError(error)) {
12
20
  console.warn("[VideoDecoder] Hardware decode failed, falling back to software decode");
13
21
  return _decodeForScrub(chunks, config, targetTimeUs, options, "prefer-software");
@@ -143,10 +151,18 @@ async function _decodeForScrub(chunks, config, targetTimeUs, options, hwAccel) {
143
151
  }
144
152
  async function decodeChunksWithoutFlush(chunks, config, options = {}) {
145
153
  const hwAccel = config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();
154
+ const platformInfo = detectPlatform();
146
155
  try {
147
156
  return await _decodeWithoutFlush(chunks, config, options, hwAccel);
148
157
  } catch (error) {
149
- console.warn("[decodeChunksWithoutFlush]decode error:", error, " hwAccel:", hwAccel);
158
+ console.warn(
159
+ "[decodeChunksWithoutFlush]decode error:",
160
+ error,
161
+ "hwAccel:",
162
+ hwAccel,
163
+ "platform:",
164
+ platformInfo.platform
165
+ );
150
166
  if (hwAccel !== "prefer-software" && isDecodeError(error)) {
151
167
  console.warn("[VideoDecoder] Hardware decode failed, falling back to software decode");
152
168
  return _decodeWithoutFlush(chunks, config, options, "prefer-software");
@@ -1 +1 @@
1
- {"version":3,"file":"video-decoder.js","sources":["../../../src/stages/decode/video-decoder.ts"],"sourcesContent":["import { getRecommendedHardwareAcceleration } from '../../utils/platform-utils';\n\nfunction isDecodeError(error: unknown): boolean {\n return error instanceof Error && error.message === 'Decoding error.';\n}\n\nexport interface DecodeResult {\n frames: VideoFrame[];\n stats: {\n inputChunks: number;\n outputFrames: number;\n durationMs: number;\n };\n}\n\nexport interface VideoDecoderConfig {\n codec: string;\n width: number;\n height: number;\n description?: ArrayBuffer;\n hardwareAcceleration?: HardwareAcceleration;\n}\n\nexport interface DecodeOptions {\n timeoutMs?: number;\n pollIntervalMs?: number;\n}\n\nexport interface DecodeScrubResult {\n before: VideoFrame | null;\n after: VideoFrame | null;\n stats: {\n inputChunks: number;\n outputFrames: number;\n durationMs: number;\n };\n}\n\nexport interface DecodeScrubOptions {\n timeoutMs?: number;\n /**\n * Backpressure: keep decodeQueueSize small so we don't decode far past target.\n */\n maxQueueSize?: number;\n /**\n * Optional cooperative abort hook (e.g., session cancelled by a newer seek).\n */\n shouldAbort?: () => boolean;\n}\n\n/**\n * Scrub decode: feed chunks in order, stop as soon as output crosses targetTimeUs.\n *\n * Why this lives in helpers:\n * - Centralize VideoDecoder quirks (no-await flush, HW accel defaults)\n * - Keep on-demand session focused on IO/demux/cache, not decode control flow\n */\nexport async function decodeChunksForScrub(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n targetTimeUs: number,\n options: DecodeScrubOptions = {}\n): Promise<DecodeScrubResult> {\n const hwAccel = config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();\n\n try {\n return await _decodeForScrub(chunks, config, targetTimeUs, options, hwAccel);\n } catch (error) {\n console.warn('[decodeChunksForScrub] decode error:', error, ' hwAccel:', hwAccel);\n if (hwAccel !== 'prefer-software' && isDecodeError(error)) {\n console.warn('[VideoDecoder] Hardware decode failed, falling back to software decode');\n return _decodeForScrub(chunks, config, targetTimeUs, options, 'prefer-software');\n }\n throw error;\n }\n}\n\nasync function _decodeForScrub(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n targetTimeUs: number,\n options: DecodeScrubOptions,\n hwAccel: HardwareAcceleration\n): Promise<DecodeScrubResult> {\n const { timeoutMs = 2000, maxQueueSize = 2, shouldAbort } = options;\n const startTime = performance.now();\n let outputFrames = 0;\n\n // Keep at most two frames: last <= target and first > target.\n let before: VideoFrame | null = null;\n let after: VideoFrame | null = null;\n\n return await new Promise<DecodeScrubResult>((resolve, reject) => {\n let settled = false;\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n };\n\n const settle = (ok: boolean, err?: unknown) => {\n if (settled) return;\n settled = true;\n cleanup();\n\n const stats = {\n inputChunks: chunks.length,\n outputFrames,\n durationMs: performance.now() - startTime,\n };\n\n if (!ok) {\n if (before) before.close();\n if (after) after.close();\n reject(err instanceof Error ? err : new Error(String(err ?? 'Scrub decode failed')));\n return;\n }\n\n resolve({ before, after, stats });\n };\n\n const decoder = new VideoDecoder({\n output: (frame) => {\n outputFrames += 1;\n\n if (settled) {\n frame.close();\n return;\n }\n\n const ts = frame.timestamp ?? 0;\n if (ts <= targetTimeUs) {\n if (before) before.close();\n before = frame;\n return;\n }\n\n if (!after) {\n after = frame;\n // We have both sides (or at least the first after); stop as soon as possible.\n try {\n decoder.close();\n } catch {\n // ignore\n }\n settle(true);\n return;\n }\n\n frame.close();\n },\n error: (e) => {\n try {\n decoder.close();\n } catch {\n /* noop */\n }\n settle(false, e);\n },\n });\n\n try {\n decoder.configure({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration: hwAccel,\n optimizeForLatency: true,\n ...(config.description && { description: config.description }),\n });\n } catch (e) {\n settle(false, e);\n return;\n }\n\n timeoutId = setTimeout(() => {\n try {\n decoder.close();\n } catch {\n // ignore\n }\n // Best-effort: if we got at least one frame, still treat as success.\n if (before || after) {\n settle(true);\n } else {\n settle(false, new Error(`Scrub decode timeout after ${timeoutMs}ms`));\n }\n }, timeoutMs);\n\n const feed = async () => {\n try {\n for (const chunk of chunks) {\n if (settled) break;\n if (shouldAbort?.()) break;\n\n // Backpressure: keep queue small so we don't decode far past target.\n while (!settled && !shouldAbort?.() && decoder.decodeQueueSize > maxQueueSize) {\n await new Promise((r) => setTimeout(r, 0));\n }\n\n if (settled) break;\n if (shouldAbort?.()) break;\n decoder.decode(chunk);\n }\n\n // If we already settled (found target), we're done.\n if (settled) return;\n if (shouldAbort?.()) {\n // If cancelled, resolve best-effort if any output exists; otherwise fail.\n if (before || after) {\n try {\n decoder.close();\n } catch {\n // ignore\n }\n settle(true);\n return;\n }\n try {\n decoder.close();\n } catch {\n // ignore\n }\n settle(false, new Error('Scrub decode aborted'));\n return;\n }\n\n // We may have hit end-of-input without seeing a frame after target (e.g., target at end).\n // Trigger flush (do not await) and give output a chance before timeout.\n decoder.flush().catch(() => {});\n } catch (e) {\n settle(false, e);\n }\n };\n\n void feed();\n });\n}\n\n/**\n * Decode chunks using native VideoDecoder without awaiting flush\n * This avoids Windows hardware acceleration flush hang bug\n *\n * Strategy:\n * - Feed all chunks to decoder\n * - Call flush() but don't await (may hang on Windows HW)\n * - Poll decodeQueueSize to detect completion\n * - Timeout fallback ensures no infinite hang\n */\nexport async function decodeChunksWithoutFlush(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n options: DecodeOptions = {}\n): Promise<DecodeResult> {\n const hwAccel = config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();\n\n try {\n return await _decodeWithoutFlush(chunks, config, options, hwAccel);\n } catch (error) {\n console.warn('[decodeChunksWithoutFlush]decode error:', error, ' hwAccel:', hwAccel);\n if (hwAccel !== 'prefer-software' && isDecodeError(error)) {\n console.warn('[VideoDecoder] Hardware decode failed, falling back to software decode');\n return _decodeWithoutFlush(chunks, config, options, 'prefer-software');\n }\n throw error;\n }\n}\n\nfunction _decodeWithoutFlush(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n options: DecodeOptions,\n hwAccel: HardwareAcceleration\n): Promise<DecodeResult> {\n const { timeoutMs = 2000, pollIntervalMs = 10 } = options;\n const startTime = performance.now();\n const frames: VideoFrame[] = [];\n\n return new Promise((resolve, reject) => {\n let isSettled = false;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (pollTimer !== null) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer !== null) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n };\n\n const settle = (success: boolean, errorMsg?: string) => {\n if (isSettled) return;\n isSettled = true;\n cleanup();\n\n const stats = {\n inputChunks: chunks.length,\n outputFrames: frames.length,\n durationMs: performance.now() - startTime,\n };\n\n if (success && frames.length > 0) {\n resolve({ frames, stats });\n } else {\n reject(new Error(errorMsg || 'Decode failed'));\n }\n };\n\n const decoder = new VideoDecoder({\n output: (frame) => {\n frames.push(frame);\n },\n error: (error) => {\n for (const f of frames) {\n try {\n f.close();\n } catch {\n /* noop */\n }\n }\n try {\n decoder.close();\n } catch {\n /* noop */\n }\n settle(false, error.message);\n },\n });\n\n decoder.configure({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration: hwAccel,\n optimizeForLatency: true,\n ...(config.description && { description: config.description }),\n });\n\n // Feed all chunks\n for (const chunk of chunks) {\n decoder.decode(chunk);\n }\n\n // Call flush but don't await (may hang on Windows HW acceleration)\n decoder.flush().catch(() => {\n // Flush errors are non-critical\n });\n\n // Poll decode queue to detect completion\n pollTimer = setInterval(() => {\n if (decoder.decodeQueueSize === 0 && frames.length > 0) {\n decoder.close();\n settle(true);\n }\n }, pollIntervalMs);\n\n // Timeout fallback\n timeoutTimer = setTimeout(() => {\n decoder.close();\n settle(frames.length > 0, `Decode timeout after ${timeoutMs}ms`);\n }, timeoutMs);\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"video-decoder.js","sources":["../../../src/stages/decode/video-decoder.ts"],"sourcesContent":["import { detectPlatform, getRecommendedHardwareAcceleration } from '../../utils/platform-utils';\n\nfunction isDecodeError(error: unknown): boolean {\n return error instanceof Error && error.message === 'Decoding error.';\n}\n\nexport interface DecodeResult {\n frames: VideoFrame[];\n stats: {\n inputChunks: number;\n outputFrames: number;\n durationMs: number;\n };\n}\n\nexport interface VideoDecoderConfig {\n codec: string;\n width: number;\n height: number;\n description?: ArrayBuffer;\n hardwareAcceleration?: HardwareAcceleration;\n}\n\nexport interface DecodeOptions {\n timeoutMs?: number;\n pollIntervalMs?: number;\n}\n\nexport interface DecodeScrubResult {\n before: VideoFrame | null;\n after: VideoFrame | null;\n stats: {\n inputChunks: number;\n outputFrames: number;\n durationMs: number;\n };\n}\n\nexport interface DecodeScrubOptions {\n timeoutMs?: number;\n /**\n * Backpressure: keep decodeQueueSize small so we don't decode far past target.\n */\n maxQueueSize?: number;\n /**\n * Optional cooperative abort hook (e.g., session cancelled by a newer seek).\n */\n shouldAbort?: () => boolean;\n}\n\n/**\n * Scrub decode: feed chunks in order, stop as soon as output crosses targetTimeUs.\n *\n * Why this lives in helpers:\n * - Centralize VideoDecoder quirks (no-await flush, HW accel defaults)\n * - Keep on-demand session focused on IO/demux/cache, not decode control flow\n */\nexport async function decodeChunksForScrub(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n targetTimeUs: number,\n options: DecodeScrubOptions = {}\n): Promise<DecodeScrubResult> {\n const hwAccel = config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();\n\n const platformInfo = detectPlatform();\n\n try {\n return await _decodeForScrub(chunks, config, targetTimeUs, options, hwAccel);\n } catch (error) {\n console.warn(\n '[decodeChunksForScrub] decode error:',\n error,\n 'hwAccel:',\n hwAccel,\n 'platform:',\n platformInfo.platform\n );\n if (hwAccel !== 'prefer-software' && isDecodeError(error)) {\n console.warn('[VideoDecoder] Hardware decode failed, falling back to software decode');\n return _decodeForScrub(chunks, config, targetTimeUs, options, 'prefer-software');\n }\n throw error;\n }\n}\n\nasync function _decodeForScrub(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n targetTimeUs: number,\n options: DecodeScrubOptions,\n hwAccel: HardwareAcceleration\n): Promise<DecodeScrubResult> {\n const { timeoutMs = 2000, maxQueueSize = 2, shouldAbort } = options;\n const startTime = performance.now();\n let outputFrames = 0;\n\n // Keep at most two frames: last <= target and first > target.\n let before: VideoFrame | null = null;\n let after: VideoFrame | null = null;\n\n return await new Promise<DecodeScrubResult>((resolve, reject) => {\n let settled = false;\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n };\n\n const settle = (ok: boolean, err?: unknown) => {\n if (settled) return;\n settled = true;\n cleanup();\n\n const stats = {\n inputChunks: chunks.length,\n outputFrames,\n durationMs: performance.now() - startTime,\n };\n\n if (!ok) {\n if (before) before.close();\n if (after) after.close();\n reject(err instanceof Error ? err : new Error(String(err ?? 'Scrub decode failed')));\n return;\n }\n\n resolve({ before, after, stats });\n };\n\n const decoder = new VideoDecoder({\n output: (frame) => {\n outputFrames += 1;\n\n if (settled) {\n frame.close();\n return;\n }\n\n const ts = frame.timestamp ?? 0;\n if (ts <= targetTimeUs) {\n if (before) before.close();\n before = frame;\n return;\n }\n\n if (!after) {\n after = frame;\n // We have both sides (or at least the first after); stop as soon as possible.\n try {\n decoder.close();\n } catch {\n // ignore\n }\n settle(true);\n return;\n }\n\n frame.close();\n },\n error: (e) => {\n try {\n decoder.close();\n } catch {\n /* noop */\n }\n settle(false, e);\n },\n });\n\n try {\n decoder.configure({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration: hwAccel,\n optimizeForLatency: true,\n ...(config.description && { description: config.description }),\n });\n } catch (e) {\n settle(false, e);\n return;\n }\n\n timeoutId = setTimeout(() => {\n try {\n decoder.close();\n } catch {\n // ignore\n }\n // Best-effort: if we got at least one frame, still treat as success.\n if (before || after) {\n settle(true);\n } else {\n settle(false, new Error(`Scrub decode timeout after ${timeoutMs}ms`));\n }\n }, timeoutMs);\n\n const feed = async () => {\n try {\n for (const chunk of chunks) {\n if (settled) break;\n if (shouldAbort?.()) break;\n\n // Backpressure: keep queue small so we don't decode far past target.\n while (!settled && !shouldAbort?.() && decoder.decodeQueueSize > maxQueueSize) {\n await new Promise((r) => setTimeout(r, 0));\n }\n\n if (settled) break;\n if (shouldAbort?.()) break;\n decoder.decode(chunk);\n }\n\n // If we already settled (found target), we're done.\n if (settled) return;\n if (shouldAbort?.()) {\n // If cancelled, resolve best-effort if any output exists; otherwise fail.\n if (before || after) {\n try {\n decoder.close();\n } catch {\n // ignore\n }\n settle(true);\n return;\n }\n try {\n decoder.close();\n } catch {\n // ignore\n }\n settle(false, new Error('Scrub decode aborted'));\n return;\n }\n\n // We may have hit end-of-input without seeing a frame after target (e.g., target at end).\n // Trigger flush (do not await) and give output a chance before timeout.\n decoder.flush().catch(() => {});\n } catch (e) {\n settle(false, e);\n }\n };\n\n void feed();\n });\n}\n\n/**\n * Decode chunks using native VideoDecoder without awaiting flush\n * This avoids Windows hardware acceleration flush hang bug\n *\n * Strategy:\n * - Feed all chunks to decoder\n * - Call flush() but don't await (may hang on Windows HW)\n * - Poll decodeQueueSize to detect completion\n * - Timeout fallback ensures no infinite hang\n */\nexport async function decodeChunksWithoutFlush(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n options: DecodeOptions = {}\n): Promise<DecodeResult> {\n const hwAccel = config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();\n\n const platformInfo = detectPlatform();\n\n try {\n return await _decodeWithoutFlush(chunks, config, options, hwAccel);\n } catch (error) {\n console.warn(\n '[decodeChunksWithoutFlush]decode error:',\n error,\n 'hwAccel:',\n hwAccel,\n 'platform:',\n platformInfo.platform\n );\n if (hwAccel !== 'prefer-software' && isDecodeError(error)) {\n console.warn('[VideoDecoder] Hardware decode failed, falling back to software decode');\n return _decodeWithoutFlush(chunks, config, options, 'prefer-software');\n }\n throw error;\n }\n}\n\nfunction _decodeWithoutFlush(\n chunks: EncodedVideoChunk[],\n config: VideoDecoderConfig,\n options: DecodeOptions,\n hwAccel: HardwareAcceleration\n): Promise<DecodeResult> {\n const { timeoutMs = 2000, pollIntervalMs = 10 } = options;\n const startTime = performance.now();\n const frames: VideoFrame[] = [];\n\n return new Promise((resolve, reject) => {\n let isSettled = false;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (pollTimer !== null) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer !== null) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n };\n\n const settle = (success: boolean, errorMsg?: string) => {\n if (isSettled) return;\n isSettled = true;\n cleanup();\n\n const stats = {\n inputChunks: chunks.length,\n outputFrames: frames.length,\n durationMs: performance.now() - startTime,\n };\n\n if (success && frames.length > 0) {\n resolve({ frames, stats });\n } else {\n reject(new Error(errorMsg || 'Decode failed'));\n }\n };\n\n const decoder = new VideoDecoder({\n output: (frame) => {\n frames.push(frame);\n },\n error: (error) => {\n for (const f of frames) {\n try {\n f.close();\n } catch {\n /* noop */\n }\n }\n try {\n decoder.close();\n } catch {\n /* noop */\n }\n settle(false, error.message);\n },\n });\n\n decoder.configure({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration: hwAccel,\n optimizeForLatency: true,\n ...(config.description && { description: config.description }),\n });\n\n // Feed all chunks\n for (const chunk of chunks) {\n decoder.decode(chunk);\n }\n\n // Call flush but don't await (may hang on Windows HW acceleration)\n decoder.flush().catch(() => {\n // Flush errors are non-critical\n });\n\n // Poll decode queue to detect completion\n pollTimer = setInterval(() => {\n if (decoder.decodeQueueSize === 0 && frames.length > 0) {\n decoder.close();\n settle(true);\n }\n }, pollIntervalMs);\n\n // Timeout fallback\n timeoutTimer = setTimeout(() => {\n decoder.close();\n settle(frames.length > 0, `Decode timeout after ${timeoutMs}ms`);\n }, timeoutMs);\n });\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meframe/core",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Next generation media processing framework based on WebCodecs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -89,7 +89,7 @@
89
89
  "streaming",
90
90
  "composition"
91
91
  ],
92
- "author": "Sheepy",
92
+ "author": "haibo",
93
93
  "license": "MIT",
94
94
  "publishConfig": {
95
95
  "access": "public",