@editframe/assets 0.18.3-beta.0 → 0.18.8-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.
@@ -1,9 +1,9 @@
1
1
  import { idempotentTask } from "../idempotentTask.js";
2
- import { MP4File } from "../MP4File.js";
3
2
  import debug from "debug";
4
- import { mp4FileWritable } from "../mp4FileWritable.js";
5
3
  import { basename } from "node:path";
6
- import { Probe, type TrackFragmentIndex } from "../Probe.js";
4
+ import { Probe } from "../Probe.js";
5
+ import { generateFragmentIndex } from "../generateFragmentIndex.js";
6
+ import type { TrackFragmentIndex } from "../Probe.js";
7
7
 
8
8
  export const generateTrackFragmentIndexFromPath = async (
9
9
  absolutePath: string,
@@ -29,109 +29,38 @@ export const generateTrackFragmentIndexFromPath = async (
29
29
  }
30
30
  }
31
31
 
32
- const readStream = probe.createConformingReadstream();
33
-
34
- const mp4File = new MP4File();
35
-
36
- log(`Generating track fragment index for ${absolutePath}`);
37
- readStream.pipe(mp4FileWritable(mp4File));
38
- await new Promise((resolve, reject) => {
39
- readStream.on("end", resolve);
40
- readStream.on("error", reject);
41
- });
32
+ log(`Generating track fragment index for ${absolutePath} using single-track approach`);
42
33
 
34
+ // FIXED: Generate fragment indexes from individual single-track files
35
+ // This ensures byte offsets match the actual single-track files that clients will request
43
36
  const trackFragmentIndexes: Record<number, TrackFragmentIndex> = {};
44
- const trackByteOffsets: Record<number, number> = {};
45
- for await (const fragment of mp4File.fragmentIterator()) {
46
- const track = mp4File
47
- .getInfo()
48
- .tracks.find((track) => track.id === fragment.track);
49
37
 
50
- if (!track) {
51
- throw new Error("Track not found");
38
+ // Process each audio/video stream as a separate track
39
+ for (let streamIndex = 0; streamIndex < probe.streams.length; streamIndex++) {
40
+ const stream = probe.streams[streamIndex]!;
41
+
42
+ // Only process audio and video streams
43
+ if (stream.codec_type !== 'audio' && stream.codec_type !== 'video') {
44
+ continue;
52
45
  }
53
46
 
54
- if (fragment.segment === "init") {
55
- trackByteOffsets[fragment.track] = fragment.data.byteLength;
56
- if (track?.type === "video") {
57
- const videoTrack = mp4File
58
- .getInfo()
59
- .videoTracks.find((track) => track.id === fragment.track);
60
- if (!videoTrack) {
61
- throw new Error("Video track not found");
62
- }
63
- trackFragmentIndexes[fragment.track] = {
64
- track: fragment.track,
65
- type: "video",
66
- width: videoTrack.video.width,
67
- height: videoTrack.video.height,
68
- timescale: track.timescale,
69
- sample_count: videoTrack.nb_samples,
70
- codec: videoTrack.codec,
71
- duration: videoTrack.duration,
72
- startTimeOffsetMs: startTimeOffsetMs, // Add FFmpeg start_time offset
73
- initSegment: {
74
- offset: 0,
75
- size: fragment.data.byteLength,
76
- },
77
- segments: [],
78
- };
79
- }
80
- if (track?.type === "audio") {
81
- const audioTrack = mp4File
82
- .getInfo()
83
- .audioTracks.find((track) => track.id === fragment.track);
84
- if (!audioTrack) {
85
- throw new Error("Audio track not found");
86
- }
87
- trackFragmentIndexes[fragment.track] = {
88
- track: fragment.track,
89
- type: "audio",
90
- channel_count: audioTrack.audio.channel_count,
91
- sample_rate: audioTrack.audio.sample_rate,
92
- sample_size: audioTrack.audio.sample_size,
93
- sample_count: audioTrack.nb_samples,
94
- timescale: track.timescale,
95
- codec: audioTrack.codec,
96
- duration: audioTrack.duration,
97
- initSegment: {
98
- offset: 0,
99
- size: fragment.data.byteLength,
100
- },
101
- segments: [],
102
- };
103
- }
104
- } else {
105
- const fragmentIndex = trackFragmentIndexes[fragment.track];
106
- if (trackByteOffsets[fragment.track] === undefined) {
107
- throw new Error("Fragment index not found");
108
- }
109
- if (!fragmentIndex) {
110
- throw new Error("Fragment index not found");
111
- }
47
+ const trackId = streamIndex + 1; // Convert to 1-based track ID
48
+ log(`Processing track ${trackId} (${stream.codec_type})`);
112
49
 
113
- // Detect composition time offset from first video segment if no timing offset was found from metadata
114
- if (fragmentIndex.type === "video" &&
115
- fragmentIndex.segments.length === 0 &&
116
- fragmentIndex.startTimeOffsetMs === undefined &&
117
- fragment.cts > fragment.dts) {
118
- // Calculate composition time offset in milliseconds
119
- const compositionOffsetMs = ((fragment.cts - fragment.dts) / fragmentIndex.timescale) * 1000;
120
- fragmentIndex.startTimeOffsetMs = compositionOffsetMs;
121
- log(`Detected composition time offset from first video segment: ${compositionOffsetMs}ms (CTS=${fragment.cts}, DTS=${fragment.dts}, timescale=${fragmentIndex.timescale})`);
122
- }
50
+ // Generate single-track file and its fragment index
51
+ const trackStream = probe.createTrackReadstream(streamIndex);
52
+ const trackIdMapping = { 1: trackId }; // Map single-track ID 1 to original track ID
123
53
 
124
- fragmentIndex.duration += fragment.duration;
125
- fragmentIndex.segments.push({
126
- cts: fragment.cts,
127
- dts: fragment.dts,
128
- duration: fragment.duration,
129
- offset: trackByteOffsets[fragment.track]!,
130
- size: fragment.data.byteLength,
131
- });
132
- trackByteOffsets[fragment.track]! += fragment.data.byteLength;
133
- }
54
+ const singleTrackIndexes = await generateFragmentIndex(
55
+ trackStream,
56
+ startTimeOffsetMs,
57
+ trackIdMapping
58
+ );
59
+
60
+ // Merge the single-track index into the combined result
61
+ Object.assign(trackFragmentIndexes, singleTrackIndexes);
134
62
  }
63
+
135
64
  return trackFragmentIndexes;
136
65
  };
137
66