@amazon/vinyl-transmux 1.0.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/index.cjs +1150 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +1130 -0
- package/dist/index.js.map +7 -0
- package/dist/types/current/aac/adts.d.ts +38 -0
- package/dist/types/current/aac/adts.d.ts.map +1 -0
- package/dist/types/current/aac/adts.js +63 -0
- package/dist/types/current/index.d.ts +7 -0
- package/dist/types/current/index.d.ts.map +1 -0
- package/dist/types/current/index.js +6 -0
- package/dist/types/current/mp4/mp4.d.ts +70 -0
- package/dist/types/current/mp4/mp4.d.ts.map +1 -0
- package/dist/types/current/mp4/mp4.js +338 -0
- package/dist/types/current/mpegts/mpegts.d.ts +68 -0
- package/dist/types/current/mpegts/mpegts.d.ts.map +1 -0
- package/dist/types/current/mpegts/mpegts.js +202 -0
- package/dist/types/current/transmux/sampleEntry.d.ts +25 -0
- package/dist/types/current/transmux/sampleEntry.d.ts.map +1 -0
- package/dist/types/current/transmux/sampleEntry.js +114 -0
- package/dist/types/current/transmux/transmux.d.ts +17 -0
- package/dist/types/current/transmux/transmux.d.ts.map +1 -0
- package/dist/types/current/transmux/transmux.js +568 -0
- package/dist/types/current/tsconfig.tsbuildinfo +1 -0
- package/dist/types/ts3.7/aac/adts.d.ts +38 -0
- package/dist/types/ts3.7/index.d.ts +7 -0
- package/dist/types/ts3.7/mp4/mp4.d.ts +70 -0
- package/dist/types/ts3.7/mpegts/mpegts.d.ts +68 -0
- package/dist/types/ts3.7/transmux/sampleEntry.d.ts +25 -0
- package/dist/types/ts3.7/transmux/transmux.d.ts +17 -0
- package/dist/types/ts4.1/aac/adts.d.ts +38 -0
- package/dist/types/ts4.1/index.d.ts +7 -0
- package/dist/types/ts4.1/mp4/mp4.d.ts +70 -0
- package/dist/types/ts4.1/mpegts/mpegts.d.ts +68 -0
- package/dist/types/ts4.1/transmux/sampleEntry.d.ts +25 -0
- package/dist/types/ts4.1/transmux/transmux.d.ts +17 -0
- package/dist/types/ts4.7/aac/adts.d.ts +38 -0
- package/dist/types/ts4.7/index.d.ts +7 -0
- package/dist/types/ts4.7/mp4/mp4.d.ts +70 -0
- package/dist/types/ts4.7/mpegts/mpegts.d.ts +68 -0
- package/dist/types/ts4.7/transmux/sampleEntry.d.ts +25 -0
- package/dist/types/ts4.7/transmux/transmux.d.ts +17 -0
- package/docs/TRANSMUX.md +131 -0
- package/package.json +82 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1130 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/aac/adts.ts
|
|
19
|
+
var ADTS_SAMPLING_RATES = [
|
|
20
|
+
96e3,
|
|
21
|
+
88200,
|
|
22
|
+
64e3,
|
|
23
|
+
48e3,
|
|
24
|
+
44100,
|
|
25
|
+
32e3,
|
|
26
|
+
24e3,
|
|
27
|
+
22050,
|
|
28
|
+
16e3,
|
|
29
|
+
12e3,
|
|
30
|
+
11025,
|
|
31
|
+
8e3,
|
|
32
|
+
7350
|
|
33
|
+
];
|
|
34
|
+
function adtsSampleRate(index) {
|
|
35
|
+
var _a;
|
|
36
|
+
return (_a = ADTS_SAMPLING_RATES[index]) != null ? _a : 0;
|
|
37
|
+
}
|
|
38
|
+
function parseAdtsFrames(data) {
|
|
39
|
+
const frames = [];
|
|
40
|
+
let offset = 0;
|
|
41
|
+
while (offset + 7 <= data.length) {
|
|
42
|
+
if (data[offset] !== 255 || (data[offset + 1] & 240) !== 240) {
|
|
43
|
+
offset++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const hasCrc = (data[offset + 1] & 1) === 0;
|
|
47
|
+
const headerSize = hasCrc ? 9 : 7;
|
|
48
|
+
const audioObjectType = (data[offset + 2] >> 6 & 3) + 1;
|
|
49
|
+
const samplingFrequencyIndex = data[offset + 2] >> 2 & 15;
|
|
50
|
+
const channelConfiguration = (data[offset + 2] & 1) << 2 | data[offset + 3] >> 6 & 3;
|
|
51
|
+
const frameLength = (data[offset + 3] & 3) << 11 | data[offset + 4] << 3 | data[offset + 5] >> 5 & 7;
|
|
52
|
+
if (frameLength < headerSize || offset + frameLength > data.length) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
frames.push({
|
|
56
|
+
offset,
|
|
57
|
+
frameLength,
|
|
58
|
+
headerSize,
|
|
59
|
+
audioObjectType,
|
|
60
|
+
samplingFrequencyIndex,
|
|
61
|
+
channelConfiguration
|
|
62
|
+
});
|
|
63
|
+
offset += frameLength;
|
|
64
|
+
}
|
|
65
|
+
return frames;
|
|
66
|
+
}
|
|
67
|
+
function buildAudioSpecificConfig(frame) {
|
|
68
|
+
const config = new Uint8Array(2);
|
|
69
|
+
config[0] = frame.audioObjectType << 3 | (frame.samplingFrequencyIndex & 14) >> 1;
|
|
70
|
+
config[1] = (frame.samplingFrequencyIndex & 1) << 7 | frame.channelConfiguration << 3;
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/transmux/sampleEntry.ts
|
|
75
|
+
import { BufferWriter as BufferWriter2 } from "@amazon/vinyl-util";
|
|
76
|
+
|
|
77
|
+
// src/mp4/mp4.ts
|
|
78
|
+
import { BufferWriter } from "@amazon/vinyl-util";
|
|
79
|
+
var UNITY_MATRIX = [
|
|
80
|
+
65536,
|
|
81
|
+
0,
|
|
82
|
+
0,
|
|
83
|
+
0,
|
|
84
|
+
65536,
|
|
85
|
+
0,
|
|
86
|
+
0,
|
|
87
|
+
0,
|
|
88
|
+
1073741824
|
|
89
|
+
];
|
|
90
|
+
var MPEG_TIMESCALE = 9e4;
|
|
91
|
+
var LANGUAGE_UNDETERMINED = 21956;
|
|
92
|
+
function box(type, ...payloads) {
|
|
93
|
+
let size = 8;
|
|
94
|
+
for (const p of payloads) size += p.byteLength;
|
|
95
|
+
const writer = new BufferWriter(size);
|
|
96
|
+
writer.writeUint32(size);
|
|
97
|
+
writer.writeString(type);
|
|
98
|
+
for (const p of payloads) writer.writeBytes(p);
|
|
99
|
+
return writer.toUint8Array();
|
|
100
|
+
}
|
|
101
|
+
function fullBox(type, version, flags, payload) {
|
|
102
|
+
const size = 12 + payload.byteLength;
|
|
103
|
+
const writer = new BufferWriter(size);
|
|
104
|
+
writer.writeUint32(size);
|
|
105
|
+
writer.writeString(type);
|
|
106
|
+
writer.writeUint8(version);
|
|
107
|
+
writer.writeUint8(flags >> 16 & 255);
|
|
108
|
+
writer.writeUint8(flags >> 8 & 255);
|
|
109
|
+
writer.writeUint8(flags & 255);
|
|
110
|
+
writer.writeBytes(payload);
|
|
111
|
+
return writer.toUint8Array();
|
|
112
|
+
}
|
|
113
|
+
var DEFAULT_FTYP = {
|
|
114
|
+
majorBrand: "isom",
|
|
115
|
+
minorVersion: 512,
|
|
116
|
+
compatibleBrands: ["isom", "iso6", "mp41"]
|
|
117
|
+
};
|
|
118
|
+
function ftyp(options) {
|
|
119
|
+
const { majorBrand, minorVersion, compatibleBrands } = __spreadValues(__spreadValues({}, DEFAULT_FTYP), options);
|
|
120
|
+
const writer = new BufferWriter(8 + compatibleBrands.length * 4);
|
|
121
|
+
writer.writeString(majorBrand);
|
|
122
|
+
writer.writeUint32(minorVersion);
|
|
123
|
+
for (const brand of compatibleBrands) writer.writeString(brand);
|
|
124
|
+
return box("ftyp", writer.toUint8Array());
|
|
125
|
+
}
|
|
126
|
+
function moov(...traks) {
|
|
127
|
+
const trakAndMvex = [];
|
|
128
|
+
for (let i = 0; i < traks.length; i++) {
|
|
129
|
+
trakAndMvex.push(traks[i], mvex(i + 1));
|
|
130
|
+
}
|
|
131
|
+
return box("moov", mvhd(), ...trakAndMvex);
|
|
132
|
+
}
|
|
133
|
+
var DEFAULT_MVHD = {
|
|
134
|
+
timescale: MPEG_TIMESCALE,
|
|
135
|
+
duration: 0,
|
|
136
|
+
rate: 1,
|
|
137
|
+
volume: 1
|
|
138
|
+
};
|
|
139
|
+
function mvhd(options) {
|
|
140
|
+
const { timescale, duration, rate, volume } = __spreadValues(__spreadValues({}, DEFAULT_MVHD), options);
|
|
141
|
+
const writer = new BufferWriter(96);
|
|
142
|
+
writer.writeUint32(0);
|
|
143
|
+
writer.writeUint32(0);
|
|
144
|
+
writer.writeUint32(timescale);
|
|
145
|
+
writer.writeUint32(duration);
|
|
146
|
+
writer.writeUint32(rate * 65536);
|
|
147
|
+
writer.writeUint16(volume * 256);
|
|
148
|
+
for (let i = 0; i < 10; i++) writer.writeUint8(0);
|
|
149
|
+
for (const v of UNITY_MATRIX) writer.writeUint32(v);
|
|
150
|
+
for (let i = 0; i < 6; i++) writer.writeUint32(0);
|
|
151
|
+
writer.writeUint32(4294967295);
|
|
152
|
+
return fullBox("mvhd", 0, 0, writer.toUint8Array());
|
|
153
|
+
}
|
|
154
|
+
function audioTrak(options) {
|
|
155
|
+
return box(
|
|
156
|
+
"trak",
|
|
157
|
+
tkhd(options.trackId, 0, 0, options.channelCount > 0 ? 1 : 0),
|
|
158
|
+
box(
|
|
159
|
+
"mdia",
|
|
160
|
+
mdhd(options.sampleRate),
|
|
161
|
+
hdlr("soun", "SoundHandler"),
|
|
162
|
+
box("minf", smhd(), dinf(), audioStbl(options.sampleEntry))
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
function videoTrak(options) {
|
|
167
|
+
var _a;
|
|
168
|
+
return box(
|
|
169
|
+
"trak",
|
|
170
|
+
tkhd(options.trackId, options.width, options.height, 0),
|
|
171
|
+
box(
|
|
172
|
+
"mdia",
|
|
173
|
+
mdhd((_a = options.timescale) != null ? _a : MPEG_TIMESCALE),
|
|
174
|
+
hdlr("vide", "VideoHandler"),
|
|
175
|
+
box("minf", vmhd(), dinf(), videoStbl(options.sampleEntry))
|
|
176
|
+
)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
function tkhd(trackId, width, height, volume) {
|
|
180
|
+
const writer = new BufferWriter(80);
|
|
181
|
+
writer.writeUint32(0);
|
|
182
|
+
writer.writeUint32(0);
|
|
183
|
+
writer.writeUint32(trackId);
|
|
184
|
+
writer.writeUint32(0);
|
|
185
|
+
writer.writeUint32(0);
|
|
186
|
+
writer.writeUint32(0);
|
|
187
|
+
writer.writeUint32(0);
|
|
188
|
+
writer.writeUint16(0);
|
|
189
|
+
writer.writeUint16(0);
|
|
190
|
+
writer.writeUint16(volume * 256);
|
|
191
|
+
writer.writeUint16(0);
|
|
192
|
+
for (const v of UNITY_MATRIX) writer.writeUint32(v);
|
|
193
|
+
writer.writeUint32(width * 65536);
|
|
194
|
+
writer.writeUint32(height * 65536);
|
|
195
|
+
return fullBox("tkhd", 0, 3, writer.toUint8Array());
|
|
196
|
+
}
|
|
197
|
+
function mdhd(timescale) {
|
|
198
|
+
const writer = new BufferWriter(20);
|
|
199
|
+
writer.writeUint32(0);
|
|
200
|
+
writer.writeUint32(0);
|
|
201
|
+
writer.writeUint32(timescale);
|
|
202
|
+
writer.writeUint32(0);
|
|
203
|
+
writer.writeUint16(LANGUAGE_UNDETERMINED);
|
|
204
|
+
writer.writeUint16(0);
|
|
205
|
+
return fullBox("mdhd", 0, 0, writer.toUint8Array());
|
|
206
|
+
}
|
|
207
|
+
function hdlr(handlerType, name) {
|
|
208
|
+
const writer = new BufferWriter(21 + name.length);
|
|
209
|
+
writer.writeUint32(0);
|
|
210
|
+
writer.writeString(handlerType);
|
|
211
|
+
writer.writeUint32(0);
|
|
212
|
+
writer.writeUint32(0);
|
|
213
|
+
writer.writeUint32(0);
|
|
214
|
+
writer.writeString(name);
|
|
215
|
+
writer.writeUint8(0);
|
|
216
|
+
return fullBox("hdlr", 0, 0, writer.toUint8Array());
|
|
217
|
+
}
|
|
218
|
+
function smhd() {
|
|
219
|
+
const writer = new BufferWriter(4);
|
|
220
|
+
writer.writeUint16(0);
|
|
221
|
+
writer.writeUint16(0);
|
|
222
|
+
return fullBox("smhd", 0, 0, writer.toUint8Array());
|
|
223
|
+
}
|
|
224
|
+
function vmhd() {
|
|
225
|
+
const writer = new BufferWriter(8);
|
|
226
|
+
writer.writeUint16(0);
|
|
227
|
+
writer.writeUint16(0);
|
|
228
|
+
writer.writeUint16(0);
|
|
229
|
+
writer.writeUint16(0);
|
|
230
|
+
return fullBox("vmhd", 0, 1, writer.toUint8Array());
|
|
231
|
+
}
|
|
232
|
+
function dinf() {
|
|
233
|
+
const writer = new BufferWriter(4);
|
|
234
|
+
writer.writeUint32(1);
|
|
235
|
+
const urlBox = fullBox("url ", 0, 1, new Uint8Array(0));
|
|
236
|
+
return box(
|
|
237
|
+
"dinf",
|
|
238
|
+
fullBox("dref", 0, 0, concatUint8(writer.toUint8Array(), urlBox))
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
function audioStbl(sampleEntry) {
|
|
242
|
+
return box("stbl", stsd(sampleEntry), stts(), stsc(), stsz(), stco());
|
|
243
|
+
}
|
|
244
|
+
function videoStbl(sampleEntry) {
|
|
245
|
+
return box("stbl", stsd(sampleEntry), stts(), stsc(), stsz(), stco());
|
|
246
|
+
}
|
|
247
|
+
function stsd(sampleEntry) {
|
|
248
|
+
const writer = new BufferWriter(4);
|
|
249
|
+
writer.writeUint32(1);
|
|
250
|
+
return fullBox(
|
|
251
|
+
"stsd",
|
|
252
|
+
0,
|
|
253
|
+
0,
|
|
254
|
+
concatUint8(writer.toUint8Array(), sampleEntry)
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
function stts() {
|
|
258
|
+
const writer = new BufferWriter(4);
|
|
259
|
+
writer.writeUint32(0);
|
|
260
|
+
return fullBox("stts", 0, 0, writer.toUint8Array());
|
|
261
|
+
}
|
|
262
|
+
function stsc() {
|
|
263
|
+
const writer = new BufferWriter(4);
|
|
264
|
+
writer.writeUint32(0);
|
|
265
|
+
return fullBox("stsc", 0, 0, writer.toUint8Array());
|
|
266
|
+
}
|
|
267
|
+
function stsz() {
|
|
268
|
+
const writer = new BufferWriter(8);
|
|
269
|
+
writer.writeUint32(0);
|
|
270
|
+
writer.writeUint32(0);
|
|
271
|
+
return fullBox("stsz", 0, 0, writer.toUint8Array());
|
|
272
|
+
}
|
|
273
|
+
function stco() {
|
|
274
|
+
const writer = new BufferWriter(4);
|
|
275
|
+
writer.writeUint32(0);
|
|
276
|
+
return fullBox("stco", 0, 0, writer.toUint8Array());
|
|
277
|
+
}
|
|
278
|
+
function mvex(trackId) {
|
|
279
|
+
return box("mvex", trex(trackId));
|
|
280
|
+
}
|
|
281
|
+
function trex(trackId) {
|
|
282
|
+
const writer = new BufferWriter(20);
|
|
283
|
+
writer.writeUint32(trackId);
|
|
284
|
+
writer.writeUint32(1);
|
|
285
|
+
writer.writeUint32(0);
|
|
286
|
+
writer.writeUint32(0);
|
|
287
|
+
writer.writeUint32(0);
|
|
288
|
+
return fullBox("trex", 0, 0, writer.toUint8Array());
|
|
289
|
+
}
|
|
290
|
+
function moof(options) {
|
|
291
|
+
return box("moof", mfhd(options.sequenceNumber), traf(options));
|
|
292
|
+
}
|
|
293
|
+
function mfhd(sequenceNumber) {
|
|
294
|
+
const writer = new BufferWriter(4);
|
|
295
|
+
writer.writeUint32(sequenceNumber);
|
|
296
|
+
return fullBox("mfhd", 0, 0, writer.toUint8Array());
|
|
297
|
+
}
|
|
298
|
+
function traf(options) {
|
|
299
|
+
return box(
|
|
300
|
+
"traf",
|
|
301
|
+
tfhd(options.trackId),
|
|
302
|
+
tfdt(options.baseDecodeTime),
|
|
303
|
+
trun(options.samples)
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
function tfhd(trackId) {
|
|
307
|
+
const writer = new BufferWriter(4);
|
|
308
|
+
writer.writeUint32(trackId);
|
|
309
|
+
return fullBox("tfhd", 0, 131072, writer.toUint8Array());
|
|
310
|
+
}
|
|
311
|
+
function tfdt(baseDecodeTime) {
|
|
312
|
+
const writer = new BufferWriter(4);
|
|
313
|
+
writer.writeUint32(baseDecodeTime);
|
|
314
|
+
return fullBox("tfdt", 0, 0, writer.toUint8Array());
|
|
315
|
+
}
|
|
316
|
+
function trun(samples) {
|
|
317
|
+
var _a, _b;
|
|
318
|
+
const hasFlags = samples.some((s) => s.flags != null);
|
|
319
|
+
const hasCto = samples.some((s) => s.compositionTimeOffset != null);
|
|
320
|
+
let flags = 1 | 256 | 512;
|
|
321
|
+
if (hasFlags) flags |= 1024;
|
|
322
|
+
if (hasCto) flags |= 2048;
|
|
323
|
+
let entrySize = 8;
|
|
324
|
+
if (hasFlags) entrySize += 4;
|
|
325
|
+
if (hasCto) entrySize += 4;
|
|
326
|
+
const writer = new BufferWriter(8 + samples.length * entrySize);
|
|
327
|
+
writer.writeUint32(samples.length);
|
|
328
|
+
writer.writeUint32(0);
|
|
329
|
+
for (const sample of samples) {
|
|
330
|
+
writer.writeUint32(sample.duration);
|
|
331
|
+
writer.writeUint32(sample.size);
|
|
332
|
+
if (hasFlags) writer.writeUint32((_a = sample.flags) != null ? _a : 0);
|
|
333
|
+
if (hasCto) writer.writeInt32((_b = sample.compositionTimeOffset) != null ? _b : 0);
|
|
334
|
+
}
|
|
335
|
+
const result = fullBox("trun", hasCto ? 1 : 0, flags, writer.toUint8Array());
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
function mdat(data) {
|
|
339
|
+
return box("mdat", data);
|
|
340
|
+
}
|
|
341
|
+
function patchDataOffset(moofData, moofSize) {
|
|
342
|
+
const dataOffset = moofSize + 8;
|
|
343
|
+
let pos = 8;
|
|
344
|
+
while (pos < moofData.length) {
|
|
345
|
+
const boxSize = (moofData[pos] << 24 | moofData[pos + 1] << 16 | moofData[pos + 2] << 8 | moofData[pos + 3]) >>> 0;
|
|
346
|
+
const boxType = String.fromCharCode(
|
|
347
|
+
moofData[pos + 4],
|
|
348
|
+
moofData[pos + 5],
|
|
349
|
+
moofData[pos + 6],
|
|
350
|
+
moofData[pos + 7]
|
|
351
|
+
);
|
|
352
|
+
if (boxType === "traf") {
|
|
353
|
+
let trafPos = pos + 8;
|
|
354
|
+
while (trafPos < pos + boxSize) {
|
|
355
|
+
const innerSize = (moofData[trafPos] << 24 | moofData[trafPos + 1] << 16 | moofData[trafPos + 2] << 8 | moofData[trafPos + 3]) >>> 0;
|
|
356
|
+
const innerType = String.fromCharCode(
|
|
357
|
+
moofData[trafPos + 4],
|
|
358
|
+
moofData[trafPos + 5],
|
|
359
|
+
moofData[trafPos + 6],
|
|
360
|
+
moofData[trafPos + 7]
|
|
361
|
+
);
|
|
362
|
+
if (innerType === "trun") {
|
|
363
|
+
const offsetPos = trafPos + 16;
|
|
364
|
+
moofData[offsetPos] = dataOffset >> 24 & 255;
|
|
365
|
+
moofData[offsetPos + 1] = dataOffset >> 16 & 255;
|
|
366
|
+
moofData[offsetPos + 2] = dataOffset >> 8 & 255;
|
|
367
|
+
moofData[offsetPos + 3] = dataOffset & 255;
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
trafPos += innerSize;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
pos += boxSize;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function concatUint8(...arrays) {
|
|
377
|
+
let total = 0;
|
|
378
|
+
for (const a of arrays) total += a.byteLength;
|
|
379
|
+
const result = new Uint8Array(total);
|
|
380
|
+
let offset = 0;
|
|
381
|
+
for (const a of arrays) {
|
|
382
|
+
result.set(a, offset);
|
|
383
|
+
offset += a.byteLength;
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/transmux/sampleEntry.ts
|
|
389
|
+
var RESOLUTION_72_DPI = 4718592;
|
|
390
|
+
var OBJECT_TYPE_AAC = 64;
|
|
391
|
+
var STREAM_TYPE_AUDIO = 21;
|
|
392
|
+
function mp4aSampleEntry(config) {
|
|
393
|
+
const writer = new BufferWriter2(28);
|
|
394
|
+
for (let i = 0; i < 6; i++) writer.writeUint8(0);
|
|
395
|
+
writer.writeUint16(1);
|
|
396
|
+
for (let i = 0; i < 8; i++) writer.writeUint8(0);
|
|
397
|
+
writer.writeUint16(config.channelConfiguration);
|
|
398
|
+
writer.writeUint16(16);
|
|
399
|
+
writer.writeUint16(0);
|
|
400
|
+
writer.writeUint16(0);
|
|
401
|
+
writer.writeUint32(config.sampleRate << 16);
|
|
402
|
+
return box("mp4a", writer.toUint8Array(), esds(config));
|
|
403
|
+
}
|
|
404
|
+
function avc1SampleEntry(config) {
|
|
405
|
+
const writer = new BufferWriter2(78);
|
|
406
|
+
for (let i = 0; i < 6; i++) writer.writeUint8(0);
|
|
407
|
+
writer.writeUint16(1);
|
|
408
|
+
for (let i = 0; i < 16; i++) writer.writeUint8(0);
|
|
409
|
+
writer.writeUint16(config.width);
|
|
410
|
+
writer.writeUint16(config.height);
|
|
411
|
+
writer.writeUint32(RESOLUTION_72_DPI);
|
|
412
|
+
writer.writeUint32(RESOLUTION_72_DPI);
|
|
413
|
+
writer.writeUint32(0);
|
|
414
|
+
writer.writeUint16(1);
|
|
415
|
+
for (let i = 0; i < 32; i++) writer.writeUint8(0);
|
|
416
|
+
writer.writeUint16(24);
|
|
417
|
+
writer.writeInt16(-1);
|
|
418
|
+
return box("avc1", writer.toUint8Array(), avcC(config));
|
|
419
|
+
}
|
|
420
|
+
function esds(config) {
|
|
421
|
+
const asc = config.audioSpecificConfig;
|
|
422
|
+
const dsi = new BufferWriter2(2 + asc.byteLength);
|
|
423
|
+
dsi.writeUint8(5);
|
|
424
|
+
dsi.writeUint8(asc.byteLength);
|
|
425
|
+
dsi.writeBytes(asc);
|
|
426
|
+
const dcd = new BufferWriter2(2 + 13 + dsi.position);
|
|
427
|
+
dcd.writeUint8(4);
|
|
428
|
+
dcd.writeUint8(13 + dsi.position);
|
|
429
|
+
dcd.writeUint8(OBJECT_TYPE_AAC);
|
|
430
|
+
dcd.writeUint8(STREAM_TYPE_AUDIO);
|
|
431
|
+
dcd.writeUint8(0);
|
|
432
|
+
dcd.writeUint8(0);
|
|
433
|
+
dcd.writeUint8(0);
|
|
434
|
+
dcd.writeUint32(0);
|
|
435
|
+
dcd.writeUint32(0);
|
|
436
|
+
dcd.writeBytes(dsi.toUint8Array());
|
|
437
|
+
const slc = new BufferWriter2(3);
|
|
438
|
+
slc.writeUint8(6);
|
|
439
|
+
slc.writeUint8(1);
|
|
440
|
+
slc.writeUint8(2);
|
|
441
|
+
const esPayloadSize = 3 + dcd.position + slc.position;
|
|
442
|
+
const es = new BufferWriter2(2 + esPayloadSize);
|
|
443
|
+
es.writeUint8(3);
|
|
444
|
+
es.writeUint8(esPayloadSize);
|
|
445
|
+
es.writeUint16(1);
|
|
446
|
+
es.writeUint8(0);
|
|
447
|
+
es.writeBytes(dcd.toUint8Array());
|
|
448
|
+
es.writeBytes(slc.toUint8Array());
|
|
449
|
+
return fullBox("esds", 0, 0, es.toUint8Array());
|
|
450
|
+
}
|
|
451
|
+
function avcC(config) {
|
|
452
|
+
const writer = new BufferWriter2(
|
|
453
|
+
11 + config.sps.byteLength + config.pps.byteLength
|
|
454
|
+
);
|
|
455
|
+
writer.writeUint8(1);
|
|
456
|
+
writer.writeUint8(config.profileIdc);
|
|
457
|
+
writer.writeUint8(config.profileCompatibility);
|
|
458
|
+
writer.writeUint8(config.levelIdc);
|
|
459
|
+
writer.writeUint8(3);
|
|
460
|
+
writer.writeUint8(225);
|
|
461
|
+
writer.writeUint16(config.sps.byteLength);
|
|
462
|
+
writer.writeBytes(config.sps);
|
|
463
|
+
writer.writeUint8(1);
|
|
464
|
+
writer.writeUint16(config.pps.byteLength);
|
|
465
|
+
writer.writeBytes(config.pps);
|
|
466
|
+
return box("avcC", writer.toUint8Array());
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/mpegts/mpegts.ts
|
|
470
|
+
var TS_PACKET_SIZE = 188;
|
|
471
|
+
var TS_SYNC_BYTE = 71;
|
|
472
|
+
var STREAM_TYPE_AAC = 15;
|
|
473
|
+
var STREAM_TYPE_H264 = 27;
|
|
474
|
+
var STREAM_TYPE_H265 = 36;
|
|
475
|
+
var STREAM_TYPE_ID3 = 21;
|
|
476
|
+
function parseTsPacketHeader(data, offset) {
|
|
477
|
+
const byte1 = data[offset + 1];
|
|
478
|
+
const byte2 = data[offset + 2];
|
|
479
|
+
const byte3 = data[offset + 3];
|
|
480
|
+
return {
|
|
481
|
+
pid: (byte1 & 31) << 8 | byte2,
|
|
482
|
+
payloadUnitStartIndicator: (byte1 & 64) !== 0,
|
|
483
|
+
adaptationFieldControl: byte3 >> 4 & 3,
|
|
484
|
+
continuityCounter: byte3 & 15
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
function getPayloadOffset(data, offset, adaptationFieldControl) {
|
|
488
|
+
let payloadOffset = offset + 4;
|
|
489
|
+
if (adaptationFieldControl >= 2) {
|
|
490
|
+
payloadOffset += 1 + data[offset + 4];
|
|
491
|
+
}
|
|
492
|
+
return payloadOffset;
|
|
493
|
+
}
|
|
494
|
+
function parsePat(data, offset) {
|
|
495
|
+
const pointerField = data[offset];
|
|
496
|
+
let pos = offset + 1 + pointerField;
|
|
497
|
+
pos += 8;
|
|
498
|
+
pos += 2;
|
|
499
|
+
return (data[pos] & 31) << 8 | data[pos + 1];
|
|
500
|
+
}
|
|
501
|
+
function parsePmt(data, offset) {
|
|
502
|
+
const pointerField = data[offset];
|
|
503
|
+
let pos = offset + 1 + pointerField;
|
|
504
|
+
pos += 1;
|
|
505
|
+
const sectionLength = (data[pos] & 15) << 8 | data[pos + 1];
|
|
506
|
+
pos += 2;
|
|
507
|
+
const sectionEnd = pos + sectionLength;
|
|
508
|
+
pos += 5;
|
|
509
|
+
pos += 2;
|
|
510
|
+
const programInfoLength = (data[pos] & 15) << 8 | data[pos + 1];
|
|
511
|
+
pos += 2 + programInfoLength;
|
|
512
|
+
const entries = [];
|
|
513
|
+
while (pos < sectionEnd - 4) {
|
|
514
|
+
const streamType = data[pos];
|
|
515
|
+
pos += 1;
|
|
516
|
+
const pid = (data[pos] & 31) << 8 | data[pos + 1];
|
|
517
|
+
pos += 2;
|
|
518
|
+
const esInfoLength = (data[pos] & 15) << 8 | data[pos + 1];
|
|
519
|
+
pos += 2 + esInfoLength;
|
|
520
|
+
entries.push({ pid, streamType });
|
|
521
|
+
}
|
|
522
|
+
return entries;
|
|
523
|
+
}
|
|
524
|
+
function demuxTsSegment(segment) {
|
|
525
|
+
let pmtPid = -1;
|
|
526
|
+
const pmtEntries = [];
|
|
527
|
+
const pesBuffers = /* @__PURE__ */ new Map();
|
|
528
|
+
for (let i = 0; i + TS_PACKET_SIZE <= segment.length; i += TS_PACKET_SIZE) {
|
|
529
|
+
if (segment[i] !== TS_SYNC_BYTE) continue;
|
|
530
|
+
const header = parseTsPacketHeader(segment, i);
|
|
531
|
+
const payloadStart = getPayloadOffset(
|
|
532
|
+
segment,
|
|
533
|
+
i,
|
|
534
|
+
header.adaptationFieldControl
|
|
535
|
+
);
|
|
536
|
+
if (header.adaptationFieldControl === 0 || header.adaptationFieldControl === 2)
|
|
537
|
+
continue;
|
|
538
|
+
if (header.pid === 0) {
|
|
539
|
+
pmtPid = parsePat(segment, payloadStart);
|
|
540
|
+
} else if (header.pid === pmtPid && pmtEntries.length === 0) {
|
|
541
|
+
pmtEntries.push(...parsePmt(segment, payloadStart));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const pidToStreamType = /* @__PURE__ */ new Map();
|
|
545
|
+
for (const entry of pmtEntries) {
|
|
546
|
+
pidToStreamType.set(entry.pid, entry.streamType);
|
|
547
|
+
pesBuffers.set(entry.pid, []);
|
|
548
|
+
}
|
|
549
|
+
for (let i = 0; i + TS_PACKET_SIZE <= segment.length; i += TS_PACKET_SIZE) {
|
|
550
|
+
if (segment[i] !== TS_SYNC_BYTE) continue;
|
|
551
|
+
const header = parseTsPacketHeader(segment, i);
|
|
552
|
+
if (header.adaptationFieldControl === 0 || header.adaptationFieldControl === 2)
|
|
553
|
+
continue;
|
|
554
|
+
const buffers = pesBuffers.get(header.pid);
|
|
555
|
+
if (!buffers) continue;
|
|
556
|
+
const payloadStart = getPayloadOffset(
|
|
557
|
+
segment,
|
|
558
|
+
i,
|
|
559
|
+
header.adaptationFieldControl
|
|
560
|
+
);
|
|
561
|
+
const payloadEnd = i + TS_PACKET_SIZE;
|
|
562
|
+
if (payloadStart < payloadEnd) {
|
|
563
|
+
buffers.push(segment.slice(payloadStart, payloadEnd));
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const streams = [];
|
|
567
|
+
for (const [pid, buffers] of pesBuffers) {
|
|
568
|
+
if (buffers.length === 0) continue;
|
|
569
|
+
streams.push({
|
|
570
|
+
pid,
|
|
571
|
+
streamType: pidToStreamType.get(pid),
|
|
572
|
+
data: buffers
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
return streams;
|
|
576
|
+
}
|
|
577
|
+
function concatBuffers(buffers) {
|
|
578
|
+
let totalLength = 0;
|
|
579
|
+
for (const buf of buffers) totalLength += buf.byteLength;
|
|
580
|
+
const result = new Uint8Array(totalLength);
|
|
581
|
+
let offset = 0;
|
|
582
|
+
for (const buf of buffers) {
|
|
583
|
+
result.set(buf, offset);
|
|
584
|
+
offset += buf.byteLength;
|
|
585
|
+
}
|
|
586
|
+
return result;
|
|
587
|
+
}
|
|
588
|
+
function stripPesHeader(pesData) {
|
|
589
|
+
if (pesData[0] !== 0 || pesData[1] !== 0 || pesData[2] !== 1) {
|
|
590
|
+
return pesData;
|
|
591
|
+
}
|
|
592
|
+
const pesHeaderDataLength = pesData[8];
|
|
593
|
+
return pesData.subarray(9 + pesHeaderDataLength);
|
|
594
|
+
}
|
|
595
|
+
function parsePesPts(pesData) {
|
|
596
|
+
if (pesData[0] !== 0 || pesData[1] !== 0 || pesData[2] !== 1) return -1;
|
|
597
|
+
const flags = pesData[7];
|
|
598
|
+
if ((flags & 128) === 0) return -1;
|
|
599
|
+
const byte9 = pesData[9];
|
|
600
|
+
const byte10 = pesData[10];
|
|
601
|
+
const byte11 = pesData[11];
|
|
602
|
+
const byte12 = pesData[12];
|
|
603
|
+
const byte13 = pesData[13];
|
|
604
|
+
return (byte9 & 14) * 536870912 + // (byte9 & 0x0E) << 29
|
|
605
|
+
((byte10 & 255) << 22) + ((byte11 & 254) << 14) + ((byte12 & 255) << 7) + ((byte13 & 254) >>> 1);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/transmux/transmux.ts
|
|
609
|
+
function createTransmuxer() {
|
|
610
|
+
const state = {
|
|
611
|
+
initSegment: null,
|
|
612
|
+
sequenceNumber: 1,
|
|
613
|
+
audioConfig: null,
|
|
614
|
+
videoConfig: null,
|
|
615
|
+
audioDecodeTime: 0,
|
|
616
|
+
videoDecodeTime: 0
|
|
617
|
+
};
|
|
618
|
+
return { transmux: (data) => transmux(state, data) };
|
|
619
|
+
}
|
|
620
|
+
function buildInitSegment(state, moovBox) {
|
|
621
|
+
const ftypBox = ftyp();
|
|
622
|
+
const init = new Uint8Array(ftypBox.byteLength + moovBox.byteLength);
|
|
623
|
+
init.set(ftypBox, 0);
|
|
624
|
+
init.set(moovBox, ftypBox.byteLength);
|
|
625
|
+
state.initSegment = init;
|
|
626
|
+
}
|
|
627
|
+
function getInitSegment(state) {
|
|
628
|
+
const init = state.initSegment;
|
|
629
|
+
return init.buffer.slice(
|
|
630
|
+
init.byteOffset,
|
|
631
|
+
init.byteOffset + init.byteLength
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
function transmux(state, data) {
|
|
635
|
+
let segment = new Uint8Array(data);
|
|
636
|
+
segment = skipId3Tags(segment);
|
|
637
|
+
const isMpegTs = segment[0] === 71;
|
|
638
|
+
const isAdts = segment[0] === 255 && (segment[1] & 240) === 240;
|
|
639
|
+
if (isMpegTs) {
|
|
640
|
+
return transmuxMpegTs(state, segment);
|
|
641
|
+
} else if (isAdts) {
|
|
642
|
+
return transmuxAdts(state, segment);
|
|
643
|
+
}
|
|
644
|
+
throw new Error("Unsupported segment format: expected MPEG-TS or ADTS");
|
|
645
|
+
}
|
|
646
|
+
function skipId3Tags(data) {
|
|
647
|
+
let offset = 0;
|
|
648
|
+
while (offset + 10 <= data.length && data[offset] === 73 && // 'I'
|
|
649
|
+
data[offset + 1] === 68 && // 'D'
|
|
650
|
+
data[offset + 2] === 51) {
|
|
651
|
+
const size = (data[offset + 6] & 127) << 21 | (data[offset + 7] & 127) << 14 | (data[offset + 8] & 127) << 7 | data[offset + 9] & 127;
|
|
652
|
+
offset += 10 + size;
|
|
653
|
+
}
|
|
654
|
+
return offset > 0 ? data.subarray(offset) : data;
|
|
655
|
+
}
|
|
656
|
+
function transmuxAdts(state, data) {
|
|
657
|
+
const frames = parseAdtsFrames(data);
|
|
658
|
+
if (frames.length === 0) {
|
|
659
|
+
throw new Error("No ADTS frames found in segment");
|
|
660
|
+
}
|
|
661
|
+
const firstFrame = frames[0];
|
|
662
|
+
if (!state.audioConfig) {
|
|
663
|
+
const sampleRate2 = adtsSampleRate(firstFrame.samplingFrequencyIndex);
|
|
664
|
+
state.audioConfig = {
|
|
665
|
+
audioObjectType: firstFrame.audioObjectType,
|
|
666
|
+
samplingFrequencyIndex: firstFrame.samplingFrequencyIndex,
|
|
667
|
+
channelConfiguration: firstFrame.channelConfiguration,
|
|
668
|
+
sampleRate: sampleRate2,
|
|
669
|
+
audioSpecificConfig: buildAudioSpecificConfig(firstFrame)
|
|
670
|
+
};
|
|
671
|
+
buildInitSegment(
|
|
672
|
+
state,
|
|
673
|
+
moov(
|
|
674
|
+
audioTrak({
|
|
675
|
+
trackId: 1,
|
|
676
|
+
sampleRate: state.audioConfig.sampleRate,
|
|
677
|
+
channelCount: state.audioConfig.channelConfiguration,
|
|
678
|
+
sampleEntry: mp4aSampleEntry(state.audioConfig)
|
|
679
|
+
})
|
|
680
|
+
)
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
const sampleRate = state.audioConfig.sampleRate;
|
|
684
|
+
const samplesPerFrame = 1024;
|
|
685
|
+
const samples = [];
|
|
686
|
+
const frameDataParts = [];
|
|
687
|
+
for (const frame of frames) {
|
|
688
|
+
const rawData = data.subarray(
|
|
689
|
+
frame.offset + frame.headerSize,
|
|
690
|
+
frame.offset + frame.frameLength
|
|
691
|
+
);
|
|
692
|
+
samples.push({
|
|
693
|
+
duration: samplesPerFrame,
|
|
694
|
+
size: rawData.byteLength
|
|
695
|
+
});
|
|
696
|
+
frameDataParts.push(rawData);
|
|
697
|
+
}
|
|
698
|
+
const rawMediaData = concatBuffers(frameDataParts);
|
|
699
|
+
const duration = frames.length * samplesPerFrame / sampleRate;
|
|
700
|
+
const moofBox = moof({
|
|
701
|
+
sequenceNumber: state.sequenceNumber++,
|
|
702
|
+
trackId: 1,
|
|
703
|
+
baseDecodeTime: state.audioDecodeTime,
|
|
704
|
+
samples
|
|
705
|
+
});
|
|
706
|
+
state.audioDecodeTime += frames.length * samplesPerFrame;
|
|
707
|
+
patchDataOffset(moofBox, moofBox.byteLength);
|
|
708
|
+
const mdatBox = mdat(rawMediaData);
|
|
709
|
+
const mediaSegment = new Uint8Array(moofBox.byteLength + mdatBox.byteLength);
|
|
710
|
+
mediaSegment.set(moofBox, 0);
|
|
711
|
+
mediaSegment.set(mdatBox, moofBox.byteLength);
|
|
712
|
+
return {
|
|
713
|
+
initSegment: getInitSegment(state),
|
|
714
|
+
mediaSegment: mediaSegment.buffer.slice(
|
|
715
|
+
mediaSegment.byteOffset,
|
|
716
|
+
mediaSegment.byteOffset + mediaSegment.byteLength
|
|
717
|
+
),
|
|
718
|
+
duration
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function transmuxMpegTs(state, data) {
|
|
722
|
+
const streams = demuxTsSegment(data);
|
|
723
|
+
const audioStream = streams.find((s) => s.streamType === STREAM_TYPE_AAC);
|
|
724
|
+
const videoStream = streams.find((s) => s.streamType === STREAM_TYPE_H264);
|
|
725
|
+
let audioFrames = [];
|
|
726
|
+
let audioRawData = null;
|
|
727
|
+
let audioConfig = state.audioConfig;
|
|
728
|
+
if (audioStream) {
|
|
729
|
+
const pesData = concatBuffers(audioStream.data);
|
|
730
|
+
const esData = extractElementaryStream(pesData);
|
|
731
|
+
audioFrames = parseAdtsFrames(esData);
|
|
732
|
+
if (audioFrames.length > 0 && !audioConfig) {
|
|
733
|
+
const firstFrame = audioFrames[0];
|
|
734
|
+
const sampleRate = adtsSampleRate(firstFrame.samplingFrequencyIndex);
|
|
735
|
+
audioConfig = {
|
|
736
|
+
audioObjectType: firstFrame.audioObjectType,
|
|
737
|
+
samplingFrequencyIndex: firstFrame.samplingFrequencyIndex,
|
|
738
|
+
channelConfiguration: firstFrame.channelConfiguration,
|
|
739
|
+
sampleRate,
|
|
740
|
+
audioSpecificConfig: buildAudioSpecificConfig(firstFrame)
|
|
741
|
+
};
|
|
742
|
+
state.audioConfig = audioConfig;
|
|
743
|
+
}
|
|
744
|
+
const parts = [];
|
|
745
|
+
for (const frame of audioFrames) {
|
|
746
|
+
parts.push(
|
|
747
|
+
esData.subarray(
|
|
748
|
+
frame.offset + frame.headerSize,
|
|
749
|
+
frame.offset + frame.frameLength
|
|
750
|
+
)
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
audioRawData = concatBuffers(parts);
|
|
754
|
+
}
|
|
755
|
+
let videoNalus = [];
|
|
756
|
+
let videoConfig = state.videoConfig;
|
|
757
|
+
let videoFrameDuration = 3754;
|
|
758
|
+
const videoPtsList = [];
|
|
759
|
+
if (videoStream) {
|
|
760
|
+
const pesData = concatBuffers(videoStream.data);
|
|
761
|
+
let pesOff = 0;
|
|
762
|
+
while (pesOff + 9 < pesData.length) {
|
|
763
|
+
if (pesData[pesOff] === 0 && pesData[pesOff + 1] === 0 && pesData[pesOff + 2] === 1 && pesData[pesOff + 3] >= 188) {
|
|
764
|
+
const pts = parsePesPts(pesData.subarray(pesOff));
|
|
765
|
+
if (pts >= 0) videoPtsList.push(pts);
|
|
766
|
+
const pLen = pesData[pesOff + 4] << 8 | pesData[pesOff + 5];
|
|
767
|
+
if (pLen > 0) {
|
|
768
|
+
pesOff += 6 + pLen;
|
|
769
|
+
} else {
|
|
770
|
+
const hdrLen = pesData[pesOff + 8];
|
|
771
|
+
pesOff += 9 + hdrLen;
|
|
772
|
+
}
|
|
773
|
+
} else {
|
|
774
|
+
pesOff++;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (videoPtsList.length >= 2) {
|
|
778
|
+
const sorted = [...videoPtsList].sort((a, b) => a - b);
|
|
779
|
+
const deltas = [];
|
|
780
|
+
for (let i = 1; i < sorted.length; i++)
|
|
781
|
+
deltas.push(sorted[i] - sorted[i - 1]);
|
|
782
|
+
deltas.sort((a, b) => a - b);
|
|
783
|
+
videoFrameDuration = deltas[deltas.length >> 1] || 3754;
|
|
784
|
+
}
|
|
785
|
+
const esData = extractElementaryStream(pesData);
|
|
786
|
+
videoNalus = extractH264Nalus(esData);
|
|
787
|
+
if (!videoConfig) {
|
|
788
|
+
const sps = videoNalus.find((n) => (n[0] & 31) === 7);
|
|
789
|
+
const pps = videoNalus.find((n) => (n[0] & 31) === 8);
|
|
790
|
+
if (sps && pps) {
|
|
791
|
+
const { width, height } = parseSpsInfo(sps);
|
|
792
|
+
videoConfig = {
|
|
793
|
+
width,
|
|
794
|
+
height,
|
|
795
|
+
sps,
|
|
796
|
+
pps,
|
|
797
|
+
profileIdc: sps[1],
|
|
798
|
+
profileCompatibility: sps[2],
|
|
799
|
+
levelIdc: sps[3]
|
|
800
|
+
};
|
|
801
|
+
state.videoConfig = videoConfig;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
if (!state.initSegment && (audioConfig || videoConfig)) {
|
|
806
|
+
const traks = [];
|
|
807
|
+
if (videoConfig) {
|
|
808
|
+
traks.push(
|
|
809
|
+
videoTrak({
|
|
810
|
+
trackId: traks.length + 1,
|
|
811
|
+
width: videoConfig.width,
|
|
812
|
+
height: videoConfig.height,
|
|
813
|
+
sampleEntry: avc1SampleEntry(videoConfig)
|
|
814
|
+
})
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
if (audioConfig) {
|
|
818
|
+
traks.push(
|
|
819
|
+
audioTrak({
|
|
820
|
+
trackId: traks.length + 1,
|
|
821
|
+
sampleRate: audioConfig.sampleRate,
|
|
822
|
+
channelCount: audioConfig.channelConfiguration,
|
|
823
|
+
sampleEntry: mp4aSampleEntry(audioConfig)
|
|
824
|
+
})
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
buildInitSegment(state, moov(...traks));
|
|
828
|
+
}
|
|
829
|
+
const fragments = [];
|
|
830
|
+
let duration = 0;
|
|
831
|
+
if (videoConfig && videoNalus.length > 0) {
|
|
832
|
+
const trackId = 1;
|
|
833
|
+
const accessUnits = [];
|
|
834
|
+
let pendingParams = [];
|
|
835
|
+
for (const nalu of videoNalus) {
|
|
836
|
+
const naluType = nalu[0] & 31;
|
|
837
|
+
if (naluType === 7 || naluType === 8) {
|
|
838
|
+
pendingParams.push(nalu);
|
|
839
|
+
} else if (naluType === 6) {
|
|
840
|
+
pendingParams.push(nalu);
|
|
841
|
+
} else if (naluType === 5) {
|
|
842
|
+
accessUnits.push({
|
|
843
|
+
nalus: [...pendingParams, nalu],
|
|
844
|
+
isIdr: true
|
|
845
|
+
});
|
|
846
|
+
pendingParams = [];
|
|
847
|
+
} else if (naluType === 1) {
|
|
848
|
+
accessUnits.push({
|
|
849
|
+
nalus: pendingParams.length > 0 ? [...pendingParams, nalu] : [nalu],
|
|
850
|
+
isIdr: false
|
|
851
|
+
});
|
|
852
|
+
pendingParams = [];
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (accessUnits.length > 0) {
|
|
856
|
+
const naluParts = [];
|
|
857
|
+
const samples = [];
|
|
858
|
+
const frameDuration = videoFrameDuration;
|
|
859
|
+
const basePts = videoPtsList.length > 0 ? Math.min(...videoPtsList) : 0;
|
|
860
|
+
const baseDts = basePts;
|
|
861
|
+
for (let auIdx = 0; auIdx < accessUnits.length; auIdx++) {
|
|
862
|
+
const au = accessUnits[auIdx];
|
|
863
|
+
let auSize = 0;
|
|
864
|
+
const auParts = [];
|
|
865
|
+
for (const nalu of au.nalus) {
|
|
866
|
+
const lengthPrefixed = new Uint8Array(4 + nalu.byteLength);
|
|
867
|
+
lengthPrefixed[0] = nalu.byteLength >> 24 & 255;
|
|
868
|
+
lengthPrefixed[1] = nalu.byteLength >> 16 & 255;
|
|
869
|
+
lengthPrefixed[2] = nalu.byteLength >> 8 & 255;
|
|
870
|
+
lengthPrefixed[3] = nalu.byteLength & 255;
|
|
871
|
+
lengthPrefixed.set(nalu, 4);
|
|
872
|
+
auParts.push(lengthPrefixed);
|
|
873
|
+
auSize += lengthPrefixed.byteLength;
|
|
874
|
+
}
|
|
875
|
+
naluParts.push(...auParts);
|
|
876
|
+
samples.push({
|
|
877
|
+
duration: frameDuration,
|
|
878
|
+
size: auSize,
|
|
879
|
+
// is_leading=0, depends_on: IDR=2(no), non-IDR=1(yes),
|
|
880
|
+
// is_depended_on=0, has_redundancy=0
|
|
881
|
+
flags: au.isIdr ? 33554432 : 16842752,
|
|
882
|
+
compositionTimeOffset: auIdx < videoPtsList.length ? videoPtsList[auIdx] - (baseDts + auIdx * frameDuration) : 0
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
const rawData = concatBuffers(naluParts);
|
|
886
|
+
const moofBox = moof({
|
|
887
|
+
sequenceNumber: state.sequenceNumber++,
|
|
888
|
+
trackId,
|
|
889
|
+
baseDecodeTime: state.videoDecodeTime,
|
|
890
|
+
samples
|
|
891
|
+
});
|
|
892
|
+
state.videoDecodeTime += accessUnits.length * frameDuration;
|
|
893
|
+
patchDataOffset(moofBox, moofBox.byteLength);
|
|
894
|
+
const mdatBox = mdat(rawData);
|
|
895
|
+
fragments.push(moofBox, mdatBox);
|
|
896
|
+
duration = accessUnits.length * frameDuration / MPEG_TIMESCALE;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (audioConfig && audioRawData && audioFrames.length > 0) {
|
|
900
|
+
const trackId = videoConfig ? 2 : 1;
|
|
901
|
+
const samplesPerFrame = 1024;
|
|
902
|
+
const samples = [];
|
|
903
|
+
const parts = [];
|
|
904
|
+
let offset = 0;
|
|
905
|
+
for (const frame of audioFrames) {
|
|
906
|
+
const size = frame.frameLength - frame.headerSize;
|
|
907
|
+
parts.push(audioRawData.subarray(offset, offset + size));
|
|
908
|
+
samples.push({
|
|
909
|
+
duration: samplesPerFrame,
|
|
910
|
+
size
|
|
911
|
+
});
|
|
912
|
+
offset += size;
|
|
913
|
+
}
|
|
914
|
+
const rawData = concatBuffers(parts);
|
|
915
|
+
const moofBox = moof({
|
|
916
|
+
sequenceNumber: state.sequenceNumber++,
|
|
917
|
+
trackId,
|
|
918
|
+
baseDecodeTime: state.audioDecodeTime,
|
|
919
|
+
samples
|
|
920
|
+
});
|
|
921
|
+
state.audioDecodeTime += audioFrames.length * samplesPerFrame;
|
|
922
|
+
patchDataOffset(moofBox, moofBox.byteLength);
|
|
923
|
+
const mdatBox = mdat(rawData);
|
|
924
|
+
fragments.push(moofBox, mdatBox);
|
|
925
|
+
const audioDuration = audioFrames.length * samplesPerFrame / audioConfig.sampleRate;
|
|
926
|
+
if (audioDuration > duration) duration = audioDuration;
|
|
927
|
+
}
|
|
928
|
+
const mediaSegment = concatBuffers(fragments);
|
|
929
|
+
return {
|
|
930
|
+
initSegment: getInitSegment(state),
|
|
931
|
+
mediaSegment: mediaSegment.buffer.slice(
|
|
932
|
+
mediaSegment.byteOffset,
|
|
933
|
+
mediaSegment.byteOffset + mediaSegment.byteLength
|
|
934
|
+
),
|
|
935
|
+
duration
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
function extractElementaryStream(pesData) {
|
|
939
|
+
const parts = [];
|
|
940
|
+
let offset = 0;
|
|
941
|
+
while (offset < pesData.length) {
|
|
942
|
+
if (offset + 9 <= pesData.length && pesData[offset] === 0 && pesData[offset + 1] === 0 && pesData[offset + 2] === 1 && pesData[offset + 3] >= 188) {
|
|
943
|
+
const headerDataLength = pesData[offset + 8];
|
|
944
|
+
const payloadStart = offset + 9 + headerDataLength;
|
|
945
|
+
const pesPacketLength = pesData[offset + 4] << 8 | pesData[offset + 5];
|
|
946
|
+
if (pesPacketLength > 0) {
|
|
947
|
+
const pesEnd = Math.min(
|
|
948
|
+
offset + 6 + pesPacketLength,
|
|
949
|
+
pesData.length
|
|
950
|
+
);
|
|
951
|
+
if (payloadStart < pesEnd) {
|
|
952
|
+
parts.push(pesData.subarray(payloadStart, pesEnd));
|
|
953
|
+
}
|
|
954
|
+
offset = pesEnd;
|
|
955
|
+
} else {
|
|
956
|
+
let nextPes = payloadStart;
|
|
957
|
+
while (nextPes + 3 < pesData.length) {
|
|
958
|
+
if (pesData[nextPes] === 0 && pesData[nextPes + 1] === 0 && pesData[nextPes + 2] === 1 && pesData[nextPes + 3] >= 188) {
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
nextPes++;
|
|
962
|
+
}
|
|
963
|
+
if (nextPes + 3 >= pesData.length) nextPes = pesData.length;
|
|
964
|
+
if (payloadStart < nextPes) {
|
|
965
|
+
parts.push(pesData.subarray(payloadStart, nextPes));
|
|
966
|
+
}
|
|
967
|
+
offset = nextPes;
|
|
968
|
+
}
|
|
969
|
+
} else {
|
|
970
|
+
offset++;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return concatBuffers(parts);
|
|
974
|
+
}
|
|
975
|
+
function extractH264Nalus(data) {
|
|
976
|
+
const nalus = [];
|
|
977
|
+
const startPositions = [];
|
|
978
|
+
let i = 0;
|
|
979
|
+
while (i < data.length - 2) {
|
|
980
|
+
if (data[i] === 0 && data[i + 1] === 0) {
|
|
981
|
+
if (i + 3 < data.length && data[i + 2] === 0 && data[i + 3] === 1) {
|
|
982
|
+
startPositions.push({ offset: i, scLen: 4 });
|
|
983
|
+
i += 4;
|
|
984
|
+
} else if (data[i + 2] === 1) {
|
|
985
|
+
startPositions.push({ offset: i, scLen: 3 });
|
|
986
|
+
i += 3;
|
|
987
|
+
} else {
|
|
988
|
+
i++;
|
|
989
|
+
}
|
|
990
|
+
} else {
|
|
991
|
+
i++;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
for (let j = 0; j < startPositions.length; j++) {
|
|
995
|
+
const naluStart = startPositions[j].offset + startPositions[j].scLen;
|
|
996
|
+
const naluEnd = j + 1 < startPositions.length ? startPositions[j + 1].offset : data.length;
|
|
997
|
+
let trimmedEnd = naluEnd;
|
|
998
|
+
while (trimmedEnd > naluStart + 1 && data[trimmedEnd - 1] === 0) {
|
|
999
|
+
trimmedEnd--;
|
|
1000
|
+
}
|
|
1001
|
+
if (trimmedEnd > naluStart) {
|
|
1002
|
+
nalus.push(data.subarray(naluStart, trimmedEnd));
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return nalus;
|
|
1006
|
+
}
|
|
1007
|
+
var HIGH_PROFILE_IDCS = /* @__PURE__ */ new Set([
|
|
1008
|
+
100,
|
|
1009
|
+
110,
|
|
1010
|
+
122,
|
|
1011
|
+
244,
|
|
1012
|
+
44,
|
|
1013
|
+
83,
|
|
1014
|
+
86,
|
|
1015
|
+
118,
|
|
1016
|
+
128,
|
|
1017
|
+
138,
|
|
1018
|
+
139,
|
|
1019
|
+
134
|
|
1020
|
+
]);
|
|
1021
|
+
function parseSpsInfo(sps) {
|
|
1022
|
+
let bitOffset = 8;
|
|
1023
|
+
function readBit() {
|
|
1024
|
+
const byteIndex = bitOffset >> 3;
|
|
1025
|
+
const bitIndex = 7 - (bitOffset & 7);
|
|
1026
|
+
bitOffset++;
|
|
1027
|
+
return sps[byteIndex] >> bitIndex & 1;
|
|
1028
|
+
}
|
|
1029
|
+
function readBits(n) {
|
|
1030
|
+
let val = 0;
|
|
1031
|
+
for (let i = 0; i < n; i++) {
|
|
1032
|
+
val = val << 1 | readBit();
|
|
1033
|
+
}
|
|
1034
|
+
return val;
|
|
1035
|
+
}
|
|
1036
|
+
function readUe() {
|
|
1037
|
+
let leadingZeros = 0;
|
|
1038
|
+
while (readBit() === 0 && leadingZeros < 32) leadingZeros++;
|
|
1039
|
+
if (leadingZeros === 0) return 0;
|
|
1040
|
+
return (1 << leadingZeros) - 1 + readBits(leadingZeros);
|
|
1041
|
+
}
|
|
1042
|
+
function readSe() {
|
|
1043
|
+
const val = readUe();
|
|
1044
|
+
return val & 1 ? val + 1 >> 1 : -(val >> 1);
|
|
1045
|
+
}
|
|
1046
|
+
const profileIdc = readBits(8);
|
|
1047
|
+
readBits(8);
|
|
1048
|
+
readBits(8);
|
|
1049
|
+
readUe();
|
|
1050
|
+
if (HIGH_PROFILE_IDCS.has(profileIdc)) {
|
|
1051
|
+
const chromaFormatIdc = readUe();
|
|
1052
|
+
if (chromaFormatIdc === 3) readBit();
|
|
1053
|
+
readUe();
|
|
1054
|
+
readUe();
|
|
1055
|
+
readBit();
|
|
1056
|
+
const seqScalingMatrixPresent = readBit();
|
|
1057
|
+
if (seqScalingMatrixPresent) {
|
|
1058
|
+
const count = chromaFormatIdc !== 3 ? 8 : 12;
|
|
1059
|
+
for (let i = 0; i < count; i++) {
|
|
1060
|
+
if (readBit()) {
|
|
1061
|
+
const size = i < 6 ? 16 : (
|
|
1062
|
+
/* c8 ignore next */
|
|
1063
|
+
64
|
|
1064
|
+
);
|
|
1065
|
+
let lastScale = 8;
|
|
1066
|
+
let nextScale = 8;
|
|
1067
|
+
for (let j = 0; j < size; j++) {
|
|
1068
|
+
if (nextScale !== 0) {
|
|
1069
|
+
const delta = readSe();
|
|
1070
|
+
nextScale = (lastScale + delta + 256) % 256;
|
|
1071
|
+
}
|
|
1072
|
+
lastScale = nextScale === 0 ? lastScale : nextScale;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
readUe();
|
|
1079
|
+
const picOrderCntType = readUe();
|
|
1080
|
+
if (picOrderCntType === 0) {
|
|
1081
|
+
readUe();
|
|
1082
|
+
} else if (picOrderCntType === 1) {
|
|
1083
|
+
readBit();
|
|
1084
|
+
readSe();
|
|
1085
|
+
readSe();
|
|
1086
|
+
const numRefFrames = readUe();
|
|
1087
|
+
for (let i = 0; i < numRefFrames; i++) readSe();
|
|
1088
|
+
}
|
|
1089
|
+
readUe();
|
|
1090
|
+
readBit();
|
|
1091
|
+
const picWidthInMbsMinus1 = readUe();
|
|
1092
|
+
const picHeightInMapUnitsMinus1 = readUe();
|
|
1093
|
+
const frameMbsOnlyFlag = readBit();
|
|
1094
|
+
const width = (picWidthInMbsMinus1 + 1) * 16;
|
|
1095
|
+
const height = (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16;
|
|
1096
|
+
return { width, height };
|
|
1097
|
+
}
|
|
1098
|
+
export {
|
|
1099
|
+
MPEG_TIMESCALE,
|
|
1100
|
+
STREAM_TYPE_AAC,
|
|
1101
|
+
STREAM_TYPE_H264,
|
|
1102
|
+
STREAM_TYPE_H265,
|
|
1103
|
+
STREAM_TYPE_ID3,
|
|
1104
|
+
TS_PACKET_SIZE,
|
|
1105
|
+
TS_SYNC_BYTE,
|
|
1106
|
+
adtsSampleRate,
|
|
1107
|
+
audioTrak,
|
|
1108
|
+
avc1SampleEntry,
|
|
1109
|
+
box,
|
|
1110
|
+
buildAudioSpecificConfig,
|
|
1111
|
+
concatBuffers,
|
|
1112
|
+
createTransmuxer,
|
|
1113
|
+
demuxTsSegment,
|
|
1114
|
+
ftyp,
|
|
1115
|
+
fullBox,
|
|
1116
|
+
getPayloadOffset,
|
|
1117
|
+
mdat,
|
|
1118
|
+
moof,
|
|
1119
|
+
moov,
|
|
1120
|
+
mp4aSampleEntry,
|
|
1121
|
+
parseAdtsFrames,
|
|
1122
|
+
parsePat,
|
|
1123
|
+
parsePesPts,
|
|
1124
|
+
parsePmt,
|
|
1125
|
+
parseTsPacketHeader,
|
|
1126
|
+
patchDataOffset,
|
|
1127
|
+
stripPesHeader,
|
|
1128
|
+
videoTrak
|
|
1129
|
+
};
|
|
1130
|
+
//# sourceMappingURL=index.js.map
|