@editframe/assets 0.16.8-beta.0 → 0.18.3-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.
@@ -0,0 +1,59 @@
1
+ import type * as MP4Box from "mp4box";
2
+ export interface KeyframePosition {
3
+ sampleNumber: number;
4
+ cts: number;
5
+ }
6
+ /**
7
+ * Handles MP4 sample analysis and time-to-sample conversion logic.
8
+ * Provides efficient access to video samples with caching and time-based lookup.
9
+ */
10
+ export declare class MP4SampleAnalyzer {
11
+ private readonly mp4boxFile;
12
+ private readonly defaultVideoTrack;
13
+ constructor(mp4boxFile: MP4Box.ISOFile, defaultVideoTrack: any);
14
+ /**
15
+ * Gets a sample by index with error handling
16
+ */
17
+ getSample(index: number): MP4Box.Sample;
18
+ /**
19
+ * Gets the timescale for the default video track
20
+ */
21
+ get timescale(): number;
22
+ /**
23
+ * Gets all video samples for the default track
24
+ */
25
+ get samples(): MP4Box.Sample[];
26
+ /**
27
+ * Gets samples sorted by composition timestamp (CTS) for display order
28
+ */
29
+ get displayOrderedSamples(): MP4Box.Sample[];
30
+ /**
31
+ * Finds the sample closest to the given time in seconds
32
+ */
33
+ getSampleClosetToTime(seconds: number): MP4Box.Sample;
34
+ /**
35
+ * Calculates the edit offset from track edits
36
+ */
37
+ get editsOffset(): number;
38
+ /**
39
+ * Gets the default video track structure from MP4Box
40
+ */
41
+ get defaultVideoTrak(): any;
42
+ /**
43
+ * Determines if this segment has multiple keyframes (typical for 30s scrub segments)
44
+ */
45
+ hasMultipleKeyframes(): boolean;
46
+ /**
47
+ * Gets positions of all keyframes within the segment
48
+ */
49
+ getKeyframePositions(): KeyframePosition[];
50
+ /**
51
+ * Calculates the average interval between keyframes in timestamp units
52
+ */
53
+ getKeyframeInterval(): number;
54
+ /**
55
+ * Finds the optimal keyframe to start decoding from for a given target sample.
56
+ * Returns the keyframe closest to but not after the target.
57
+ */
58
+ findOptimalKeyframeForSeek(targetSample: MP4Box.Sample): MP4Box.Sample;
59
+ }
@@ -0,0 +1,119 @@
1
+ import { memoize } from "./memoize.js";
2
+ import _decorate from "@oxc-project/runtime/helpers/decorate";
3
+ /**
4
+ * Handles MP4 sample analysis and time-to-sample conversion logic.
5
+ * Provides efficient access to video samples with caching and time-based lookup.
6
+ */
7
+ var MP4SampleAnalyzer = class {
8
+ constructor(mp4boxFile, defaultVideoTrack) {
9
+ this.mp4boxFile = mp4boxFile;
10
+ this.defaultVideoTrack = defaultVideoTrack;
11
+ }
12
+ /**
13
+ * Gets a sample by index with error handling
14
+ */
15
+ getSample(index) {
16
+ const sample = this.samples?.[index];
17
+ if (!sample) throw new Error(`Sample not found at index ${index}`);
18
+ return sample;
19
+ }
20
+ /**
21
+ * Gets the timescale for the default video track
22
+ */
23
+ get timescale() {
24
+ if (!this.defaultVideoTrack) throw new Error("No default video track found");
25
+ return this.defaultVideoTrack.timescale;
26
+ }
27
+ /**
28
+ * Gets all video samples for the default track
29
+ */
30
+ get samples() {
31
+ if (!this.defaultVideoTrak.samples) throw new Error("No video samples found");
32
+ return this.defaultVideoTrak.samples;
33
+ }
34
+ /**
35
+ * Gets samples sorted by composition timestamp (CTS) for display order
36
+ */
37
+ get displayOrderedSamples() {
38
+ return Array.from(this.samples).sort((a, b) => {
39
+ return a.cts - b.cts;
40
+ });
41
+ }
42
+ /**
43
+ * Finds the sample closest to the given time in seconds
44
+ */
45
+ getSampleClosetToTime(seconds) {
46
+ const targetTime = Math.round(seconds * this.timescale + this.editsOffset);
47
+ const sampleIndex = this.displayOrderedSamples.findIndex((sample) => sample.cts >= targetTime);
48
+ if (sampleIndex === -1) return this.displayOrderedSamples[this.displayOrderedSamples.length - 1];
49
+ return this.displayOrderedSamples[sampleIndex];
50
+ }
51
+ /**
52
+ * Calculates the edit offset from track edits
53
+ */
54
+ get editsOffset() {
55
+ if (!this.defaultVideoTrack?.edits) return 0;
56
+ return this.defaultVideoTrack.edits.reduce((acc, edit) => {
57
+ return acc + edit.media_time;
58
+ }, 0);
59
+ }
60
+ /**
61
+ * Gets the default video track structure from MP4Box
62
+ */
63
+ get defaultVideoTrak() {
64
+ return this.mp4boxFile.getTrackById(this.defaultVideoTrack?.id ?? -1);
65
+ }
66
+ /**
67
+ * Determines if this segment has multiple keyframes (typical for 30s scrub segments)
68
+ */
69
+ hasMultipleKeyframes() {
70
+ const keyframes = this.samples.filter((sample) => sample.is_sync);
71
+ return keyframes.length > 1;
72
+ }
73
+ /**
74
+ * Gets positions of all keyframes within the segment
75
+ */
76
+ getKeyframePositions() {
77
+ return this.samples.filter((sample) => sample.is_sync).map((sample) => ({
78
+ sampleNumber: sample.number,
79
+ cts: sample.cts
80
+ }));
81
+ }
82
+ /**
83
+ * Calculates the average interval between keyframes in timestamp units
84
+ */
85
+ getKeyframeInterval() {
86
+ const keyframes = this.getKeyframePositions();
87
+ if (keyframes.length <= 1) return 0;
88
+ let totalInterval = 0;
89
+ for (let i = 1; i < keyframes.length; i++) {
90
+ const currentKeyframe = keyframes[i];
91
+ const previousKeyframe = keyframes[i - 1];
92
+ if (currentKeyframe && previousKeyframe) totalInterval += currentKeyframe.cts - previousKeyframe.cts;
93
+ }
94
+ return totalInterval / (keyframes.length - 1);
95
+ }
96
+ /**
97
+ * Finds the optimal keyframe to start decoding from for a given target sample.
98
+ * Returns the keyframe closest to but not after the target.
99
+ */
100
+ findOptimalKeyframeForSeek(targetSample) {
101
+ const keyframes = this.getKeyframePositions();
102
+ if (keyframes.length === 0) throw new Error("No keyframes found for optimal seek");
103
+ if (targetSample.is_sync) return targetSample;
104
+ let optimalKeyframe = keyframes[0];
105
+ if (!optimalKeyframe) throw new Error("No keyframes available");
106
+ for (const keyframe of keyframes) if (keyframe.cts <= targetSample.cts) optimalKeyframe = keyframe;
107
+ else break;
108
+ const sample = this.samples[optimalKeyframe.sampleNumber];
109
+ if (!sample) throw new Error(`Sample not found at keyframe position ${optimalKeyframe.sampleNumber}`);
110
+ return sample;
111
+ }
112
+ };
113
+ _decorate([memoize], MP4SampleAnalyzer.prototype, "timescale", null);
114
+ _decorate([memoize], MP4SampleAnalyzer.prototype, "samples", null);
115
+ _decorate([memoize], MP4SampleAnalyzer.prototype, "displayOrderedSamples", null);
116
+ _decorate([memoize], MP4SampleAnalyzer.prototype, "editsOffset", null);
117
+ _decorate([memoize], MP4SampleAnalyzer.prototype, "defaultVideoTrak", null);
118
+ _decorate([memoize], MP4SampleAnalyzer.prototype, "getKeyframePositions", null);
119
+ export { MP4SampleAnalyzer };
package/dist/Probe.d.ts CHANGED
@@ -612,6 +612,7 @@ export interface VideoTrackFragmentIndex {
612
612
  height: number;
613
613
  sample_count: number;
614
614
  codec: string;
615
+ startTimeOffsetMs?: number;
615
616
  initSegment: {
616
617
  offset: 0;
617
618
  size: number;