@meframe/core 0.2.9 → 0.3.1

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.
Files changed (46) hide show
  1. package/dist/controllers/PlaybackController.d.ts +6 -0
  2. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  3. package/dist/controllers/PlaybackController.js +66 -17
  4. package/dist/controllers/PlaybackController.js.map +1 -1
  5. package/dist/controllers/PlaybackStateMachine.d.ts.map +1 -1
  6. package/dist/controllers/PlaybackStateMachine.js +1 -3
  7. package/dist/controllers/PlaybackStateMachine.js.map +1 -1
  8. package/dist/controllers/types.d.ts +1 -4
  9. package/dist/controllers/types.d.ts.map +1 -1
  10. package/dist/controllers/types.js +0 -1
  11. package/dist/controllers/types.js.map +1 -1
  12. package/dist/medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
  13. package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +2 -2
  14. package/dist/medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
  15. package/dist/orchestrator/ExportScheduler.d.ts +1 -0
  16. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  17. package/dist/orchestrator/ExportScheduler.js +56 -3
  18. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  19. package/dist/orchestrator/OnDemandVideoSession.d.ts +8 -8
  20. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
  21. package/dist/orchestrator/OnDemandVideoSession.js +112 -39
  22. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
  23. package/dist/orchestrator/Orchestrator.d.ts +0 -5
  24. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  25. package/dist/orchestrator/Orchestrator.js +15 -43
  26. package/dist/orchestrator/Orchestrator.js.map +1 -1
  27. package/dist/orchestrator/types.d.ts +6 -1
  28. package/dist/orchestrator/types.d.ts.map +1 -1
  29. package/dist/stages/mux/MP4Muxer.d.ts +2 -0
  30. package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
  31. package/dist/stages/mux/MP4Muxer.js +16 -8
  32. package/dist/stages/mux/MP4Muxer.js.map +1 -1
  33. package/dist/stages/mux/MuxManager.d.ts +1 -0
  34. package/dist/stages/mux/MuxManager.d.ts.map +1 -1
  35. package/dist/stages/mux/MuxManager.js +2 -1
  36. package/dist/stages/mux/MuxManager.js.map +1 -1
  37. package/dist/utils/mp4box.js +1 -1
  38. package/dist/utils/video-decoder-helpers.d.ts +28 -0
  39. package/dist/utils/video-decoder-helpers.d.ts.map +1 -1
  40. package/dist/utils/video-decoder-helpers.js +123 -0
  41. package/dist/utils/video-decoder-helpers.js.map +1 -1
  42. package/dist/workers/MP4Demuxer.DfWiwyjB.js.map +1 -1
  43. package/package.json +1 -1
  44. package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
  45. package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
  46. /package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +0 -0
@@ -1,4 +1,126 @@
1
1
  import { getRecommendedHardwareAcceleration } from "./platform-utils.js";
2
+ async function decodeChunksForScrub(chunks, config, targetTimeUs, options = {}) {
3
+ const { timeoutMs = 2e3, maxQueueSize = 2, shouldAbort } = options;
4
+ const startTime = performance.now();
5
+ let outputFrames = 0;
6
+ let before = null;
7
+ let after = null;
8
+ return await new Promise((resolve, reject) => {
9
+ let settled = false;
10
+ let timeoutId = null;
11
+ const cleanup = () => {
12
+ if (timeoutId !== null) {
13
+ clearTimeout(timeoutId);
14
+ timeoutId = null;
15
+ }
16
+ };
17
+ const settle = (ok, err) => {
18
+ if (settled) return;
19
+ settled = true;
20
+ cleanup();
21
+ const stats = {
22
+ inputChunks: chunks.length,
23
+ outputFrames,
24
+ durationMs: performance.now() - startTime
25
+ };
26
+ if (!ok) {
27
+ if (before) before.close();
28
+ if (after) after.close();
29
+ reject(err instanceof Error ? err : new Error(String(err ?? "Scrub decode failed")));
30
+ return;
31
+ }
32
+ resolve({ before, after, stats });
33
+ };
34
+ const decoder = new VideoDecoder({
35
+ output: (frame) => {
36
+ outputFrames += 1;
37
+ if (settled) {
38
+ frame.close();
39
+ return;
40
+ }
41
+ const ts = frame.timestamp ?? 0;
42
+ if (ts <= targetTimeUs) {
43
+ if (before) before.close();
44
+ before = frame;
45
+ return;
46
+ }
47
+ if (!after) {
48
+ after = frame;
49
+ try {
50
+ decoder.close();
51
+ } catch {
52
+ }
53
+ settle(true);
54
+ return;
55
+ }
56
+ frame.close();
57
+ },
58
+ error: (e) => {
59
+ settle(false, e);
60
+ }
61
+ });
62
+ try {
63
+ decoder.configure({
64
+ codec: config.codec,
65
+ codedWidth: config.width,
66
+ codedHeight: config.height,
67
+ hardwareAcceleration: config.hardwareAcceleration ?? getRecommendedHardwareAcceleration(),
68
+ optimizeForLatency: true,
69
+ ...config.description && { description: config.description }
70
+ });
71
+ } catch (e) {
72
+ settle(false, e);
73
+ return;
74
+ }
75
+ timeoutId = setTimeout(() => {
76
+ try {
77
+ decoder.close();
78
+ } catch {
79
+ }
80
+ if (before || after) {
81
+ settle(true);
82
+ } else {
83
+ settle(false, new Error(`Scrub decode timeout after ${timeoutMs}ms`));
84
+ }
85
+ }, timeoutMs);
86
+ const feed = async () => {
87
+ try {
88
+ for (const chunk of chunks) {
89
+ if (settled) break;
90
+ if (shouldAbort?.()) break;
91
+ while (!settled && !shouldAbort?.() && decoder.decodeQueueSize > maxQueueSize) {
92
+ await new Promise((r) => setTimeout(r, 0));
93
+ }
94
+ if (settled) break;
95
+ if (shouldAbort?.()) break;
96
+ decoder.decode(chunk);
97
+ }
98
+ if (settled) return;
99
+ if (shouldAbort?.()) {
100
+ if (before || after) {
101
+ try {
102
+ decoder.close();
103
+ } catch {
104
+ }
105
+ settle(true);
106
+ return;
107
+ }
108
+ try {
109
+ decoder.close();
110
+ } catch {
111
+ }
112
+ settle(false, new Error("Scrub decode aborted"));
113
+ return;
114
+ }
115
+ decoder.flush().catch(() => {
116
+ });
117
+ } catch (e) {
118
+ settle(false, e);
119
+ }
120
+ };
121
+ void feed();
122
+ });
123
+ }
2
124
  async function decodeChunksWithoutFlush(chunks, config, options = {}) {
3
125
  const { timeoutMs = 2e3, pollIntervalMs = 10 } = options;
4
126
  const startTime = performance.now();
@@ -66,6 +188,7 @@ async function decodeChunksWithoutFlush(chunks, config, options = {}) {
66
188
  });
67
189
  }
68
190
  export {
191
+ decodeChunksForScrub,
69
192
  decodeChunksWithoutFlush
70
193
  };
71
194
  //# sourceMappingURL=video-decoder-helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"video-decoder-helpers.js","sources":["../../src/utils/video-decoder-helpers.ts"],"sourcesContent":["import { getRecommendedHardwareAcceleration } from './platform-utils';\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\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 { 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 settle(false, error.message);\n },\n });\n\n decoder.configure({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration: config.hardwareAcceleration ?? getRecommendedHardwareAcceleration(),\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":";AAkCA,eAAsB,yBACpB,QACA,QACA,UAAyB,CAAA,GACF;AACvB,QAAM,EAAE,YAAY,KAAM,iBAAiB,OAAO;AAClD,QAAM,YAAY,YAAY,IAAA;AAC9B,QAAM,SAAuB,CAAA;AAE7B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,YAAY;AAChB,QAAI,YAAmD;AACvD,QAAI,eAAqD;AAEzD,UAAM,UAAU,MAAM;AACpB,UAAI,cAAc,MAAM;AACtB,sBAAc,SAAS;AACvB,oBAAY;AAAA,MACd;AACA,UAAI,iBAAiB,MAAM;AACzB,qBAAa,YAAY;AACzB,uBAAe;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,SAAS,CAAC,SAAkB,aAAsB;AACtD,UAAI,UAAW;AACf,kBAAY;AACZ,cAAA;AAEA,YAAM,QAAQ;AAAA,QACZ,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,YAAY,YAAY,QAAQ;AAAA,MAAA;AAGlC,UAAI,WAAW,OAAO,SAAS,GAAG;AAChC,gBAAQ,EAAE,QAAQ,OAAO;AAAA,MAC3B,OAAO;AACL,eAAO,IAAI,MAAM,YAAY,eAAe,CAAC;AAAA,MAC/C;AAAA,IACF;AAEA,UAAM,UAAU,IAAI,aAAa;AAAA,MAC/B,QAAQ,CAAC,UAAU;AACjB,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,MACA,OAAO,CAAC,UAAU;AAChB,eAAO,OAAO,MAAM,OAAO;AAAA,MAC7B;AAAA,IAAA,CACD;AAED,YAAQ,UAAU;AAAA,MAChB,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,aAAa,OAAO;AAAA,MACpB,sBAAsB,OAAO,wBAAwB,mCAAA;AAAA,MACrD,oBAAoB;AAAA,MACpB,GAAI,OAAO,eAAe,EAAE,aAAa,OAAO,YAAA;AAAA,IAAY,CAC7D;AAGD,eAAW,SAAS,QAAQ;AAC1B,cAAQ,OAAO,KAAK;AAAA,IACtB;AAGA,YAAQ,QAAQ,MAAM,MAAM;AAAA,IAE5B,CAAC;AAGD,gBAAY,YAAY,MAAM;AAC5B,UAAI,QAAQ,oBAAoB,KAAK,OAAO,SAAS,GAAG;AACtD,gBAAQ,MAAA;AACR,eAAO,IAAI;AAAA,MACb;AAAA,IACF,GAAG,cAAc;AAGjB,mBAAe,WAAW,MAAM;AAC9B,cAAQ,MAAA;AACR,aAAO,OAAO,SAAS,GAAG,wBAAwB,SAAS,IAAI;AAAA,IACjE,GAAG,SAAS;AAAA,EACd,CAAC;AACH;"}
1
+ {"version":3,"file":"video-decoder-helpers.js","sources":["../../src/utils/video-decoder-helpers.ts"],"sourcesContent":["import { getRecommendedHardwareAcceleration } from './platform-utils';\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 { 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 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: config.hardwareAcceleration ?? getRecommendedHardwareAcceleration(),\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 { 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 settle(false, error.message);\n },\n });\n\n decoder.configure({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration: config.hardwareAcceleration ?? getRecommendedHardwareAcceleration(),\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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"}