@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.
Files changed (43) hide show
  1. package/dist/index.cjs +1150 -0
  2. package/dist/index.cjs.map +7 -0
  3. package/dist/index.js +1130 -0
  4. package/dist/index.js.map +7 -0
  5. package/dist/types/current/aac/adts.d.ts +38 -0
  6. package/dist/types/current/aac/adts.d.ts.map +1 -0
  7. package/dist/types/current/aac/adts.js +63 -0
  8. package/dist/types/current/index.d.ts +7 -0
  9. package/dist/types/current/index.d.ts.map +1 -0
  10. package/dist/types/current/index.js +6 -0
  11. package/dist/types/current/mp4/mp4.d.ts +70 -0
  12. package/dist/types/current/mp4/mp4.d.ts.map +1 -0
  13. package/dist/types/current/mp4/mp4.js +338 -0
  14. package/dist/types/current/mpegts/mpegts.d.ts +68 -0
  15. package/dist/types/current/mpegts/mpegts.d.ts.map +1 -0
  16. package/dist/types/current/mpegts/mpegts.js +202 -0
  17. package/dist/types/current/transmux/sampleEntry.d.ts +25 -0
  18. package/dist/types/current/transmux/sampleEntry.d.ts.map +1 -0
  19. package/dist/types/current/transmux/sampleEntry.js +114 -0
  20. package/dist/types/current/transmux/transmux.d.ts +17 -0
  21. package/dist/types/current/transmux/transmux.d.ts.map +1 -0
  22. package/dist/types/current/transmux/transmux.js +568 -0
  23. package/dist/types/current/tsconfig.tsbuildinfo +1 -0
  24. package/dist/types/ts3.7/aac/adts.d.ts +38 -0
  25. package/dist/types/ts3.7/index.d.ts +7 -0
  26. package/dist/types/ts3.7/mp4/mp4.d.ts +70 -0
  27. package/dist/types/ts3.7/mpegts/mpegts.d.ts +68 -0
  28. package/dist/types/ts3.7/transmux/sampleEntry.d.ts +25 -0
  29. package/dist/types/ts3.7/transmux/transmux.d.ts +17 -0
  30. package/dist/types/ts4.1/aac/adts.d.ts +38 -0
  31. package/dist/types/ts4.1/index.d.ts +7 -0
  32. package/dist/types/ts4.1/mp4/mp4.d.ts +70 -0
  33. package/dist/types/ts4.1/mpegts/mpegts.d.ts +68 -0
  34. package/dist/types/ts4.1/transmux/sampleEntry.d.ts +25 -0
  35. package/dist/types/ts4.1/transmux/transmux.d.ts +17 -0
  36. package/dist/types/ts4.7/aac/adts.d.ts +38 -0
  37. package/dist/types/ts4.7/index.d.ts +7 -0
  38. package/dist/types/ts4.7/mp4/mp4.d.ts +70 -0
  39. package/dist/types/ts4.7/mpegts/mpegts.d.ts +68 -0
  40. package/dist/types/ts4.7/transmux/sampleEntry.d.ts +25 -0
  41. package/dist/types/ts4.7/transmux/transmux.d.ts +17 -0
  42. package/docs/TRANSMUX.md +131 -0
  43. package/package.json +82 -0
@@ -0,0 +1,338 @@
1
+ /*
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * ISO BMFF (MP4) box writing utilities.
7
+ *
8
+ * Provides functions to construct the fMP4 boxes required by MSE:
9
+ * - `ftyp`: file type
10
+ * - `moov` / `mvhd` / `trak` / `mdia` / `minf` / `stbl`: track metadata
11
+ * - `moof` / `mdat`: media fragments
12
+ *
13
+ * @module
14
+ */
15
+ import { BufferWriter } from '@amazon/vinyl-util';
16
+ /** ISO 14496-12 identity matrix in 16.16 / 2.30 fixed point. */
17
+ const UNITY_MATRIX = [
18
+ 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000,
19
+ ];
20
+ /** 90 kHz timescale from ISO 13818-1 (MPEG system clock). */
21
+ export const MPEG_TIMESCALE = 90000;
22
+ /** ISO 639-2/T packed language code for 'und' (undetermined). */
23
+ const LANGUAGE_UNDETERMINED = 0x55c4;
24
+ /**
25
+ * Writes a generic MP4 box (atom). The size field is automatically computed.
26
+ */
27
+ export function box(type, ...payloads) {
28
+ let size = 8;
29
+ for (const p of payloads)
30
+ size += p.byteLength;
31
+ const writer = new BufferWriter(size);
32
+ writer.writeUint32(size);
33
+ writer.writeString(type);
34
+ for (const p of payloads)
35
+ writer.writeBytes(p);
36
+ return writer.toUint8Array();
37
+ }
38
+ /**
39
+ * Writes a full box with version and flags.
40
+ */
41
+ export function fullBox(type, version, flags, payload) {
42
+ const size = 12 + payload.byteLength;
43
+ const writer = new BufferWriter(size);
44
+ writer.writeUint32(size);
45
+ writer.writeString(type);
46
+ writer.writeUint8(version);
47
+ // 24-bit flags
48
+ writer.writeUint8((flags >> 16) & 0xff);
49
+ writer.writeUint8((flags >> 8) & 0xff);
50
+ writer.writeUint8(flags & 0xff);
51
+ writer.writeBytes(payload);
52
+ return writer.toUint8Array();
53
+ }
54
+ const DEFAULT_FTYP = {
55
+ majorBrand: 'isom',
56
+ minorVersion: 0x200,
57
+ compatibleBrands: ['isom', 'iso6', 'mp41'],
58
+ };
59
+ /**
60
+ * Generates the ftyp box.
61
+ */
62
+ export function ftyp(options) {
63
+ const { majorBrand, minorVersion, compatibleBrands } = {
64
+ ...DEFAULT_FTYP,
65
+ ...options,
66
+ };
67
+ const writer = new BufferWriter(8 + compatibleBrands.length * 4);
68
+ writer.writeString(majorBrand);
69
+ writer.writeUint32(minorVersion);
70
+ for (const brand of compatibleBrands)
71
+ writer.writeString(brand);
72
+ return box('ftyp', writer.toUint8Array());
73
+ }
74
+ /**
75
+ * Generates a moov box from pre-built track boxes.
76
+ */
77
+ export function moov(...traks) {
78
+ const trakAndMvex = [];
79
+ for (let i = 0; i < traks.length; i++) {
80
+ trakAndMvex.push(traks[i], mvex(i + 1));
81
+ }
82
+ return box('moov', mvhd(), ...trakAndMvex);
83
+ }
84
+ const DEFAULT_MVHD = {
85
+ timescale: MPEG_TIMESCALE,
86
+ duration: 0,
87
+ rate: 1.0,
88
+ volume: 1.0,
89
+ };
90
+ function mvhd(options) {
91
+ const { timescale, duration, rate, volume } = {
92
+ ...DEFAULT_MVHD,
93
+ ...options,
94
+ };
95
+ const writer = new BufferWriter(96);
96
+ writer.writeUint32(0); // creation_time
97
+ writer.writeUint32(0); // modification_time
98
+ writer.writeUint32(timescale);
99
+ writer.writeUint32(duration);
100
+ writer.writeUint32(rate * 0x10000); // 16.16 fixed point
101
+ writer.writeUint16(volume * 0x100); // 8.8 fixed point
102
+ // 10 bytes reserved
103
+ for (let i = 0; i < 10; i++)
104
+ writer.writeUint8(0);
105
+ for (const v of UNITY_MATRIX)
106
+ writer.writeUint32(v);
107
+ // 6 x uint32 pre_defined
108
+ for (let i = 0; i < 6; i++)
109
+ writer.writeUint32(0);
110
+ writer.writeUint32(0xffffffff); // next_track_ID
111
+ return fullBox('mvhd', 0, 0, writer.toUint8Array());
112
+ }
113
+ export function audioTrak(options) {
114
+ return box('trak', tkhd(options.trackId, 0, 0, options.channelCount > 0 ? 1.0 : 0), box('mdia', mdhd(options.sampleRate), hdlr('soun', 'SoundHandler'), box('minf', smhd(), dinf(), audioStbl(options.sampleEntry))));
115
+ }
116
+ export function videoTrak(options) {
117
+ return box('trak', tkhd(options.trackId, options.width, options.height, 0), box('mdia', mdhd(options.timescale ?? MPEG_TIMESCALE), hdlr('vide', 'VideoHandler'), box('minf', vmhd(), dinf(), videoStbl(options.sampleEntry))));
118
+ }
119
+ function tkhd(trackId, width, height, volume) {
120
+ const writer = new BufferWriter(80);
121
+ writer.writeUint32(0); // creation_time
122
+ writer.writeUint32(0); // modification_time
123
+ writer.writeUint32(trackId);
124
+ writer.writeUint32(0); // reserved
125
+ writer.writeUint32(0); // duration
126
+ writer.writeUint32(0); // reserved
127
+ writer.writeUint32(0); // reserved
128
+ writer.writeUint16(0); // layer
129
+ writer.writeUint16(0); // alternate_group
130
+ writer.writeUint16(volume * 0x100); // volume (8.8 fixed point)
131
+ writer.writeUint16(0); // reserved
132
+ // unity matrix
133
+ for (const v of UNITY_MATRIX)
134
+ writer.writeUint32(v);
135
+ writer.writeUint32(width * 0x10000); // width (16.16 fixed point)
136
+ writer.writeUint32(height * 0x10000); // height (16.16 fixed point)
137
+ return fullBox('tkhd', 0, 0x000003, writer.toUint8Array());
138
+ }
139
+ function mdhd(timescale) {
140
+ const writer = new BufferWriter(20);
141
+ writer.writeUint32(0); // creation_time
142
+ writer.writeUint32(0); // modification_time
143
+ writer.writeUint32(timescale);
144
+ writer.writeUint32(0); // duration
145
+ writer.writeUint16(LANGUAGE_UNDETERMINED);
146
+ writer.writeUint16(0); // pre_defined
147
+ return fullBox('mdhd', 0, 0, writer.toUint8Array());
148
+ }
149
+ function hdlr(handlerType, name) {
150
+ const writer = new BufferWriter(21 + name.length);
151
+ writer.writeUint32(0); // pre_defined
152
+ writer.writeString(handlerType);
153
+ writer.writeUint32(0); // reserved
154
+ writer.writeUint32(0); // reserved
155
+ writer.writeUint32(0); // reserved
156
+ writer.writeString(name);
157
+ writer.writeUint8(0); // null terminator
158
+ return fullBox('hdlr', 0, 0, writer.toUint8Array());
159
+ }
160
+ function smhd() {
161
+ const writer = new BufferWriter(4);
162
+ writer.writeUint16(0); // balance
163
+ writer.writeUint16(0); // reserved
164
+ return fullBox('smhd', 0, 0, writer.toUint8Array());
165
+ }
166
+ function vmhd() {
167
+ const writer = new BufferWriter(8);
168
+ writer.writeUint16(0); // graphicsmode
169
+ writer.writeUint16(0); // opcolor
170
+ writer.writeUint16(0);
171
+ writer.writeUint16(0);
172
+ return fullBox('vmhd', 0, 0x000001, writer.toUint8Array());
173
+ }
174
+ function dinf() {
175
+ const writer = new BufferWriter(4);
176
+ writer.writeUint32(1); // entry_count
177
+ const urlBox = fullBox('url ', 0, 0x000001, new Uint8Array(0));
178
+ return box('dinf', fullBox('dref', 0, 0, concatUint8(writer.toUint8Array(), urlBox)));
179
+ }
180
+ function audioStbl(sampleEntry) {
181
+ return box('stbl', stsd(sampleEntry), stts(), stsc(), stsz(), stco());
182
+ }
183
+ function videoStbl(sampleEntry) {
184
+ return box('stbl', stsd(sampleEntry), stts(), stsc(), stsz(), stco());
185
+ }
186
+ function stsd(sampleEntry) {
187
+ const writer = new BufferWriter(4);
188
+ writer.writeUint32(1); // entry_count
189
+ return fullBox('stsd', 0, 0, concatUint8(writer.toUint8Array(), sampleEntry));
190
+ }
191
+ function stts() {
192
+ const writer = new BufferWriter(4);
193
+ writer.writeUint32(0); // entry_count
194
+ return fullBox('stts', 0, 0, writer.toUint8Array());
195
+ }
196
+ function stsc() {
197
+ const writer = new BufferWriter(4);
198
+ writer.writeUint32(0); // entry_count
199
+ return fullBox('stsc', 0, 0, writer.toUint8Array());
200
+ }
201
+ function stsz() {
202
+ const writer = new BufferWriter(8);
203
+ writer.writeUint32(0); // sample_size
204
+ writer.writeUint32(0); // sample_count
205
+ return fullBox('stsz', 0, 0, writer.toUint8Array());
206
+ }
207
+ function stco() {
208
+ const writer = new BufferWriter(4);
209
+ writer.writeUint32(0); // entry_count
210
+ return fullBox('stco', 0, 0, writer.toUint8Array());
211
+ }
212
+ function mvex(trackId) {
213
+ return box('mvex', trex(trackId));
214
+ }
215
+ function trex(trackId) {
216
+ const writer = new BufferWriter(20);
217
+ writer.writeUint32(trackId);
218
+ writer.writeUint32(1); // default_sample_description_index
219
+ writer.writeUint32(0); // default_sample_duration
220
+ writer.writeUint32(0); // default_sample_size
221
+ writer.writeUint32(0); // default_sample_flags
222
+ return fullBox('trex', 0, 0, writer.toUint8Array());
223
+ }
224
+ /**
225
+ * Generates a moof (movie fragment) box.
226
+ */
227
+ export function moof(options) {
228
+ return box('moof', mfhd(options.sequenceNumber), traf(options));
229
+ }
230
+ function mfhd(sequenceNumber) {
231
+ const writer = new BufferWriter(4);
232
+ writer.writeUint32(sequenceNumber);
233
+ return fullBox('mfhd', 0, 0, writer.toUint8Array());
234
+ }
235
+ function traf(options) {
236
+ return box('traf', tfhd(options.trackId), tfdt(options.baseDecodeTime), trun(options.samples));
237
+ }
238
+ function tfhd(trackId) {
239
+ const writer = new BufferWriter(4);
240
+ writer.writeUint32(trackId);
241
+ // flags: default-base-is-moof (0x020000)
242
+ return fullBox('tfhd', 0, 0x020000, writer.toUint8Array());
243
+ }
244
+ function tfdt(baseDecodeTime) {
245
+ const writer = new BufferWriter(4);
246
+ writer.writeUint32(baseDecodeTime);
247
+ return fullBox('tfdt', 0, 0, writer.toUint8Array());
248
+ }
249
+ function trun(samples) {
250
+ // flags: data-offset-present (0x01), sample-duration-present (0x100),
251
+ // sample-size-present (0x200), sample-flags-present (0x400)
252
+ const hasFlags = samples.some((s) => s.flags != null);
253
+ const hasCto = samples.some((s) => s.compositionTimeOffset != null);
254
+ let flags = 0x000001 | 0x000100 | 0x000200;
255
+ if (hasFlags)
256
+ flags |= 0x000400;
257
+ if (hasCto)
258
+ flags |= 0x000800;
259
+ let entrySize = 8; // duration + size
260
+ if (hasFlags)
261
+ entrySize += 4;
262
+ if (hasCto)
263
+ entrySize += 4;
264
+ const writer = new BufferWriter(8 + samples.length * entrySize);
265
+ writer.writeUint32(samples.length); // sample_count
266
+ writer.writeUint32(0); // data_offset (placeholder, patched later)
267
+ for (const sample of samples) {
268
+ writer.writeUint32(sample.duration);
269
+ writer.writeUint32(sample.size);
270
+ if (hasFlags)
271
+ writer.writeUint32(sample.flags ?? 0);
272
+ if (hasCto)
273
+ writer.writeInt32(sample.compositionTimeOffset ?? 0);
274
+ }
275
+ const result = fullBox('trun', hasCto ? 1 : 0, flags, writer.toUint8Array());
276
+ return result;
277
+ }
278
+ /**
279
+ * Generates an mdat box wrapping raw media data.
280
+ */
281
+ export function mdat(data) {
282
+ return box('mdat', data);
283
+ }
284
+ /**
285
+ * Patches the trun data_offset field. The data_offset is the byte offset from
286
+ * the start of the moof to the first byte of the mdat payload.
287
+ */
288
+ export function patchDataOffset(moofData, moofSize) {
289
+ // data_offset = moofSize + 8 (mdat header)
290
+ const dataOffset = moofSize + 8;
291
+ // Find trun box and patch the data_offset field.
292
+ // trun is inside moof > traf. Walk the box tree.
293
+ let pos = 8; // skip moof header
294
+ while (pos < moofData.length) {
295
+ const boxSize = ((moofData[pos] << 24) |
296
+ (moofData[pos + 1] << 16) |
297
+ (moofData[pos + 2] << 8) |
298
+ moofData[pos + 3]) >>>
299
+ 0;
300
+ const boxType = String.fromCharCode(moofData[pos + 4], moofData[pos + 5], moofData[pos + 6], moofData[pos + 7]);
301
+ if (boxType === 'traf') {
302
+ // Recurse into traf
303
+ let trafPos = pos + 8;
304
+ while (trafPos < pos + boxSize) {
305
+ const innerSize = ((moofData[trafPos] << 24) |
306
+ (moofData[trafPos + 1] << 16) |
307
+ (moofData[trafPos + 2] << 8) |
308
+ moofData[trafPos + 3]) >>>
309
+ 0;
310
+ const innerType = String.fromCharCode(moofData[trafPos + 4], moofData[trafPos + 5], moofData[trafPos + 6], moofData[trafPos + 7]);
311
+ if (innerType === 'trun') {
312
+ // data_offset is at offset 12+4 = 16 from box start
313
+ // (8 box header + 4 version/flags + 4 sample_count)
314
+ const offsetPos = trafPos + 16;
315
+ moofData[offsetPos] = (dataOffset >> 24) & 0xff;
316
+ moofData[offsetPos + 1] = (dataOffset >> 16) & 0xff;
317
+ moofData[offsetPos + 2] = (dataOffset >> 8) & 0xff;
318
+ moofData[offsetPos + 3] = dataOffset & 0xff;
319
+ return;
320
+ }
321
+ trafPos += innerSize;
322
+ }
323
+ }
324
+ pos += boxSize;
325
+ }
326
+ }
327
+ function concatUint8(...arrays) {
328
+ let total = 0;
329
+ for (const a of arrays)
330
+ total += a.byteLength;
331
+ const result = new Uint8Array(total);
332
+ let offset = 0;
333
+ for (const a of arrays) {
334
+ result.set(a, offset);
335
+ offset += a.byteLength;
336
+ }
337
+ return result;
338
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * MPEG-TS constants and packet-level parsing.
3
+ *
4
+ * An MPEG Transport Stream consists of fixed 188-byte packets. Each packet
5
+ * starts with a sync byte (0x47) and carries a 13-bit PID identifying the
6
+ * elementary stream. The PAT (PID 0) maps program numbers to PMT PIDs, and
7
+ * the PMT lists the elementary stream PIDs with their stream types.
8
+ *
9
+ * @module
10
+ */
11
+ export declare const TS_PACKET_SIZE = 188;
12
+ export declare const TS_SYNC_BYTE = 71;
13
+ /** Well-known stream type identifiers from ISO/IEC 13818-1. */
14
+ export declare const STREAM_TYPE_AAC = 15;
15
+ export declare const STREAM_TYPE_H264 = 27;
16
+ export declare const STREAM_TYPE_H265 = 36;
17
+ export declare const STREAM_TYPE_ID3 = 21;
18
+ export interface TsPacketHeader {
19
+ readonly pid: number;
20
+ readonly payloadUnitStartIndicator: boolean;
21
+ readonly adaptationFieldControl: number;
22
+ readonly continuityCounter: number;
23
+ }
24
+ export interface PmtEntry {
25
+ readonly pid: number;
26
+ readonly streamType: number;
27
+ }
28
+ /**
29
+ * Parses a single 188-byte TS packet header.
30
+ */
31
+ export declare function parseTsPacketHeader(data: Uint8Array, offset: number): TsPacketHeader;
32
+ /**
33
+ * Returns the offset of the payload within a TS packet.
34
+ */
35
+ export declare function getPayloadOffset(data: Uint8Array, offset: number, adaptationFieldControl: number): number;
36
+ /**
37
+ * Parses the PAT (Program Association Table) to find the PMT PID.
38
+ */
39
+ export declare function parsePat(data: Uint8Array, offset: number): number;
40
+ /**
41
+ * Parses the PMT (Program Map Table) to extract elementary stream entries.
42
+ */
43
+ export declare function parsePmt(data: Uint8Array, offset: number): PmtEntry[];
44
+ export interface DemuxedStream {
45
+ readonly streamType: number;
46
+ readonly pid: number;
47
+ readonly data: Uint8Array[];
48
+ }
49
+ /**
50
+ * Demuxes a complete MPEG-TS segment into elementary streams.
51
+ * Reassembles PES packets from TS packets for each PID.
52
+ */
53
+ export declare function demuxTsSegment(segment: Uint8Array): DemuxedStream[];
54
+ /**
55
+ * Concatenates an array of Uint8Arrays into a single Uint8Array.
56
+ */
57
+ export declare function concatBuffers(buffers: Uint8Array[]): Uint8Array;
58
+ /**
59
+ * Strips the PES header from raw PES packet data, returning the elementary
60
+ * stream payload.
61
+ */
62
+ export declare function stripPesHeader(pesData: Uint8Array): Uint8Array;
63
+ /**
64
+ * Extracts the PTS (Presentation Time Stamp) from a PES header.
65
+ * Returns the PTS in 90kHz clock ticks, or -1 if not present.
66
+ */
67
+ export declare function parsePesPts(pesData: Uint8Array): number;
68
+ //# sourceMappingURL=mpegts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mpegts.d.ts","sourceRoot":"","sources":["../../../../src/mpegts/mpegts.ts"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AAEH,eAAO,MAAM,cAAc,MAAM,CAAA;AACjC,eAAO,MAAM,YAAY,KAAO,CAAA;AAEhC,+DAA+D;AAC/D,eAAO,MAAM,eAAe,KAAO,CAAA;AACnC,eAAO,MAAM,gBAAgB,KAAO,CAAA;AACpC,eAAO,MAAM,gBAAgB,KAAO,CAAA;AACpC,eAAO,MAAM,eAAe,KAAO,CAAA;AAEnC,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,yBAAyB,EAAE,OAAO,CAAA;IAC3C,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAA;IACvC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;CACrC;AAED,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACf,cAAc,CAUhB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,sBAAsB,EAAE,MAAM,GAC/B,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAWjE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,CA6BrE;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,CAAA;CAC9B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,aAAa,EAAE,CAoEnE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,CAU/D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAS9D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAiBvD"}
@@ -0,0 +1,202 @@
1
+ /*
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * MPEG-TS constants and packet-level parsing.
7
+ *
8
+ * An MPEG Transport Stream consists of fixed 188-byte packets. Each packet
9
+ * starts with a sync byte (0x47) and carries a 13-bit PID identifying the
10
+ * elementary stream. The PAT (PID 0) maps program numbers to PMT PIDs, and
11
+ * the PMT lists the elementary stream PIDs with their stream types.
12
+ *
13
+ * @module
14
+ */
15
+ export const TS_PACKET_SIZE = 188;
16
+ export const TS_SYNC_BYTE = 0x47;
17
+ /** Well-known stream type identifiers from ISO/IEC 13818-1. */
18
+ export const STREAM_TYPE_AAC = 0x0f;
19
+ export const STREAM_TYPE_H264 = 0x1b;
20
+ export const STREAM_TYPE_H265 = 0x24;
21
+ export const STREAM_TYPE_ID3 = 0x15;
22
+ /**
23
+ * Parses a single 188-byte TS packet header.
24
+ */
25
+ export function parseTsPacketHeader(data, offset) {
26
+ const byte1 = data[offset + 1];
27
+ const byte2 = data[offset + 2];
28
+ const byte3 = data[offset + 3];
29
+ return {
30
+ pid: ((byte1 & 0x1f) << 8) | byte2,
31
+ payloadUnitStartIndicator: (byte1 & 0x40) !== 0,
32
+ adaptationFieldControl: (byte3 >> 4) & 0x03,
33
+ continuityCounter: byte3 & 0x0f,
34
+ };
35
+ }
36
+ /**
37
+ * Returns the offset of the payload within a TS packet.
38
+ */
39
+ export function getPayloadOffset(data, offset, adaptationFieldControl) {
40
+ let payloadOffset = offset + 4;
41
+ if (adaptationFieldControl >= 2) {
42
+ // Adaptation field present
43
+ payloadOffset += 1 + data[offset + 4];
44
+ }
45
+ return payloadOffset;
46
+ }
47
+ /**
48
+ * Parses the PAT (Program Association Table) to find the PMT PID.
49
+ */
50
+ export function parsePat(data, offset) {
51
+ // Skip pointer field
52
+ const pointerField = data[offset];
53
+ let pos = offset + 1 + pointerField;
54
+ // Skip table_id(1), section_syntax_indicator+length(2), transport_stream_id(2),
55
+ // version/current(1), section_number(1), last_section_number(1)
56
+ pos += 8;
57
+ // First program entry: program_number(2) + PMT_PID(2)
58
+ // Skip program_number
59
+ pos += 2;
60
+ return ((data[pos] & 0x1f) << 8) | data[pos + 1];
61
+ }
62
+ /**
63
+ * Parses the PMT (Program Map Table) to extract elementary stream entries.
64
+ */
65
+ export function parsePmt(data, offset) {
66
+ const pointerField = data[offset];
67
+ let pos = offset + 1 + pointerField;
68
+ // table_id(1)
69
+ pos += 1;
70
+ const sectionLength = ((data[pos] & 0x0f) << 8) | data[pos + 1];
71
+ pos += 2;
72
+ const sectionEnd = pos + sectionLength;
73
+ // transport_stream_id(2), version/current(1), section_number(1), last_section_number(1)
74
+ pos += 5;
75
+ // PCR_PID(2)
76
+ pos += 2;
77
+ // program_info_length
78
+ const programInfoLength = ((data[pos] & 0x0f) << 8) | data[pos + 1];
79
+ pos += 2 + programInfoLength;
80
+ const entries = [];
81
+ // -4 for CRC32
82
+ while (pos < sectionEnd - 4) {
83
+ const streamType = data[pos];
84
+ pos += 1;
85
+ const pid = ((data[pos] & 0x1f) << 8) | data[pos + 1];
86
+ pos += 2;
87
+ const esInfoLength = ((data[pos] & 0x0f) << 8) | data[pos + 1];
88
+ pos += 2 + esInfoLength;
89
+ entries.push({ pid, streamType });
90
+ }
91
+ return entries;
92
+ }
93
+ /**
94
+ * Demuxes a complete MPEG-TS segment into elementary streams.
95
+ * Reassembles PES packets from TS packets for each PID.
96
+ */
97
+ export function demuxTsSegment(segment) {
98
+ let pmtPid = -1;
99
+ const pmtEntries = [];
100
+ const pesBuffers = new Map();
101
+ // First pass: find PAT and PMT
102
+ for (let i = 0; i + TS_PACKET_SIZE <= segment.length; i += TS_PACKET_SIZE) {
103
+ if (segment[i] !== TS_SYNC_BYTE)
104
+ continue;
105
+ const header = parseTsPacketHeader(segment, i);
106
+ const payloadStart = getPayloadOffset(segment, i, header.adaptationFieldControl);
107
+ if (header.adaptationFieldControl === 0 ||
108
+ header.adaptationFieldControl === 2)
109
+ continue;
110
+ if (header.pid === 0) {
111
+ pmtPid = parsePat(segment, payloadStart);
112
+ }
113
+ else if (header.pid === pmtPid && pmtEntries.length === 0) {
114
+ pmtEntries.push(...parsePmt(segment, payloadStart));
115
+ }
116
+ }
117
+ // Initialize buffers for known elementary streams
118
+ const pidToStreamType = new Map();
119
+ for (const entry of pmtEntries) {
120
+ pidToStreamType.set(entry.pid, entry.streamType);
121
+ pesBuffers.set(entry.pid, []);
122
+ }
123
+ // Second pass: collect PES data
124
+ for (let i = 0; i + TS_PACKET_SIZE <= segment.length; i += TS_PACKET_SIZE) {
125
+ if (segment[i] !== TS_SYNC_BYTE)
126
+ continue;
127
+ const header = parseTsPacketHeader(segment, i);
128
+ if (header.adaptationFieldControl === 0 ||
129
+ header.adaptationFieldControl === 2)
130
+ continue;
131
+ const buffers = pesBuffers.get(header.pid);
132
+ if (!buffers)
133
+ continue;
134
+ const payloadStart = getPayloadOffset(segment, i, header.adaptationFieldControl);
135
+ const payloadEnd = i + TS_PACKET_SIZE;
136
+ if (payloadStart < payloadEnd) {
137
+ buffers.push(segment.slice(payloadStart, payloadEnd));
138
+ }
139
+ }
140
+ const streams = [];
141
+ for (const [pid, buffers] of pesBuffers) {
142
+ if (buffers.length === 0)
143
+ continue;
144
+ streams.push({
145
+ pid,
146
+ streamType: pidToStreamType.get(pid),
147
+ data: buffers,
148
+ });
149
+ }
150
+ return streams;
151
+ }
152
+ /**
153
+ * Concatenates an array of Uint8Arrays into a single Uint8Array.
154
+ */
155
+ export function concatBuffers(buffers) {
156
+ let totalLength = 0;
157
+ for (const buf of buffers)
158
+ totalLength += buf.byteLength;
159
+ const result = new Uint8Array(totalLength);
160
+ let offset = 0;
161
+ for (const buf of buffers) {
162
+ result.set(buf, offset);
163
+ offset += buf.byteLength;
164
+ }
165
+ return result;
166
+ }
167
+ /**
168
+ * Strips the PES header from raw PES packet data, returning the elementary
169
+ * stream payload.
170
+ */
171
+ export function stripPesHeader(pesData) {
172
+ // PES start code: 0x00 0x00 0x01
173
+ if (pesData[0] !== 0 || pesData[1] !== 0 || pesData[2] !== 1) {
174
+ // No PES header, return as-is (continuation data)
175
+ return pesData;
176
+ }
177
+ // PES header data length is at byte 8
178
+ const pesHeaderDataLength = pesData[8];
179
+ return pesData.subarray(9 + pesHeaderDataLength);
180
+ }
181
+ /**
182
+ * Extracts the PTS (Presentation Time Stamp) from a PES header.
183
+ * Returns the PTS in 90kHz clock ticks, or -1 if not present.
184
+ */
185
+ export function parsePesPts(pesData) {
186
+ if (pesData[0] !== 0 || pesData[1] !== 0 || pesData[2] !== 1)
187
+ return -1;
188
+ const flags = pesData[7];
189
+ if ((flags & 0x80) === 0)
190
+ return -1;
191
+ // PTS is encoded in 5 bytes starting at byte 9
192
+ const byte9 = pesData[9];
193
+ const byte10 = pesData[10];
194
+ const byte11 = pesData[11];
195
+ const byte12 = pesData[12];
196
+ const byte13 = pesData[13];
197
+ return ((byte9 & 0x0e) * 0x20000000 + // (byte9 & 0x0E) << 29
198
+ ((byte10 & 0xff) << 22) +
199
+ ((byte11 & 0xfe) << 14) +
200
+ ((byte12 & 0xff) << 7) +
201
+ ((byte13 & 0xfe) >>> 1));
202
+ }
@@ -0,0 +1,25 @@
1
+ export interface AudioTrackConfig {
2
+ readonly audioObjectType: number;
3
+ readonly samplingFrequencyIndex: number;
4
+ readonly channelConfiguration: number;
5
+ readonly sampleRate: number;
6
+ readonly audioSpecificConfig: Uint8Array;
7
+ }
8
+ export interface VideoTrackConfig {
9
+ readonly width: number;
10
+ readonly height: number;
11
+ readonly sps: Uint8Array;
12
+ readonly pps: Uint8Array;
13
+ readonly profileIdc: number;
14
+ readonly profileCompatibility: number;
15
+ readonly levelIdc: number;
16
+ }
17
+ /**
18
+ * Builds an mp4a sample entry box for AAC audio.
19
+ */
20
+ export declare function mp4aSampleEntry(config: AudioTrackConfig): Uint8Array;
21
+ /**
22
+ * Builds an avc1 sample entry box for H.264 video.
23
+ */
24
+ export declare function avc1SampleEntry(config: VideoTrackConfig): Uint8Array;
25
+ //# sourceMappingURL=sampleEntry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampleEntry.d.ts","sourceRoot":"","sources":["../../../../src/transmux/sampleEntry.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAA;IACvC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,UAAU,CAAA;CAC3C;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC5B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAapE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAkBpE"}