@alessmicrosystems/mpegts.js 1.8.1
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/LICENSE +202 -0
- package/README.md +158 -0
- package/README_ja.md +153 -0
- package/README_zh.md +157 -0
- package/d.ts/mpegts.d.ts +524 -0
- package/d.ts/src/core/mse-events.d.ts +9 -0
- package/d.ts/src/core/transmuxing-events.d.ts +24 -0
- package/d.ts/src/demux/aac.d.ts +44 -0
- package/d.ts/src/demux/ac3.d.ts +70 -0
- package/d.ts/src/demux/av1-parser.d.ts +77 -0
- package/d.ts/src/demux/av1.d.ts +11 -0
- package/d.ts/src/demux/base-demuxer.d.ts +55 -0
- package/d.ts/src/demux/h264.d.ts +40 -0
- package/d.ts/src/demux/h265.d.ts +65 -0
- package/d.ts/src/demux/klv.d.ts +17 -0
- package/d.ts/src/demux/mp3.d.ts +6 -0
- package/d.ts/src/demux/mpeg4-audio.d.ts +28 -0
- package/d.ts/src/demux/pat-pmt-pes.d.ts +106 -0
- package/d.ts/src/demux/patpmt.d.ts +40 -0
- package/d.ts/src/demux/pes-private-data.d.ts +14 -0
- package/d.ts/src/demux/pgs-data.d.ts +9 -0
- package/d.ts/src/demux/scte35.d.ts +250 -0
- package/d.ts/src/demux/sei.d.ts +8 -0
- package/d.ts/src/demux/smpte2038.d.ts +22 -0
- package/d.ts/src/demux/ts-demuxer.d.ts +124 -0
- package/d.ts/src/player/live-latency-chaser.d.ts +10 -0
- package/d.ts/src/player/live-latency-synchronizer.d.ts +10 -0
- package/d.ts/src/player/loading-controller.d.ts +19 -0
- package/d.ts/src/player/mse-player.d.ts +30 -0
- package/d.ts/src/player/player-engine-dedicated-thread-worker.d.ts +2 -0
- package/d.ts/src/player/player-engine-dedicated-thread.d.ts +48 -0
- package/d.ts/src/player/player-engine-main-thread.d.ts +50 -0
- package/d.ts/src/player/player-engine-worker-cmd-def.d.ts +25 -0
- package/d.ts/src/player/player-engine-worker-msg-def.d.ts +54 -0
- package/d.ts/src/player/player-engine-worker.d.ts +2 -0
- package/d.ts/src/player/player-engine.d.ts +16 -0
- package/d.ts/src/player/player-events.d.ts +21 -0
- package/d.ts/src/player/seeking-handler.d.ts +22 -0
- package/d.ts/src/player/startup-stall-jumper.d.ts +14 -0
- package/d.ts/src/utils/typedarray-equality.d.ts +2 -0
- package/dist/mpegts.js +3 -0
- package/dist/mpegts.js.LICENSE.txt +7 -0
- package/dist/mpegts.js.map +1 -0
- package/package.json +53 -0
- package/src/config.js +67 -0
- package/src/core/features.js +88 -0
- package/src/core/media-info.js +127 -0
- package/src/core/media-segment-info.js +230 -0
- package/src/core/mse-controller.js +599 -0
- package/src/core/mse-events.ts +28 -0
- package/src/core/transmuxer.js +346 -0
- package/src/core/transmuxing-controller.js +628 -0
- package/src/core/transmuxing-events.ts +43 -0
- package/src/core/transmuxing-worker.js +286 -0
- package/src/demux/aac.ts +397 -0
- package/src/demux/ac3.ts +335 -0
- package/src/demux/amf-parser.js +243 -0
- package/src/demux/av1-parser.ts +629 -0
- package/src/demux/av1.ts +103 -0
- package/src/demux/base-demuxer.ts +69 -0
- package/src/demux/demux-errors.js +26 -0
- package/src/demux/exp-golomb.js +116 -0
- package/src/demux/flv-demuxer.js +1854 -0
- package/src/demux/h264.ts +187 -0
- package/src/demux/h265-parser.js +501 -0
- package/src/demux/h265.ts +214 -0
- package/src/demux/klv.ts +40 -0
- package/src/demux/mp3.ts +7 -0
- package/src/demux/mpeg4-audio.ts +45 -0
- package/src/demux/pat-pmt-pes.ts +132 -0
- package/src/demux/pes-private-data.ts +16 -0
- package/src/demux/pgs-data.ts +11 -0
- package/src/demux/scte35.ts +723 -0
- package/src/demux/sei.ts +99 -0
- package/src/demux/smpte2038.ts +89 -0
- package/src/demux/sps-parser.js +298 -0
- package/src/demux/ts-demuxer.ts +2405 -0
- package/src/index.js +4 -0
- package/src/io/fetch-stream-loader.js +266 -0
- package/src/io/io-controller.js +647 -0
- package/src/io/loader.js +134 -0
- package/src/io/param-seek-handler.js +85 -0
- package/src/io/range-seek-handler.js +52 -0
- package/src/io/speed-sampler.js +93 -0
- package/src/io/websocket-loader.js +151 -0
- package/src/io/xhr-moz-chunked-loader.js +211 -0
- package/src/io/xhr-msstream-loader.js +307 -0
- package/src/io/xhr-range-loader.js +366 -0
- package/src/mpegts.js +95 -0
- package/src/player/live-latency-chaser.ts +66 -0
- package/src/player/live-latency-synchronizer.ts +79 -0
- package/src/player/loading-controller.ts +142 -0
- package/src/player/mse-player.ts +150 -0
- package/src/player/native-player.js +262 -0
- package/src/player/player-engine-dedicated-thread.ts +479 -0
- package/src/player/player-engine-main-thread.ts +463 -0
- package/src/player/player-engine-worker-cmd-def.ts +62 -0
- package/src/player/player-engine-worker-msg-def.ts +102 -0
- package/src/player/player-engine-worker.ts +370 -0
- package/src/player/player-engine.ts +35 -0
- package/src/player/player-errors.js +39 -0
- package/src/player/player-events.ts +40 -0
- package/src/player/seeking-handler.ts +205 -0
- package/src/player/startup-stall-jumper.ts +86 -0
- package/src/remux/aac-silent.js +56 -0
- package/src/remux/mp4-generator.js +866 -0
- package/src/remux/mp4-remuxer.js +778 -0
- package/src/utils/browser.js +128 -0
- package/src/utils/exception.js +73 -0
- package/src/utils/logger.js +140 -0
- package/src/utils/logging-control.js +165 -0
- package/src/utils/polyfill.js +68 -0
- package/src/utils/typedarray-equality.ts +69 -0
- package/src/utils/utf8-conv.js +84 -0
- package/src/utils/webworkify-webpack.js +202 -0
- package/tsconfig.json +16 -0
- package/tslint.json +1 -0
- package/types/index.d.ts +3 -0
- package/types/test-flv.ts +8 -0
- package/types/tsconfig.json +24 -0
- package/webpack.config.js +55 -0
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2016 Bilibili. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* This file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js)
|
|
5
|
+
* @author zheng qian <xqq@xqq.im>
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// MP4 boxes generator for ISO BMFF (ISO Base Media File Format, defined in ISO/IEC 14496-12)
|
|
21
|
+
class MP4 {
|
|
22
|
+
|
|
23
|
+
static init() {
|
|
24
|
+
MP4.types = {
|
|
25
|
+
avc1: [], avcC: [], btrt: [], dinf: [],
|
|
26
|
+
dref: [], esds: [], ftyp: [], hdlr: [],
|
|
27
|
+
hvc1: [], hvcC: [], av01: [], av1C: [],
|
|
28
|
+
mdat: [], mdhd: [], mdia: [], mfhd: [],
|
|
29
|
+
minf: [], moof: [], moov: [], mp4a: [],
|
|
30
|
+
mvex: [], mvhd: [], sdtp: [], stbl: [],
|
|
31
|
+
stco: [], stsc: [], stsd: [], stsz: [],
|
|
32
|
+
stts: [], tfdt: [], tfhd: [], traf: [],
|
|
33
|
+
trak: [], trun: [], trex: [], tkhd: [],
|
|
34
|
+
vmhd: [], smhd: [], chnl: [],
|
|
35
|
+
'.mp3': [],
|
|
36
|
+
Opus: [], dOps: [], fLaC: [], dfLa: [],
|
|
37
|
+
ipcm: [], pcmC: [],
|
|
38
|
+
'ac-3': [], dac3: [], 'ec-3': [], dec3: [],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
for (let name in MP4.types) {
|
|
42
|
+
if (MP4.types.hasOwnProperty(name)) {
|
|
43
|
+
MP4.types[name] = [
|
|
44
|
+
name.charCodeAt(0),
|
|
45
|
+
name.charCodeAt(1),
|
|
46
|
+
name.charCodeAt(2),
|
|
47
|
+
name.charCodeAt(3)
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let constants = MP4.constants = {};
|
|
53
|
+
|
|
54
|
+
constants.FTYP = new Uint8Array([
|
|
55
|
+
0x69, 0x73, 0x6F, 0x6D, // major_brand: isom
|
|
56
|
+
0x0, 0x0, 0x0, 0x1, // minor_version: 0x01
|
|
57
|
+
0x69, 0x73, 0x6F, 0x6D, // isom
|
|
58
|
+
0x61, 0x76, 0x63, 0x31 // avc1
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
constants.STSD_PREFIX = new Uint8Array([
|
|
62
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
63
|
+
0x00, 0x00, 0x00, 0x01 // entry_count
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
constants.STTS = new Uint8Array([
|
|
67
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
68
|
+
0x00, 0x00, 0x00, 0x00 // entry_count
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
constants.STSC = constants.STCO = constants.STTS;
|
|
72
|
+
|
|
73
|
+
constants.STSZ = new Uint8Array([
|
|
74
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
75
|
+
0x00, 0x00, 0x00, 0x00, // sample_size
|
|
76
|
+
0x00, 0x00, 0x00, 0x00 // sample_count
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
constants.HDLR_VIDEO = new Uint8Array([
|
|
80
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
81
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined
|
|
82
|
+
0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
|
|
83
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes
|
|
84
|
+
0x00, 0x00, 0x00, 0x00,
|
|
85
|
+
0x00, 0x00, 0x00, 0x00,
|
|
86
|
+
0x56, 0x69, 0x64, 0x65,
|
|
87
|
+
0x6F, 0x48, 0x61, 0x6E,
|
|
88
|
+
0x64, 0x6C, 0x65, 0x72, 0x00 // name: VideoHandler
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
constants.HDLR_AUDIO = new Uint8Array([
|
|
92
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
93
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined
|
|
94
|
+
0x73, 0x6F, 0x75, 0x6E, // handler_type: 'soun'
|
|
95
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes
|
|
96
|
+
0x00, 0x00, 0x00, 0x00,
|
|
97
|
+
0x00, 0x00, 0x00, 0x00,
|
|
98
|
+
0x53, 0x6F, 0x75, 0x6E,
|
|
99
|
+
0x64, 0x48, 0x61, 0x6E,
|
|
100
|
+
0x64, 0x6C, 0x65, 0x72, 0x00 // name: SoundHandler
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
constants.DREF = new Uint8Array([
|
|
104
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
105
|
+
0x00, 0x00, 0x00, 0x01, // entry_count
|
|
106
|
+
0x00, 0x00, 0x00, 0x0C, // entry_size
|
|
107
|
+
0x75, 0x72, 0x6C, 0x20, // type 'url '
|
|
108
|
+
0x00, 0x00, 0x00, 0x01 // version(0) + flags
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
// Sound media header
|
|
112
|
+
constants.SMHD = new Uint8Array([
|
|
113
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
114
|
+
0x00, 0x00, 0x00, 0x00 // balance(2) + reserved(2)
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
// video media header
|
|
118
|
+
constants.VMHD = new Uint8Array([
|
|
119
|
+
0x00, 0x00, 0x00, 0x01, // version(0) + flags
|
|
120
|
+
0x00, 0x00, // graphicsmode: 2 bytes
|
|
121
|
+
0x00, 0x00, 0x00, 0x00, // opcolor: 3 * 2 bytes
|
|
122
|
+
0x00, 0x00
|
|
123
|
+
]);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Generate a box
|
|
127
|
+
static box(type) {
|
|
128
|
+
let size = 8;
|
|
129
|
+
let result = null;
|
|
130
|
+
let datas = Array.prototype.slice.call(arguments, 1);
|
|
131
|
+
let arrayCount = datas.length;
|
|
132
|
+
|
|
133
|
+
for (let i = 0; i < arrayCount; i++) {
|
|
134
|
+
size += datas[i].byteLength;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
result = new Uint8Array(size);
|
|
138
|
+
result[0] = (size >>> 24) & 0xFF; // size
|
|
139
|
+
result[1] = (size >>> 16) & 0xFF;
|
|
140
|
+
result[2] = (size >>> 8) & 0xFF;
|
|
141
|
+
result[3] = (size) & 0xFF;
|
|
142
|
+
|
|
143
|
+
result.set(type, 4); // type
|
|
144
|
+
|
|
145
|
+
let offset = 8;
|
|
146
|
+
for (let i = 0; i < arrayCount; i++) { // data body
|
|
147
|
+
result.set(datas[i], offset);
|
|
148
|
+
offset += datas[i].byteLength;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// emit ftyp & moov
|
|
155
|
+
static generateInitSegment(meta) {
|
|
156
|
+
let ftyp = MP4.box(MP4.types.ftyp, MP4.constants.FTYP);
|
|
157
|
+
let moov = MP4.moov(meta);
|
|
158
|
+
|
|
159
|
+
let result = new Uint8Array(ftyp.byteLength + moov.byteLength);
|
|
160
|
+
result.set(ftyp, 0);
|
|
161
|
+
result.set(moov, ftyp.byteLength);
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Movie metadata box
|
|
166
|
+
static moov(meta) {
|
|
167
|
+
let mvhd = MP4.mvhd(meta.timescale, meta.duration);
|
|
168
|
+
let trak = MP4.trak(meta);
|
|
169
|
+
let mvex = MP4.mvex(meta);
|
|
170
|
+
return MP4.box(MP4.types.moov, mvhd, trak, mvex);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Movie header box
|
|
174
|
+
static mvhd(timescale, duration) {
|
|
175
|
+
return MP4.box(MP4.types.mvhd, new Uint8Array([
|
|
176
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
177
|
+
0x00, 0x00, 0x00, 0x00, // creation_time
|
|
178
|
+
0x00, 0x00, 0x00, 0x00, // modification_time
|
|
179
|
+
(timescale >>> 24) & 0xFF, // timescale: 4 bytes
|
|
180
|
+
(timescale >>> 16) & 0xFF,
|
|
181
|
+
(timescale >>> 8) & 0xFF,
|
|
182
|
+
(timescale) & 0xFF,
|
|
183
|
+
(duration >>> 24) & 0xFF, // duration: 4 bytes
|
|
184
|
+
(duration >>> 16) & 0xFF,
|
|
185
|
+
(duration >>> 8) & 0xFF,
|
|
186
|
+
(duration) & 0xFF,
|
|
187
|
+
0x00, 0x01, 0x00, 0x00, // Preferred rate: 1.0
|
|
188
|
+
0x01, 0x00, 0x00, 0x00, // PreferredVolume(1.0, 2bytes) + reserved(2bytes)
|
|
189
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 4 + 4 bytes
|
|
190
|
+
0x00, 0x00, 0x00, 0x00,
|
|
191
|
+
0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----
|
|
192
|
+
0x00, 0x00, 0x00, 0x00,
|
|
193
|
+
0x00, 0x00, 0x00, 0x00,
|
|
194
|
+
0x00, 0x00, 0x00, 0x00,
|
|
195
|
+
0x00, 0x01, 0x00, 0x00,
|
|
196
|
+
0x00, 0x00, 0x00, 0x00,
|
|
197
|
+
0x00, 0x00, 0x00, 0x00,
|
|
198
|
+
0x00, 0x00, 0x00, 0x00,
|
|
199
|
+
0x40, 0x00, 0x00, 0x00, // ----end composition matrix----
|
|
200
|
+
0x00, 0x00, 0x00, 0x00, // ----begin pre_defined 6 * 4 bytes----
|
|
201
|
+
0x00, 0x00, 0x00, 0x00,
|
|
202
|
+
0x00, 0x00, 0x00, 0x00,
|
|
203
|
+
0x00, 0x00, 0x00, 0x00,
|
|
204
|
+
0x00, 0x00, 0x00, 0x00,
|
|
205
|
+
0x00, 0x00, 0x00, 0x00, // ----end pre_defined 6 * 4 bytes----
|
|
206
|
+
0xFF, 0xFF, 0xFF, 0xFF // next_track_ID
|
|
207
|
+
]));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Track box
|
|
211
|
+
static trak(meta) {
|
|
212
|
+
return MP4.box(MP4.types.trak, MP4.tkhd(meta), MP4.mdia(meta));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Track header box
|
|
216
|
+
static tkhd(meta) {
|
|
217
|
+
let trackId = meta.id, duration = meta.duration;
|
|
218
|
+
let width = meta.presentWidth, height = meta.presentHeight;
|
|
219
|
+
|
|
220
|
+
return MP4.box(MP4.types.tkhd, new Uint8Array([
|
|
221
|
+
0x00, 0x00, 0x00, 0x07, // version(0) + flags
|
|
222
|
+
0x00, 0x00, 0x00, 0x00, // creation_time
|
|
223
|
+
0x00, 0x00, 0x00, 0x00, // modification_time
|
|
224
|
+
(trackId >>> 24) & 0xFF, // track_ID: 4 bytes
|
|
225
|
+
(trackId >>> 16) & 0xFF,
|
|
226
|
+
(trackId >>> 8) & 0xFF,
|
|
227
|
+
(trackId) & 0xFF,
|
|
228
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
|
|
229
|
+
(duration >>> 24) & 0xFF, // duration: 4 bytes
|
|
230
|
+
(duration >>> 16) & 0xFF,
|
|
231
|
+
(duration >>> 8) & 0xFF,
|
|
232
|
+
(duration) & 0xFF,
|
|
233
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
234
|
+
0x00, 0x00, 0x00, 0x00,
|
|
235
|
+
0x00, 0x00, 0x00, 0x00, // layer(2bytes) + alternate_group(2bytes)
|
|
236
|
+
0x00, 0x00, 0x00, 0x00, // volume(2bytes) + reserved(2bytes)
|
|
237
|
+
0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----
|
|
238
|
+
0x00, 0x00, 0x00, 0x00,
|
|
239
|
+
0x00, 0x00, 0x00, 0x00,
|
|
240
|
+
0x00, 0x00, 0x00, 0x00,
|
|
241
|
+
0x00, 0x01, 0x00, 0x00,
|
|
242
|
+
0x00, 0x00, 0x00, 0x00,
|
|
243
|
+
0x00, 0x00, 0x00, 0x00,
|
|
244
|
+
0x00, 0x00, 0x00, 0x00,
|
|
245
|
+
0x40, 0x00, 0x00, 0x00, // ----end composition matrix----
|
|
246
|
+
(width >>> 8) & 0xFF, // width and height
|
|
247
|
+
(width) & 0xFF,
|
|
248
|
+
0x00, 0x00,
|
|
249
|
+
(height >>> 8) & 0xFF,
|
|
250
|
+
(height) & 0xFF,
|
|
251
|
+
0x00, 0x00
|
|
252
|
+
]));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Media Box
|
|
256
|
+
static mdia(meta) {
|
|
257
|
+
return MP4.box(MP4.types.mdia, MP4.mdhd(meta), MP4.hdlr(meta), MP4.minf(meta));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Media header box
|
|
261
|
+
static mdhd(meta) {
|
|
262
|
+
let timescale = meta.timescale;
|
|
263
|
+
let duration = meta.duration;
|
|
264
|
+
return MP4.box(MP4.types.mdhd, new Uint8Array([
|
|
265
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
266
|
+
0x00, 0x00, 0x00, 0x00, // creation_time
|
|
267
|
+
0x00, 0x00, 0x00, 0x00, // modification_time
|
|
268
|
+
(timescale >>> 24) & 0xFF, // timescale: 4 bytes
|
|
269
|
+
(timescale >>> 16) & 0xFF,
|
|
270
|
+
(timescale >>> 8) & 0xFF,
|
|
271
|
+
(timescale) & 0xFF,
|
|
272
|
+
(duration >>> 24) & 0xFF, // duration: 4 bytes
|
|
273
|
+
(duration >>> 16) & 0xFF,
|
|
274
|
+
(duration >>> 8) & 0xFF,
|
|
275
|
+
(duration) & 0xFF,
|
|
276
|
+
0x55, 0xC4, // language: und (undetermined)
|
|
277
|
+
0x00, 0x00 // pre_defined = 0
|
|
278
|
+
]));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Media handler reference box
|
|
282
|
+
static hdlr(meta) {
|
|
283
|
+
let data = null;
|
|
284
|
+
if (meta.type === 'audio') {
|
|
285
|
+
data = MP4.constants.HDLR_AUDIO;
|
|
286
|
+
} else {
|
|
287
|
+
data = MP4.constants.HDLR_VIDEO;
|
|
288
|
+
}
|
|
289
|
+
return MP4.box(MP4.types.hdlr, data);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Media infomation box
|
|
293
|
+
static minf(meta) {
|
|
294
|
+
let xmhd = null;
|
|
295
|
+
if (meta.type === 'audio') {
|
|
296
|
+
xmhd = MP4.box(MP4.types.smhd, MP4.constants.SMHD);
|
|
297
|
+
} else {
|
|
298
|
+
xmhd = MP4.box(MP4.types.vmhd, MP4.constants.VMHD);
|
|
299
|
+
}
|
|
300
|
+
return MP4.box(MP4.types.minf, xmhd, MP4.dinf(), MP4.stbl(meta));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Data infomation box
|
|
304
|
+
static dinf() {
|
|
305
|
+
let result = MP4.box(MP4.types.dinf,
|
|
306
|
+
MP4.box(MP4.types.dref, MP4.constants.DREF)
|
|
307
|
+
);
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Sample table box
|
|
312
|
+
static stbl(meta) {
|
|
313
|
+
let result = MP4.box(MP4.types.stbl, // type: stbl
|
|
314
|
+
MP4.stsd(meta), // Sample Description Table
|
|
315
|
+
MP4.box(MP4.types.stts, MP4.constants.STTS), // Time-To-Sample
|
|
316
|
+
MP4.box(MP4.types.stsc, MP4.constants.STSC), // Sample-To-Chunk
|
|
317
|
+
MP4.box(MP4.types.stsz, MP4.constants.STSZ), // Sample size
|
|
318
|
+
MP4.box(MP4.types.stco, MP4.constants.STCO) // Chunk offset
|
|
319
|
+
);
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Sample description box
|
|
324
|
+
static stsd(meta) {
|
|
325
|
+
if (meta.type === 'audio') {
|
|
326
|
+
if (meta.codec === 'mp3') {
|
|
327
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp3(meta));
|
|
328
|
+
} else if (meta.codec === 'ac-3') {
|
|
329
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.ac3(meta));
|
|
330
|
+
} else if (meta.codec === 'ec-3') {
|
|
331
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.ec3(meta));
|
|
332
|
+
} else if(meta.codec === 'opus') {
|
|
333
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.Opus(meta));
|
|
334
|
+
} else if (meta.codec == 'flac') {
|
|
335
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.fLaC(meta));
|
|
336
|
+
} else if (meta.codec == 'ipcm') {
|
|
337
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.ipcm(meta));
|
|
338
|
+
}
|
|
339
|
+
// else: aac -> mp4a
|
|
340
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp4a(meta));
|
|
341
|
+
} else if (meta.type === 'video' && meta.codec.startsWith('hvc1')) {
|
|
342
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.hvc1(meta));
|
|
343
|
+
} else if (meta.type === 'video' && meta.codec.startsWith('av01')) {
|
|
344
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.av01(meta));
|
|
345
|
+
} else {
|
|
346
|
+
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.avc1(meta));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
static mp3(meta) {
|
|
351
|
+
let channelCount = meta.channelCount;
|
|
352
|
+
let sampleRate = meta.audioSampleRate;
|
|
353
|
+
|
|
354
|
+
let data = new Uint8Array([
|
|
355
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
356
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
357
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
358
|
+
0x00, 0x00, 0x00, 0x00,
|
|
359
|
+
0x00, channelCount, // channelCount(2)
|
|
360
|
+
0x00, 0x10, // sampleSize(2)
|
|
361
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
362
|
+
(sampleRate >>> 8) & 0xFF, // Audio sample rate
|
|
363
|
+
(sampleRate) & 0xFF,
|
|
364
|
+
0x00, 0x00
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
return MP4.box(MP4.types['.mp3'], data);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
static mp4a(meta) {
|
|
371
|
+
let channelCount = meta.channelCount;
|
|
372
|
+
let sampleRate = meta.audioSampleRate;
|
|
373
|
+
|
|
374
|
+
let data = new Uint8Array([
|
|
375
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
376
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
377
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
378
|
+
0x00, 0x00, 0x00, 0x00,
|
|
379
|
+
0x00, channelCount, // channelCount(2)
|
|
380
|
+
0x00, 0x10, // sampleSize(2)
|
|
381
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
382
|
+
(sampleRate >>> 8) & 0xFF, // Audio sample rate
|
|
383
|
+
(sampleRate) & 0xFF,
|
|
384
|
+
0x00, 0x00
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
return MP4.box(MP4.types.mp4a, data, MP4.esds(meta));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
static ac3(meta) {
|
|
391
|
+
let channelCount = meta.channelCount;
|
|
392
|
+
let sampleRate = meta.audioSampleRate;
|
|
393
|
+
|
|
394
|
+
let data = new Uint8Array([
|
|
395
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
396
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
397
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
398
|
+
0x00, 0x00, 0x00, 0x00,
|
|
399
|
+
0x00, channelCount, // channelCount(2)
|
|
400
|
+
0x00, 0x10, // sampleSize(2)
|
|
401
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
402
|
+
(sampleRate >>> 8) & 0xFF, // Audio sample rate
|
|
403
|
+
(sampleRate) & 0xFF,
|
|
404
|
+
0x00, 0x00
|
|
405
|
+
]);
|
|
406
|
+
|
|
407
|
+
return MP4.box(MP4.types['ac-3'], data, MP4.box(MP4.types.dac3, new Uint8Array(meta.config)));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
static ec3(meta) {
|
|
411
|
+
let channelCount = meta.channelCount;
|
|
412
|
+
let sampleRate = meta.audioSampleRate;
|
|
413
|
+
|
|
414
|
+
let data = new Uint8Array([
|
|
415
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
416
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
417
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
418
|
+
0x00, 0x00, 0x00, 0x00,
|
|
419
|
+
0x00, channelCount, // channelCount(2)
|
|
420
|
+
0x00, 0x10, // sampleSize(2)
|
|
421
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
422
|
+
(sampleRate >>> 8) & 0xFF, // Audio sample rate
|
|
423
|
+
(sampleRate) & 0xFF,
|
|
424
|
+
0x00, 0x00
|
|
425
|
+
]);
|
|
426
|
+
|
|
427
|
+
return MP4.box(MP4.types['ec-3'], data, MP4.box(MP4.types.dec3, new Uint8Array(meta.config)));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
static esds(meta) {
|
|
431
|
+
let config = meta.config || [];
|
|
432
|
+
let configSize = config.length;
|
|
433
|
+
let data = new Uint8Array([
|
|
434
|
+
0x00, 0x00, 0x00, 0x00, // version 0 + flags
|
|
435
|
+
|
|
436
|
+
0x03, // descriptor_type
|
|
437
|
+
0x17 + configSize, // length3
|
|
438
|
+
0x00, 0x01, // es_id
|
|
439
|
+
0x00, // stream_priority
|
|
440
|
+
|
|
441
|
+
0x04, // descriptor_type
|
|
442
|
+
0x0F + configSize, // length
|
|
443
|
+
0x40, // codec: mpeg4_audio
|
|
444
|
+
0x15, // stream_type: Audio
|
|
445
|
+
0x00, 0x00, 0x00, // buffer_size
|
|
446
|
+
0x00, 0x00, 0x00, 0x00, // maxBitrate
|
|
447
|
+
0x00, 0x00, 0x00, 0x00, // avgBitrate
|
|
448
|
+
|
|
449
|
+
0x05 // descriptor_type
|
|
450
|
+
].concat([
|
|
451
|
+
configSize
|
|
452
|
+
]).concat(
|
|
453
|
+
config
|
|
454
|
+
).concat([
|
|
455
|
+
0x06, 0x01, 0x02 // GASpecificConfig
|
|
456
|
+
]));
|
|
457
|
+
return MP4.box(MP4.types.esds, data);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
static Opus(meta) {
|
|
461
|
+
let channelCount = meta.channelCount;
|
|
462
|
+
let sampleRate = meta.audioSampleRate;
|
|
463
|
+
|
|
464
|
+
let data = new Uint8Array([
|
|
465
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
466
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
467
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
468
|
+
0x00, 0x00, 0x00, 0x00,
|
|
469
|
+
0x00, channelCount, // channelCount(2)
|
|
470
|
+
0x00, 0x10, // sampleSize(2)
|
|
471
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
472
|
+
(sampleRate >>> 8) & 0xFF, // Audio sample rate
|
|
473
|
+
(sampleRate) & 0xFF,
|
|
474
|
+
0x00, 0x00
|
|
475
|
+
]);
|
|
476
|
+
|
|
477
|
+
return MP4.box(MP4.types.Opus, data, MP4.dOps(meta));
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
static dOps(meta) {
|
|
481
|
+
let channelCount = meta.channelCount;
|
|
482
|
+
let channelConfigCode = meta.channelConfigCode;
|
|
483
|
+
let sampleRate = meta.audioSampleRate;
|
|
484
|
+
|
|
485
|
+
if (meta.config) {
|
|
486
|
+
return MP4.box(MP4.types.dOps, meta.config);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let mapping = [];
|
|
490
|
+
switch (channelConfigCode) {
|
|
491
|
+
case 0x01:
|
|
492
|
+
case 0x02:
|
|
493
|
+
mapping = [0x0];
|
|
494
|
+
break;
|
|
495
|
+
case 0x00: // dualmono
|
|
496
|
+
mapping = [0xFF, 1, 1, 0, 1];
|
|
497
|
+
break;
|
|
498
|
+
case 0x80: // dualmono
|
|
499
|
+
mapping = [0xFF, 2, 0, 0, 1];
|
|
500
|
+
break;
|
|
501
|
+
case 0x03:
|
|
502
|
+
mapping = [0x01, 2, 1, 0, 2, 1];
|
|
503
|
+
break;
|
|
504
|
+
case 0x04:
|
|
505
|
+
mapping = [0x01, 2, 2, 0, 1, 2, 3];
|
|
506
|
+
break;
|
|
507
|
+
case 0x05:
|
|
508
|
+
mapping = [0x01, 3, 2, 0, 4, 1, 2, 3];
|
|
509
|
+
break;
|
|
510
|
+
case 0x06:
|
|
511
|
+
mapping = [0x01, 4, 2, 0, 4, 1, 2, 3, 5];
|
|
512
|
+
break;
|
|
513
|
+
case 0x07:
|
|
514
|
+
mapping = [0x01, 4, 2, 0, 4, 1, 2, 3, 5, 6];
|
|
515
|
+
break;
|
|
516
|
+
case 0x08:
|
|
517
|
+
mapping = [0x01, 5, 3, 0, 6, 1, 2, 3, 4, 5, 7];
|
|
518
|
+
break;
|
|
519
|
+
case 0x82:
|
|
520
|
+
mapping = [0x01, 1, 2, 0, 1];
|
|
521
|
+
break;
|
|
522
|
+
case 0x83:
|
|
523
|
+
mapping = [0x01, 1, 3, 0, 1, 2];
|
|
524
|
+
break;
|
|
525
|
+
case 0x84:
|
|
526
|
+
mapping = [0x01, 1, 4, 0, 1, 2, 3];
|
|
527
|
+
break;
|
|
528
|
+
case 0x85:
|
|
529
|
+
mapping = [0x01, 1, 5, 0, 1, 2, 3, 4];
|
|
530
|
+
break;
|
|
531
|
+
case 0x86:
|
|
532
|
+
mapping = [0x01, 1, 6, 0, 1, 2, 3, 4, 5];
|
|
533
|
+
break;
|
|
534
|
+
case 0x87:
|
|
535
|
+
mapping = [0x01, 1, 7, 0, 1, 2, 3, 4, 5, 6];
|
|
536
|
+
break;
|
|
537
|
+
case 0x88:
|
|
538
|
+
mapping = [0x01, 1, 8, 0, 1, 2, 3, 4, 5, 6, 7];
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
let data = new Uint8Array([
|
|
543
|
+
0x00, // Version (1)
|
|
544
|
+
channelCount, // OutputChannelCount: 2
|
|
545
|
+
0x00, 0x00, // PreSkip: 2
|
|
546
|
+
(sampleRate >>> 24) & 0xFF, // Audio sample rate: 4
|
|
547
|
+
(sampleRate >>> 17) & 0xFF,
|
|
548
|
+
(sampleRate >>> 8) & 0xFF,
|
|
549
|
+
(sampleRate >>> 0) & 0xFF,
|
|
550
|
+
0x00, 0x00, // Global Gain : 2
|
|
551
|
+
... mapping
|
|
552
|
+
]);
|
|
553
|
+
return MP4.box(MP4.types.dOps, data);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
static fLaC(meta) {
|
|
557
|
+
let channelCount = meta.channelCount;
|
|
558
|
+
let sampleRate = Math.min(meta.audioSampleRate, 65535);
|
|
559
|
+
let sampleSize = meta.sampleSize;
|
|
560
|
+
|
|
561
|
+
let data = new Uint8Array([
|
|
562
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
563
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
564
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
565
|
+
0x00, 0x00, 0x00, 0x00,
|
|
566
|
+
0x00, channelCount, // channelCount(2)
|
|
567
|
+
0x00, (sampleSize), // sampleSize(2)
|
|
568
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
569
|
+
(sampleRate >>> 8) & 0xFF, // Audio sample rate
|
|
570
|
+
(sampleRate) & 0xFF,
|
|
571
|
+
0x00, 0x00
|
|
572
|
+
]);
|
|
573
|
+
|
|
574
|
+
return MP4.box(MP4.types.fLaC, data, MP4.dfLa(meta));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
static dfLa(meta) {
|
|
578
|
+
let data = new Uint8Array([
|
|
579
|
+
0x00, 0x00, 0x00, 0x00, // version, flag
|
|
580
|
+
... meta.config
|
|
581
|
+
]);
|
|
582
|
+
return MP4.box(MP4.types.dfLa, data);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
static ipcm(meta) {
|
|
586
|
+
let channelCount = meta.channelCount;
|
|
587
|
+
let sampleRate = Math.min(meta.audioSampleRate, 65535);
|
|
588
|
+
let sampleSize = meta.sampleSize;
|
|
589
|
+
|
|
590
|
+
let data = new Uint8Array([
|
|
591
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
592
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
593
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
|
594
|
+
0x00, 0x00, 0x00, 0x00,
|
|
595
|
+
0x00, channelCount, // channelCount(2)
|
|
596
|
+
0x00, (sampleSize), // sampleSize(2)
|
|
597
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
598
|
+
(sampleRate >>> 8) & 0xFF, // Audio sample rate
|
|
599
|
+
(sampleRate) & 0xFF,
|
|
600
|
+
0x00, 0x00
|
|
601
|
+
]);
|
|
602
|
+
|
|
603
|
+
if (meta.channelCount === 1) {
|
|
604
|
+
return MP4.box(MP4.types.ipcm, data, MP4.pcmC(meta));
|
|
605
|
+
} else {
|
|
606
|
+
return MP4.box(MP4.types.ipcm, data, MP4.chnl(meta), MP4.pcmC(meta));
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
static chnl(meta) {
|
|
611
|
+
let data = new Uint8Array([
|
|
612
|
+
0x00, 0x00, 0x00, 0x00, // version, flag
|
|
613
|
+
0x01, // Channel Based Layout
|
|
614
|
+
meta.channelCount, // AudioConfiguration
|
|
615
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // omittedChannelsMap
|
|
616
|
+
]);
|
|
617
|
+
return MP4.box(MP4.types.chnl, data);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
static pcmC(meta) {
|
|
621
|
+
let littleEndian = meta.littleEndian ? 0x01 : 0x00
|
|
622
|
+
let sampleSize = meta.sampleSize;
|
|
623
|
+
let data = new Uint8Array([
|
|
624
|
+
0x00, 0x00, 0x00, 0x00, // version, flag
|
|
625
|
+
littleEndian, sampleSize
|
|
626
|
+
]);
|
|
627
|
+
return MP4.box(MP4.types.pcmC, data);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
static avc1(meta) {
|
|
631
|
+
let avcc = meta.avcc;
|
|
632
|
+
let width = meta.codecWidth, height = meta.codecHeight;
|
|
633
|
+
|
|
634
|
+
let data = new Uint8Array([
|
|
635
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
636
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
637
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2)
|
|
638
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes
|
|
639
|
+
0x00, 0x00, 0x00, 0x00,
|
|
640
|
+
0x00, 0x00, 0x00, 0x00,
|
|
641
|
+
(width >>> 8) & 0xFF, // width: 2 bytes
|
|
642
|
+
(width) & 0xFF,
|
|
643
|
+
(height >>> 8) & 0xFF, // height: 2 bytes
|
|
644
|
+
(height) & 0xFF,
|
|
645
|
+
0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes
|
|
646
|
+
0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes
|
|
647
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
|
|
648
|
+
0x00, 0x01, // frame_count
|
|
649
|
+
0x0A, // strlen
|
|
650
|
+
0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes
|
|
651
|
+
0x66, 0x6C, 0x76, 0x2E,
|
|
652
|
+
0x6A, 0x73, 0x00, 0x00,
|
|
653
|
+
0x00, 0x00, 0x00, 0x00,
|
|
654
|
+
0x00, 0x00, 0x00, 0x00,
|
|
655
|
+
0x00, 0x00, 0x00, 0x00,
|
|
656
|
+
0x00, 0x00, 0x00, 0x00,
|
|
657
|
+
0x00, 0x00, 0x00,
|
|
658
|
+
0x00, 0x18, // depth
|
|
659
|
+
0xFF, 0xFF // pre_defined = -1
|
|
660
|
+
]);
|
|
661
|
+
return MP4.box(MP4.types.avc1, data, MP4.box(MP4.types.avcC, avcc));
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
static hvc1(meta) {
|
|
665
|
+
let hvcc = meta.hvcc;
|
|
666
|
+
let width = meta.codecWidth, height = meta.codecHeight;
|
|
667
|
+
|
|
668
|
+
let data = new Uint8Array([
|
|
669
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
670
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
671
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2)
|
|
672
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes
|
|
673
|
+
0x00, 0x00, 0x00, 0x00,
|
|
674
|
+
0x00, 0x00, 0x00, 0x00,
|
|
675
|
+
(width >>> 8) & 0xFF, // width: 2 bytes
|
|
676
|
+
(width) & 0xFF,
|
|
677
|
+
(height >>> 8) & 0xFF, // height: 2 bytes
|
|
678
|
+
(height) & 0xFF,
|
|
679
|
+
0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes
|
|
680
|
+
0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes
|
|
681
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
|
|
682
|
+
0x00, 0x01, // frame_count
|
|
683
|
+
0x0A, // strlen
|
|
684
|
+
0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes
|
|
685
|
+
0x66, 0x6C, 0x76, 0x2E,
|
|
686
|
+
0x6A, 0x73, 0x00, 0x00,
|
|
687
|
+
0x00, 0x00, 0x00, 0x00,
|
|
688
|
+
0x00, 0x00, 0x00, 0x00,
|
|
689
|
+
0x00, 0x00, 0x00, 0x00,
|
|
690
|
+
0x00, 0x00, 0x00, 0x00,
|
|
691
|
+
0x00, 0x00, 0x00,
|
|
692
|
+
0x00, 0x18, // depth
|
|
693
|
+
0xFF, 0xFF // pre_defined = -1
|
|
694
|
+
]);
|
|
695
|
+
return MP4.box(MP4.types.hvc1, data, MP4.box(MP4.types.hvcC, hvcc));
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
static av01(meta) {
|
|
699
|
+
let av1c = meta.av1c;
|
|
700
|
+
let width = meta.codecWidth || 192, height = meta.codecHeight || 108;
|
|
701
|
+
|
|
702
|
+
let data = new Uint8Array([
|
|
703
|
+
0x00, 0x00, 0x00, 0x00, // reserved(4)
|
|
704
|
+
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
|
705
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2)
|
|
706
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes
|
|
707
|
+
0x00, 0x00, 0x00, 0x00,
|
|
708
|
+
0x00, 0x00, 0x00, 0x00,
|
|
709
|
+
(width >>> 8) & 0xFF, // width: 2 bytes
|
|
710
|
+
(width) & 0xFF,
|
|
711
|
+
(height >>> 8) & 0xFF, // height: 2 bytes
|
|
712
|
+
(height) & 0xFF,
|
|
713
|
+
0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes
|
|
714
|
+
0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes
|
|
715
|
+
0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
|
|
716
|
+
0x00, 0x01, // frame_count
|
|
717
|
+
0x0A, // strlen
|
|
718
|
+
0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes
|
|
719
|
+
0x66, 0x6C, 0x76, 0x2E,
|
|
720
|
+
0x6A, 0x73, 0x00, 0x00,
|
|
721
|
+
0x00, 0x00, 0x00, 0x00,
|
|
722
|
+
0x00, 0x00, 0x00, 0x00,
|
|
723
|
+
0x00, 0x00, 0x00, 0x00,
|
|
724
|
+
0x00, 0x00, 0x00, 0x00,
|
|
725
|
+
0x00, 0x00, 0x00,
|
|
726
|
+
0x00, 0x18, // depth
|
|
727
|
+
0xFF, 0xFF // pre_defined = -1
|
|
728
|
+
]);
|
|
729
|
+
return MP4.box(MP4.types.av01, data, MP4.box(MP4.types.av1C, av1c));
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// Movie Extends box
|
|
733
|
+
static mvex(meta) {
|
|
734
|
+
return MP4.box(MP4.types.mvex, MP4.trex(meta));
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Track Extends box
|
|
738
|
+
static trex(meta) {
|
|
739
|
+
let trackId = meta.id;
|
|
740
|
+
let data = new Uint8Array([
|
|
741
|
+
0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
|
742
|
+
(trackId >>> 24) & 0xFF, // track_ID
|
|
743
|
+
(trackId >>> 16) & 0xFF,
|
|
744
|
+
(trackId >>> 8) & 0xFF,
|
|
745
|
+
(trackId) & 0xFF,
|
|
746
|
+
0x00, 0x00, 0x00, 0x01, // default_sample_description_index
|
|
747
|
+
0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
|
748
|
+
0x00, 0x00, 0x00, 0x00, // default_sample_size
|
|
749
|
+
0x00, 0x01, 0x00, 0x01 // default_sample_flags
|
|
750
|
+
]);
|
|
751
|
+
return MP4.box(MP4.types.trex, data);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Movie fragment box
|
|
755
|
+
static moof(track, baseMediaDecodeTime) {
|
|
756
|
+
return MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), MP4.traf(track, baseMediaDecodeTime));
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
static mfhd(sequenceNumber) {
|
|
760
|
+
let data = new Uint8Array([
|
|
761
|
+
0x00, 0x00, 0x00, 0x00,
|
|
762
|
+
(sequenceNumber >>> 24) & 0xFF, // sequence_number: int32
|
|
763
|
+
(sequenceNumber >>> 16) & 0xFF,
|
|
764
|
+
(sequenceNumber >>> 8) & 0xFF,
|
|
765
|
+
(sequenceNumber) & 0xFF
|
|
766
|
+
]);
|
|
767
|
+
return MP4.box(MP4.types.mfhd, data);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Track fragment box
|
|
771
|
+
static traf(track, baseMediaDecodeTime) {
|
|
772
|
+
let trackId = track.id;
|
|
773
|
+
|
|
774
|
+
// Track fragment header box
|
|
775
|
+
let tfhd = MP4.box(MP4.types.tfhd, new Uint8Array([
|
|
776
|
+
0x00, 0x00, 0x00, 0x00, // version(0) & flags
|
|
777
|
+
(trackId >>> 24) & 0xFF, // track_ID
|
|
778
|
+
(trackId >>> 16) & 0xFF,
|
|
779
|
+
(trackId >>> 8) & 0xFF,
|
|
780
|
+
(trackId) & 0xFF
|
|
781
|
+
]));
|
|
782
|
+
// Track Fragment Decode Time
|
|
783
|
+
let tfdt = MP4.box(MP4.types.tfdt, new Uint8Array([
|
|
784
|
+
0x00, 0x00, 0x00, 0x00, // version(0) & flags
|
|
785
|
+
(baseMediaDecodeTime >>> 24) & 0xFF, // baseMediaDecodeTime: int32
|
|
786
|
+
(baseMediaDecodeTime >>> 16) & 0xFF,
|
|
787
|
+
(baseMediaDecodeTime >>> 8) & 0xFF,
|
|
788
|
+
(baseMediaDecodeTime) & 0xFF
|
|
789
|
+
]));
|
|
790
|
+
let sdtp = MP4.sdtp(track);
|
|
791
|
+
let trun = MP4.trun(track, sdtp.byteLength + 16 + 16 + 8 + 16 + 8 + 8);
|
|
792
|
+
|
|
793
|
+
return MP4.box(MP4.types.traf, tfhd, tfdt, trun, sdtp);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Sample Dependency Type box
|
|
797
|
+
static sdtp(track) {
|
|
798
|
+
let samples = track.samples || [];
|
|
799
|
+
let sampleCount = samples.length;
|
|
800
|
+
let data = new Uint8Array(4 + sampleCount);
|
|
801
|
+
// 0~4 bytes: version(0) & flags
|
|
802
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
803
|
+
let flags = samples[i].flags;
|
|
804
|
+
data[i + 4] = (flags.isLeading << 6) // is_leading: 2 (bit)
|
|
805
|
+
| (flags.dependsOn << 4) // sample_depends_on
|
|
806
|
+
| (flags.isDependedOn << 2) // sample_is_depended_on
|
|
807
|
+
| (flags.hasRedundancy); // sample_has_redundancy
|
|
808
|
+
}
|
|
809
|
+
return MP4.box(MP4.types.sdtp, data);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Track fragment run box
|
|
813
|
+
static trun(track, offset) {
|
|
814
|
+
let samples = track.samples || [];
|
|
815
|
+
let sampleCount = samples.length;
|
|
816
|
+
let dataSize = 12 + 16 * sampleCount;
|
|
817
|
+
let data = new Uint8Array(dataSize);
|
|
818
|
+
offset += 8 + dataSize;
|
|
819
|
+
|
|
820
|
+
data.set([
|
|
821
|
+
0x00, 0x00, 0x0F, 0x01, // version(0) & flags
|
|
822
|
+
(sampleCount >>> 24) & 0xFF, // sample_count
|
|
823
|
+
(sampleCount >>> 16) & 0xFF,
|
|
824
|
+
(sampleCount >>> 8) & 0xFF,
|
|
825
|
+
(sampleCount) & 0xFF,
|
|
826
|
+
(offset >>> 24) & 0xFF, // data_offset
|
|
827
|
+
(offset >>> 16) & 0xFF,
|
|
828
|
+
(offset >>> 8) & 0xFF,
|
|
829
|
+
(offset) & 0xFF
|
|
830
|
+
], 0);
|
|
831
|
+
|
|
832
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
833
|
+
let duration = samples[i].duration;
|
|
834
|
+
let size = samples[i].size;
|
|
835
|
+
let flags = samples[i].flags;
|
|
836
|
+
let cts = samples[i].cts;
|
|
837
|
+
data.set([
|
|
838
|
+
(duration >>> 24) & 0xFF, // sample_duration
|
|
839
|
+
(duration >>> 16) & 0xFF,
|
|
840
|
+
(duration >>> 8) & 0xFF,
|
|
841
|
+
(duration) & 0xFF,
|
|
842
|
+
(size >>> 24) & 0xFF, // sample_size
|
|
843
|
+
(size >>> 16) & 0xFF,
|
|
844
|
+
(size >>> 8) & 0xFF,
|
|
845
|
+
(size) & 0xFF,
|
|
846
|
+
(flags.isLeading << 2) | flags.dependsOn, // sample_flags
|
|
847
|
+
(flags.isDependedOn << 6) | (flags.hasRedundancy << 4) | flags.isNonSync,
|
|
848
|
+
0x00, 0x00, // sample_degradation_priority
|
|
849
|
+
(cts >>> 24) & 0xFF, // sample_composition_time_offset
|
|
850
|
+
(cts >>> 16) & 0xFF,
|
|
851
|
+
(cts >>> 8) & 0xFF,
|
|
852
|
+
(cts) & 0xFF
|
|
853
|
+
], 12 + 16 * i);
|
|
854
|
+
}
|
|
855
|
+
return MP4.box(MP4.types.trun, data);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
static mdat(data) {
|
|
859
|
+
return MP4.box(MP4.types.mdat, data);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
MP4.init();
|
|
865
|
+
|
|
866
|
+
export default MP4;
|