@editframe/elements 0.18.20-beta.0 → 0.18.22-beta.0

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 (41) hide show
  1. package/dist/elements/EFAudio.d.ts +1 -12
  2. package/dist/elements/EFAudio.js +3 -18
  3. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +1 -1
  4. package/dist/elements/EFMedia/AssetMediaEngine.js +3 -3
  5. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +15 -9
  6. package/dist/elements/EFMedia/BufferedSeekingInput.js +76 -78
  7. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +12 -10
  8. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +2 -18
  9. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +12 -10
  10. package/dist/elements/EFTemporal.d.ts +0 -1
  11. package/dist/elements/EFTemporal.js +4 -8
  12. package/dist/elements/EFTimegroup.d.ts +4 -4
  13. package/dist/elements/EFTimegroup.js +52 -60
  14. package/dist/elements/EFVideo.d.ts +1 -32
  15. package/dist/elements/EFVideo.js +13 -51
  16. package/dist/elements/SampleBuffer.js +1 -1
  17. package/dist/gui/ContextMixin.browsertest.d.ts +1 -1
  18. package/dist/gui/ContextMixin.js +1 -1
  19. package/package.json +2 -2
  20. package/src/elements/EFAudio.browsertest.ts +0 -3
  21. package/src/elements/EFAudio.ts +3 -22
  22. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +39 -1
  23. package/src/elements/EFMedia/AssetMediaEngine.ts +5 -4
  24. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +90 -185
  25. package/src/elements/EFMedia/BufferedSeekingInput.ts +119 -130
  26. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +21 -21
  27. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +10 -5
  28. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +33 -34
  29. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +22 -20
  30. package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +0 -3
  31. package/src/elements/EFMedia.browsertest.ts +72 -60
  32. package/src/elements/EFTemporal.ts +5 -15
  33. package/src/elements/EFTimegroup.browsertest.ts +9 -4
  34. package/src/elements/EFTimegroup.ts +79 -95
  35. package/src/elements/EFVideo.browsertest.ts +172 -160
  36. package/src/elements/EFVideo.ts +17 -73
  37. package/src/elements/SampleBuffer.ts +1 -2
  38. package/src/gui/ContextMixin.browsertest.ts +5 -2
  39. package/src/gui/ContextMixin.ts +7 -0
  40. package/test/EFVideo.framegen.browsertest.ts +0 -54
  41. package/types.json +1 -1
@@ -25,17 +25,28 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
25
25
  constructor(..._args) {
26
26
  super(..._args);
27
27
  this._timeGroupContext = this;
28
- this.isFrameUpdateInProgress = false;
29
- this.queuedTimeUpdate = null;
30
28
  this.mode = "contain";
31
29
  this.overlapMs = 0;
32
30
  this.fit = "none";
33
31
  this.frameTask = new Task(this, {
34
32
  autoRun: EF_INTERACTIVE,
35
33
  args: () => [this.ownCurrentTimeMs, this.currentTimeMs],
36
- task: async ([], { signal: _signal }) => {
37
- let fullyUpdated = await this.updateComplete;
38
- while (!fullyUpdated) fullyUpdated = await this.updateComplete;
34
+ task: async ([], { signal }) => {
35
+ if (this.isRootTimegroup) await this.waitForFrameTasks(signal);
36
+ }
37
+ });
38
+ this.seekTask = new Task(this, {
39
+ args: () => [this.#pendingSeekTime ?? this.#currentTime],
40
+ task: async ([targetTime], { signal }) => {
41
+ const newTime = Math.max(0, Math.min(targetTime, this.durationMs / 1e3));
42
+ this.#currentTime = newTime;
43
+ this.requestUpdate("currentTime");
44
+ await this.updateComplete;
45
+ signal.throwIfAborted();
46
+ const videoElements = this.querySelectorAll("ef-video");
47
+ for (const video of videoElements) if (video.videoSeekTask) video.videoSeekTask.run();
48
+ await this.frameTask.run();
49
+ this.#saveTimeToLocalStorage(newTime);
39
50
  }
40
51
  });
41
52
  }
@@ -53,18 +64,23 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
53
64
  }
54
65
  #currentTime = 0;
55
66
  #resizeObserver;
56
- #childObserver;
67
+ #seekInProgress = false;
68
+ #pendingSeekTime;
57
69
  set currentTime(time) {
58
- const newTime = Math.max(0, Math.min(time, this.durationMs / 1e3));
59
- if (this.isRootTimegroup && this.isFrameUpdateInProgress) {
60
- this.queuedTimeUpdate = newTime;
70
+ if (this.#seekInProgress) {
71
+ this.#pendingSeekTime = time;
61
72
  return;
62
73
  }
63
- if (this.isRootTimegroup) this.#executeTimeUpdate(newTime);
64
- else {
65
- this.#currentTime = newTime;
66
- this.#saveTimeToLocalStorage(newTime);
67
- }
74
+ this.#seekInProgress = true;
75
+ this.#pendingSeekTime = time;
76
+ this.seekTask.run().finally(() => {
77
+ this.#seekInProgress = false;
78
+ if (this.#pendingSeekTime !== void 0 && this.#pendingSeekTime !== time) {
79
+ const pendingTime = this.#pendingSeekTime;
80
+ this.#pendingSeekTime = void 0;
81
+ this.currentTime = pendingTime;
82
+ } else this.#pendingSeekTime = void 0;
83
+ });
68
84
  }
69
85
  get currentTime() {
70
86
  return this.#currentTime;
@@ -82,26 +98,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
82
98
  return this.closest("ef-timegroup") === this;
83
99
  }
84
100
  /**
85
- * Executes time update with frame locking for root timegroups
86
- */
87
- async #executeTimeUpdate(time) {
88
- this.isFrameUpdateInProgress = true;
89
- this.#currentTime = time;
90
- try {
91
- this.#saveTimeToLocalStorage(time);
92
- await this.waitForFrameTasks();
93
- } catch (error) {
94
- console.error("⚠️ [TIME_UPDATE_ERROR] Error during frame update:", error);
95
- } finally {
96
- this.isFrameUpdateInProgress = false;
97
- if (this.queuedTimeUpdate !== null && this.queuedTimeUpdate !== time) {
98
- const nextTime = this.queuedTimeUpdate;
99
- this.queuedTimeUpdate = null;
100
- setTimeout(() => this.#executeTimeUpdate(nextTime), 0);
101
- }
102
- }
103
- }
104
- /**
105
101
  * Saves time to localStorage (extracted for reuse)
106
102
  */
107
103
  #saveTimeToLocalStorage(time) {
@@ -129,25 +125,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
129
125
  });
130
126
  if (this.parentTimegroup) new TimegroupController(this.parentTimegroup, this);
131
127
  if (this.shouldWrapWithWorkbench()) this.wrapWithWorkbench();
132
- this.#childObserver = new MutationObserver((mutations) => {
133
- let shouldUpdate = false;
134
- for (const mutation of mutations) if (mutation.type === "childList") shouldUpdate = true;
135
- else if (mutation.type === "attributes") {
136
- if (mutation.attributeName === "duration" || mutation.attributeName === "mode") shouldUpdate = true;
137
- }
138
- if (shouldUpdate) {
139
- import("./EFTemporal.js").then(({ clearTemporalCacheForElement }) => {
140
- clearTemporalCacheForElement(this);
141
- });
142
- this.requestUpdate();
143
- }
144
- });
145
- this.#childObserver.observe(this, {
146
- childList: true,
147
- subtree: true,
148
- attributes: true,
149
- attributeFilter: ["duration", "mode"]
150
- });
151
128
  requestAnimationFrame(() => {
152
129
  this.updateAnimations();
153
130
  });
@@ -155,7 +132,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
155
132
  disconnectedCallback() {
156
133
  super.disconnectedCallback();
157
134
  this.#resizeObserver?.disconnect();
158
- this.#childObserver?.disconnect();
159
135
  }
160
136
  get storageKey() {
161
137
  if (!this.id) throw new Error("Timegroup must have an id to use localStorage.");
@@ -196,21 +172,37 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
196
172
  default: throw new Error(`Invalid time mode: ${this.mode}`);
197
173
  }
198
174
  }
199
- async getPendingFrameTasks() {
200
- await this.updateComplete;
175
+ async getPendingFrameTasks(signal) {
176
+ await this.waitForNestedUpdates(signal);
177
+ signal?.throwIfAborted();
201
178
  const temporals = deepGetElementsWithFrameTasks(this);
202
179
  return temporals.map((temporal) => temporal.frameTask).filter((task) => task.status < TaskStatus.COMPLETE);
203
180
  }
204
- async waitForFrameTasks() {
181
+ async waitForNestedUpdates(signal) {
182
+ const limit = 10;
183
+ let steps = 0;
184
+ let isComplete = true;
185
+ while (true) {
186
+ steps++;
187
+ if (steps > limit) throw new Error("Reached update depth limit.");
188
+ isComplete = await this.updateComplete;
189
+ signal?.throwIfAborted();
190
+ if (isComplete) break;
191
+ }
192
+ }
193
+ async waitForFrameTasks(signal) {
205
194
  const limit = 10;
206
195
  let step = 0;
207
- await this.updateComplete;
196
+ await this.waitForNestedUpdates(signal);
208
197
  while (step < limit) {
209
198
  step++;
210
- let pendingTasks = await this.getPendingFrameTasks();
199
+ let pendingTasks = await this.getPendingFrameTasks(signal);
200
+ signal?.throwIfAborted();
211
201
  await Promise.all(pendingTasks.map((task) => task.taskComplete));
202
+ signal?.throwIfAborted();
212
203
  await this.updateComplete;
213
- pendingTasks = await this.getPendingFrameTasks();
204
+ signal?.throwIfAborted();
205
+ pendingTasks = await this.getPendingFrameTasks(signal);
214
206
  if (pendingTasks.length === 0) break;
215
207
  }
216
208
  }
@@ -78,42 +78,11 @@ export declare class EFVideo extends EFVideo_base {
78
78
  * Check if EF_FRAMEGEN has explicitly started frame rendering (not just initialization)
79
79
  */
80
80
  private isFrameRenderingActive;
81
- /**
82
- * Effective mode - always returns "asset" for EFVideo
83
- */
84
- get effectiveMode(): string;
85
- /**
86
- * Legacy getter for asset index loader (maps to mediaEngine task)
87
- */
88
- get assetIndexLoader(): Task<readonly [string, string | null], import('../transcoding/types/index.ts').MediaEngine>;
89
81
  /**
90
82
  * Legacy getter for fragment index task (maps to videoSegmentIdTask)
83
+ * Still used by EFCaptions
91
84
  */
92
85
  get fragmentIndexTask(): Task<readonly [import('../transcoding/types/index.ts').MediaEngine | undefined, number], number | undefined>;
93
- /**
94
- * Legacy getter for seek task (maps to videoSeekTask)
95
- */
96
- get seekTask(): Task<readonly [number, import('./EFMedia/BufferedSeekingInput.ts').BufferedSeekingInput | undefined], import('mediabunny').VideoSample | undefined>;
97
- /**
98
- * Legacy getter for media segments task (maps to videoSegmentFetchTask)
99
- */
100
- get mediaSegmentsTask(): Task<readonly [import('../transcoding/types/index.ts').MediaEngine | undefined, number | undefined], ArrayBuffer>;
101
- /**
102
- * Legacy getter for asset segment keys task (maps to videoSegmentIdTask)
103
- */
104
- get assetSegmentKeysTask(): Task<readonly [import('../transcoding/types/index.ts').MediaEngine | undefined, number], number | undefined>;
105
- /**
106
- * Legacy getter for asset init segments task (maps to videoInitSegmentFetchTask)
107
- */
108
- get assetInitSegmentsTask(): Task<readonly [import('../transcoding/types/index.ts').MediaEngine | undefined], ArrayBuffer>;
109
- /**
110
- * Legacy getter for asset segment loader (maps to videoSegmentFetchTask)
111
- */
112
- get assetSegmentLoader(): Task<readonly [import('../transcoding/types/index.ts').MediaEngine | undefined, number | undefined], ArrayBuffer>;
113
- /**
114
- * Legacy getter for video asset task (maps to videoBufferTask)
115
- */
116
- get videoAssetTask(): Task<readonly [number], import('./EFMedia/videoTasks/makeVideoBufferTask.ts').VideoBufferState>;
117
86
  /**
118
87
  * Clean up resources when component is disconnected
119
88
  */
@@ -1,3 +1,4 @@
1
+ import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
1
2
  import { EFMedia } from "./EFMedia.js";
2
3
  import { TWMixin } from "../gui/TWMixin2.js";
3
4
  import { DelayedLoadingState } from "../DelayedLoadingState.js";
@@ -94,6 +95,7 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
94
95
  message: ""
95
96
  };
96
97
  this.frameTask = new Task(this, {
98
+ autoRun: EF_INTERACTIVE,
97
99
  args: () => [this.desiredSeekTimeMs],
98
100
  onError: (error) => {
99
101
  console.error("frameTask error", error);
@@ -117,8 +119,11 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
117
119
  const sample = this.videoSeekTask.value;
118
120
  if (sample) {
119
121
  const videoFrame = sample.toVideoFrame();
120
- this.displayFrame(videoFrame, _seekToMs);
121
- videoFrame.close();
122
+ try {
123
+ this.displayFrame(videoFrame, _seekToMs);
124
+ } finally {
125
+ videoFrame.close();
126
+ }
122
127
  }
123
128
  if (!isProductionRendering) {
124
129
  if (!this.rootTimegroup || this.rootTimegroup.currentTimeMs === 0 && this.desiredSeekTimeMs === 0) return;
@@ -150,7 +155,11 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
150
155
  `;
151
156
  }
152
157
  get canvasElement() {
153
- return this.canvasRef.value;
158
+ const referencedCanvas = this.canvasRef.value;
159
+ if (referencedCanvas) return referencedCanvas;
160
+ const shadowCanvas = this.shadowRoot?.querySelector("canvas");
161
+ if (shadowCanvas) return shadowCanvas;
162
+ return void 0;
154
163
  }
155
164
  updated(changedProperties) {
156
165
  super.updated(changedProperties);
@@ -234,60 +243,13 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
234
243
  return currentTime >= renderStartTime;
235
244
  }
236
245
  /**
237
- * Effective mode - always returns "asset" for EFVideo
238
- */
239
- get effectiveMode() {
240
- return "asset";
241
- }
242
- /**
243
- * Legacy getter for asset index loader (maps to mediaEngine task)
244
- */
245
- get assetIndexLoader() {
246
- return this.mediaEngineTask;
247
- }
248
- /**
249
246
  * Legacy getter for fragment index task (maps to videoSegmentIdTask)
247
+ * Still used by EFCaptions
250
248
  */
251
249
  get fragmentIndexTask() {
252
250
  return this.videoSegmentIdTask;
253
251
  }
254
252
  /**
255
- * Legacy getter for seek task (maps to videoSeekTask)
256
- */
257
- get seekTask() {
258
- return this.videoSeekTask;
259
- }
260
- /**
261
- * Legacy getter for media segments task (maps to videoSegmentFetchTask)
262
- */
263
- get mediaSegmentsTask() {
264
- return this.videoSegmentFetchTask;
265
- }
266
- /**
267
- * Legacy getter for asset segment keys task (maps to videoSegmentIdTask)
268
- */
269
- get assetSegmentKeysTask() {
270
- return this.videoSegmentIdTask;
271
- }
272
- /**
273
- * Legacy getter for asset init segments task (maps to videoInitSegmentFetchTask)
274
- */
275
- get assetInitSegmentsTask() {
276
- return this.videoInitSegmentFetchTask;
277
- }
278
- /**
279
- * Legacy getter for asset segment loader (maps to videoSegmentFetchTask)
280
- */
281
- get assetSegmentLoader() {
282
- return this.videoSegmentFetchTask;
283
- }
284
- /**
285
- * Legacy getter for video asset task (maps to videoBufferTask)
286
- */
287
- get videoAssetTask() {
288
- return this.videoBufferTask;
289
- }
290
- /**
291
253
  * Clean up resources when component is disconnected
292
254
  */
293
255
  disconnectedCallback() {
@@ -34,7 +34,7 @@ var SampleBuffer = class {
34
34
  const sampleStartMs = roundToMilliseconds((sample.timestamp || 0) * 1e3);
35
35
  const sampleDurationMs = roundToMilliseconds((sample.duration || 0) * 1e3);
36
36
  const sampleEndMs = roundToMilliseconds(sampleStartMs + sampleDurationMs);
37
- if (targetTimeMs >= sampleStartMs && targetTimeMs <= sampleEndMs) return sample;
37
+ if (targetTimeMs >= sampleStartMs && targetTimeMs < sampleEndMs) return sample;
38
38
  }
39
39
  return void 0;
40
40
  }
@@ -4,7 +4,7 @@ declare class TestContext extends TestContext_base {
4
4
  }
5
5
  declare const TestContextElement_base: (new (...args: any[]) => import('./ContextMixin.js').ContextMixinInterface) & typeof LitElement;
6
6
  declare class TestContextElement extends TestContextElement_base {
7
- render(): import('../elements/EFTimegroup.js').EFTimegroup & Element;
7
+ render(): import('lit-html').TemplateResult<1>;
8
8
  }
9
9
  declare global {
10
10
  interface HTMLElementTagNameMap {
@@ -149,7 +149,7 @@ function ContextMixin(superClass) {
149
149
  if (newTimegroup !== this.targetTimegroup) {
150
150
  this.targetTimegroup = newTimegroup;
151
151
  shouldUpdate = true;
152
- }
152
+ } else if (mutation.target instanceof Element && (mutation.target.tagName === "EF-TIMEGROUP" || mutation.target.closest("ef-timegroup"))) shouldUpdate = true;
153
153
  } else if (mutation.type === "attributes") {
154
154
  if (mutation.attributeName === "duration" || mutation.attributeName === "mode" || mutation.target instanceof Element && (mutation.target.tagName === "EF-TIMEGROUP" || mutation.target.closest("ef-timegroup"))) shouldUpdate = true;
155
155
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.18.20-beta.0",
3
+ "version": "0.18.22-beta.0",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
@@ -27,7 +27,7 @@
27
27
  "license": "UNLICENSED",
28
28
  "dependencies": {
29
29
  "@bramus/style-observer": "^1.3.0",
30
- "@editframe/assets": "0.18.20-beta.0",
30
+ "@editframe/assets": "0.18.22-beta.0",
31
31
  "@lit/context": "^1.1.2",
32
32
  "@lit/task": "^1.0.1",
33
33
  "d3": "^7.9.0",
@@ -237,9 +237,6 @@ describe("EFAudio", () => {
237
237
 
238
238
  // Should coordinate the expected tasks
239
239
  expect(audio.fragmentIndexTask).toBeDefined();
240
- expect(audio.mediaSegmentsTask).toBeDefined();
241
- expect(audio.seekTask).toBeDefined();
242
- expect(audio.videoAssetTask).toBeDefined();
243
240
  });
244
241
 
245
242
  test("frameTask handles missing dependencies", ({ expect }) => {
@@ -2,6 +2,7 @@ import { Task } from "@lit/task";
2
2
  import { html } from "lit";
3
3
  import { customElement, property } from "lit/decorators.js";
4
4
  import { createRef, ref } from "lit/directives/ref.js";
5
+ import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
5
6
  import { TWMixin } from "../gui/TWMixin.js";
6
7
  import { EFMedia } from "./EFMedia.js";
7
8
 
@@ -21,6 +22,7 @@ export class EFAudio extends TWMixin(EFMedia) {
21
22
  }
22
23
 
23
24
  frameTask = new Task(this, {
25
+ autoRun: EF_INTERACTIVE,
24
26
  args: () =>
25
27
  [
26
28
  this.audioBufferTask.status,
@@ -37,34 +39,13 @@ export class EFAudio extends TWMixin(EFMedia) {
37
39
  },
38
40
  });
39
41
 
40
- // Getter properties for backward compatibility with tests
41
42
  /**
42
43
  * Legacy getter for fragment index task (maps to audioSegmentIdTask)
44
+ * Still used by EFCaptions
43
45
  */
44
46
  get fragmentIndexTask() {
45
47
  return this.audioSegmentIdTask;
46
48
  }
47
-
48
- /**
49
- * Legacy getter for media segments task (maps to audioSegmentFetchTask)
50
- */
51
- get mediaSegmentsTask() {
52
- return this.audioSegmentFetchTask;
53
- }
54
-
55
- /**
56
- * Legacy getter for seek task (maps to audioSeekTask)
57
- */
58
- get seekTask() {
59
- return this.audioSeekTask;
60
- }
61
-
62
- /**
63
- * Legacy getter for audio asset task (maps to audioBufferTask)
64
- */
65
- get videoAssetTask() {
66
- return this.audioBufferTask;
67
- }
68
49
  }
69
50
 
70
51
  declare global {
@@ -40,7 +40,7 @@ describe("AssetMediaEngine", () => {
40
40
  mediaEngine,
41
41
  expect,
42
42
  }) => {
43
- expect(mediaEngine.durationMs).toBeCloseTo(10023, 0); // Updated: improved mediabunny processing changed duration
43
+ expect(mediaEngine.durationMs).toBeCloseTo(10031, 0); // Updated: improved mediabunny processing changed duration
44
44
  });
45
45
 
46
46
  test("provides source URL from constructor", async ({
@@ -97,4 +97,42 @@ describe("AssetMediaEngine", () => {
97
97
  expect(mediaEngine.computeSegmentId(500, audio as any)).toBe(0);
98
98
  expect(mediaEngine.computeSegmentId(1500, audio as any)).toBe(0);
99
99
  });
100
+
101
+ describe("bars n tone segment id computation", () => {
102
+ test("computes 0ms is 0", ({ expect, mediaEngine }) => {
103
+ expect(
104
+ mediaEngine.computeSegmentId(0, mediaEngine.getVideoRendition()),
105
+ ).toBe(0);
106
+ });
107
+
108
+ test("computes 2000 is 1", ({ expect, mediaEngine }) => {
109
+ expect(
110
+ mediaEngine.computeSegmentId(2000, mediaEngine.getVideoRendition()),
111
+ ).toBe(1);
112
+ });
113
+
114
+ test("computes 4000 is 2", ({ expect, mediaEngine }) => {
115
+ expect(
116
+ mediaEngine.computeSegmentId(4000, mediaEngine.getVideoRendition()),
117
+ ).toBe(2);
118
+ });
119
+
120
+ test("computes 6000 is 3", ({ expect, mediaEngine }) => {
121
+ expect(
122
+ mediaEngine.computeSegmentId(6000, mediaEngine.getVideoRendition()),
123
+ ).toBe(3);
124
+ });
125
+
126
+ test("computes 8000 is 4", ({ expect, mediaEngine }) => {
127
+ expect(
128
+ mediaEngine.computeSegmentId(8000, mediaEngine.getVideoRendition()),
129
+ ).toBe(4);
130
+ });
131
+
132
+ test("computes 7975 is 3", ({ expect, mediaEngine }) => {
133
+ expect(
134
+ mediaEngine.computeSegmentId(7975, mediaEngine.getVideoRendition()),
135
+ ).toBe(3);
136
+ });
137
+ });
100
138
  });
@@ -214,7 +214,7 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
214
214
  return segmentRanges;
215
215
  }
216
216
 
217
- computeSegmentId(desiredSeekTimeMs: number, rendition: MediaRendition) {
217
+ computeSegmentId(seekTimeMs: number, rendition: MediaRendition) {
218
218
  if (!rendition.trackId) {
219
219
  throw new Error("Track ID is required for asset metadata");
220
220
  }
@@ -227,11 +227,12 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
227
227
  // Apply startTimeOffsetMs to map user timeline to media timeline for segment selection
228
228
  const startTimeOffsetMs =
229
229
  ("startTimeOffsetMs" in rendition && rendition.startTimeOffsetMs) || 0;
230
- const mediaTimeMs = roundToMilliseconds(
231
- desiredSeekTimeMs + startTimeOffsetMs,
230
+
231
+ const offsetSeekTimeMs = roundToMilliseconds(
232
+ seekTimeMs + startTimeOffsetMs,
232
233
  );
233
234
  // Convert to timescale units using consistent precision
234
- const scaledSeekTime = convertToScaledTime(mediaTimeMs, timescale);
235
+ const scaledSeekTime = convertToScaledTime(offsetSeekTimeMs, timescale);
235
236
 
236
237
  // Find the segment that contains the actual seek time
237
238
  for (let i = segments.length - 1; i >= 0; i--) {