@editframe/assets 0.16.7-beta.0 → 0.17.6-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.
- package/dist/DecoderManager.d.ts +62 -0
- package/dist/DecoderManager.js +114 -0
- package/dist/EncodedAsset.d.ts +58 -16
- package/dist/EncodedAsset.js +436 -565
- package/dist/FrameBuffer.d.ts +62 -0
- package/dist/FrameBuffer.js +89 -0
- package/dist/MP4File.d.ts +9 -1
- package/dist/MP4File.js +205 -219
- package/dist/MP4SampleAnalyzer.d.ts +59 -0
- package/dist/MP4SampleAnalyzer.js +119 -0
- package/dist/Probe.d.ts +1 -0
- package/dist/Probe.js +273 -301
- package/dist/SeekStrategy.d.ts +82 -0
- package/dist/SeekStrategy.js +101 -0
- package/dist/VideoRenderOptions.js +31 -33
- package/dist/idempotentTask.js +78 -78
- package/dist/index.js +1 -15
- package/dist/md5.js +35 -51
- package/dist/memoize.js +9 -12
- package/dist/mp4FileWritable.js +16 -18
- package/dist/tasks/cacheImage.js +13 -15
- package/dist/tasks/findOrCreateCaptions.js +18 -21
- package/dist/tasks/generateTrack.js +45 -63
- package/dist/tasks/generateTrackFragmentIndex.js +88 -101
- package/package.json +4 -4
- package/types.json +1 -1
|
@@ -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 };
|