@meframe/core 0.0.3 → 0.0.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.
Files changed (76) hide show
  1. package/dist/Meframe.d.ts.map +1 -1
  2. package/dist/Meframe.js +5 -4
  3. package/dist/Meframe.js.map +1 -1
  4. package/dist/cache/CacheManager.d.ts +2 -2
  5. package/dist/cache/CacheManager.d.ts.map +1 -1
  6. package/dist/cache/CacheManager.js +4 -3
  7. package/dist/cache/CacheManager.js.map +1 -1
  8. package/dist/cache/l1/VideoL1Cache.d.ts +2 -2
  9. package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
  10. package/dist/cache/l1/VideoL1Cache.js +13 -8
  11. package/dist/cache/l1/VideoL1Cache.js.map +1 -1
  12. package/dist/config/defaults.d.ts.map +1 -1
  13. package/dist/config/defaults.js +2 -1
  14. package/dist/config/defaults.js.map +1 -1
  15. package/dist/config/types.d.ts +3 -0
  16. package/dist/config/types.d.ts.map +1 -1
  17. package/dist/controllers/PlaybackController.d.ts +7 -8
  18. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  19. package/dist/controllers/PlaybackController.js +56 -76
  20. package/dist/controllers/PlaybackController.js.map +1 -1
  21. package/dist/controllers/types.d.ts +2 -3
  22. package/dist/controllers/types.d.ts.map +1 -1
  23. package/dist/event/events.d.ts +1 -4
  24. package/dist/event/events.d.ts.map +1 -1
  25. package/dist/event/events.js.map +1 -1
  26. package/dist/model/CompositionModel.d.ts +1 -0
  27. package/dist/model/CompositionModel.d.ts.map +1 -1
  28. package/dist/model/CompositionModel.js +2 -0
  29. package/dist/model/CompositionModel.js.map +1 -1
  30. package/dist/model/patch.d.ts +6 -2
  31. package/dist/model/patch.d.ts.map +1 -1
  32. package/dist/model/patch.js +76 -2
  33. package/dist/model/patch.js.map +1 -1
  34. package/dist/model/types.d.ts +1 -0
  35. package/dist/model/types.d.ts.map +1 -1
  36. package/dist/orchestrator/CompositionPlanner.d.ts +8 -7
  37. package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -1
  38. package/dist/orchestrator/CompositionPlanner.js +33 -56
  39. package/dist/orchestrator/CompositionPlanner.js.map +1 -1
  40. package/dist/orchestrator/Orchestrator.d.ts +0 -1
  41. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  42. package/dist/orchestrator/Orchestrator.js +39 -19
  43. package/dist/orchestrator/Orchestrator.js.map +1 -1
  44. package/dist/orchestrator/VideoClipSession.d.ts +3 -5
  45. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  46. package/dist/orchestrator/VideoClipSession.js +66 -69
  47. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  48. package/dist/orchestrator/types.d.ts +1 -0
  49. package/dist/orchestrator/types.d.ts.map +1 -1
  50. package/dist/stages/compose/GlobalAudioSession.d.ts +6 -0
  51. package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
  52. package/dist/stages/compose/GlobalAudioSession.js +17 -1
  53. package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
  54. package/dist/stages/load/ResourceLoader.d.ts +22 -1
  55. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  56. package/dist/stages/load/ResourceLoader.js +71 -25
  57. package/dist/stages/load/ResourceLoader.js.map +1 -1
  58. package/dist/stages/load/TaskManager.d.ts +1 -1
  59. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  60. package/dist/stages/load/TaskManager.js +3 -2
  61. package/dist/stages/load/TaskManager.js.map +1 -1
  62. package/dist/stages/load/types.d.ts +2 -0
  63. package/dist/stages/load/types.d.ts.map +1 -1
  64. package/dist/worker/WorkerPool.d.ts +2 -0
  65. package/dist/worker/WorkerPool.d.ts.map +1 -1
  66. package/dist/worker/WorkerPool.js +4 -1
  67. package/dist/worker/WorkerPool.js.map +1 -1
  68. package/dist/workers/stages/compose/video-compose.worker.js +113 -81
  69. package/dist/workers/stages/compose/video-compose.worker.js.map +1 -1
  70. package/package.json +1 -1
  71. package/dist/controllers/PreviewHandle.d.ts +0 -25
  72. package/dist/controllers/PreviewHandle.d.ts.map +0 -1
  73. package/dist/controllers/PreviewHandle.js +0 -45
  74. package/dist/controllers/PreviewHandle.js.map +0 -1
  75. package/dist/model/dirty-range.js +0 -220
  76. package/dist/model/dirty-range.js.map +0 -1
@@ -832,8 +832,11 @@ class VideoComposer {
832
832
  this.filterProcessor.clearCache();
833
833
  }
834
834
  }
835
- function resolveActiveLayers(layers, timestamp, _frame) {
835
+ function resolveActiveLayers(layers, timestamp) {
836
836
  return layers.filter((layer) => {
837
+ if (layer.layerId.includes("base-video")) {
838
+ return true;
839
+ }
837
840
  if (layer.status !== "ready") {
838
841
  return false;
839
842
  }
@@ -892,11 +895,12 @@ class VideoComposeWorker {
892
895
  channel;
893
896
  composer = null;
894
897
  composeStream = null;
895
- downstreamPorts = /* @__PURE__ */ new Map();
896
- upstreamPorts = /* @__PURE__ */ new Map();
897
- instructionRegistry = /* @__PURE__ */ new Map();
898
- pendingReplay = /* @__PURE__ */ new Map();
899
- streamState = /* @__PURE__ */ new Map();
898
+ clipId = null;
899
+ downstreamPort = null;
900
+ upstreamPort = null;
901
+ instructions = null;
902
+ streamState = null;
903
+ imageBitmap = null;
900
904
  constructor() {
901
905
  this.channel = new WorkerChannel(self, {
902
906
  name: "VideoComposeWorker",
@@ -910,7 +914,7 @@ class VideoComposeWorker {
910
914
  this.channel.registerHandler("flush", this.handleFlush.bind(this));
911
915
  this.channel.registerHandler("get_stats", this.handleGetStats.bind(this));
912
916
  this.channel.registerHandler("install_instructions", this.handleInstallInstructions.bind(this));
913
- this.channel.registerHandler("sync_clip", this.handleSyncClip.bind(this));
917
+ this.channel.registerHandler("receive_image", this.handleReceiveImage.bind(this));
914
918
  this.channel.registerHandler("dispose_clip", this.handleDisposeClip.bind(this));
915
919
  this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));
916
920
  }
@@ -918,9 +922,12 @@ class VideoComposeWorker {
918
922
  * Unified connect handler used by stream pipeline
919
923
  */
920
924
  async handleConnect(payload) {
921
- const { port, direction, clipId = "default" } = payload;
925
+ const { port, direction, clipId } = payload;
926
+ if (clipId && !this.clipId) {
927
+ this.clipId = clipId;
928
+ }
922
929
  if (direction === "upstream") {
923
- this.upstreamPorts.set(clipId, port);
930
+ this.upstreamPort = port;
924
931
  const channel = new WorkerChannel(port, {
925
932
  name: "VideoCompose-Decode",
926
933
  timeout: 3e4
@@ -928,7 +935,7 @@ class VideoComposeWorker {
928
935
  channel.receiveStream(this.handleReceiveStream.bind(this));
929
936
  }
930
937
  if (direction === "downstream") {
931
- this.downstreamPorts.set(clipId, port);
938
+ this.downstreamPort = port;
932
939
  }
933
940
  return { success: true };
934
941
  }
@@ -967,14 +974,12 @@ class VideoComposeWorker {
967
974
  };
968
975
  }
969
976
  async handleReceiveStream(stream, metadata) {
970
- const { clipId = "default" } = metadata || {};
971
977
  if (!this.composer) {
972
978
  console.error("[VideoComposeWorker] Composer not configured");
973
979
  return;
974
980
  }
975
- const instruction = this.instructionRegistry.get(clipId);
976
- if (!instruction) {
977
- console.warn("[VideoComposeWorker] No instructions for clip", clipId);
981
+ if (!this.instructions) {
982
+ console.warn("[VideoComposeWorker] No instructions installed");
978
983
  return;
979
984
  }
980
985
  const filteredStream = stream.pipeThrough(
@@ -984,12 +989,7 @@ class VideoComposeWorker {
984
989
  const frame = wrappedFrame.frame || wrappedFrame;
985
990
  const gopSerial = wrappedFrame.gopSerial;
986
991
  const isKeyframe = wrappedFrame.isKeyframe;
987
- const timestamp = frame.timestamp ?? 0;
988
- if (this.shouldSkipFrame(clipId, timestamp)) {
989
- frame.close();
990
- return;
991
- }
992
- const request = this.buildComposeRequest(clipId, instruction, frame, timestamp);
992
+ const request = this.buildComposeRequest(this.instructions, frame);
993
993
  if (!request) {
994
994
  frame.close();
995
995
  return;
@@ -1008,7 +1008,7 @@ class VideoComposeWorker {
1008
1008
  const { composeStream, cacheStream } = this.composer.createStreams();
1009
1009
  this.channel.sendStream(cacheStream, metadata);
1010
1010
  filteredStream.pipeTo(composeStream).catch((error) => {
1011
- console.error("[VideoComposeWorker] compose stream error", clipId, error);
1011
+ console.error("[VideoComposeWorker] compose stream error", this.clipId, error);
1012
1012
  });
1013
1013
  }
1014
1014
  // private handleGetStream(): ReadableStream<VideoFrame> | undefined {
@@ -1047,61 +1047,96 @@ class VideoComposeWorker {
1047
1047
  this.composer = null;
1048
1048
  }
1049
1049
  this.composeStream = null;
1050
- this.downstreamPorts.get("default")?.close();
1051
- this.upstreamPorts.get("default")?.close();
1052
- this.downstreamPorts.clear();
1053
- this.upstreamPorts.clear();
1050
+ this.downstreamPort?.close();
1051
+ this.upstreamPort?.close();
1052
+ this.downstreamPort = null;
1053
+ this.upstreamPort = null;
1054
+ this.imageBitmap?.close();
1055
+ this.imageBitmap = null;
1056
+ this.instructions = null;
1057
+ this.streamState = null;
1054
1058
  this.channel.state = WorkerState.Disposed;
1055
1059
  return { success: true };
1056
1060
  }
1057
- async handleInstallInstructions(data) {
1058
- const { clipId, revision } = data;
1059
- const current = this.instructionRegistry.get(clipId);
1060
- if (current && current.revision > revision) {
1061
+ async handleInstallInstructions(payload) {
1062
+ const { clipId, revision } = payload;
1063
+ if (!this.clipId) {
1064
+ this.clipId = clipId;
1065
+ }
1066
+ if (this.instructions && this.instructions.revision > revision) {
1061
1067
  return { success: false };
1062
1068
  }
1063
- this.instructionRegistry.set(clipId, data);
1069
+ this.instructions = payload;
1064
1070
  return { success: true };
1065
1071
  }
1066
- async handleSyncClip(payload) {
1067
- const { clipId, revision, range } = payload;
1068
- const current = this.instructionRegistry.get(clipId);
1069
- if (!current || current.revision > revision) {
1070
- return { success: false };
1072
+ /**
1073
+ * Receive image data from ResourceLoader
1074
+ * Note: ImageBitmap is required because VideoFrame constructor in Worker context
1075
+ * only accepts ImageBitmap/OffscreenCanvas, not HTMLImageElement or Blob
1076
+ */
1077
+ async handleReceiveImage(payload) {
1078
+ const { clipId, imageBitmap } = payload;
1079
+ if (!this.clipId) {
1080
+ this.clipId = clipId;
1081
+ }
1082
+ if (this.imageBitmap) {
1083
+ this.imageBitmap.close();
1084
+ }
1085
+ this.imageBitmap = imageBitmap;
1086
+ if (this.instructions) {
1087
+ await this.startImageFrameStream();
1071
1088
  }
1072
- this.pendingReplay.set(clipId, { ...range, revision });
1073
- this.channel.notify("sync_ack", { clipId, revision });
1074
1089
  return { success: true };
1075
1090
  }
1076
- async handleDisposeClip(payload) {
1077
- const { clipId } = payload;
1078
- this.instructionRegistry.delete(clipId);
1079
- this.pendingReplay.delete(clipId);
1080
- this.downstreamPorts.get(clipId)?.close();
1081
- this.upstreamPorts.get(clipId)?.close();
1082
- this.downstreamPorts.delete(clipId);
1083
- this.upstreamPorts.delete(clipId);
1091
+ async handleDisposeClip() {
1092
+ this.instructions = null;
1093
+ this.streamState = null;
1094
+ this.downstreamPort?.close();
1095
+ this.upstreamPort?.close();
1096
+ this.downstreamPort = null;
1097
+ this.upstreamPort = null;
1098
+ this.imageBitmap?.close();
1099
+ this.imageBitmap = null;
1084
1100
  return { success: true };
1085
1101
  }
1086
- /**
1087
- * Check if frame should be skipped (outside dirty range)
1088
- * Returns true if frame is NOT in the dirty range and should use cached version
1089
- */
1090
- shouldSkipFrame(clipId, timestamp) {
1091
- const dirtyRange = this.pendingReplay.get(clipId);
1092
- if (!dirtyRange) {
1093
- return false;
1094
- }
1095
- if (timestamp >= dirtyRange.startUs && timestamp <= dirtyRange.endUs) {
1096
- return false;
1102
+ async startImageFrameStream() {
1103
+ if (!this.instructions || !this.composer) {
1104
+ return;
1097
1105
  }
1098
- if (timestamp > dirtyRange.endUs) {
1099
- this.pendingReplay.delete(clipId);
1106
+ const timeline = this.instructions.baseConfig.timeline;
1107
+ if (!timeline) {
1108
+ return;
1100
1109
  }
1101
- return true;
1110
+ const { composeStream, cacheStream } = this.composer.createStreams();
1111
+ const { clipDurationUs, compositionFps } = timeline;
1112
+ let currentTimeUs = 0;
1113
+ const readableStream = new ReadableStream({
1114
+ pull: (controller) => {
1115
+ if (currentTimeUs >= clipDurationUs) {
1116
+ controller.close();
1117
+ return;
1118
+ }
1119
+ const videoFrame = new VideoFrame(this.imageBitmap, {
1120
+ timestamp: currentTimeUs,
1121
+ duration: frameDurationFromFps(compositionFps)
1122
+ });
1123
+ const request = this.buildComposeRequest(this.instructions, videoFrame);
1124
+ if (request) {
1125
+ controller.enqueue(request);
1126
+ }
1127
+ currentTimeUs += frameDurationFromFps(compositionFps);
1128
+ }
1129
+ });
1130
+ this.channel.sendStream(cacheStream, {
1131
+ streamType: "video",
1132
+ clipId: this.clipId
1133
+ });
1134
+ readableStream.pipeTo(composeStream).catch((error) => {
1135
+ console.error("[VideoComposeWorker] image frame stream error", this.clipId, error);
1136
+ });
1102
1137
  }
1103
- buildComposeRequest(clipId, instruction, frame, _timestamp) {
1104
- const normalizedTime = this.computeTimelineTimestamp(clipId, frame, instruction.baseConfig);
1138
+ buildComposeRequest(instruction, frame) {
1139
+ const normalizedTime = this.computeTimelineTimestamp(frame, instruction.baseConfig);
1105
1140
  const clipStartUs = instruction.baseConfig.timeline?.clipStartUs ?? 0;
1106
1141
  const clipDurationUs = instruction.baseConfig.timeline?.clipDurationUs ?? Infinity;
1107
1142
  const clipEndUs = clipStartUs + clipDurationUs;
@@ -1142,43 +1177,40 @@ class VideoComposeWorker {
1142
1177
  direction: entry.params.payload?.direction
1143
1178
  };
1144
1179
  }
1145
- computeTimelineTimestamp(clipId, frame, config) {
1146
- const key = clipId;
1147
- let state = this.streamState.get(key);
1148
- if (!state) {
1149
- state = {
1180
+ computeTimelineTimestamp(frame, config) {
1181
+ if (!this.streamState) {
1182
+ this.streamState = {
1150
1183
  baseTimestamp: null,
1151
1184
  lastSourceTimestamp: null,
1152
1185
  nextFrameIndex: 0
1153
1186
  };
1154
- this.streamState.set(key, state);
1155
1187
  }
1156
1188
  const timeline = config.timeline;
1157
1189
  if (!timeline) {
1158
1190
  const ts = frame.timestamp ?? 0;
1159
- state.lastSourceTimestamp = frame.timestamp ?? null;
1191
+ this.streamState.lastSourceTimestamp = frame.timestamp ?? null;
1160
1192
  return ts;
1161
1193
  }
1162
1194
  const { clipStartUs, compositionFps } = timeline;
1163
1195
  const sourceTimestamp = frame.timestamp ?? null;
1164
- if (sourceTimestamp !== null && state.lastSourceTimestamp !== null && sourceTimestamp < state.lastSourceTimestamp) {
1165
- state.baseTimestamp = null;
1166
- state.nextFrameIndex = 0;
1167
- }
1168
- if (state.baseTimestamp === null) {
1169
- state.baseTimestamp = sourceTimestamp ?? 0;
1170
- state.nextFrameIndex = 0;
1171
- if (state.baseTimestamp > 1e3) {
1196
+ if (sourceTimestamp !== null && this.streamState.lastSourceTimestamp !== null && sourceTimestamp < this.streamState.lastSourceTimestamp) {
1197
+ this.streamState.baseTimestamp = null;
1198
+ this.streamState.nextFrameIndex = 0;
1199
+ }
1200
+ if (this.streamState.baseTimestamp === null) {
1201
+ this.streamState.baseTimestamp = sourceTimestamp ?? 0;
1202
+ this.streamState.nextFrameIndex = 0;
1203
+ if (this.streamState.baseTimestamp > 1e3) {
1172
1204
  console.warn(
1173
- `[VideoComposeWorker] First frame timestamp is ${state.baseTimestamp}us, expected ~0. Check MP4Demuxer normalization.`
1205
+ `[VideoComposeWorker] First frame timestamp is ${this.streamState.baseTimestamp}us, expected ~0. Check MP4Demuxer normalization.`
1174
1206
  );
1175
1207
  }
1176
1208
  }
1177
1209
  const frameDuration = frameDurationFromFps(compositionFps);
1178
- let frameIndex = state.nextFrameIndex;
1210
+ let frameIndex = this.streamState.nextFrameIndex;
1179
1211
  if (sourceTimestamp !== null) {
1180
1212
  const approxIndex = frameIndexFromTimestamp(
1181
- state.baseTimestamp,
1213
+ this.streamState.baseTimestamp,
1182
1214
  sourceTimestamp,
1183
1215
  compositionFps,
1184
1216
  "nearest"
@@ -1192,8 +1224,8 @@ class VideoComposeWorker {
1192
1224
  compositionFps,
1193
1225
  "nearest"
1194
1226
  );
1195
- state.nextFrameIndex = frameIndex + 1;
1196
- state.lastSourceTimestamp = sourceTimestamp;
1227
+ this.streamState.nextFrameIndex = frameIndex + 1;
1228
+ this.streamState.lastSourceTimestamp = sourceTimestamp;
1197
1229
  return timelineTime;
1198
1230
  }
1199
1231
  }