video-player-dd 2.0.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/base.css +13 -0
- package/index.ts +274 -0
- package/js/flv.js +12390 -0
- package/js/h265/H265Decoder.js +208 -0
- package/js/h265/H265Player.js +22 -0
- package/js/h265/libffmpeg_265.js +1 -0
- package/js/h265/libffmpeg_265.wasm +0 -0
- package/js/jmuxer.min.js +2546 -0
- package/js/quill.min.js +8 -0
- package/js/vue.runtime.min.js +6 -0
- package/package.json +24 -0
- package/player.ts +111 -0
- package/stream.ts +53 -0
- package/type.ts +30 -0
- package/utils/index.ts +159 -0
- package/utils/mitter.ts +3 -0
- package/video.ts +55 -0
package/js/jmuxer.min.js
ADDED
|
@@ -0,0 +1,2546 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global.JMuxer = factory());
|
|
5
|
+
}(this, (function () { 'use strict';
|
|
6
|
+
|
|
7
|
+
var logger = void 0;
|
|
8
|
+
var errorLogger = void 0;
|
|
9
|
+
|
|
10
|
+
function setLogger() {
|
|
11
|
+
/*eslint-disable */
|
|
12
|
+
logger = console.log;
|
|
13
|
+
errorLogger = console.error;
|
|
14
|
+
/*eslint-enable */
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function log(message) {
|
|
20
|
+
if (logger) {
|
|
21
|
+
for (var _len = arguments.length, optionalParams = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
22
|
+
optionalParams[_key - 1] = arguments[_key];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
logger.apply(undefined, [message].concat(optionalParams));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function error(message) {
|
|
29
|
+
if (errorLogger) {
|
|
30
|
+
for (var _len2 = arguments.length, optionalParams = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
|
31
|
+
optionalParams[_key2 - 1] = arguments[_key2];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
errorLogger.apply(undefined, [message].concat(optionalParams));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var asyncGenerator = function () {
|
|
39
|
+
function AwaitValue(value) {
|
|
40
|
+
this.value = value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function AsyncGenerator(gen) {
|
|
44
|
+
var front, back;
|
|
45
|
+
|
|
46
|
+
function send(key, arg) {
|
|
47
|
+
return new Promise(function (resolve, reject) {
|
|
48
|
+
var request = {
|
|
49
|
+
key: key,
|
|
50
|
+
arg: arg,
|
|
51
|
+
resolve: resolve,
|
|
52
|
+
reject: reject,
|
|
53
|
+
next: null
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (back) {
|
|
57
|
+
back = back.next = request;
|
|
58
|
+
} else {
|
|
59
|
+
front = back = request;
|
|
60
|
+
resume(key, arg);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resume(key, arg) {
|
|
66
|
+
try {
|
|
67
|
+
var result = gen[key](arg);
|
|
68
|
+
var value = result.value;
|
|
69
|
+
|
|
70
|
+
if (value instanceof AwaitValue) {
|
|
71
|
+
Promise.resolve(value.value).then(function (arg) {
|
|
72
|
+
resume("next", arg);
|
|
73
|
+
}, function (arg) {
|
|
74
|
+
resume("throw", arg);
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
settle(result.done ? "return" : "normal", result.value);
|
|
78
|
+
}
|
|
79
|
+
} catch (err) {
|
|
80
|
+
settle("throw", err);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function settle(type, value) {
|
|
85
|
+
switch (type) {
|
|
86
|
+
case "return":
|
|
87
|
+
front.resolve({
|
|
88
|
+
value: value,
|
|
89
|
+
done: true
|
|
90
|
+
});
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
case "throw":
|
|
94
|
+
front.reject(value);
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
default:
|
|
98
|
+
front.resolve({
|
|
99
|
+
value: value,
|
|
100
|
+
done: false
|
|
101
|
+
});
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
front = front.next;
|
|
106
|
+
|
|
107
|
+
if (front) {
|
|
108
|
+
resume(front.key, front.arg);
|
|
109
|
+
} else {
|
|
110
|
+
back = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this._invoke = send;
|
|
115
|
+
|
|
116
|
+
if (typeof gen.return !== "function") {
|
|
117
|
+
this.return = undefined;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof Symbol === "function" && Symbol.asyncIterator) {
|
|
122
|
+
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
|
|
123
|
+
return this;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
AsyncGenerator.prototype.next = function (arg) {
|
|
128
|
+
return this._invoke("next", arg);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
AsyncGenerator.prototype.throw = function (arg) {
|
|
132
|
+
return this._invoke("throw", arg);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
AsyncGenerator.prototype.return = function (arg) {
|
|
136
|
+
return this._invoke("return", arg);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
wrap: function (fn) {
|
|
141
|
+
return function () {
|
|
142
|
+
return new AsyncGenerator(fn.apply(this, arguments));
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
await: function (value) {
|
|
146
|
+
return new AwaitValue(value);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}();
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
var classCallCheck = function (instance, Constructor) {
|
|
156
|
+
if (!(instance instanceof Constructor)) {
|
|
157
|
+
throw new TypeError("Cannot call a class as a function");
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
var createClass = function () {
|
|
162
|
+
function defineProperties(target, props) {
|
|
163
|
+
for (var i = 0; i < props.length; i++) {
|
|
164
|
+
var descriptor = props[i];
|
|
165
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
166
|
+
descriptor.configurable = true;
|
|
167
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
168
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return function (Constructor, protoProps, staticProps) {
|
|
173
|
+
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
|
174
|
+
if (staticProps) defineProperties(Constructor, staticProps);
|
|
175
|
+
return Constructor;
|
|
176
|
+
};
|
|
177
|
+
}();
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
var defineProperty = function (obj, key, value) {
|
|
184
|
+
if (key in obj) {
|
|
185
|
+
Object.defineProperty(obj, key, {
|
|
186
|
+
value: value,
|
|
187
|
+
enumerable: true,
|
|
188
|
+
configurable: true,
|
|
189
|
+
writable: true
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
obj[key] = value;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return obj;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
var inherits = function (subClass, superClass) {
|
|
201
|
+
if (typeof superClass !== "function" && superClass !== null) {
|
|
202
|
+
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
|
206
|
+
constructor: {
|
|
207
|
+
value: subClass,
|
|
208
|
+
enumerable: false,
|
|
209
|
+
writable: true,
|
|
210
|
+
configurable: true
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
var possibleConstructorReturn = function (self, call) {
|
|
227
|
+
if (!self) {
|
|
228
|
+
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return call && (typeof call === "object" || typeof call === "function") ? call : self;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
var NALU = function () {
|
|
235
|
+
createClass(NALU, null, [{
|
|
236
|
+
key: 'type',
|
|
237
|
+
value: function type(nalu) {
|
|
238
|
+
if (nalu.ntype in NALU.TYPES) {
|
|
239
|
+
return NALU.TYPES[nalu.ntype];
|
|
240
|
+
} else {
|
|
241
|
+
return 'UNKNOWN';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}, {
|
|
245
|
+
key: 'NDR',
|
|
246
|
+
get: function get$$1() {
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
}, {
|
|
250
|
+
key: 'IDR',
|
|
251
|
+
get: function get$$1() {
|
|
252
|
+
return 5;
|
|
253
|
+
}
|
|
254
|
+
}, {
|
|
255
|
+
key: 'SEI',
|
|
256
|
+
get: function get$$1() {
|
|
257
|
+
return 6;
|
|
258
|
+
}
|
|
259
|
+
}, {
|
|
260
|
+
key: 'SPS',
|
|
261
|
+
get: function get$$1() {
|
|
262
|
+
return 7;
|
|
263
|
+
}
|
|
264
|
+
}, {
|
|
265
|
+
key: 'PPS',
|
|
266
|
+
get: function get$$1() {
|
|
267
|
+
return 8;
|
|
268
|
+
}
|
|
269
|
+
}, {
|
|
270
|
+
key: 'AUD',
|
|
271
|
+
get: function get$$1() {
|
|
272
|
+
return 9;
|
|
273
|
+
}
|
|
274
|
+
}, {
|
|
275
|
+
key: 'TYPES',
|
|
276
|
+
get: function get$$1() {
|
|
277
|
+
var _ref;
|
|
278
|
+
|
|
279
|
+
return _ref = {}, babelHelpers.defineProperty(_ref, NALU.IDR, 'IDR'), babelHelpers.defineProperty(_ref, NALU.SEI, 'SEI'), babelHelpers.defineProperty(_ref, NALU.SPS, 'SPS'), babelHelpers.defineProperty(_ref, NALU.PPS, 'PPS'), babelHelpers.defineProperty(_ref, NALU.NDR, 'NDR'), babelHelpers.defineProperty(_ref, NALU.AUD, 'AUD'), _ref;
|
|
280
|
+
}
|
|
281
|
+
}]);
|
|
282
|
+
|
|
283
|
+
function NALU(data) {
|
|
284
|
+
classCallCheck(this, NALU);
|
|
285
|
+
|
|
286
|
+
this.payload = data;
|
|
287
|
+
this.nri = (this.payload[0] & 0x60) >> 5;
|
|
288
|
+
this.ntype = this.payload[0] & 0x1f;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
createClass(NALU, [{
|
|
292
|
+
key: 'toString',
|
|
293
|
+
value: function toString() {
|
|
294
|
+
return NALU.type(this) + ': NRI: ' + this.getNri();
|
|
295
|
+
}
|
|
296
|
+
}, {
|
|
297
|
+
key: 'getNri',
|
|
298
|
+
value: function getNri() {
|
|
299
|
+
return this.nri >> 6;
|
|
300
|
+
}
|
|
301
|
+
}, {
|
|
302
|
+
key: 'type',
|
|
303
|
+
value: function type() {
|
|
304
|
+
return this.ntype;
|
|
305
|
+
}
|
|
306
|
+
}, {
|
|
307
|
+
key: 'isKeyframe',
|
|
308
|
+
value: function isKeyframe() {
|
|
309
|
+
return this.ntype == NALU.IDR;
|
|
310
|
+
}
|
|
311
|
+
}, {
|
|
312
|
+
key: 'getSize',
|
|
313
|
+
value: function getSize() {
|
|
314
|
+
return 4 + this.payload.byteLength;
|
|
315
|
+
}
|
|
316
|
+
}, {
|
|
317
|
+
key: 'getData',
|
|
318
|
+
value: function getData() {
|
|
319
|
+
var result = new Uint8Array(this.getSize());
|
|
320
|
+
var view = new DataView(result.buffer);
|
|
321
|
+
view.setUint32(0, this.getSize() - 4);
|
|
322
|
+
|
|
323
|
+
result.set(this.payload, 4);
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
}]);
|
|
327
|
+
return NALU;
|
|
328
|
+
}();
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
|
|
332
|
+
*/
|
|
333
|
+
|
|
334
|
+
var ExpGolomb = function () {
|
|
335
|
+
function ExpGolomb(data) {
|
|
336
|
+
classCallCheck(this, ExpGolomb);
|
|
337
|
+
|
|
338
|
+
this.data = data;
|
|
339
|
+
this.index = 0;
|
|
340
|
+
this.bitLength = data.byteLength * 8;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
createClass(ExpGolomb, [{
|
|
344
|
+
key: "skipBits",
|
|
345
|
+
value: function skipBits(size) {
|
|
346
|
+
// console.log(` skip bits: size=${size}, ${this.index}.`);
|
|
347
|
+
if (this.bitsAvailable < size) {
|
|
348
|
+
//throw new Error('no bytes available');
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
this.index += size;
|
|
352
|
+
}
|
|
353
|
+
}, {
|
|
354
|
+
key: "readBits",
|
|
355
|
+
value: function readBits(size) {
|
|
356
|
+
var moveIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
357
|
+
|
|
358
|
+
// console.log(` read bits: size=${size}, ${this.index}.`);
|
|
359
|
+
var result = this.getBits(size, this.index, moveIndex);
|
|
360
|
+
// console.log(` read bits: result=${result}`);
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
}, {
|
|
364
|
+
key: "getBits",
|
|
365
|
+
value: function getBits(size, offsetBits) {
|
|
366
|
+
var moveIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
367
|
+
|
|
368
|
+
if (this.bitsAvailable < size) {
|
|
369
|
+
//throw new Error('no bytes available');
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
var offset = offsetBits % 8;
|
|
373
|
+
var byte = this.data[offsetBits / 8 | 0] & 0xff >>> offset;
|
|
374
|
+
var bits = 8 - offset;
|
|
375
|
+
if (bits >= size) {
|
|
376
|
+
if (moveIndex) {
|
|
377
|
+
this.index += size;
|
|
378
|
+
}
|
|
379
|
+
return byte >> bits - size;
|
|
380
|
+
} else {
|
|
381
|
+
if (moveIndex) {
|
|
382
|
+
this.index += bits;
|
|
383
|
+
}
|
|
384
|
+
var nextSize = size - bits;
|
|
385
|
+
return byte << nextSize | this.getBits(nextSize, offsetBits + bits, moveIndex);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}, {
|
|
389
|
+
key: "skipLZ",
|
|
390
|
+
value: function skipLZ() {
|
|
391
|
+
var leadingZeroCount = void 0;
|
|
392
|
+
for (leadingZeroCount = 0; leadingZeroCount < this.bitLength - this.index; ++leadingZeroCount) {
|
|
393
|
+
if (this.getBits(1, this.index + leadingZeroCount, false) !== 0) {
|
|
394
|
+
// console.log(` skip LZ : size=${leadingZeroCount}, ${this.index}.`);
|
|
395
|
+
this.index += leadingZeroCount;
|
|
396
|
+
return leadingZeroCount;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return leadingZeroCount;
|
|
400
|
+
}
|
|
401
|
+
}, {
|
|
402
|
+
key: "skipUEG",
|
|
403
|
+
value: function skipUEG() {
|
|
404
|
+
this.skipBits(1 + this.skipLZ());
|
|
405
|
+
}
|
|
406
|
+
}, {
|
|
407
|
+
key: "skipEG",
|
|
408
|
+
value: function skipEG() {
|
|
409
|
+
this.skipBits(1 + this.skipLZ());
|
|
410
|
+
}
|
|
411
|
+
}, {
|
|
412
|
+
key: "readUEG",
|
|
413
|
+
value: function readUEG() {
|
|
414
|
+
var prefix = this.skipLZ();
|
|
415
|
+
return this.readBits(prefix + 1) - 1;
|
|
416
|
+
}
|
|
417
|
+
}, {
|
|
418
|
+
key: "readEG",
|
|
419
|
+
value: function readEG() {
|
|
420
|
+
var value = this.readUEG();
|
|
421
|
+
if (0x01 & value) {
|
|
422
|
+
// the number is odd if the low order bit is set
|
|
423
|
+
return 1 + value >>> 1; // add 1 to make it even, and divide by 2
|
|
424
|
+
} else {
|
|
425
|
+
return -1 * (value >>> 1); // divide by two then make it negative
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}, {
|
|
429
|
+
key: "readBoolean",
|
|
430
|
+
value: function readBoolean() {
|
|
431
|
+
return this.readBits(1) === 1;
|
|
432
|
+
}
|
|
433
|
+
}, {
|
|
434
|
+
key: "readUByte",
|
|
435
|
+
value: function readUByte() {
|
|
436
|
+
var numberOfBytes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
|
|
437
|
+
|
|
438
|
+
return this.readBits(numberOfBytes * 8);
|
|
439
|
+
}
|
|
440
|
+
}, {
|
|
441
|
+
key: "readUShort",
|
|
442
|
+
value: function readUShort() {
|
|
443
|
+
return this.readBits(16);
|
|
444
|
+
}
|
|
445
|
+
}, {
|
|
446
|
+
key: "readUInt",
|
|
447
|
+
value: function readUInt() {
|
|
448
|
+
return this.readBits(32);
|
|
449
|
+
}
|
|
450
|
+
}, {
|
|
451
|
+
key: "bitsAvailable",
|
|
452
|
+
get: function get$$1() {
|
|
453
|
+
return this.bitLength - this.index;
|
|
454
|
+
}
|
|
455
|
+
}]);
|
|
456
|
+
return ExpGolomb;
|
|
457
|
+
}();
|
|
458
|
+
|
|
459
|
+
var H264Parser = function () {
|
|
460
|
+
createClass(H264Parser, null, [{
|
|
461
|
+
key: 'extractNALu',
|
|
462
|
+
value: function extractNALu(buffer) {
|
|
463
|
+
var i = 0,
|
|
464
|
+
length = buffer.byteLength,
|
|
465
|
+
value = void 0,
|
|
466
|
+
state = 0,
|
|
467
|
+
result = [],
|
|
468
|
+
lastIndex = void 0;
|
|
469
|
+
|
|
470
|
+
while (i < length) {
|
|
471
|
+
value = buffer[i++];
|
|
472
|
+
// finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
|
|
473
|
+
switch (state) {
|
|
474
|
+
case 0:
|
|
475
|
+
if (value === 0) {
|
|
476
|
+
state = 1;
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
case 1:
|
|
480
|
+
if (value === 0) {
|
|
481
|
+
state = 2;
|
|
482
|
+
} else {
|
|
483
|
+
state = 0;
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
case 2:
|
|
487
|
+
case 3:
|
|
488
|
+
if (value === 0) {
|
|
489
|
+
state = 3;
|
|
490
|
+
} else if (value === 1 && i < length) {
|
|
491
|
+
if (lastIndex) {
|
|
492
|
+
result.push(buffer.subarray(lastIndex, i - state - 1));
|
|
493
|
+
}
|
|
494
|
+
lastIndex = i;
|
|
495
|
+
state = 0;
|
|
496
|
+
} else {
|
|
497
|
+
state = 0;
|
|
498
|
+
}
|
|
499
|
+
break;
|
|
500
|
+
default:
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (lastIndex) {
|
|
506
|
+
result.push(buffer.subarray(lastIndex, length));
|
|
507
|
+
}
|
|
508
|
+
return result;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Advance the ExpGolomb decoder past a scaling list. The scaling
|
|
513
|
+
* list is optionally transmitted as part of a sequence parameter
|
|
514
|
+
* set and is not relevant to transmuxing.
|
|
515
|
+
* @param decoder {ExpGolomb} exp golomb decoder
|
|
516
|
+
* @param count {number} the number of entries in this scaling list
|
|
517
|
+
* @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
|
|
518
|
+
*/
|
|
519
|
+
|
|
520
|
+
}, {
|
|
521
|
+
key: 'skipScalingList',
|
|
522
|
+
value: function skipScalingList(decoder, count) {
|
|
523
|
+
var lastScale = 8,
|
|
524
|
+
nextScale = 8,
|
|
525
|
+
deltaScale = void 0;
|
|
526
|
+
for (var j = 0; j < count; j++) {
|
|
527
|
+
if (nextScale !== 0) {
|
|
528
|
+
deltaScale = decoder.readEG();
|
|
529
|
+
nextScale = (lastScale + deltaScale + 256) % 256;
|
|
530
|
+
}
|
|
531
|
+
lastScale = nextScale === 0 ? lastScale : nextScale;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Read a sequence parameter set and return some interesting video
|
|
537
|
+
* properties. A sequence parameter set is the H264 metadata that
|
|
538
|
+
* describes the properties of upcoming video frames.
|
|
539
|
+
* @param data {Uint8Array} the bytes of a sequence parameter set
|
|
540
|
+
* @return {object} an object with configuration parsed from the
|
|
541
|
+
* sequence parameter set, including the dimensions of the
|
|
542
|
+
* associated video frames.
|
|
543
|
+
*/
|
|
544
|
+
|
|
545
|
+
}, {
|
|
546
|
+
key: 'readSPS',
|
|
547
|
+
value: function readSPS(data) {
|
|
548
|
+
var decoder = new ExpGolomb(data);
|
|
549
|
+
var frameCropLeftOffset = 0,
|
|
550
|
+
frameCropRightOffset = 0,
|
|
551
|
+
frameCropTopOffset = 0,
|
|
552
|
+
frameCropBottomOffset = 0,
|
|
553
|
+
sarScale = 1,
|
|
554
|
+
profileIdc = void 0,
|
|
555
|
+
profileCompat = void 0,
|
|
556
|
+
levelIdc = void 0,
|
|
557
|
+
numRefFramesInPicOrderCntCycle = void 0,
|
|
558
|
+
picWidthInMbsMinus1 = void 0,
|
|
559
|
+
picHeightInMapUnitsMinus1 = void 0,
|
|
560
|
+
frameMbsOnlyFlag = void 0,
|
|
561
|
+
scalingListCount = void 0;
|
|
562
|
+
decoder.readUByte();
|
|
563
|
+
profileIdc = decoder.readUByte(); // profile_idc
|
|
564
|
+
profileCompat = decoder.readBits(5); // constraint_set[0-4]_flag, u(5)
|
|
565
|
+
decoder.skipBits(3); // reserved_zero_3bits u(3),
|
|
566
|
+
levelIdc = decoder.readUByte(); // level_idc u(8)
|
|
567
|
+
decoder.skipUEG(); // seq_parameter_set_id
|
|
568
|
+
// some profiles have more optional data we don't need
|
|
569
|
+
if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
|
|
570
|
+
var chromaFormatIdc = decoder.readUEG();
|
|
571
|
+
if (chromaFormatIdc === 3) {
|
|
572
|
+
decoder.skipBits(1); // separate_colour_plane_flag
|
|
573
|
+
}
|
|
574
|
+
decoder.skipUEG(); // bit_depth_luma_minus8
|
|
575
|
+
decoder.skipUEG(); // bit_depth_chroma_minus8
|
|
576
|
+
decoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
|
|
577
|
+
if (decoder.readBoolean()) {
|
|
578
|
+
// seq_scaling_matrix_present_flag
|
|
579
|
+
scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
|
|
580
|
+
for (var i = 0; i < scalingListCount; ++i) {
|
|
581
|
+
if (decoder.readBoolean()) {
|
|
582
|
+
// seq_scaling_list_present_flag[ i ]
|
|
583
|
+
if (i < 6) {
|
|
584
|
+
H264Parser.skipScalingList(decoder, 16);
|
|
585
|
+
} else {
|
|
586
|
+
H264Parser.skipScalingList(decoder, 64);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
decoder.skipUEG(); // log2_max_frame_num_minus4
|
|
593
|
+
var picOrderCntType = decoder.readUEG();
|
|
594
|
+
if (picOrderCntType === 0) {
|
|
595
|
+
decoder.readUEG(); // log2_max_pic_order_cnt_lsb_minus4
|
|
596
|
+
} else if (picOrderCntType === 1) {
|
|
597
|
+
decoder.skipBits(1); // delta_pic_order_always_zero_flag
|
|
598
|
+
decoder.skipEG(); // offset_for_non_ref_pic
|
|
599
|
+
decoder.skipEG(); // offset_for_top_to_bottom_field
|
|
600
|
+
numRefFramesInPicOrderCntCycle = decoder.readUEG();
|
|
601
|
+
for (var _i = 0; _i < numRefFramesInPicOrderCntCycle; ++_i) {
|
|
602
|
+
decoder.skipEG(); // offset_for_ref_frame[ i ]
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
decoder.skipUEG(); // max_num_ref_frames
|
|
606
|
+
decoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
|
|
607
|
+
picWidthInMbsMinus1 = decoder.readUEG();
|
|
608
|
+
picHeightInMapUnitsMinus1 = decoder.readUEG();
|
|
609
|
+
frameMbsOnlyFlag = decoder.readBits(1);
|
|
610
|
+
if (frameMbsOnlyFlag === 0) {
|
|
611
|
+
decoder.skipBits(1); // mb_adaptive_frame_field_flag
|
|
612
|
+
}
|
|
613
|
+
decoder.skipBits(1); // direct_8x8_inference_flag
|
|
614
|
+
if (decoder.readBoolean()) {
|
|
615
|
+
// frame_cropping_flag
|
|
616
|
+
frameCropLeftOffset = decoder.readUEG();
|
|
617
|
+
frameCropRightOffset = decoder.readUEG();
|
|
618
|
+
frameCropTopOffset = decoder.readUEG();
|
|
619
|
+
frameCropBottomOffset = decoder.readUEG();
|
|
620
|
+
}
|
|
621
|
+
if (decoder.readBoolean()) {
|
|
622
|
+
// vui_parameters_present_flag
|
|
623
|
+
if (decoder.readBoolean()) {
|
|
624
|
+
// aspect_ratio_info_present_flag
|
|
625
|
+
var sarRatio = void 0;
|
|
626
|
+
var aspectRatioIdc = decoder.readUByte();
|
|
627
|
+
switch (aspectRatioIdc) {
|
|
628
|
+
case 1:
|
|
629
|
+
sarRatio = [1, 1];break;
|
|
630
|
+
case 2:
|
|
631
|
+
sarRatio = [12, 11];break;
|
|
632
|
+
case 3:
|
|
633
|
+
sarRatio = [10, 11];break;
|
|
634
|
+
case 4:
|
|
635
|
+
sarRatio = [16, 11];break;
|
|
636
|
+
case 5:
|
|
637
|
+
sarRatio = [40, 33];break;
|
|
638
|
+
case 6:
|
|
639
|
+
sarRatio = [24, 11];break;
|
|
640
|
+
case 7:
|
|
641
|
+
sarRatio = [20, 11];break;
|
|
642
|
+
case 8:
|
|
643
|
+
sarRatio = [32, 11];break;
|
|
644
|
+
case 9:
|
|
645
|
+
sarRatio = [80, 33];break;
|
|
646
|
+
case 10:
|
|
647
|
+
sarRatio = [18, 11];break;
|
|
648
|
+
case 11:
|
|
649
|
+
sarRatio = [15, 11];break;
|
|
650
|
+
case 12:
|
|
651
|
+
sarRatio = [64, 33];break;
|
|
652
|
+
case 13:
|
|
653
|
+
sarRatio = [160, 99];break;
|
|
654
|
+
case 14:
|
|
655
|
+
sarRatio = [4, 3];break;
|
|
656
|
+
case 15:
|
|
657
|
+
sarRatio = [3, 2];break;
|
|
658
|
+
case 16:
|
|
659
|
+
sarRatio = [2, 1];break;
|
|
660
|
+
case 255:
|
|
661
|
+
{
|
|
662
|
+
sarRatio = [decoder.readUByte() << 8 | decoder.readUByte(), decoder.readUByte() << 8 | decoder.readUByte()];
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (sarRatio) {
|
|
667
|
+
sarScale = sarRatio[0] / sarRatio[1];
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (decoder.readBoolean()) {
|
|
671
|
+
decoder.skipBits(1);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (decoder.readBoolean()) {
|
|
675
|
+
decoder.skipBits(4);
|
|
676
|
+
if (decoder.readBoolean()) {
|
|
677
|
+
decoder.skipBits(24);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (decoder.readBoolean()) {
|
|
681
|
+
decoder.skipUEG();
|
|
682
|
+
decoder.skipUEG();
|
|
683
|
+
}
|
|
684
|
+
if (decoder.readBoolean()) {
|
|
685
|
+
var unitsInTick = decoder.readUInt();
|
|
686
|
+
var timeScale = decoder.readUInt();
|
|
687
|
+
var fixedFrameRate = decoder.readBoolean();
|
|
688
|
+
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
|
|
693
|
+
height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset)
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}]);
|
|
697
|
+
|
|
698
|
+
function H264Parser(remuxer) {
|
|
699
|
+
classCallCheck(this, H264Parser);
|
|
700
|
+
|
|
701
|
+
this.remuxer = remuxer;
|
|
702
|
+
this.track = remuxer.mp4track;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
createClass(H264Parser, [{
|
|
706
|
+
key: 'parseSPS',
|
|
707
|
+
value: function parseSPS(sps) {
|
|
708
|
+
var config = H264Parser.readSPS(new Uint8Array(sps));
|
|
709
|
+
|
|
710
|
+
this.track.width = config.width;
|
|
711
|
+
this.track.height = config.height;
|
|
712
|
+
this.track.sps = [new Uint8Array(sps)];
|
|
713
|
+
this.track.codec = 'avc1.';
|
|
714
|
+
|
|
715
|
+
var codecarray = new DataView(sps.buffer, sps.byteOffset + 1, 4);
|
|
716
|
+
for (var i = 0; i < 3; ++i) {
|
|
717
|
+
var h = codecarray.getUint8(i).toString(16);
|
|
718
|
+
if (h.length < 2) {
|
|
719
|
+
h = '0' + h;
|
|
720
|
+
}
|
|
721
|
+
this.track.codec += h;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}, {
|
|
725
|
+
key: 'parsePPS',
|
|
726
|
+
value: function parsePPS(pps) {
|
|
727
|
+
this.track.pps = [new Uint8Array(pps)];
|
|
728
|
+
}
|
|
729
|
+
}, {
|
|
730
|
+
key: 'parseNAL',
|
|
731
|
+
value: function parseNAL(unit) {
|
|
732
|
+
if (!unit) return false;
|
|
733
|
+
|
|
734
|
+
var push = false;
|
|
735
|
+
switch (unit.type()) {
|
|
736
|
+
case NALU.NDR:
|
|
737
|
+
push = true;
|
|
738
|
+
break;
|
|
739
|
+
case NALU.IDR:
|
|
740
|
+
push = true;
|
|
741
|
+
break;
|
|
742
|
+
case NALU.PPS:
|
|
743
|
+
if (!this.track.pps) {
|
|
744
|
+
this.parsePPS(unit.getData().subarray(4));
|
|
745
|
+
if (!this.remuxer.readyToDecode && this.track.pps && this.track.sps) {
|
|
746
|
+
this.remuxer.readyToDecode = true;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
push = true;
|
|
750
|
+
break;
|
|
751
|
+
case NALU.SPS:
|
|
752
|
+
if (!this.track.sps) {
|
|
753
|
+
this.parseSPS(unit.getData().subarray(4));
|
|
754
|
+
if (!this.remuxer.readyToDecode && this.track.pps && this.track.sps) {
|
|
755
|
+
this.remuxer.readyToDecode = true;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
push = true;
|
|
759
|
+
break;
|
|
760
|
+
case NALU.AUD:
|
|
761
|
+
log('AUD - ignoing and disable HD mode for live channel');
|
|
762
|
+
if (this.remuxer.isHDAvail) {
|
|
763
|
+
this.remuxer.isHDAvail = false;
|
|
764
|
+
}
|
|
765
|
+
break;
|
|
766
|
+
case NALU.SEI:
|
|
767
|
+
log('SEI - ignoing');
|
|
768
|
+
break;
|
|
769
|
+
default:
|
|
770
|
+
}
|
|
771
|
+
return push;
|
|
772
|
+
}
|
|
773
|
+
}]);
|
|
774
|
+
return H264Parser;
|
|
775
|
+
}();
|
|
776
|
+
|
|
777
|
+
var aacHeader = void 0;
|
|
778
|
+
var AACParser = function () {
|
|
779
|
+
createClass(AACParser, null, [{
|
|
780
|
+
key: 'getHeaderLength',
|
|
781
|
+
value: function getHeaderLength(data) {
|
|
782
|
+
return data[1] & 0x01 ? 7 : 9; // without CRC 7 and with CRC 9 Refs: https://wiki.multimedia.cx/index.php?title=ADTS
|
|
783
|
+
}
|
|
784
|
+
}, {
|
|
785
|
+
key: 'getFrameLength',
|
|
786
|
+
value: function getFrameLength(data) {
|
|
787
|
+
return (data[3] & 0x03) << 11 | data[4] << 3 | (data[5] & 0xE0) >>> 5; // 13 bits length ref: https://wiki.multimedia.cx/index.php?title=ADTS
|
|
788
|
+
}
|
|
789
|
+
}, {
|
|
790
|
+
key: 'isAACPattern',
|
|
791
|
+
value: function isAACPattern(data) {
|
|
792
|
+
return data[0] === 0xff && (data[1] & 0xf0) === 0xf0 && (data[1] & 0x06) === 0x00;
|
|
793
|
+
}
|
|
794
|
+
}, {
|
|
795
|
+
key: 'extractAAC',
|
|
796
|
+
value: function extractAAC(buffer) {
|
|
797
|
+
var i = 0,
|
|
798
|
+
length = buffer.byteLength,
|
|
799
|
+
result = [],
|
|
800
|
+
headerLength = void 0,
|
|
801
|
+
frameLength = void 0;
|
|
802
|
+
|
|
803
|
+
if (!AACParser.isAACPattern(buffer)) {
|
|
804
|
+
error('Invalid ADTS audio format');
|
|
805
|
+
return result;
|
|
806
|
+
}
|
|
807
|
+
headerLength = AACParser.getHeaderLength(buffer);
|
|
808
|
+
if (!aacHeader) {
|
|
809
|
+
aacHeader = buffer.subarray(0, headerLength);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
while (i < length) {
|
|
813
|
+
frameLength = AACParser.getFrameLength(buffer);
|
|
814
|
+
result.push(buffer.subarray(headerLength, frameLength));
|
|
815
|
+
buffer = buffer.slice(frameLength);
|
|
816
|
+
i += frameLength;
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
}, {
|
|
821
|
+
key: 'samplingRateMap',
|
|
822
|
+
get: function get$$1() {
|
|
823
|
+
return [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
|
|
824
|
+
}
|
|
825
|
+
}, {
|
|
826
|
+
key: 'getAACHeaderData',
|
|
827
|
+
get: function get$$1() {
|
|
828
|
+
return aacHeader;
|
|
829
|
+
}
|
|
830
|
+
}]);
|
|
831
|
+
|
|
832
|
+
function AACParser(remuxer) {
|
|
833
|
+
classCallCheck(this, AACParser);
|
|
834
|
+
|
|
835
|
+
this.remuxer = remuxer;
|
|
836
|
+
this.track = remuxer.mp4track;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
createClass(AACParser, [{
|
|
840
|
+
key: 'setAACConfig',
|
|
841
|
+
value: function setAACConfig() {
|
|
842
|
+
var objectType = void 0,
|
|
843
|
+
sampleIndex = void 0,
|
|
844
|
+
channelCount = void 0,
|
|
845
|
+
config = new Uint8Array(2),
|
|
846
|
+
headerData = AACParser.getAACHeaderData;
|
|
847
|
+
|
|
848
|
+
if (!headerData) return;
|
|
849
|
+
|
|
850
|
+
objectType = ((headerData[2] & 0xC0) >>> 6) + 1;
|
|
851
|
+
sampleIndex = (headerData[2] & 0x3C) >>> 2;
|
|
852
|
+
channelCount = (headerData[2] & 0x01) << 2;
|
|
853
|
+
channelCount |= (headerData[3] & 0xC0) >>> 6;
|
|
854
|
+
|
|
855
|
+
/* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config */
|
|
856
|
+
config[0] = objectType << 3;
|
|
857
|
+
config[0] |= (sampleIndex & 0x0E) >> 1;
|
|
858
|
+
config[1] |= (sampleIndex & 0x01) << 7;
|
|
859
|
+
config[1] |= channelCount << 3;
|
|
860
|
+
|
|
861
|
+
this.track.codec = 'mp4a.40.' + objectType;
|
|
862
|
+
this.track.channelCount = channelCount;
|
|
863
|
+
this.track.config = config;
|
|
864
|
+
this.remuxer.readyToDecode = true;
|
|
865
|
+
}
|
|
866
|
+
}]);
|
|
867
|
+
return AACParser;
|
|
868
|
+
}();
|
|
869
|
+
|
|
870
|
+
var Event = function () {
|
|
871
|
+
function Event(type) {
|
|
872
|
+
classCallCheck(this, Event);
|
|
873
|
+
|
|
874
|
+
this.listener = {};
|
|
875
|
+
this.type = type | '';
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
createClass(Event, [{
|
|
879
|
+
key: 'on',
|
|
880
|
+
value: function on(event, fn) {
|
|
881
|
+
if (!this.listener[event]) {
|
|
882
|
+
this.listener[event] = [];
|
|
883
|
+
}
|
|
884
|
+
this.listener[event].push(fn);
|
|
885
|
+
return true;
|
|
886
|
+
}
|
|
887
|
+
}, {
|
|
888
|
+
key: 'off',
|
|
889
|
+
value: function off(event, fn) {
|
|
890
|
+
if (this.listener[event]) {
|
|
891
|
+
var index = this.listener[event].indexOf(fn);
|
|
892
|
+
if (index > -1) {
|
|
893
|
+
this.listener[event].splice(index, 1);
|
|
894
|
+
}
|
|
895
|
+
return true;
|
|
896
|
+
}
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
}, {
|
|
900
|
+
key: 'offAll',
|
|
901
|
+
value: function offAll() {
|
|
902
|
+
this.listener = {};
|
|
903
|
+
}
|
|
904
|
+
}, {
|
|
905
|
+
key: 'dispatch',
|
|
906
|
+
value: function dispatch(event, data) {
|
|
907
|
+
if (this.listener[event]) {
|
|
908
|
+
this.listener[event].map(function (each) {
|
|
909
|
+
each.apply(null, [data]);
|
|
910
|
+
});
|
|
911
|
+
return true;
|
|
912
|
+
}
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
}]);
|
|
916
|
+
return Event;
|
|
917
|
+
}();
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Generate MP4 Box
|
|
921
|
+
* taken from: https://github.com/dailymotion/hls.js
|
|
922
|
+
*/
|
|
923
|
+
|
|
924
|
+
var MP4 = function () {
|
|
925
|
+
function MP4() {
|
|
926
|
+
classCallCheck(this, MP4);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
createClass(MP4, null, [{
|
|
930
|
+
key: 'init',
|
|
931
|
+
value: function init() {
|
|
932
|
+
MP4.types = {
|
|
933
|
+
avc1: [], // codingname
|
|
934
|
+
avcC: [],
|
|
935
|
+
btrt: [],
|
|
936
|
+
dinf: [],
|
|
937
|
+
dref: [],
|
|
938
|
+
esds: [],
|
|
939
|
+
ftyp: [],
|
|
940
|
+
hdlr: [],
|
|
941
|
+
mdat: [],
|
|
942
|
+
mdhd: [],
|
|
943
|
+
mdia: [],
|
|
944
|
+
mfhd: [],
|
|
945
|
+
minf: [],
|
|
946
|
+
moof: [],
|
|
947
|
+
moov: [],
|
|
948
|
+
mp4a: [],
|
|
949
|
+
mvex: [],
|
|
950
|
+
mvhd: [],
|
|
951
|
+
sdtp: [],
|
|
952
|
+
stbl: [],
|
|
953
|
+
stco: [],
|
|
954
|
+
stsc: [],
|
|
955
|
+
stsd: [],
|
|
956
|
+
stsz: [],
|
|
957
|
+
stts: [],
|
|
958
|
+
tfdt: [],
|
|
959
|
+
tfhd: [],
|
|
960
|
+
traf: [],
|
|
961
|
+
trak: [],
|
|
962
|
+
trun: [],
|
|
963
|
+
trex: [],
|
|
964
|
+
tkhd: [],
|
|
965
|
+
vmhd: [],
|
|
966
|
+
smhd: []
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
var i;
|
|
970
|
+
for (i in MP4.types) {
|
|
971
|
+
if (MP4.types.hasOwnProperty(i)) {
|
|
972
|
+
MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
var videoHdlr = new Uint8Array([0x00, // version 0
|
|
977
|
+
0x00, 0x00, 0x00, // flags
|
|
978
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined
|
|
979
|
+
0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
|
|
980
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
981
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
982
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
983
|
+
0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00]);
|
|
984
|
+
|
|
985
|
+
var audioHdlr = new Uint8Array([0x00, // version 0
|
|
986
|
+
0x00, 0x00, 0x00, // flags
|
|
987
|
+
0x00, 0x00, 0x00, 0x00, // pre_defined
|
|
988
|
+
0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
|
|
989
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
990
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
991
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
992
|
+
0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00]);
|
|
993
|
+
|
|
994
|
+
MP4.HDLR_TYPES = {
|
|
995
|
+
video: videoHdlr,
|
|
996
|
+
audio: audioHdlr
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
var dref = new Uint8Array([0x00, // version 0
|
|
1000
|
+
0x00, 0x00, 0x00, // flags
|
|
1001
|
+
0x00, 0x00, 0x00, 0x01, // entry_count
|
|
1002
|
+
0x00, 0x00, 0x00, 0x0c, // entry_size
|
|
1003
|
+
0x75, 0x72, 0x6c, 0x20, // 'url' type
|
|
1004
|
+
0x00, // version 0
|
|
1005
|
+
0x00, 0x00, 0x01]);
|
|
1006
|
+
|
|
1007
|
+
var stco = new Uint8Array([0x00, // version
|
|
1008
|
+
0x00, 0x00, 0x00, // flags
|
|
1009
|
+
0x00, 0x00, 0x00, 0x00]);
|
|
1010
|
+
|
|
1011
|
+
MP4.STTS = MP4.STSC = MP4.STCO = stco;
|
|
1012
|
+
|
|
1013
|
+
MP4.STSZ = new Uint8Array([0x00, // version
|
|
1014
|
+
0x00, 0x00, 0x00, // flags
|
|
1015
|
+
0x00, 0x00, 0x00, 0x00, // sample_size
|
|
1016
|
+
0x00, 0x00, 0x00, 0x00]);
|
|
1017
|
+
MP4.VMHD = new Uint8Array([0x00, // version
|
|
1018
|
+
0x00, 0x00, 0x01, // flags
|
|
1019
|
+
0x00, 0x00, // graphicsmode
|
|
1020
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
|
1021
|
+
MP4.SMHD = new Uint8Array([0x00, // version
|
|
1022
|
+
0x00, 0x00, 0x00, // flags
|
|
1023
|
+
0x00, 0x00, // balance
|
|
1024
|
+
0x00, 0x00]);
|
|
1025
|
+
|
|
1026
|
+
MP4.STSD = new Uint8Array([0x00, // version 0
|
|
1027
|
+
0x00, 0x00, 0x00, // flags
|
|
1028
|
+
0x00, 0x00, 0x00, 0x01]); // entry_count
|
|
1029
|
+
|
|
1030
|
+
var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom
|
|
1031
|
+
var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1
|
|
1032
|
+
var minorVersion = new Uint8Array([0, 0, 0, 1]);
|
|
1033
|
+
|
|
1034
|
+
MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand);
|
|
1035
|
+
MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref));
|
|
1036
|
+
}
|
|
1037
|
+
}, {
|
|
1038
|
+
key: 'box',
|
|
1039
|
+
value: function box(type) {
|
|
1040
|
+
for (var _len = arguments.length, payload = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
1041
|
+
payload[_key - 1] = arguments[_key];
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
var size = 8,
|
|
1045
|
+
i = payload.length,
|
|
1046
|
+
len = i,
|
|
1047
|
+
result;
|
|
1048
|
+
// calculate the total size we need to allocate
|
|
1049
|
+
while (i--) {
|
|
1050
|
+
size += payload[i].byteLength;
|
|
1051
|
+
}
|
|
1052
|
+
result = new Uint8Array(size);
|
|
1053
|
+
result[0] = size >> 24 & 0xff;
|
|
1054
|
+
result[1] = size >> 16 & 0xff;
|
|
1055
|
+
result[2] = size >> 8 & 0xff;
|
|
1056
|
+
result[3] = size & 0xff;
|
|
1057
|
+
result.set(type, 4);
|
|
1058
|
+
// copy the payload into the result
|
|
1059
|
+
for (i = 0, size = 8; i < len; ++i) {
|
|
1060
|
+
// copy payload[i] array @ offset size
|
|
1061
|
+
result.set(payload[i], size);
|
|
1062
|
+
size += payload[i].byteLength;
|
|
1063
|
+
}
|
|
1064
|
+
return result;
|
|
1065
|
+
}
|
|
1066
|
+
}, {
|
|
1067
|
+
key: 'hdlr',
|
|
1068
|
+
value: function hdlr(type) {
|
|
1069
|
+
return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]);
|
|
1070
|
+
}
|
|
1071
|
+
}, {
|
|
1072
|
+
key: 'mdat',
|
|
1073
|
+
value: function mdat(data) {
|
|
1074
|
+
return MP4.box(MP4.types.mdat, data);
|
|
1075
|
+
}
|
|
1076
|
+
}, {
|
|
1077
|
+
key: 'mdhd',
|
|
1078
|
+
value: function mdhd(timescale, duration) {
|
|
1079
|
+
return MP4.box(MP4.types.mdhd, new Uint8Array([0x00, // version 0
|
|
1080
|
+
0x00, 0x00, 0x00, // flags
|
|
1081
|
+
0x00, 0x00, 0x00, 0x02, // creation_time
|
|
1082
|
+
0x00, 0x00, 0x00, 0x03, // modification_time
|
|
1083
|
+
timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale
|
|
1084
|
+
duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration
|
|
1085
|
+
0x55, 0xc4, // 'und' language (undetermined)
|
|
1086
|
+
0x00, 0x00]));
|
|
1087
|
+
}
|
|
1088
|
+
}, {
|
|
1089
|
+
key: 'mdia',
|
|
1090
|
+
value: function mdia(track) {
|
|
1091
|
+
return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track));
|
|
1092
|
+
}
|
|
1093
|
+
}, {
|
|
1094
|
+
key: 'mfhd',
|
|
1095
|
+
value: function mfhd(sequenceNumber) {
|
|
1096
|
+
return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags
|
|
1097
|
+
sequenceNumber >> 24, sequenceNumber >> 16 & 0xFF, sequenceNumber >> 8 & 0xFF, sequenceNumber & 0xFF]) // sequence_number
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
}, {
|
|
1101
|
+
key: 'minf',
|
|
1102
|
+
value: function minf(track) {
|
|
1103
|
+
if (track.type === 'audio') {
|
|
1104
|
+
return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track));
|
|
1105
|
+
} else {
|
|
1106
|
+
return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track));
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}, {
|
|
1110
|
+
key: 'moof',
|
|
1111
|
+
value: function moof(sn, baseMediaDecodeTime, track) {
|
|
1112
|
+
return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime));
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* @param tracks... (optional) {array} the tracks associated with this movie
|
|
1116
|
+
*/
|
|
1117
|
+
|
|
1118
|
+
}, {
|
|
1119
|
+
key: 'moov',
|
|
1120
|
+
value: function moov(tracks, duration, timescale) {
|
|
1121
|
+
var i = tracks.length,
|
|
1122
|
+
boxes = [];
|
|
1123
|
+
|
|
1124
|
+
while (i--) {
|
|
1125
|
+
boxes[i] = MP4.trak(tracks[i]);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(timescale, duration)].concat(boxes).concat(MP4.mvex(tracks)));
|
|
1129
|
+
}
|
|
1130
|
+
}, {
|
|
1131
|
+
key: 'mvex',
|
|
1132
|
+
value: function mvex(tracks) {
|
|
1133
|
+
var i = tracks.length,
|
|
1134
|
+
boxes = [];
|
|
1135
|
+
|
|
1136
|
+
while (i--) {
|
|
1137
|
+
boxes[i] = MP4.trex(tracks[i]);
|
|
1138
|
+
}
|
|
1139
|
+
return MP4.box.apply(null, [MP4.types.mvex].concat(boxes));
|
|
1140
|
+
}
|
|
1141
|
+
}, {
|
|
1142
|
+
key: 'mvhd',
|
|
1143
|
+
value: function mvhd(timescale, duration) {
|
|
1144
|
+
var bytes = new Uint8Array([0x00, // version 0
|
|
1145
|
+
0x00, 0x00, 0x00, // flags
|
|
1146
|
+
0x00, 0x00, 0x00, 0x01, // creation_time
|
|
1147
|
+
0x00, 0x00, 0x00, 0x02, // modification_time
|
|
1148
|
+
timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale
|
|
1149
|
+
duration >> 24 & 0xFF, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration
|
|
1150
|
+
0x00, 0x01, 0x00, 0x00, // 1.0 rate
|
|
1151
|
+
0x01, 0x00, // 1.0 volume
|
|
1152
|
+
0x00, 0x00, // reserved
|
|
1153
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
1154
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
1155
|
+
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
|
|
1156
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
|
|
1157
|
+
0xff, 0xff, 0xff, 0xff]);
|
|
1158
|
+
return MP4.box(MP4.types.mvhd, bytes);
|
|
1159
|
+
}
|
|
1160
|
+
}, {
|
|
1161
|
+
key: 'sdtp',
|
|
1162
|
+
value: function sdtp(track) {
|
|
1163
|
+
var samples = track.samples || [],
|
|
1164
|
+
bytes = new Uint8Array(4 + samples.length),
|
|
1165
|
+
flags,
|
|
1166
|
+
i;
|
|
1167
|
+
// leave the full box header (4 bytes) all zero
|
|
1168
|
+
// write the sample table
|
|
1169
|
+
for (i = 0; i < samples.length; i++) {
|
|
1170
|
+
flags = samples[i].flags;
|
|
1171
|
+
bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
return MP4.box(MP4.types.sdtp, bytes);
|
|
1175
|
+
}
|
|
1176
|
+
}, {
|
|
1177
|
+
key: 'stbl',
|
|
1178
|
+
value: function stbl(track) {
|
|
1179
|
+
return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO));
|
|
1180
|
+
}
|
|
1181
|
+
}, {
|
|
1182
|
+
key: 'avc1',
|
|
1183
|
+
value: function avc1(track) {
|
|
1184
|
+
var sps = [],
|
|
1185
|
+
pps = [],
|
|
1186
|
+
i,
|
|
1187
|
+
data,
|
|
1188
|
+
len;
|
|
1189
|
+
// assemble the SPSs
|
|
1190
|
+
|
|
1191
|
+
for (i = 0; i < track.sps.length; i++) {
|
|
1192
|
+
data = track.sps[i];
|
|
1193
|
+
len = data.byteLength;
|
|
1194
|
+
sps.push(len >>> 8 & 0xFF);
|
|
1195
|
+
sps.push(len & 0xFF);
|
|
1196
|
+
sps = sps.concat(Array.prototype.slice.call(data)); // SPS
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// assemble the PPSs
|
|
1200
|
+
for (i = 0; i < track.pps.length; i++) {
|
|
1201
|
+
data = track.pps[i];
|
|
1202
|
+
len = data.byteLength;
|
|
1203
|
+
pps.push(len >>> 8 & 0xFF);
|
|
1204
|
+
pps.push(len & 0xFF);
|
|
1205
|
+
pps = pps.concat(Array.prototype.slice.call(data));
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, // version
|
|
1209
|
+
sps[3], // profile
|
|
1210
|
+
sps[4], // profile compat
|
|
1211
|
+
sps[5], // level
|
|
1212
|
+
0xfc | 3, // lengthSizeMinusOne, hard-coded to 4 bytes
|
|
1213
|
+
0xE0 | track.sps.length].concat(sps).concat([track.pps.length] // numOfPictureParameterSets
|
|
1214
|
+
).concat(pps))),
|
|
1215
|
+
// "PPS"
|
|
1216
|
+
width = track.width,
|
|
1217
|
+
height = track.height;
|
|
1218
|
+
// console.log('avcc:' + Hex.hexDump(avcc));
|
|
1219
|
+
return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, // reserved
|
|
1220
|
+
0x00, 0x00, 0x00, // reserved
|
|
1221
|
+
0x00, 0x01, // data_reference_index
|
|
1222
|
+
0x00, 0x00, // pre_defined
|
|
1223
|
+
0x00, 0x00, // reserved
|
|
1224
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
|
|
1225
|
+
width >> 8 & 0xFF, width & 0xff, // width
|
|
1226
|
+
height >> 8 & 0xFF, height & 0xff, // height
|
|
1227
|
+
0x00, 0x48, 0x00, 0x00, // horizresolution
|
|
1228
|
+
0x00, 0x48, 0x00, 0x00, // vertresolution
|
|
1229
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
1230
|
+
0x00, 0x01, // frame_count
|
|
1231
|
+
0x12, 0x62, 0x69, 0x6E, 0x65, // binelpro.ru
|
|
1232
|
+
0x6C, 0x70, 0x72, 0x6F, 0x2E, 0x72, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname
|
|
1233
|
+
0x00, 0x18, // depth = 24
|
|
1234
|
+
0x11, 0x11]), // pre_defined = -1
|
|
1235
|
+
avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
|
|
1236
|
+
0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
|
|
1237
|
+
0x00, 0x2d, 0xc6, 0xc0])) // avgBitrate
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
}, {
|
|
1241
|
+
key: 'esds',
|
|
1242
|
+
value: function esds(track) {
|
|
1243
|
+
var configlen = track.config.byteLength;
|
|
1244
|
+
var data = new Uint8Array(26 + configlen + 3);
|
|
1245
|
+
data.set([0x00, // version 0
|
|
1246
|
+
0x00, 0x00, 0x00, // flags
|
|
1247
|
+
|
|
1248
|
+
0x03, // descriptor_type
|
|
1249
|
+
0x17 + configlen, // length
|
|
1250
|
+
0x00, 0x01, // es_id
|
|
1251
|
+
0x00, // stream_priority
|
|
1252
|
+
|
|
1253
|
+
0x04, // descriptor_type
|
|
1254
|
+
0x0f + configlen, // length
|
|
1255
|
+
0x40, // codec : mpeg4_audio
|
|
1256
|
+
0x15, // stream_type
|
|
1257
|
+
0x00, 0x00, 0x00, // buffer_size
|
|
1258
|
+
0x00, 0x00, 0x00, 0x00, // maxBitrate
|
|
1259
|
+
0x00, 0x00, 0x00, 0x00, // avgBitrate
|
|
1260
|
+
|
|
1261
|
+
0x05, // descriptor_type
|
|
1262
|
+
configlen]);
|
|
1263
|
+
data.set(track.config, 26);
|
|
1264
|
+
data.set([0x06, 0x01, 0x02], 26 + configlen);
|
|
1265
|
+
// return new Uint8Array([
|
|
1266
|
+
// 0x00, // version 0
|
|
1267
|
+
// 0x00, 0x00, 0x00, // flags
|
|
1268
|
+
//
|
|
1269
|
+
// 0x03, // descriptor_type
|
|
1270
|
+
// 0x17+configlen, // length
|
|
1271
|
+
// 0x00, 0x01, //es_id
|
|
1272
|
+
// 0x00, // stream_priority
|
|
1273
|
+
//
|
|
1274
|
+
// 0x04, // descriptor_type
|
|
1275
|
+
// 0x0f+configlen, // length
|
|
1276
|
+
// 0x40, //codec : mpeg4_audio
|
|
1277
|
+
// 0x15, // stream_type
|
|
1278
|
+
// 0x00, 0x00, 0x00, // buffer_size
|
|
1279
|
+
// 0x00, 0x00, 0x00, 0x00, // maxBitrate
|
|
1280
|
+
// 0x00, 0x00, 0x00, 0x00, // avgBitrate
|
|
1281
|
+
//
|
|
1282
|
+
// 0x05 // descriptor_type
|
|
1283
|
+
// ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
|
|
1284
|
+
return data;
|
|
1285
|
+
}
|
|
1286
|
+
}, {
|
|
1287
|
+
key: 'mp4a',
|
|
1288
|
+
value: function mp4a(track) {
|
|
1289
|
+
var audiosamplerate = track.audiosamplerate;
|
|
1290
|
+
return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00, // reserved
|
|
1291
|
+
0x00, 0x00, 0x00, // reserved
|
|
1292
|
+
0x00, 0x01, // data_reference_index
|
|
1293
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
|
1294
|
+
0x00, track.channelCount, // channelcount
|
|
1295
|
+
0x00, 0x10, // sampleSize:16bits
|
|
1296
|
+
0x00, 0x00, // pre_defined
|
|
1297
|
+
0x00, 0x00, // reserved2
|
|
1298
|
+
audiosamplerate >> 8 & 0xFF, audiosamplerate & 0xff, //
|
|
1299
|
+
0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track)));
|
|
1300
|
+
}
|
|
1301
|
+
}, {
|
|
1302
|
+
key: 'stsd',
|
|
1303
|
+
value: function stsd(track) {
|
|
1304
|
+
if (track.type === 'audio') {
|
|
1305
|
+
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
|
|
1306
|
+
} else {
|
|
1307
|
+
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}, {
|
|
1311
|
+
key: 'tkhd',
|
|
1312
|
+
value: function tkhd(track) {
|
|
1313
|
+
var id = track.id,
|
|
1314
|
+
duration = track.duration,
|
|
1315
|
+
width = track.width,
|
|
1316
|
+
height = track.height,
|
|
1317
|
+
volume = track.volume;
|
|
1318
|
+
return MP4.box(MP4.types.tkhd, new Uint8Array([0x00, // version 0
|
|
1319
|
+
0x00, 0x00, 0x07, // flags
|
|
1320
|
+
0x00, 0x00, 0x00, 0x00, // creation_time
|
|
1321
|
+
0x00, 0x00, 0x00, 0x00, // modification_time
|
|
1322
|
+
id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF, // track_ID
|
|
1323
|
+
0x00, 0x00, 0x00, 0x00, // reserved
|
|
1324
|
+
duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration
|
|
1325
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
|
1326
|
+
0x00, 0x00, // layer
|
|
1327
|
+
0x00, 0x00, // alternate_group
|
|
1328
|
+
volume >> 0 & 0xff, volume % 1 * 10 >> 0 & 0xff, // track volume // FIXME
|
|
1329
|
+
0x00, 0x00, // reserved
|
|
1330
|
+
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
|
|
1331
|
+
width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00, // width
|
|
1332
|
+
height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00]) // height
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
}, {
|
|
1336
|
+
key: 'traf',
|
|
1337
|
+
value: function traf(track, baseMediaDecodeTime) {
|
|
1338
|
+
var sampleDependencyTable = MP4.sdtp(track),
|
|
1339
|
+
id = track.id;
|
|
1340
|
+
return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, // version 0
|
|
1341
|
+
0x00, 0x00, 0x00, // flags
|
|
1342
|
+
id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF]) // track_ID
|
|
1343
|
+
), MP4.box(MP4.types.tfdt, new Uint8Array([0x00, // version 0
|
|
1344
|
+
0x00, 0x00, 0x00, // flags
|
|
1345
|
+
baseMediaDecodeTime >> 24, baseMediaDecodeTime >> 16 & 0XFF, baseMediaDecodeTime >> 8 & 0XFF, baseMediaDecodeTime & 0xFF]) // baseMediaDecodeTime
|
|
1346
|
+
), MP4.trun(track, sampleDependencyTable.length + 16 + // tfhd
|
|
1347
|
+
16 + // tfdt
|
|
1348
|
+
8 + // traf header
|
|
1349
|
+
16 + // mfhd
|
|
1350
|
+
8 + // moof header
|
|
1351
|
+
8), // mdat header
|
|
1352
|
+
sampleDependencyTable);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/**
|
|
1356
|
+
* Generate a track box.
|
|
1357
|
+
* @param track {object} a track definition
|
|
1358
|
+
* @return {Uint8Array} the track box
|
|
1359
|
+
*/
|
|
1360
|
+
|
|
1361
|
+
}, {
|
|
1362
|
+
key: 'trak',
|
|
1363
|
+
value: function trak(track) {
|
|
1364
|
+
track.duration = track.duration || 0xffffffff;
|
|
1365
|
+
return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track));
|
|
1366
|
+
}
|
|
1367
|
+
}, {
|
|
1368
|
+
key: 'trex',
|
|
1369
|
+
value: function trex(track) {
|
|
1370
|
+
var id = track.id;
|
|
1371
|
+
return MP4.box(MP4.types.trex, new Uint8Array([0x00, // version 0
|
|
1372
|
+
0x00, 0x00, 0x00, // flags
|
|
1373
|
+
id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF, // track_ID
|
|
1374
|
+
0x00, 0x00, 0x00, 0x01, // default_sample_description_index
|
|
1375
|
+
0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
|
1376
|
+
0x00, 0x00, 0x00, 0x00, // default_sample_size
|
|
1377
|
+
0x00, 0x01, 0x00, 0x01]) // default_sample_flags
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
}, {
|
|
1381
|
+
key: 'trun',
|
|
1382
|
+
value: function trun(track, offset) {
|
|
1383
|
+
var samples = track.samples || [],
|
|
1384
|
+
len = samples.length,
|
|
1385
|
+
arraylen = 12 + 16 * len,
|
|
1386
|
+
array = new Uint8Array(arraylen),
|
|
1387
|
+
i,
|
|
1388
|
+
sample,
|
|
1389
|
+
duration,
|
|
1390
|
+
size,
|
|
1391
|
+
flags,
|
|
1392
|
+
cts;
|
|
1393
|
+
offset += 8 + arraylen;
|
|
1394
|
+
array.set([0x00, // version 0
|
|
1395
|
+
0x00, 0x0f, 0x01, // flags
|
|
1396
|
+
len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF, // sample_count
|
|
1397
|
+
offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF], 0);
|
|
1398
|
+
for (i = 0; i < len; i++) {
|
|
1399
|
+
sample = samples[i];
|
|
1400
|
+
duration = sample.duration;
|
|
1401
|
+
size = sample.size;
|
|
1402
|
+
flags = sample.flags;
|
|
1403
|
+
cts = sample.cts;
|
|
1404
|
+
array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, // sample_duration
|
|
1405
|
+
size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, // sample_size
|
|
1406
|
+
flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F, // sample_flags
|
|
1407
|
+
cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF], 12 + 16 * i);
|
|
1408
|
+
}
|
|
1409
|
+
return MP4.box(MP4.types.trun, array);
|
|
1410
|
+
}
|
|
1411
|
+
}, {
|
|
1412
|
+
key: 'initSegment',
|
|
1413
|
+
value: function initSegment(tracks, duration, timescale) {
|
|
1414
|
+
if (!MP4.types) {
|
|
1415
|
+
MP4.init();
|
|
1416
|
+
}
|
|
1417
|
+
var movie = MP4.moov(tracks, duration, timescale),
|
|
1418
|
+
result;
|
|
1419
|
+
result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength);
|
|
1420
|
+
result.set(MP4.FTYP);
|
|
1421
|
+
result.set(movie, MP4.FTYP.byteLength);
|
|
1422
|
+
return result;
|
|
1423
|
+
}
|
|
1424
|
+
}]);
|
|
1425
|
+
return MP4;
|
|
1426
|
+
}();
|
|
1427
|
+
|
|
1428
|
+
var track_id = 1;
|
|
1429
|
+
var BaseRemuxer = function () {
|
|
1430
|
+
createClass(BaseRemuxer, null, [{
|
|
1431
|
+
key: 'getTrackID',
|
|
1432
|
+
value: function getTrackID() {
|
|
1433
|
+
return track_id++;
|
|
1434
|
+
}
|
|
1435
|
+
}]);
|
|
1436
|
+
|
|
1437
|
+
function BaseRemuxer() {
|
|
1438
|
+
classCallCheck(this, BaseRemuxer);
|
|
1439
|
+
|
|
1440
|
+
this.seq = 1;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
createClass(BaseRemuxer, [{
|
|
1444
|
+
key: 'flush',
|
|
1445
|
+
value: function flush() {
|
|
1446
|
+
this.seq++;
|
|
1447
|
+
this.mp4track.len = 0;
|
|
1448
|
+
this.mp4track.samples = [];
|
|
1449
|
+
}
|
|
1450
|
+
}, {
|
|
1451
|
+
key: 'isReady',
|
|
1452
|
+
value: function isReady() {
|
|
1453
|
+
if (!this.readyToDecode || !this.samples.length) return null;
|
|
1454
|
+
return true;
|
|
1455
|
+
}
|
|
1456
|
+
}]);
|
|
1457
|
+
return BaseRemuxer;
|
|
1458
|
+
}();
|
|
1459
|
+
|
|
1460
|
+
var AACRemuxer = function (_BaseRemuxer) {
|
|
1461
|
+
inherits(AACRemuxer, _BaseRemuxer);
|
|
1462
|
+
|
|
1463
|
+
function AACRemuxer() {
|
|
1464
|
+
classCallCheck(this, AACRemuxer);
|
|
1465
|
+
|
|
1466
|
+
var _this = possibleConstructorReturn(this, (AACRemuxer.__proto__ || Object.getPrototypeOf(AACRemuxer)).call(this));
|
|
1467
|
+
|
|
1468
|
+
_this.readyToDecode = false;
|
|
1469
|
+
_this.nextDts = 0;
|
|
1470
|
+
_this.dts = 0;
|
|
1471
|
+
_this.timescale = 1000;
|
|
1472
|
+
|
|
1473
|
+
_this.mp4track = {
|
|
1474
|
+
id: BaseRemuxer.getTrackID(),
|
|
1475
|
+
type: 'audio',
|
|
1476
|
+
channelCount: 0,
|
|
1477
|
+
len: 0,
|
|
1478
|
+
fragmented: true,
|
|
1479
|
+
timescale: _this.timescale,
|
|
1480
|
+
duration: _this.timescale,
|
|
1481
|
+
samples: [],
|
|
1482
|
+
config: '',
|
|
1483
|
+
codec: ''
|
|
1484
|
+
};
|
|
1485
|
+
|
|
1486
|
+
_this.samples = [];
|
|
1487
|
+
_this.aac = new AACParser(_this);
|
|
1488
|
+
return _this;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
createClass(AACRemuxer, [{
|
|
1492
|
+
key: 'resetTrack',
|
|
1493
|
+
value: function resetTrack() {
|
|
1494
|
+
this.readyToDecode = false;
|
|
1495
|
+
this.mp4track.codec = '';
|
|
1496
|
+
this.mp4track.channelCount = '';
|
|
1497
|
+
this.mp4track.config = '';
|
|
1498
|
+
this.mp4track.timescale = this.timescale;
|
|
1499
|
+
}
|
|
1500
|
+
}, {
|
|
1501
|
+
key: 'remux',
|
|
1502
|
+
value: function remux(samples) {
|
|
1503
|
+
var config = void 0,
|
|
1504
|
+
sample = void 0,
|
|
1505
|
+
size = void 0,
|
|
1506
|
+
payload = void 0;
|
|
1507
|
+
var _iteratorNormalCompletion = true;
|
|
1508
|
+
var _didIteratorError = false;
|
|
1509
|
+
var _iteratorError = undefined;
|
|
1510
|
+
|
|
1511
|
+
try {
|
|
1512
|
+
for (var _iterator = samples[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
1513
|
+
var _sample = _step.value;
|
|
1514
|
+
|
|
1515
|
+
payload = _sample.units;
|
|
1516
|
+
size = payload.byteLength;
|
|
1517
|
+
this.samples.push({
|
|
1518
|
+
units: payload,
|
|
1519
|
+
size: size,
|
|
1520
|
+
duration: _sample.duration
|
|
1521
|
+
});
|
|
1522
|
+
this.mp4track.len += size;
|
|
1523
|
+
if (!this.readyToDecode) {
|
|
1524
|
+
this.aac.setAACConfig();
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
} catch (err) {
|
|
1528
|
+
_didIteratorError = true;
|
|
1529
|
+
_iteratorError = err;
|
|
1530
|
+
} finally {
|
|
1531
|
+
try {
|
|
1532
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
1533
|
+
_iterator.return();
|
|
1534
|
+
}
|
|
1535
|
+
} finally {
|
|
1536
|
+
if (_didIteratorError) {
|
|
1537
|
+
throw _iteratorError;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}, {
|
|
1543
|
+
key: 'getPayload',
|
|
1544
|
+
value: function getPayload() {
|
|
1545
|
+
if (!this.isReady()) {
|
|
1546
|
+
return null;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
var payload = new Uint8Array(this.mp4track.len);
|
|
1550
|
+
var offset = 0;
|
|
1551
|
+
var samples = this.mp4track.samples;
|
|
1552
|
+
var mp4Sample = void 0,
|
|
1553
|
+
duration = void 0;
|
|
1554
|
+
|
|
1555
|
+
this.dts = this.nextDts;
|
|
1556
|
+
|
|
1557
|
+
while (this.samples.length) {
|
|
1558
|
+
var sample = this.samples.shift();
|
|
1559
|
+
|
|
1560
|
+
duration = sample.duration;
|
|
1561
|
+
|
|
1562
|
+
if (duration <= 0) {
|
|
1563
|
+
log('remuxer: invalid sample duration at DTS: ' + this.nextDts + ' :' + duration);
|
|
1564
|
+
this.mp4track.len -= sample.size;
|
|
1565
|
+
continue;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
this.nextDts += duration;
|
|
1569
|
+
mp4Sample = {
|
|
1570
|
+
size: sample.size,
|
|
1571
|
+
duration: duration,
|
|
1572
|
+
cts: 0,
|
|
1573
|
+
flags: {
|
|
1574
|
+
isLeading: 0,
|
|
1575
|
+
isDependedOn: 0,
|
|
1576
|
+
hasRedundancy: 0,
|
|
1577
|
+
degradPrio: 0,
|
|
1578
|
+
dependsOn: 1
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
|
|
1582
|
+
payload.set(sample.units, offset);
|
|
1583
|
+
offset += sample.size;
|
|
1584
|
+
samples.push(mp4Sample);
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
if (!samples.length) return null;
|
|
1588
|
+
|
|
1589
|
+
return new Uint8Array(payload.buffer, 0, this.mp4track.len);
|
|
1590
|
+
}
|
|
1591
|
+
}]);
|
|
1592
|
+
return AACRemuxer;
|
|
1593
|
+
}(BaseRemuxer);
|
|
1594
|
+
|
|
1595
|
+
var H264Remuxer = function (_BaseRemuxer) {
|
|
1596
|
+
inherits(H264Remuxer, _BaseRemuxer);
|
|
1597
|
+
|
|
1598
|
+
function H264Remuxer() {
|
|
1599
|
+
classCallCheck(this, H264Remuxer);
|
|
1600
|
+
|
|
1601
|
+
var _this = possibleConstructorReturn(this, (H264Remuxer.__proto__ || Object.getPrototypeOf(H264Remuxer)).call(this));
|
|
1602
|
+
|
|
1603
|
+
_this.readyToDecode = false;
|
|
1604
|
+
_this.nextDts = 0;
|
|
1605
|
+
_this.dts = 0;
|
|
1606
|
+
_this.timescale = 1000;
|
|
1607
|
+
|
|
1608
|
+
_this.mp4track = {
|
|
1609
|
+
id: BaseRemuxer.getTrackID(),
|
|
1610
|
+
type: 'video',
|
|
1611
|
+
len: 0,
|
|
1612
|
+
fragmented: true,
|
|
1613
|
+
sps: '',
|
|
1614
|
+
pps: '',
|
|
1615
|
+
width: 0,
|
|
1616
|
+
height: 0,
|
|
1617
|
+
timescale: _this.timescale,
|
|
1618
|
+
duration: _this.timescale,
|
|
1619
|
+
samples: []
|
|
1620
|
+
};
|
|
1621
|
+
|
|
1622
|
+
_this.samples = [];
|
|
1623
|
+
_this.h264 = new H264Parser(_this);
|
|
1624
|
+
return _this;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
createClass(H264Remuxer, [{
|
|
1628
|
+
key: 'resetTrack',
|
|
1629
|
+
value: function resetTrack() {
|
|
1630
|
+
this.readyToDecode = false;
|
|
1631
|
+
this.mp4track.sps = '';
|
|
1632
|
+
this.mp4track.pps = '';
|
|
1633
|
+
}
|
|
1634
|
+
}, {
|
|
1635
|
+
key: 'remux',
|
|
1636
|
+
value: function remux(samples) {
|
|
1637
|
+
var sample = void 0,
|
|
1638
|
+
units = void 0,
|
|
1639
|
+
unit = void 0,
|
|
1640
|
+
size = void 0,
|
|
1641
|
+
keyFrame = void 0;
|
|
1642
|
+
var _iteratorNormalCompletion = true;
|
|
1643
|
+
var _didIteratorError = false;
|
|
1644
|
+
var _iteratorError = undefined;
|
|
1645
|
+
|
|
1646
|
+
try {
|
|
1647
|
+
for (var _iterator = samples[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
1648
|
+
sample = _step.value;
|
|
1649
|
+
|
|
1650
|
+
units = [];
|
|
1651
|
+
size = 0;
|
|
1652
|
+
keyFrame = false;
|
|
1653
|
+
var _iteratorNormalCompletion2 = true;
|
|
1654
|
+
var _didIteratorError2 = false;
|
|
1655
|
+
var _iteratorError2 = undefined;
|
|
1656
|
+
|
|
1657
|
+
try {
|
|
1658
|
+
for (var _iterator2 = sample.units[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
1659
|
+
unit = _step2.value;
|
|
1660
|
+
|
|
1661
|
+
if (this.h264.parseNAL(unit)) {
|
|
1662
|
+
units.push(unit);
|
|
1663
|
+
size += unit.getSize();
|
|
1664
|
+
if (!keyFrame) {
|
|
1665
|
+
keyFrame = unit.isKeyframe();
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
} catch (err) {
|
|
1670
|
+
_didIteratorError2 = true;
|
|
1671
|
+
_iteratorError2 = err;
|
|
1672
|
+
} finally {
|
|
1673
|
+
try {
|
|
1674
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
1675
|
+
_iterator2.return();
|
|
1676
|
+
}
|
|
1677
|
+
} finally {
|
|
1678
|
+
if (_didIteratorError2) {
|
|
1679
|
+
throw _iteratorError2;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
if (units.length > 0 && this.readyToDecode) {
|
|
1685
|
+
this.mp4track.len += size;
|
|
1686
|
+
this.samples.push({
|
|
1687
|
+
units: units,
|
|
1688
|
+
size: size,
|
|
1689
|
+
keyFrame: keyFrame,
|
|
1690
|
+
duration: sample.duration
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
} catch (err) {
|
|
1695
|
+
_didIteratorError = true;
|
|
1696
|
+
_iteratorError = err;
|
|
1697
|
+
} finally {
|
|
1698
|
+
try {
|
|
1699
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
1700
|
+
_iterator.return();
|
|
1701
|
+
}
|
|
1702
|
+
} finally {
|
|
1703
|
+
if (_didIteratorError) {
|
|
1704
|
+
throw _iteratorError;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
}, {
|
|
1710
|
+
key: 'getPayload',
|
|
1711
|
+
value: function getPayload() {
|
|
1712
|
+
if (!this.isReady()) {
|
|
1713
|
+
return null;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
var payload = new Uint8Array(this.mp4track.len);
|
|
1717
|
+
var offset = 0;
|
|
1718
|
+
var samples = this.mp4track.samples;
|
|
1719
|
+
var mp4Sample = void 0,
|
|
1720
|
+
duration = void 0;
|
|
1721
|
+
|
|
1722
|
+
this.dts = this.nextDts;
|
|
1723
|
+
|
|
1724
|
+
while (this.samples.length) {
|
|
1725
|
+
var sample = this.samples.shift(),
|
|
1726
|
+
units = sample.units;
|
|
1727
|
+
|
|
1728
|
+
duration = sample.duration;
|
|
1729
|
+
|
|
1730
|
+
if (duration <= 0) {
|
|
1731
|
+
log('remuxer: invalid sample duration at DTS: ' + this.nextDts + ' :' + duration);
|
|
1732
|
+
this.mp4track.len -= sample.size;
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
this.nextDts += duration;
|
|
1737
|
+
mp4Sample = {
|
|
1738
|
+
size: sample.size,
|
|
1739
|
+
duration: duration,
|
|
1740
|
+
cts: 0,
|
|
1741
|
+
flags: {
|
|
1742
|
+
isLeading: 0,
|
|
1743
|
+
isDependedOn: 0,
|
|
1744
|
+
hasRedundancy: 0,
|
|
1745
|
+
degradPrio: 0,
|
|
1746
|
+
isNonSync: sample.keyFrame ? 0 : 1,
|
|
1747
|
+
dependsOn: sample.keyFrame ? 2 : 1
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
var _iteratorNormalCompletion3 = true;
|
|
1752
|
+
var _didIteratorError3 = false;
|
|
1753
|
+
var _iteratorError3 = undefined;
|
|
1754
|
+
|
|
1755
|
+
try {
|
|
1756
|
+
for (var _iterator3 = units[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
1757
|
+
var unit = _step3.value;
|
|
1758
|
+
|
|
1759
|
+
payload.set(unit.getData(), offset);
|
|
1760
|
+
offset += unit.getSize();
|
|
1761
|
+
}
|
|
1762
|
+
} catch (err) {
|
|
1763
|
+
_didIteratorError3 = true;
|
|
1764
|
+
_iteratorError3 = err;
|
|
1765
|
+
} finally {
|
|
1766
|
+
try {
|
|
1767
|
+
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
1768
|
+
_iterator3.return();
|
|
1769
|
+
}
|
|
1770
|
+
} finally {
|
|
1771
|
+
if (_didIteratorError3) {
|
|
1772
|
+
throw _iteratorError3;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
samples.push(mp4Sample);
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
if (!samples.length) return null;
|
|
1781
|
+
|
|
1782
|
+
return new Uint8Array(payload.buffer, 0, this.mp4track.len);
|
|
1783
|
+
}
|
|
1784
|
+
}]);
|
|
1785
|
+
return H264Remuxer;
|
|
1786
|
+
}(BaseRemuxer);
|
|
1787
|
+
|
|
1788
|
+
function appendByteArray(buffer1, buffer2) {
|
|
1789
|
+
var tmp = new Uint8Array((buffer1.byteLength | 0) + (buffer2.byteLength | 0));
|
|
1790
|
+
tmp.set(buffer1, 0);
|
|
1791
|
+
tmp.set(buffer2, buffer1.byteLength | 0);
|
|
1792
|
+
return tmp;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
function secToTime(sec) {
|
|
1796
|
+
var seconds = void 0,
|
|
1797
|
+
hours = void 0,
|
|
1798
|
+
minutes = void 0,
|
|
1799
|
+
result = '';
|
|
1800
|
+
|
|
1801
|
+
seconds = Math.floor(sec);
|
|
1802
|
+
hours = parseInt(seconds / 3600, 10) % 24;
|
|
1803
|
+
minutes = parseInt(seconds / 60, 10) % 60;
|
|
1804
|
+
seconds = seconds < 0 ? 0 : seconds % 60;
|
|
1805
|
+
|
|
1806
|
+
if (hours > 0) {
|
|
1807
|
+
result += (hours < 10 ? '0' + hours : hours) + ':';
|
|
1808
|
+
}
|
|
1809
|
+
result += (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
|
|
1810
|
+
return result;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
var RemuxController = function (_Event) {
|
|
1814
|
+
inherits(RemuxController, _Event);
|
|
1815
|
+
|
|
1816
|
+
function RemuxController(streaming) {
|
|
1817
|
+
classCallCheck(this, RemuxController);
|
|
1818
|
+
|
|
1819
|
+
var _this = possibleConstructorReturn(this, (RemuxController.__proto__ || Object.getPrototypeOf(RemuxController)).call(this, 'remuxer'));
|
|
1820
|
+
|
|
1821
|
+
_this.initialized = false;
|
|
1822
|
+
_this.trackTypes = [];
|
|
1823
|
+
_this.tracks = {};
|
|
1824
|
+
_this.mediaDuration = streaming ? Infinity : 1000;
|
|
1825
|
+
return _this;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
createClass(RemuxController, [{
|
|
1829
|
+
key: 'addTrack',
|
|
1830
|
+
value: function addTrack(type) {
|
|
1831
|
+
if (type === 'video' || type === 'both') {
|
|
1832
|
+
this.tracks.video = new H264Remuxer();
|
|
1833
|
+
this.trackTypes.push('video');
|
|
1834
|
+
}
|
|
1835
|
+
if (type === 'audio' || type === 'both') {
|
|
1836
|
+
this.tracks.audio = new AACRemuxer();
|
|
1837
|
+
this.trackTypes.push('audio');
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}, {
|
|
1841
|
+
key: 'reset',
|
|
1842
|
+
value: function reset() {
|
|
1843
|
+
var _iteratorNormalCompletion = true;
|
|
1844
|
+
var _didIteratorError = false;
|
|
1845
|
+
var _iteratorError = undefined;
|
|
1846
|
+
|
|
1847
|
+
try {
|
|
1848
|
+
for (var _iterator = this.trackTypes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
1849
|
+
var type = _step.value;
|
|
1850
|
+
|
|
1851
|
+
this.tracks[type].resetTrack();
|
|
1852
|
+
}
|
|
1853
|
+
} catch (err) {
|
|
1854
|
+
_didIteratorError = true;
|
|
1855
|
+
_iteratorError = err;
|
|
1856
|
+
} finally {
|
|
1857
|
+
try {
|
|
1858
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
1859
|
+
_iterator.return();
|
|
1860
|
+
}
|
|
1861
|
+
} finally {
|
|
1862
|
+
if (_didIteratorError) {
|
|
1863
|
+
throw _iteratorError;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
this.initialized = false;
|
|
1869
|
+
}
|
|
1870
|
+
}, {
|
|
1871
|
+
key: 'destroy',
|
|
1872
|
+
value: function destroy() {
|
|
1873
|
+
this.tracks = {};
|
|
1874
|
+
this.offAll();
|
|
1875
|
+
}
|
|
1876
|
+
}, {
|
|
1877
|
+
key: 'flush',
|
|
1878
|
+
value: function flush() {
|
|
1879
|
+
if (!this.initialized) {
|
|
1880
|
+
if (this.isReady()) {
|
|
1881
|
+
this.dispatch('ready');
|
|
1882
|
+
var _iteratorNormalCompletion2 = true;
|
|
1883
|
+
var _didIteratorError2 = false;
|
|
1884
|
+
var _iteratorError2 = undefined;
|
|
1885
|
+
|
|
1886
|
+
try {
|
|
1887
|
+
for (var _iterator2 = this.trackTypes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
1888
|
+
var type = _step2.value;
|
|
1889
|
+
|
|
1890
|
+
var track = this.tracks[type];
|
|
1891
|
+
var data = {
|
|
1892
|
+
type: type,
|
|
1893
|
+
payload: MP4.initSegment([track.mp4track], this.mediaDuration, track.mp4track.timescale)
|
|
1894
|
+
};
|
|
1895
|
+
this.dispatch('buffer', data);
|
|
1896
|
+
}
|
|
1897
|
+
} catch (err) {
|
|
1898
|
+
_didIteratorError2 = true;
|
|
1899
|
+
_iteratorError2 = err;
|
|
1900
|
+
} finally {
|
|
1901
|
+
try {
|
|
1902
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
1903
|
+
_iterator2.return();
|
|
1904
|
+
}
|
|
1905
|
+
} finally {
|
|
1906
|
+
if (_didIteratorError2) {
|
|
1907
|
+
throw _iteratorError2;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
log('Initial segment generated.');
|
|
1913
|
+
this.initialized = true;
|
|
1914
|
+
}
|
|
1915
|
+
} else {
|
|
1916
|
+
var _iteratorNormalCompletion3 = true;
|
|
1917
|
+
var _didIteratorError3 = false;
|
|
1918
|
+
var _iteratorError3 = undefined;
|
|
1919
|
+
|
|
1920
|
+
try {
|
|
1921
|
+
for (var _iterator3 = this.trackTypes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
1922
|
+
var _type = _step3.value;
|
|
1923
|
+
|
|
1924
|
+
var _track = this.tracks[_type];
|
|
1925
|
+
var pay = _track.getPayload();
|
|
1926
|
+
if (pay && pay.byteLength) {
|
|
1927
|
+
var moof = MP4.moof(_track.seq, _track.dts, _track.mp4track);
|
|
1928
|
+
var mdat = MP4.mdat(pay);
|
|
1929
|
+
var payload = appendByteArray(moof, mdat);
|
|
1930
|
+
var _data = {
|
|
1931
|
+
type: _type,
|
|
1932
|
+
payload: payload,
|
|
1933
|
+
dts: _track.dts
|
|
1934
|
+
};
|
|
1935
|
+
this.dispatch('buffer', _data);
|
|
1936
|
+
var duration = secToTime(_track.dts / 1000);
|
|
1937
|
+
log('put segment (' + _type + '): ' + _track.seq + ' dts: ' + _track.dts + ' samples: ' + _track.mp4track.samples.length + ' second: ' + duration);
|
|
1938
|
+
_track.flush();
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
} catch (err) {
|
|
1942
|
+
_didIteratorError3 = true;
|
|
1943
|
+
_iteratorError3 = err;
|
|
1944
|
+
} finally {
|
|
1945
|
+
try {
|
|
1946
|
+
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
1947
|
+
_iterator3.return();
|
|
1948
|
+
}
|
|
1949
|
+
} finally {
|
|
1950
|
+
if (_didIteratorError3) {
|
|
1951
|
+
throw _iteratorError3;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
}, {
|
|
1958
|
+
key: 'isReady',
|
|
1959
|
+
value: function isReady() {
|
|
1960
|
+
var _iteratorNormalCompletion4 = true;
|
|
1961
|
+
var _didIteratorError4 = false;
|
|
1962
|
+
var _iteratorError4 = undefined;
|
|
1963
|
+
|
|
1964
|
+
try {
|
|
1965
|
+
for (var _iterator4 = this.trackTypes[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
|
1966
|
+
var type = _step4.value;
|
|
1967
|
+
|
|
1968
|
+
if (!this.tracks[type].readyToDecode || !this.tracks[type].samples.length) return false;
|
|
1969
|
+
}
|
|
1970
|
+
} catch (err) {
|
|
1971
|
+
_didIteratorError4 = true;
|
|
1972
|
+
_iteratorError4 = err;
|
|
1973
|
+
} finally {
|
|
1974
|
+
try {
|
|
1975
|
+
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
|
1976
|
+
_iterator4.return();
|
|
1977
|
+
}
|
|
1978
|
+
} finally {
|
|
1979
|
+
if (_didIteratorError4) {
|
|
1980
|
+
throw _iteratorError4;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
return true;
|
|
1986
|
+
}
|
|
1987
|
+
}, {
|
|
1988
|
+
key: 'remux',
|
|
1989
|
+
value: function remux(data) {
|
|
1990
|
+
var _iteratorNormalCompletion5 = true;
|
|
1991
|
+
var _didIteratorError5 = false;
|
|
1992
|
+
var _iteratorError5 = undefined;
|
|
1993
|
+
|
|
1994
|
+
try {
|
|
1995
|
+
for (var _iterator5 = this.trackTypes[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
|
1996
|
+
var type = _step5.value;
|
|
1997
|
+
|
|
1998
|
+
var samples = data[type];
|
|
1999
|
+
if (type === 'audio' && this.tracks.video && !this.tracks.video.readyToDecode) continue; /* if video is present, don't add audio until video get ready */
|
|
2000
|
+
if (samples.length > 0) {
|
|
2001
|
+
this.tracks[type].remux(samples);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
} catch (err) {
|
|
2005
|
+
_didIteratorError5 = true;
|
|
2006
|
+
_iteratorError5 = err;
|
|
2007
|
+
} finally {
|
|
2008
|
+
try {
|
|
2009
|
+
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
|
2010
|
+
_iterator5.return();
|
|
2011
|
+
}
|
|
2012
|
+
} finally {
|
|
2013
|
+
if (_didIteratorError5) {
|
|
2014
|
+
throw _iteratorError5;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
this.flush();
|
|
2020
|
+
}
|
|
2021
|
+
}]);
|
|
2022
|
+
return RemuxController;
|
|
2023
|
+
}(Event);
|
|
2024
|
+
|
|
2025
|
+
var BufferController = function (_Event) {
|
|
2026
|
+
inherits(BufferController, _Event);
|
|
2027
|
+
|
|
2028
|
+
function BufferController(sourceBuffer, type) {
|
|
2029
|
+
classCallCheck(this, BufferController);
|
|
2030
|
+
|
|
2031
|
+
var _this = possibleConstructorReturn(this, (BufferController.__proto__ || Object.getPrototypeOf(BufferController)).call(this, 'buffer'));
|
|
2032
|
+
|
|
2033
|
+
_this.type = type;
|
|
2034
|
+
_this.queue = new Uint8Array();
|
|
2035
|
+
|
|
2036
|
+
_this.cleaning = false;
|
|
2037
|
+
_this.pendingCleaning = 0;
|
|
2038
|
+
_this.cleanOffset = 30;
|
|
2039
|
+
_this.cleanRanges = [];
|
|
2040
|
+
|
|
2041
|
+
_this.sourceBuffer = sourceBuffer;
|
|
2042
|
+
_this.sourceBuffer.addEventListener('updateend', function () {
|
|
2043
|
+
if (_this.pendingCleaning > 0) {
|
|
2044
|
+
_this.initCleanup(_this.pendingCleaning);
|
|
2045
|
+
_this.pendingCleaning = 0;
|
|
2046
|
+
}
|
|
2047
|
+
_this.cleaning = false;
|
|
2048
|
+
if (_this.cleanRanges.length) {
|
|
2049
|
+
_this.doCleanup();
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
});
|
|
2053
|
+
|
|
2054
|
+
_this.sourceBuffer.addEventListener('error', function () {
|
|
2055
|
+
_this.dispatch('error', { type: _this.type, name: 'buffer', error: 'buffer error' });
|
|
2056
|
+
});
|
|
2057
|
+
return _this;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
createClass(BufferController, [{
|
|
2061
|
+
key: 'destroy',
|
|
2062
|
+
value: function destroy() {
|
|
2063
|
+
this.queue = null;
|
|
2064
|
+
this.sourceBuffer = null;
|
|
2065
|
+
this.offAll();
|
|
2066
|
+
}
|
|
2067
|
+
}, {
|
|
2068
|
+
key: 'doCleanup',
|
|
2069
|
+
value: function doCleanup() {
|
|
2070
|
+
if (!this.cleanRanges.length) {
|
|
2071
|
+
this.cleaning = false;
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
var range = this.cleanRanges.shift();
|
|
2075
|
+
log(this.type + ' remove range [' + range[0] + ' - ' + range[1] + ')');
|
|
2076
|
+
this.cleaning = true;
|
|
2077
|
+
this.sourceBuffer.remove(range[0], range[1]);
|
|
2078
|
+
}
|
|
2079
|
+
}, {
|
|
2080
|
+
key: 'initCleanup',
|
|
2081
|
+
value: function initCleanup(cleanMaxLimit) {
|
|
2082
|
+
if (this.sourceBuffer.updating) {
|
|
2083
|
+
this.pendingCleaning = cleanMaxLimit;
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
if (this.sourceBuffer.buffered && this.sourceBuffer.buffered.length && !this.cleaning) {
|
|
2087
|
+
for (var i = 0; i < this.sourceBuffer.buffered.length; ++i) {
|
|
2088
|
+
var start = this.sourceBuffer.buffered.start(i);
|
|
2089
|
+
var end = this.sourceBuffer.buffered.end(i);
|
|
2090
|
+
|
|
2091
|
+
if (cleanMaxLimit - start > this.cleanOffset) {
|
|
2092
|
+
end = cleanMaxLimit - this.cleanOffset;
|
|
2093
|
+
if (start < end) {
|
|
2094
|
+
this.cleanRanges.push([start, end]);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
this.doCleanup();
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}, {
|
|
2102
|
+
key: 'doAppend',
|
|
2103
|
+
value: function doAppend() {
|
|
2104
|
+
if (!this.queue.length) return;
|
|
2105
|
+
|
|
2106
|
+
if (this.sourceBuffer.updating) {
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
try {
|
|
2111
|
+
this.sourceBuffer.appendBuffer(this.queue);
|
|
2112
|
+
this.queue = new Uint8Array();
|
|
2113
|
+
} catch (e) {
|
|
2114
|
+
if (e.name === 'QuotaExceededError') {
|
|
2115
|
+
log(this.type + ' buffer quota full');
|
|
2116
|
+
this.dispatch('error', { type: this.type, name: 'QuotaExceeded', error: 'buffer error' });
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
error('Error occured while appending ' + this.type + ' buffer - ' + e.name + ': ' + e.message);
|
|
2120
|
+
this.dispatch('error', { type: this.type, name: 'unexpectedError', error: 'buffer error' });
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}, {
|
|
2124
|
+
key: 'feed',
|
|
2125
|
+
value: function feed(data) {
|
|
2126
|
+
this.queue = appendByteArray(this.queue, data);
|
|
2127
|
+
}
|
|
2128
|
+
}]);
|
|
2129
|
+
return BufferController;
|
|
2130
|
+
}(Event);
|
|
2131
|
+
|
|
2132
|
+
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
|
|
2133
|
+
|
|
2134
|
+
var JMuxmer = function (_Event) {
|
|
2135
|
+
inherits(JMuxmer, _Event);
|
|
2136
|
+
createClass(JMuxmer, null, [{
|
|
2137
|
+
key: 'isSupported',
|
|
2138
|
+
value: function isSupported(codec) {
|
|
2139
|
+
return window.MediaSource && window.MediaSource.isTypeSupported(codec);
|
|
2140
|
+
}
|
|
2141
|
+
}]);
|
|
2142
|
+
|
|
2143
|
+
function JMuxmer(options) {
|
|
2144
|
+
classCallCheck(this, JMuxmer);
|
|
2145
|
+
|
|
2146
|
+
var _this = possibleConstructorReturn(this, (JMuxmer.__proto__ || Object.getPrototypeOf(JMuxmer)).call(this, 'jmuxer'));
|
|
2147
|
+
|
|
2148
|
+
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
|
|
2149
|
+
|
|
2150
|
+
var defaults$$1 = {
|
|
2151
|
+
node: '',
|
|
2152
|
+
mode: 'both', // both, audio, video
|
|
2153
|
+
flushingTime: 1500,
|
|
2154
|
+
clearBuffer: true,
|
|
2155
|
+
onReady: null, // function called when MSE is ready to accept frames
|
|
2156
|
+
fps: 30,
|
|
2157
|
+
debug: false
|
|
2158
|
+
};
|
|
2159
|
+
_this.options = Object.assign({}, defaults$$1, options);
|
|
2160
|
+
|
|
2161
|
+
if (_this.options.debug) {
|
|
2162
|
+
setLogger();
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
if (typeof _this.options.node === 'string' && _this.options.node == '') {
|
|
2166
|
+
error('no video element were found to render, provide a valid video element');
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
if (!_this.options.fps) {
|
|
2170
|
+
_this.options.fps = 30;
|
|
2171
|
+
}
|
|
2172
|
+
_this.frameDuration = 1000 / _this.options.fps | 0;
|
|
2173
|
+
|
|
2174
|
+
_this.lastVideoTimestamp = 0;
|
|
2175
|
+
_this.lastAudioTimestamp = 0;
|
|
2176
|
+
|
|
2177
|
+
_this.node = typeof _this.options.node === 'string' ? document.getElementById(_this.options.node) : _this.options.node;
|
|
2178
|
+
|
|
2179
|
+
_this.sourceBuffers = {};
|
|
2180
|
+
_this.isMSESupported = !!window.MediaSource;
|
|
2181
|
+
|
|
2182
|
+
if (!_this.isMSESupported) {
|
|
2183
|
+
throw 'Oops! Browser does not support media source extension.';
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
_this.setupMSE();
|
|
2187
|
+
_this.remuxController = new RemuxController(_this.options.clearBuffer);
|
|
2188
|
+
_this.remuxController.addTrack(_this.options.mode);
|
|
2189
|
+
|
|
2190
|
+
_this.mseReady = false;
|
|
2191
|
+
_this.lastCleaningTime = Date.now();
|
|
2192
|
+
_this.keyframeCache = [];
|
|
2193
|
+
_this.frameCounter = 0;
|
|
2194
|
+
|
|
2195
|
+
/* events callback */
|
|
2196
|
+
_this.remuxController.on('buffer', _this.onBuffer.bind(_this));
|
|
2197
|
+
_this.remuxController.on('ready', _this.createBuffer.bind(_this));
|
|
2198
|
+
_this.startInterval();
|
|
2199
|
+
return _this;
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
createClass(JMuxmer, [{
|
|
2203
|
+
key: 'setupMSE',
|
|
2204
|
+
value: function setupMSE() {
|
|
2205
|
+
this.mediaSource = new MediaSource();
|
|
2206
|
+
this.node.src = URL.createObjectURL(this.mediaSource);
|
|
2207
|
+
this.mediaSource.addEventListener('sourceopen', this.onMSEOpen.bind(this));
|
|
2208
|
+
this.mediaSource.addEventListener('sourceclose', this.onMSEClose.bind(this));
|
|
2209
|
+
this.mediaSource.addEventListener('webkitsourceopen', this.onMSEOpen.bind(this));
|
|
2210
|
+
this.mediaSource.addEventListener('webkitsourceclose', this.onMSEClose.bind(this));
|
|
2211
|
+
}
|
|
2212
|
+
}, {
|
|
2213
|
+
key: 'feed',
|
|
2214
|
+
value: function feed(data) {
|
|
2215
|
+
var remux = false,
|
|
2216
|
+
nalus = void 0,
|
|
2217
|
+
aacFrames = void 0,
|
|
2218
|
+
duration = void 0,
|
|
2219
|
+
chunks = {
|
|
2220
|
+
video: [],
|
|
2221
|
+
audio: []
|
|
2222
|
+
};
|
|
2223
|
+
|
|
2224
|
+
if (!data || !this.remuxController) return;
|
|
2225
|
+
|
|
2226
|
+
duration = data.duration ? parseInt(data.duration) : 0;
|
|
2227
|
+
if (data.video) {
|
|
2228
|
+
|
|
2229
|
+
duration = this.getVideoDuration(data.video_timestamp);
|
|
2230
|
+
|
|
2231
|
+
nalus = H264Parser.extractNALu(data.video);
|
|
2232
|
+
if (nalus.length > 0) {
|
|
2233
|
+
chunks.video = this.getVideoFrames(nalus, duration);
|
|
2234
|
+
remux = true;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
if (data.audio) {
|
|
2238
|
+
|
|
2239
|
+
duration = this.getAudioDuration(data.audio_timestamp);
|
|
2240
|
+
|
|
2241
|
+
aacFrames = AACParser.extractAAC(data.audio);
|
|
2242
|
+
if (aacFrames.length > 0) {
|
|
2243
|
+
chunks.audio = this.getAudioFrames(aacFrames, duration);
|
|
2244
|
+
remux = true;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
if (!remux) {
|
|
2248
|
+
error('Input object must have video and/or audio property. Make sure it is not empty and valid typed array');
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
this.remuxController.remux(chunks);
|
|
2252
|
+
|
|
2253
|
+
}
|
|
2254
|
+
}, {
|
|
2255
|
+
key: 'getVideoDuration',
|
|
2256
|
+
value: function getVideoDuration(timestamp) {
|
|
2257
|
+
let duration = timestamp - this.lastVideoTimestamp
|
|
2258
|
+
this.lastVideoTimestamp = timestamp
|
|
2259
|
+
if(duration > 1000){
|
|
2260
|
+
duration = 0
|
|
2261
|
+
}
|
|
2262
|
+
return duration
|
|
2263
|
+
}
|
|
2264
|
+
}, {
|
|
2265
|
+
key: 'getAudioDuration',
|
|
2266
|
+
value: function getAudioDuration(timestamp) {
|
|
2267
|
+
let duration = timestamp - this.lastAudioTimestamp
|
|
2268
|
+
this.lastAudioTimestamp = timestamp
|
|
2269
|
+
if(duration > 1000){
|
|
2270
|
+
duration = 0
|
|
2271
|
+
}
|
|
2272
|
+
return duration
|
|
2273
|
+
}
|
|
2274
|
+
}, {
|
|
2275
|
+
key: 'getVideoFrames',
|
|
2276
|
+
value: function getVideoFrames(nalus, duration) {
|
|
2277
|
+
var nalu = void 0,
|
|
2278
|
+
units = [],
|
|
2279
|
+
samples = [],
|
|
2280
|
+
naluObj = void 0,
|
|
2281
|
+
sampleDuration = void 0,
|
|
2282
|
+
adjustDuration = 0,
|
|
2283
|
+
numberOfFrames = [];
|
|
2284
|
+
|
|
2285
|
+
var _iteratorNormalCompletion = true;
|
|
2286
|
+
var _didIteratorError = false;
|
|
2287
|
+
var _iteratorError = undefined;
|
|
2288
|
+
|
|
2289
|
+
try {
|
|
2290
|
+
for (var _iterator = nalus[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
2291
|
+
nalu = _step.value;
|
|
2292
|
+
|
|
2293
|
+
naluObj = new NALU(nalu);
|
|
2294
|
+
units.push(naluObj);
|
|
2295
|
+
if (naluObj.type() === NALU.IDR || naluObj.type() === NALU.NDR) {
|
|
2296
|
+
samples.push({ units: units });
|
|
2297
|
+
units = [];
|
|
2298
|
+
if (this.options.clearBuffer) {
|
|
2299
|
+
if (naluObj.type() === NALU.IDR) {
|
|
2300
|
+
numberOfFrames.push(this.frameCounter);
|
|
2301
|
+
}
|
|
2302
|
+
this.frameCounter++;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
} catch (err) {
|
|
2307
|
+
_didIteratorError = true;
|
|
2308
|
+
_iteratorError = err;
|
|
2309
|
+
} finally {
|
|
2310
|
+
try {
|
|
2311
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
2312
|
+
_iterator.return();
|
|
2313
|
+
}
|
|
2314
|
+
} finally {
|
|
2315
|
+
if (_didIteratorError) {
|
|
2316
|
+
throw _iteratorError;
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
if (duration) {
|
|
2321
|
+
sampleDuration = duration / samples.length | 0;
|
|
2322
|
+
adjustDuration = duration - sampleDuration * samples.length;
|
|
2323
|
+
} else {
|
|
2324
|
+
sampleDuration = this.frameDuration;
|
|
2325
|
+
}
|
|
2326
|
+
samples.map(function (sample) {
|
|
2327
|
+
sample.duration = adjustDuration > 0 ? sampleDuration + 1 : sampleDuration;
|
|
2328
|
+
if (adjustDuration !== 0) {
|
|
2329
|
+
adjustDuration--;
|
|
2330
|
+
}
|
|
2331
|
+
});
|
|
2332
|
+
|
|
2333
|
+
/* cache keyframe times if clearBuffer set true */
|
|
2334
|
+
if (this.options.clearBuffer) {
|
|
2335
|
+
numberOfFrames = numberOfFrames.map(function (total) {
|
|
2336
|
+
return total * sampleDuration / 1000;
|
|
2337
|
+
});
|
|
2338
|
+
this.keyframeCache = this.keyframeCache.concat(numberOfFrames);
|
|
2339
|
+
}
|
|
2340
|
+
return samples;
|
|
2341
|
+
}
|
|
2342
|
+
}, {
|
|
2343
|
+
key: 'getAudioFrames',
|
|
2344
|
+
value: function getAudioFrames(aacFrames, duration) {
|
|
2345
|
+
var samples = [],
|
|
2346
|
+
units = void 0,
|
|
2347
|
+
sampleDuration = void 0,
|
|
2348
|
+
adjustDuration = 0;
|
|
2349
|
+
|
|
2350
|
+
var _iteratorNormalCompletion2 = true;
|
|
2351
|
+
var _didIteratorError2 = false;
|
|
2352
|
+
var _iteratorError2 = undefined;
|
|
2353
|
+
|
|
2354
|
+
try {
|
|
2355
|
+
for (var _iterator2 = aacFrames[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
2356
|
+
units = _step2.value;
|
|
2357
|
+
|
|
2358
|
+
samples.push({ units: units });
|
|
2359
|
+
}
|
|
2360
|
+
} catch (err) {
|
|
2361
|
+
_didIteratorError2 = true;
|
|
2362
|
+
_iteratorError2 = err;
|
|
2363
|
+
} finally {
|
|
2364
|
+
try {
|
|
2365
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
2366
|
+
_iterator2.return();
|
|
2367
|
+
}
|
|
2368
|
+
} finally {
|
|
2369
|
+
if (_didIteratorError2) {
|
|
2370
|
+
throw _iteratorError2;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
if (duration) {
|
|
2376
|
+
sampleDuration = duration / samples.length | 0;
|
|
2377
|
+
adjustDuration = duration - sampleDuration * samples.length;
|
|
2378
|
+
} else {
|
|
2379
|
+
sampleDuration = this.frameDuration;
|
|
2380
|
+
}
|
|
2381
|
+
samples.map(function (sample) {
|
|
2382
|
+
sample.duration = adjustDuration > 0 ? sampleDuration + 1 : sampleDuration;
|
|
2383
|
+
if (adjustDuration !== 0) {
|
|
2384
|
+
adjustDuration--;
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
return samples;
|
|
2388
|
+
}
|
|
2389
|
+
}, {
|
|
2390
|
+
key: 'destroy',
|
|
2391
|
+
value: function destroy() {
|
|
2392
|
+
this.stopInterval();
|
|
2393
|
+
if (this.mediaSource) {
|
|
2394
|
+
try {
|
|
2395
|
+
if (this.bufferControllers) {
|
|
2396
|
+
this.mediaSource.endOfStream();
|
|
2397
|
+
}
|
|
2398
|
+
} catch (e) {
|
|
2399
|
+
error('mediasource is not available to end ' + e.message);
|
|
2400
|
+
}
|
|
2401
|
+
this.mediaSource = null;
|
|
2402
|
+
}
|
|
2403
|
+
if (this.remuxController) {
|
|
2404
|
+
this.remuxController.destroy();
|
|
2405
|
+
this.remuxController = null;
|
|
2406
|
+
}
|
|
2407
|
+
if (this.bufferControllers) {
|
|
2408
|
+
for (var type in this.bufferControllers) {
|
|
2409
|
+
this.bufferControllers[type].destroy();
|
|
2410
|
+
}
|
|
2411
|
+
this.bufferControllers = null;
|
|
2412
|
+
}
|
|
2413
|
+
this.node = false;
|
|
2414
|
+
this.mseReady = false;
|
|
2415
|
+
this.videoStarted = false;
|
|
2416
|
+
}
|
|
2417
|
+
}, {
|
|
2418
|
+
key: 'createBuffer',
|
|
2419
|
+
value: function createBuffer() {
|
|
2420
|
+
if (!this.mseReady || !this.remuxController || !this.remuxController.isReady() || this.bufferControllers) return;
|
|
2421
|
+
this.bufferControllers = {};
|
|
2422
|
+
for (var type in this.remuxController.tracks) {
|
|
2423
|
+
var track = this.remuxController.tracks[type];
|
|
2424
|
+
if (!JMuxmer.isSupported(type + '/mp4; codecs="' + track.mp4track.codec + '"')) {
|
|
2425
|
+
error('Browser does not support codec');
|
|
2426
|
+
return false;
|
|
2427
|
+
}
|
|
2428
|
+
var sb = this.mediaSource.addSourceBuffer(type + '/mp4; codecs="' + track.mp4track.codec + '"');
|
|
2429
|
+
this.bufferControllers[type] = new BufferController(sb, type);
|
|
2430
|
+
this.sourceBuffers[type] = sb;
|
|
2431
|
+
this.bufferControllers[type].on('error', this.onBufferError.bind(this));
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
}, {
|
|
2435
|
+
key: 'startInterval',
|
|
2436
|
+
value: function startInterval() {
|
|
2437
|
+
var _this2 = this;
|
|
2438
|
+
|
|
2439
|
+
this.interval = setInterval(function () {
|
|
2440
|
+
if (_this2.bufferControllers) {
|
|
2441
|
+
_this2.releaseBuffer();
|
|
2442
|
+
_this2.clearBuffer();
|
|
2443
|
+
}
|
|
2444
|
+
}, this.options.flushingTime);
|
|
2445
|
+
}
|
|
2446
|
+
}, {
|
|
2447
|
+
key: 'stopInterval',
|
|
2448
|
+
value: function stopInterval() {
|
|
2449
|
+
if (this.interval) {
|
|
2450
|
+
clearInterval(this.interval);
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
}, {
|
|
2454
|
+
key: 'releaseBuffer',
|
|
2455
|
+
value: function releaseBuffer() {
|
|
2456
|
+
for (var type in this.bufferControllers) {
|
|
2457
|
+
this.bufferControllers[type].doAppend();
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}, {
|
|
2461
|
+
key: 'getSafeBufferClearLimit',
|
|
2462
|
+
value: function getSafeBufferClearLimit(offset) {
|
|
2463
|
+
var maxLimit = this.options.mode === 'audio' && offset || 0,
|
|
2464
|
+
adjacentOffset = void 0;
|
|
2465
|
+
|
|
2466
|
+
for (var i = 0; i < this.keyframeCache.length; i++) {
|
|
2467
|
+
if (this.keyframeCache[i] >= offset) {
|
|
2468
|
+
break;
|
|
2469
|
+
}
|
|
2470
|
+
adjacentOffset = this.keyframeCache[i];
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
if (adjacentOffset) {
|
|
2474
|
+
this.keyframeCache = this.keyframeCache.filter(function (keyframePoint) {
|
|
2475
|
+
if (keyframePoint < adjacentOffset) {
|
|
2476
|
+
maxLimit = keyframePoint;
|
|
2477
|
+
}
|
|
2478
|
+
return keyframePoint >= adjacentOffset;
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
return maxLimit;
|
|
2483
|
+
}
|
|
2484
|
+
}, {
|
|
2485
|
+
key: 'clearBuffer',
|
|
2486
|
+
value: function clearBuffer() {
|
|
2487
|
+
if (this.options.clearBuffer && Date.now() - this.lastCleaningTime > 10000) {
|
|
2488
|
+
for (var type in this.bufferControllers) {
|
|
2489
|
+
var cleanMaxLimit = this.getSafeBufferClearLimit(this.node.currentTime);
|
|
2490
|
+
this.bufferControllers[type].initCleanup(cleanMaxLimit);
|
|
2491
|
+
}
|
|
2492
|
+
this.lastCleaningTime = Date.now();
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
}, {
|
|
2496
|
+
key: 'onBuffer',
|
|
2497
|
+
value: function onBuffer(data) {
|
|
2498
|
+
if (this.bufferControllers && this.bufferControllers[data.type]) {
|
|
2499
|
+
this.bufferControllers[data.type].feed(data.payload);
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
/* Events on MSE */
|
|
2504
|
+
|
|
2505
|
+
}, {
|
|
2506
|
+
key: 'onMSEOpen',
|
|
2507
|
+
value: function onMSEOpen() {
|
|
2508
|
+
this.mseReady = true;
|
|
2509
|
+
if (typeof this.options.onReady === 'function') {
|
|
2510
|
+
this.options.onReady();
|
|
2511
|
+
this.options.onReady = null;
|
|
2512
|
+
}
|
|
2513
|
+
this.createBuffer();
|
|
2514
|
+
}
|
|
2515
|
+
}, {
|
|
2516
|
+
key: 'onMSEClose',
|
|
2517
|
+
value: function onMSEClose() {
|
|
2518
|
+
this.mseReady = false;
|
|
2519
|
+
this.videoStarted = false;
|
|
2520
|
+
}
|
|
2521
|
+
}, {
|
|
2522
|
+
key: 'onBufferError',
|
|
2523
|
+
value: function onBufferError(data) {
|
|
2524
|
+
if (data.name == 'QuotaExceeded') {
|
|
2525
|
+
this.bufferControllers[data.type].initCleanup(this.node.currentTime);
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
if (this.mediaSource.sourceBuffers.length > 0 && this.sourceBuffers[data.type]) {
|
|
2530
|
+
this.mediaSource.removeSourceBuffer(this.sourceBuffers[data.type]);
|
|
2531
|
+
}
|
|
2532
|
+
if (this.mediaSource.sourceBuffers.length == 0) {
|
|
2533
|
+
try {
|
|
2534
|
+
this.mediaSource.endOfStream();
|
|
2535
|
+
} catch (e) {
|
|
2536
|
+
error('mediasource is not available to end');
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}]);
|
|
2541
|
+
return JMuxmer;
|
|
2542
|
+
}(Event);
|
|
2543
|
+
|
|
2544
|
+
return JMuxmer;
|
|
2545
|
+
|
|
2546
|
+
})));
|