@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.
- 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 -309
- package/dist/SeekStrategy.d.ts +82 -0
- package/dist/SeekStrategy.js +101 -0
- package/dist/VideoRenderOptions.d.ts +27 -5
- package/dist/VideoRenderOptions.js +31 -33
- package/dist/idempotentTask.js +114 -79
- 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/cacheRemoteAsset.d.ts +0 -1
- package/dist/tasks/findOrCreateCaptions.js +18 -21
- package/dist/tasks/generateTrack.js +45 -63
- package/dist/tasks/generateTrackFragmentIndex.js +105 -101
- package/package.json +4 -4
- package/src/tasks/generateTrackFragmentIndex.ts +32 -0
- package/types.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrameBuffer manages a collection of decoded VideoFrames with automatic LRU eviction.
|
|
3
|
+
*
|
|
4
|
+
* This class extracts the frame buffering logic from VideoAsset to improve
|
|
5
|
+
* testability and separation of concerns while maintaining the exact same
|
|
6
|
+
* memory management behavior.
|
|
7
|
+
*/
|
|
8
|
+
export declare class FrameBuffer {
|
|
9
|
+
private _frames;
|
|
10
|
+
private readonly _maxSize;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new FrameBuffer with the specified maximum size.
|
|
13
|
+
*
|
|
14
|
+
* @param maxSize Maximum number of frames to keep in buffer (default: 30)
|
|
15
|
+
*/
|
|
16
|
+
constructor(maxSize?: number);
|
|
17
|
+
/**
|
|
18
|
+
* Gets the maximum size of the buffer.
|
|
19
|
+
*/
|
|
20
|
+
get maxSize(): number;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the current number of frames in the buffer.
|
|
23
|
+
*/
|
|
24
|
+
get size(): number;
|
|
25
|
+
/**
|
|
26
|
+
* Gets a copy of the frames array for compatibility.
|
|
27
|
+
* Note: Returns a shallow copy to prevent external modification while allowing access.
|
|
28
|
+
*/
|
|
29
|
+
get frames(): VideoFrame[];
|
|
30
|
+
/**
|
|
31
|
+
* Adds a frame to the buffer. If the buffer exceeds maxSize after adding,
|
|
32
|
+
* the oldest frames will be automatically pruned and closed.
|
|
33
|
+
*
|
|
34
|
+
* @param frame The VideoFrame to add to the buffer
|
|
35
|
+
*/
|
|
36
|
+
add(frame: VideoFrame): void;
|
|
37
|
+
/**
|
|
38
|
+
* Finds a frame in the buffer by its timestamp.
|
|
39
|
+
*
|
|
40
|
+
* @param timestamp The timestamp to search for
|
|
41
|
+
* @returns The VideoFrame with matching timestamp, or undefined if not found
|
|
42
|
+
*/
|
|
43
|
+
findByTimestamp(timestamp: number): VideoFrame | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Checks if the buffer contains the specified frame instance.
|
|
46
|
+
*
|
|
47
|
+
* @param frame The VideoFrame instance to check for
|
|
48
|
+
* @returns true if the frame is in the buffer, false otherwise
|
|
49
|
+
*/
|
|
50
|
+
includes(frame: VideoFrame): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Removes all frames from the buffer and closes them to free memory.
|
|
53
|
+
* This method is safe to call even if some frames are already closed.
|
|
54
|
+
*/
|
|
55
|
+
clear(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Prunes the buffer to the maximum size by removing and closing the oldest frames.
|
|
58
|
+
* This maintains the LRU (Least Recently Used) eviction policy where oldest
|
|
59
|
+
* frames are removed first.
|
|
60
|
+
*/
|
|
61
|
+
pruneToSize(): void;
|
|
62
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrameBuffer manages a collection of decoded VideoFrames with automatic LRU eviction.
|
|
3
|
+
*
|
|
4
|
+
* This class extracts the frame buffering logic from VideoAsset to improve
|
|
5
|
+
* testability and separation of concerns while maintaining the exact same
|
|
6
|
+
* memory management behavior.
|
|
7
|
+
*/
|
|
8
|
+
var FrameBuffer = class {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new FrameBuffer with the specified maximum size.
|
|
11
|
+
*
|
|
12
|
+
* @param maxSize Maximum number of frames to keep in buffer (default: 30)
|
|
13
|
+
*/
|
|
14
|
+
constructor(maxSize = 30) {
|
|
15
|
+
this._frames = [];
|
|
16
|
+
this._maxSize = maxSize;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Gets the maximum size of the buffer.
|
|
20
|
+
*/
|
|
21
|
+
get maxSize() {
|
|
22
|
+
return this._maxSize;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Gets the current number of frames in the buffer.
|
|
26
|
+
*/
|
|
27
|
+
get size() {
|
|
28
|
+
return this._frames.length;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Gets a copy of the frames array for compatibility.
|
|
32
|
+
* Note: Returns a shallow copy to prevent external modification while allowing access.
|
|
33
|
+
*/
|
|
34
|
+
get frames() {
|
|
35
|
+
return [...this._frames];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Adds a frame to the buffer. If the buffer exceeds maxSize after adding,
|
|
39
|
+
* the oldest frames will be automatically pruned and closed.
|
|
40
|
+
*
|
|
41
|
+
* @param frame The VideoFrame to add to the buffer
|
|
42
|
+
*/
|
|
43
|
+
add(frame) {
|
|
44
|
+
this._frames.push(frame);
|
|
45
|
+
this.pruneToSize();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Finds a frame in the buffer by its timestamp.
|
|
49
|
+
*
|
|
50
|
+
* @param timestamp The timestamp to search for
|
|
51
|
+
* @returns The VideoFrame with matching timestamp, or undefined if not found
|
|
52
|
+
*/
|
|
53
|
+
findByTimestamp(timestamp) {
|
|
54
|
+
return this._frames.find((frame) => frame.timestamp === timestamp);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Checks if the buffer contains the specified frame instance.
|
|
58
|
+
*
|
|
59
|
+
* @param frame The VideoFrame instance to check for
|
|
60
|
+
* @returns true if the frame is in the buffer, false otherwise
|
|
61
|
+
*/
|
|
62
|
+
includes(frame) {
|
|
63
|
+
return this._frames.includes(frame);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Removes all frames from the buffer and closes them to free memory.
|
|
67
|
+
* This method is safe to call even if some frames are already closed.
|
|
68
|
+
*/
|
|
69
|
+
clear() {
|
|
70
|
+
for (const frame of this._frames) try {
|
|
71
|
+
frame.close();
|
|
72
|
+
} catch (error) {}
|
|
73
|
+
this._frames = [];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Prunes the buffer to the maximum size by removing and closing the oldest frames.
|
|
77
|
+
* This maintains the LRU (Least Recently Used) eviction policy where oldest
|
|
78
|
+
* frames are removed first.
|
|
79
|
+
*/
|
|
80
|
+
pruneToSize() {
|
|
81
|
+
while (this._frames.length > this._maxSize) {
|
|
82
|
+
const oldestFrame = this._frames.shift();
|
|
83
|
+
if (oldestFrame) try {
|
|
84
|
+
oldestFrame.close();
|
|
85
|
+
} catch (error) {}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
export { FrameBuffer };
|
package/dist/MP4File.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import * as MP4Box from "mp4box";
|
|
2
|
+
export interface MP4FileOptions {
|
|
3
|
+
readyTimeoutMs?: number;
|
|
4
|
+
sampleWaitTimeoutMs?: number;
|
|
5
|
+
}
|
|
2
6
|
export declare class MP4File extends MP4Box.ISOFile {
|
|
3
|
-
|
|
7
|
+
private timeoutId?;
|
|
8
|
+
private readonly readyTimeoutMs;
|
|
9
|
+
private readonly sampleWaitTimeoutMs;
|
|
10
|
+
readonly readyPromise: Promise<void>;
|
|
11
|
+
constructor(options?: MP4FileOptions);
|
|
4
12
|
setSegmentOptions(id: number, user: any, options: MP4Box.SegmentOptions): void;
|
|
5
13
|
/**
|
|
6
14
|
* Fragments all tracks in a file into separate array buffers.
|
package/dist/MP4File.js
CHANGED
|
@@ -1,223 +1,209 @@
|
|
|
1
1
|
import * as MP4Box from "mp4box";
|
|
2
2
|
import debug from "debug";
|
|
3
3
|
const log = debug("ef:av:mp4file");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
return new Promise((resolve) => {
|
|
210
|
-
this.waitingForSamples.push(resolve);
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
processSamples(last) {
|
|
214
|
-
this._hasSeenLastSamples = last;
|
|
215
|
-
for (const observer of this.waitingForSamples) {
|
|
216
|
-
observer(last);
|
|
217
|
-
}
|
|
218
|
-
this.waitingForSamples = [];
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
export {
|
|
222
|
-
MP4File
|
|
4
|
+
var MP4File = class extends MP4Box.ISOFile {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
super();
|
|
7
|
+
this.waitingForSamples = [];
|
|
8
|
+
this._hasSeenLastSamples = false;
|
|
9
|
+
this._arrayBufferFileStart = 0;
|
|
10
|
+
this.readyTimeoutMs = options.readyTimeoutMs ?? 1e3;
|
|
11
|
+
this.sampleWaitTimeoutMs = options.sampleWaitTimeoutMs ?? 1e3;
|
|
12
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
13
|
+
this.onReady = () => {
|
|
14
|
+
if (this.timeoutId) {
|
|
15
|
+
clearTimeout(this.timeoutId);
|
|
16
|
+
this.timeoutId = void 0;
|
|
17
|
+
}
|
|
18
|
+
resolve();
|
|
19
|
+
};
|
|
20
|
+
this.onError = (error) => {
|
|
21
|
+
if (this.timeoutId) {
|
|
22
|
+
clearTimeout(this.timeoutId);
|
|
23
|
+
this.timeoutId = void 0;
|
|
24
|
+
}
|
|
25
|
+
reject(error);
|
|
26
|
+
};
|
|
27
|
+
this.timeoutId = setTimeout(() => {
|
|
28
|
+
this.timeoutId = void 0;
|
|
29
|
+
reject(/* @__PURE__ */ new Error(`MP4File ready timeout ${this.readyTimeoutMs}ms - file may be invalid or incomplete`));
|
|
30
|
+
}, this.readyTimeoutMs);
|
|
31
|
+
});
|
|
32
|
+
this.readyPromise.catch(() => {});
|
|
33
|
+
}
|
|
34
|
+
setSegmentOptions(id, user, options) {
|
|
35
|
+
const trak = this.getTrackById(id);
|
|
36
|
+
if (trak) {
|
|
37
|
+
trak.nextSample = 0;
|
|
38
|
+
this.fragmentedTracks.push({
|
|
39
|
+
id,
|
|
40
|
+
user,
|
|
41
|
+
trak,
|
|
42
|
+
segmentStream: null,
|
|
43
|
+
nb_samples: "nbSamples" in options && options.nbSamples || 1e3,
|
|
44
|
+
rapAlignement: ("rapAlignement" in options && options.rapAlignement) ?? true
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fragments all tracks in a file into separate array buffers.
|
|
50
|
+
*/
|
|
51
|
+
async fragmentAllTracks() {
|
|
52
|
+
const trackBuffers = {};
|
|
53
|
+
try {
|
|
54
|
+
for await (const segment of this.fragmentIterator()) (trackBuffers[segment.track] ??= []).push(segment.data);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn("fragmentAllTracks failed:", error);
|
|
57
|
+
}
|
|
58
|
+
return trackBuffers;
|
|
59
|
+
}
|
|
60
|
+
async *fragmentIterator() {
|
|
61
|
+
try {
|
|
62
|
+
await this.readyPromise;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.warn("MP4File not ready:", error);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const trackInfo = {};
|
|
68
|
+
const videoTracks = this.getInfo().videoTracks;
|
|
69
|
+
const audioTracks = this.getInfo().audioTracks;
|
|
70
|
+
if (videoTracks.length === 0 && audioTracks.length === 0) {
|
|
71
|
+
console.warn("No video or audio tracks found in MP4 file");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
for (const videoTrack of videoTracks) {
|
|
75
|
+
trackInfo[videoTrack.id] = { index: 0 };
|
|
76
|
+
this.setSegmentOptions(videoTrack.id, null, { rapAlignement: true });
|
|
77
|
+
}
|
|
78
|
+
for (const audioTrack of audioTracks) {
|
|
79
|
+
trackInfo[audioTrack.id] = { index: 0 };
|
|
80
|
+
const sampleRate = audioTrack.audio.sample_rate;
|
|
81
|
+
const probablePacketSize = 1024;
|
|
82
|
+
const probableFourSecondsOfSamples = Math.ceil(sampleRate / probablePacketSize * 4);
|
|
83
|
+
this.setSegmentOptions(audioTrack.id, null, { nbSamples: probableFourSecondsOfSamples });
|
|
84
|
+
}
|
|
85
|
+
if (this.fragmentedTracks.length === 0) {
|
|
86
|
+
console.warn("No fragmented tracks set up");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const initSegments = this.initializeSegmentation();
|
|
90
|
+
for (const initSegment of initSegments) yield {
|
|
91
|
+
track: initSegment.id,
|
|
92
|
+
segment: "init",
|
|
93
|
+
data: initSegment.buffer
|
|
94
|
+
};
|
|
95
|
+
const fragmentStartSamples = {};
|
|
96
|
+
let finishedReading = false;
|
|
97
|
+
do {
|
|
98
|
+
/**
|
|
99
|
+
* For each track marked for fragmentation, check if the next sample is
|
|
100
|
+
* there (i.e. if the sample information is known (i.e. moof has arrived)
|
|
101
|
+
* and if it has been downloaded) and create a fragment with it
|
|
102
|
+
*/
|
|
103
|
+
for (const fragTrak of this.fragmentedTracks) {
|
|
104
|
+
const trak = fragTrak.trak;
|
|
105
|
+
if (trak.nextSample === void 0) throw new Error("trak.nextSample is undefined");
|
|
106
|
+
if (trak.samples === void 0) throw new Error("trak.samples is undefined");
|
|
107
|
+
log("trak.nextSample", fragTrak.id, trak.nextSample);
|
|
108
|
+
log("trak.samples.length", fragTrak.id, trak.samples.length);
|
|
109
|
+
while (trak.nextSample < trak.samples.length) {
|
|
110
|
+
let result = void 0;
|
|
111
|
+
const fragTrakNextSample = trak.samples[trak.nextSample];
|
|
112
|
+
if (fragTrakNextSample) fragmentStartSamples[fragTrak.id] ||= fragTrakNextSample;
|
|
113
|
+
try {
|
|
114
|
+
result = this.createFragment(fragTrak.id, trak.nextSample, fragTrak.segmentStream);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error("Failed to createFragment", error);
|
|
117
|
+
}
|
|
118
|
+
if (result) {
|
|
119
|
+
fragTrak.segmentStream = result;
|
|
120
|
+
trak.nextSample++;
|
|
121
|
+
} else {
|
|
122
|
+
finishedReading = await this.waitForMoreSamples();
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
const nextSample = trak.samples[trak.nextSample];
|
|
126
|
+
const emitSegment = fragTrak.rapAlignement === true && nextSample?.is_sync || !fragTrak.rapAlignement && trak.nextSample % fragTrak.nb_samples === 0 || trak.nextSample >= trak.samples.length;
|
|
127
|
+
if (emitSegment) {
|
|
128
|
+
const trackInfoForFrag = trackInfo[fragTrak.id];
|
|
129
|
+
if (!trackInfoForFrag) throw new Error("trackInfoForFrag is undefined");
|
|
130
|
+
const startSample = fragmentStartSamples[fragTrak.id];
|
|
131
|
+
const endSample = trak.samples[trak.nextSample - 1];
|
|
132
|
+
if (!startSample || !endSample) throw new Error("startSample or endSample is undefined");
|
|
133
|
+
log(`Yielding fragment #${trackInfoForFrag.index} for track=${fragTrak.id}`, `startTime=${startSample.cts}`, `endTime=${endSample.cts + endSample.duration}`);
|
|
134
|
+
yield {
|
|
135
|
+
track: fragTrak.id,
|
|
136
|
+
segment: trackInfoForFrag.index,
|
|
137
|
+
data: fragTrak.segmentStream.buffer,
|
|
138
|
+
cts: startSample.cts,
|
|
139
|
+
dts: startSample.dts,
|
|
140
|
+
duration: endSample.cts - startSample.cts + endSample.duration
|
|
141
|
+
};
|
|
142
|
+
trackInfoForFrag.index += 1;
|
|
143
|
+
fragTrak.segmentStream = null;
|
|
144
|
+
delete fragmentStartSamples[fragTrak.id];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
finishedReading = await this.waitForMoreSamples();
|
|
149
|
+
} while (!finishedReading);
|
|
150
|
+
for (const fragTrak of this.fragmentedTracks) {
|
|
151
|
+
const trak = fragTrak.trak;
|
|
152
|
+
if (trak.nextSample === void 0) throw new Error("trak.nextSample is undefined");
|
|
153
|
+
if (trak.samples === void 0) throw new Error("trak.samples is undefined");
|
|
154
|
+
while (trak.nextSample < trak.samples.length) {
|
|
155
|
+
let result = void 0;
|
|
156
|
+
try {
|
|
157
|
+
result = this.createFragment(fragTrak.id, trak.nextSample, fragTrak.segmentStream);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error("Failed to createFragment", error);
|
|
160
|
+
}
|
|
161
|
+
if (result) {
|
|
162
|
+
fragTrak.segmentStream = result;
|
|
163
|
+
trak.nextSample++;
|
|
164
|
+
} else {
|
|
165
|
+
finishedReading = await this.waitForMoreSamples();
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
const nextSample = trak.samples[trak.nextSample];
|
|
169
|
+
const emitSegment = fragTrak.rapAlignement === true && nextSample?.is_sync || !fragTrak.rapAlignement && trak.nextSample % fragTrak.nb_samples === 0 || trak.nextSample >= trak.samples.length;
|
|
170
|
+
if (emitSegment) {
|
|
171
|
+
const trackInfoForFrag = trackInfo[fragTrak.id];
|
|
172
|
+
if (!trackInfoForFrag) throw new Error("trackInfoForFrag is undefined");
|
|
173
|
+
const startSample = fragmentStartSamples[fragTrak.id];
|
|
174
|
+
const endSample = trak.samples[trak.nextSample - 1];
|
|
175
|
+
if (!startSample || !endSample) throw new Error("startSample or endSample is undefined");
|
|
176
|
+
log(`Yielding fragment #${trackInfoForFrag.index} for track=${fragTrak.id}`, `startTime=${startSample.cts}`, `endTime=${endSample.cts + endSample.duration}`);
|
|
177
|
+
yield {
|
|
178
|
+
track: fragTrak.id,
|
|
179
|
+
segment: trackInfoForFrag.index,
|
|
180
|
+
data: fragTrak.segmentStream.buffer,
|
|
181
|
+
cts: startSample.cts,
|
|
182
|
+
dts: startSample.dts,
|
|
183
|
+
duration: endSample.cts - startSample.cts + endSample.duration
|
|
184
|
+
};
|
|
185
|
+
trackInfoForFrag.index += 1;
|
|
186
|
+
fragTrak.segmentStream = null;
|
|
187
|
+
delete fragmentStartSamples[fragTrak.id];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
waitForMoreSamples() {
|
|
193
|
+
if (this._hasSeenLastSamples) return Promise.resolve(true);
|
|
194
|
+
return new Promise((resolve) => {
|
|
195
|
+
this.waitingForSamples.push(resolve);
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
const index = this.waitingForSamples.indexOf(resolve);
|
|
198
|
+
if (index !== -1) this.waitingForSamples.splice(index, 1);
|
|
199
|
+
resolve(true);
|
|
200
|
+
}, this.sampleWaitTimeoutMs);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
processSamples(last) {
|
|
204
|
+
this._hasSeenLastSamples = last;
|
|
205
|
+
for (const observer of this.waitingForSamples) observer(last);
|
|
206
|
+
this.waitingForSamples = [];
|
|
207
|
+
}
|
|
223
208
|
};
|
|
209
|
+
export { MP4File };
|