jmuxer 2.0.1 → 2.0.4
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/README.md +22 -14
- package/dist/jmuxer.js +314 -158
- package/dist/jmuxer.min.js +1 -1
- package/package.json +9 -8
- package/rollup.config.js +2 -1
- package/src/controller/buffer.js +24 -20
- package/src/controller/remux.js +5 -2
- package/src/jmuxer.js +116 -50
- package/src/parsers/h264.js +33 -8
- package/src/remuxer/aac.js +3 -3
- package/src/remuxer/h264.js +5 -3
- package/src/util/exp-golomb.js +6 -0
package/src/jmuxer.js
CHANGED
|
@@ -8,19 +8,22 @@ import RemuxController from './controller/remux.js';
|
|
|
8
8
|
import BufferController from './controller/buffer.js';
|
|
9
9
|
import { Duplex } from 'stream';
|
|
10
10
|
|
|
11
|
-
export default class
|
|
11
|
+
export default class JMuxer extends Event {
|
|
12
12
|
static isSupported(codec) {
|
|
13
13
|
return (window.MediaSource && window.MediaSource.isTypeSupported(codec));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
constructor(options) {
|
|
17
17
|
super('jmuxer');
|
|
18
|
+
this.isReset = false;
|
|
18
19
|
let defaults = {
|
|
19
20
|
node: '',
|
|
20
21
|
mode: 'both', // both, audio, video
|
|
21
|
-
flushingTime:
|
|
22
|
+
flushingTime: 500,
|
|
23
|
+
maxDelay: 500,
|
|
22
24
|
clearBuffer: true,
|
|
23
25
|
fps: 30,
|
|
26
|
+
readFpsFromTrack: false, // set true to fetch fps value from NALu
|
|
24
27
|
debug: false,
|
|
25
28
|
onReady: function() {}, // function called when MSE is ready to accept frames
|
|
26
29
|
onError: function() {}, // function called when jmuxer encounters any buffer related error
|
|
@@ -37,11 +40,8 @@ export default class JMuxmer extends Event {
|
|
|
37
40
|
this.frameDuration = (1000 / this.options.fps) | 0;
|
|
38
41
|
this.remuxController = new RemuxController(this.env);
|
|
39
42
|
this.remuxController.addTrack(this.options.mode);
|
|
40
|
-
|
|
41
|
-
this.
|
|
42
|
-
this.kfCounter = 0;
|
|
43
|
-
this.pendingUnits = {};
|
|
44
|
-
this.remainingData = new Uint8Array();
|
|
43
|
+
|
|
44
|
+
this.initData();
|
|
45
45
|
|
|
46
46
|
/* events callback */
|
|
47
47
|
this.remuxController.on('buffer', this.onBuffer.bind(this));
|
|
@@ -49,6 +49,14 @@ export default class JMuxmer extends Event {
|
|
|
49
49
|
this.remuxController.on('ready', this.createBuffer.bind(this));
|
|
50
50
|
this.initBrowser();
|
|
51
51
|
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
initData() {
|
|
55
|
+
this.lastCleaningTime = Date.now();
|
|
56
|
+
this.kfPosition = [];
|
|
57
|
+
this.kfCounter = 0;
|
|
58
|
+
this.pendingUnits = {};
|
|
59
|
+
this.remainingData = new Uint8Array();
|
|
52
60
|
this.startInterval();
|
|
53
61
|
}
|
|
54
62
|
|
|
@@ -59,7 +67,6 @@ export default class JMuxmer extends Event {
|
|
|
59
67
|
this.node = typeof this.options.node === 'string' ? document.getElementById(this.options.node) : this.options.node;
|
|
60
68
|
this.mseReady = false;
|
|
61
69
|
this.setupMSE();
|
|
62
|
-
this.sourceBuffers = {};
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
createStream() {
|
|
@@ -88,13 +95,26 @@ export default class JMuxmer extends Event {
|
|
|
88
95
|
}
|
|
89
96
|
this.isMSESupported = !!window.MediaSource;
|
|
90
97
|
this.mediaSource = new MediaSource();
|
|
91
|
-
this.
|
|
98
|
+
this.url = URL.createObjectURL(this.mediaSource);
|
|
99
|
+
this.node.src = this.url;
|
|
100
|
+
this.mseEnded = false;
|
|
92
101
|
this.mediaSource.addEventListener('sourceopen', this.onMSEOpen.bind(this));
|
|
93
102
|
this.mediaSource.addEventListener('sourceclose', this.onMSEClose.bind(this));
|
|
94
103
|
this.mediaSource.addEventListener('webkitsourceopen', this.onMSEOpen.bind(this));
|
|
95
104
|
this.mediaSource.addEventListener('webkitsourceclose', this.onMSEClose.bind(this));
|
|
96
105
|
}
|
|
97
106
|
|
|
107
|
+
endMSE() {
|
|
108
|
+
if (!this.mseEnded) {
|
|
109
|
+
try {
|
|
110
|
+
this.mseEnded = true;
|
|
111
|
+
this.mediaSource.endOfStream();
|
|
112
|
+
} catch (e) {
|
|
113
|
+
debug.error('mediasource is not available to end');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
98
118
|
feed(data) {
|
|
99
119
|
let remux = false,
|
|
100
120
|
slices,
|
|
@@ -107,20 +127,28 @@ export default class JMuxmer extends Event {
|
|
|
107
127
|
|
|
108
128
|
if (!data || !this.remuxController) return;
|
|
109
129
|
duration = data.duration ? parseInt(data.duration) : 0;
|
|
130
|
+
|
|
110
131
|
if (data.video) {
|
|
111
132
|
data.video = appendByteArray(this.remainingData, data.video);
|
|
112
133
|
[slices, left] = H264Parser.extractNALu(data.video);
|
|
134
|
+
this.remainingData = left || new Uint8Array();
|
|
135
|
+
|
|
113
136
|
if (slices.length > 0) {
|
|
114
137
|
chunks.video = this.getVideoFrames(slices, duration);
|
|
115
138
|
remux = true;
|
|
139
|
+
} else {
|
|
140
|
+
debug.error('Failed to extract any NAL units from video data:', left);
|
|
141
|
+
return;
|
|
116
142
|
}
|
|
117
|
-
this.remainingData = left || new Uint8Array();
|
|
118
143
|
}
|
|
119
144
|
if (data.audio) {
|
|
120
145
|
slices = AACParser.extractAAC(data.audio);
|
|
121
146
|
if (slices.length > 0) {
|
|
122
147
|
chunks.audio = this.getAudioFrames(slices, duration);
|
|
123
148
|
remux = true;
|
|
149
|
+
} else {
|
|
150
|
+
debug.error('Failed to extract audio data from:', data.audio);
|
|
151
|
+
return;
|
|
124
152
|
}
|
|
125
153
|
}
|
|
126
154
|
if (!remux) {
|
|
@@ -138,9 +166,9 @@ export default class JMuxmer extends Event {
|
|
|
138
166
|
keyFrame = false,
|
|
139
167
|
vcl = false;
|
|
140
168
|
if (this.pendingUnits.units) {
|
|
141
|
-
units
|
|
142
|
-
vcl
|
|
143
|
-
keyFrame
|
|
169
|
+
units = this.pendingUnits.units;
|
|
170
|
+
vcl = this.pendingUnits.vcl;
|
|
171
|
+
keyFrame = this.pendingUnits.keyFrame;
|
|
144
172
|
this.pendingUnits = {};
|
|
145
173
|
}
|
|
146
174
|
for (let nalu of nalus) {
|
|
@@ -177,12 +205,14 @@ export default class JMuxmer extends Event {
|
|
|
177
205
|
});
|
|
178
206
|
} else {
|
|
179
207
|
let last = frames.length - 1;
|
|
180
|
-
|
|
208
|
+
if (last >= 0) {
|
|
209
|
+
frames[last].units = frames[last].units.concat(units);
|
|
210
|
+
}
|
|
181
211
|
}
|
|
182
212
|
}
|
|
183
213
|
fd = duration ? duration / frames.length | 0 : this.frameDuration;
|
|
184
214
|
tt = duration ? (duration - (fd * frames.length)) : 0;
|
|
185
|
-
|
|
215
|
+
|
|
186
216
|
frames.map((frame) => {
|
|
187
217
|
frame.duration = fd;
|
|
188
218
|
if (tt > 0) {
|
|
@@ -204,7 +234,7 @@ export default class JMuxmer extends Event {
|
|
|
204
234
|
tt = 0;
|
|
205
235
|
|
|
206
236
|
for (let units of aacFrames) {
|
|
207
|
-
frames.push({units});
|
|
237
|
+
frames.push({ units });
|
|
208
238
|
}
|
|
209
239
|
fd = duration ? duration / frames.length | 0 : this.frameDuration;
|
|
210
240
|
tt = duration ? (duration - (fd * frames.length)) : 0;
|
|
@@ -220,16 +250,6 @@ export default class JMuxmer extends Event {
|
|
|
220
250
|
|
|
221
251
|
destroy() {
|
|
222
252
|
this.stopInterval();
|
|
223
|
-
if (this.mediaSource) {
|
|
224
|
-
try {
|
|
225
|
-
if (this.bufferControllers) {
|
|
226
|
-
this.mediaSource.endOfStream();
|
|
227
|
-
}
|
|
228
|
-
} catch (e) {
|
|
229
|
-
debug.error(`mediasource is not available to end ${e.message}`);
|
|
230
|
-
}
|
|
231
|
-
this.mediaSource = null;
|
|
232
|
-
}
|
|
233
253
|
if (this.stream) {
|
|
234
254
|
this.remuxController.flush();
|
|
235
255
|
this.stream.push(null);
|
|
@@ -244,10 +264,33 @@ export default class JMuxmer extends Event {
|
|
|
244
264
|
this.bufferControllers[type].destroy();
|
|
245
265
|
}
|
|
246
266
|
this.bufferControllers = null;
|
|
267
|
+
this.endMSE();
|
|
247
268
|
}
|
|
248
269
|
this.node = false;
|
|
249
270
|
this.mseReady = false;
|
|
250
271
|
this.videoStarted = false;
|
|
272
|
+
this.mediaSource = null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
reset() {
|
|
276
|
+
this.stopInterval();
|
|
277
|
+
this.isReset = true;
|
|
278
|
+
this.node.pause();
|
|
279
|
+
if (this.remuxController) {
|
|
280
|
+
this.remuxController.reset();
|
|
281
|
+
}
|
|
282
|
+
if (this.bufferControllers) {
|
|
283
|
+
for (let type in this.bufferControllers) {
|
|
284
|
+
this.bufferControllers[type].destroy();
|
|
285
|
+
}
|
|
286
|
+
this.bufferControllers = null;
|
|
287
|
+
this.endMSE();
|
|
288
|
+
}
|
|
289
|
+
this.initData();
|
|
290
|
+
if (this.env == 'browser') {
|
|
291
|
+
this.initBrowser();
|
|
292
|
+
}
|
|
293
|
+
debug.log('JMuxer was reset');
|
|
251
294
|
}
|
|
252
295
|
|
|
253
296
|
createBuffer() {
|
|
@@ -255,24 +298,24 @@ export default class JMuxmer extends Event {
|
|
|
255
298
|
this.bufferControllers = {};
|
|
256
299
|
for (let type in this.remuxController.tracks) {
|
|
257
300
|
let track = this.remuxController.tracks[type];
|
|
258
|
-
if (!
|
|
301
|
+
if (!JMuxer.isSupported(`${type}/mp4; codecs="${track.mp4track.codec}"`)) {
|
|
259
302
|
debug.error('Browser does not support codec');
|
|
260
303
|
return false;
|
|
261
304
|
}
|
|
262
305
|
let sb = this.mediaSource.addSourceBuffer(`${type}/mp4; codecs="${track.mp4track.codec}"`);
|
|
263
306
|
this.bufferControllers[type] = new BufferController(sb, type);
|
|
264
|
-
this.sourceBuffers[type] = sb;
|
|
265
307
|
this.bufferControllers[type].on('error', this.onBufferError.bind(this));
|
|
266
308
|
}
|
|
267
309
|
}
|
|
268
310
|
|
|
269
311
|
startInterval() {
|
|
270
|
-
this.interval = setInterval(()=>{
|
|
271
|
-
if (this.
|
|
272
|
-
this.
|
|
273
|
-
|
|
312
|
+
this.interval = setInterval(() => {
|
|
313
|
+
if (this.options.flushingTime) {
|
|
314
|
+
this.applyAndClearBuffer();
|
|
315
|
+
} else if (this.bufferControllers) {
|
|
316
|
+
this.cancelDelay();
|
|
274
317
|
}
|
|
275
|
-
}, this.options.flushingTime);
|
|
318
|
+
}, this.options.flushingTime || 1000);
|
|
276
319
|
}
|
|
277
320
|
|
|
278
321
|
stopInterval() {
|
|
@@ -281,12 +324,29 @@ export default class JMuxmer extends Event {
|
|
|
281
324
|
}
|
|
282
325
|
}
|
|
283
326
|
|
|
327
|
+
cancelDelay() {
|
|
328
|
+
if (this.node.buffered && this.node.buffered.length > 0 && !this.node.seeking) {
|
|
329
|
+
const end = this.node.buffered.end(0);
|
|
330
|
+
if (end - this.node.currentTime > (this.options.maxDelay / 1000)) {
|
|
331
|
+
console.log('delay');
|
|
332
|
+
this.node.currentTime = end - 0.001;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
284
337
|
releaseBuffer() {
|
|
285
338
|
for (let type in this.bufferControllers) {
|
|
286
339
|
this.bufferControllers[type].doAppend();
|
|
287
340
|
}
|
|
288
341
|
}
|
|
289
342
|
|
|
343
|
+
applyAndClearBuffer() {
|
|
344
|
+
if (this.bufferControllers) {
|
|
345
|
+
this.releaseBuffer();
|
|
346
|
+
this.clearBuffer();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
290
350
|
getSafeClearOffsetOfBuffer(offset) {
|
|
291
351
|
let maxLimit = (this.options.mode === 'audio' && offset) || 0,
|
|
292
352
|
adjacentOffset;
|
|
@@ -297,7 +357,7 @@ export default class JMuxmer extends Event {
|
|
|
297
357
|
adjacentOffset = this.kfPosition[i];
|
|
298
358
|
}
|
|
299
359
|
if (adjacentOffset) {
|
|
300
|
-
this.kfPosition = this.kfPosition.filter(
|
|
360
|
+
this.kfPosition = this.kfPosition.filter(kfDelimiter => {
|
|
301
361
|
if (kfDelimiter < adjacentOffset) {
|
|
302
362
|
maxLimit = kfDelimiter;
|
|
303
363
|
}
|
|
@@ -318,22 +378,31 @@ export default class JMuxmer extends Event {
|
|
|
318
378
|
}
|
|
319
379
|
|
|
320
380
|
onBuffer(data) {
|
|
381
|
+
if (this.options.readFpsFromTrack && typeof data.fps !== 'undefined' && this.options.fps != data.fps) {
|
|
382
|
+
this.options.fps = data.fps;
|
|
383
|
+
this.frameDuration = Math.ceil(1000 / data.fps);
|
|
384
|
+
debug.log(`JMuxer changed FPS to ${data.fps} from track data`);
|
|
385
|
+
}
|
|
321
386
|
if (this.env == 'browser') {
|
|
322
387
|
if (this.bufferControllers && this.bufferControllers[data.type]) {
|
|
323
388
|
this.bufferControllers[data.type].feed(data.payload);
|
|
324
389
|
}
|
|
325
|
-
} else if(this.stream) {
|
|
390
|
+
} else if (this.stream) {
|
|
326
391
|
this.stream.push(data.payload);
|
|
327
392
|
}
|
|
393
|
+
if (this.options.flushingTime === 0) {
|
|
394
|
+
this.applyAndClearBuffer();
|
|
395
|
+
}
|
|
328
396
|
}
|
|
329
397
|
|
|
330
398
|
/* Events on MSE */
|
|
331
399
|
onMSEOpen() {
|
|
332
400
|
this.mseReady = true;
|
|
401
|
+
URL.revokeObjectURL(this.url);
|
|
402
|
+
// this.createBuffer();
|
|
333
403
|
if (typeof this.options.onReady === 'function') {
|
|
334
|
-
this.options.onReady.call(null);
|
|
404
|
+
this.options.onReady.call(null, this.isReset);
|
|
335
405
|
}
|
|
336
|
-
this.createBuffer();
|
|
337
406
|
}
|
|
338
407
|
|
|
339
408
|
onMSEClose() {
|
|
@@ -342,23 +411,20 @@ export default class JMuxmer extends Event {
|
|
|
342
411
|
}
|
|
343
412
|
|
|
344
413
|
onBufferError(data) {
|
|
345
|
-
if (typeof this.options.onError === 'function') {
|
|
346
|
-
this.options.onError.call(null, data);
|
|
347
|
-
}
|
|
348
414
|
if (data.name == 'QuotaExceeded') {
|
|
415
|
+
debug.log(`JMuxer cleaning ${data.type} buffer due to QuotaExceeded error`);
|
|
349
416
|
this.bufferControllers[data.type].initCleanup(this.node.currentTime);
|
|
350
417
|
return;
|
|
351
418
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
this.
|
|
419
|
+
else if (data.name == 'InvalidStateError') {
|
|
420
|
+
debug.log('JMuxer is reseting due to InvalidStateError');
|
|
421
|
+
this.reset();
|
|
355
422
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
423
|
+
else {
|
|
424
|
+
this.endMSE();
|
|
425
|
+
}
|
|
426
|
+
if (typeof this.options.onError === 'function') {
|
|
427
|
+
this.options.onError.call(null, data);
|
|
362
428
|
}
|
|
363
429
|
}
|
|
364
|
-
}
|
|
430
|
+
}
|
package/src/parsers/h264.js
CHANGED
|
@@ -98,8 +98,30 @@ export class H264Parser {
|
|
|
98
98
|
picWidthInMbsMinus1,
|
|
99
99
|
picHeightInMapUnitsMinus1,
|
|
100
100
|
frameMbsOnlyFlag,
|
|
101
|
-
scalingListCount
|
|
102
|
-
|
|
101
|
+
scalingListCount,
|
|
102
|
+
fps = 0;
|
|
103
|
+
decoder.readUByte(); // skip NAL header
|
|
104
|
+
|
|
105
|
+
// rewrite NAL
|
|
106
|
+
let rbsp = [],
|
|
107
|
+
hdr_bytes = 1,
|
|
108
|
+
nal_bytes = data.byteLength;
|
|
109
|
+
for (let i = hdr_bytes; i < nal_bytes; i ++) {
|
|
110
|
+
if ((i + 2) < nal_bytes && decoder.readBits(24, false) === 0x000003) {
|
|
111
|
+
rbsp.push(decoder.readBits(8));
|
|
112
|
+
rbsp.push(decoder.readBits(8));
|
|
113
|
+
i += 2;
|
|
114
|
+
|
|
115
|
+
// emulation_prevention_three_byte
|
|
116
|
+
decoder.readBits(8);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
rbsp.push(decoder.readBits(8));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
decoder.setData(new Uint8Array(rbsp));
|
|
123
|
+
// end of rewrite data
|
|
124
|
+
|
|
103
125
|
profileIdc = decoder.readUByte(); // profile_idc
|
|
104
126
|
profileCompat = decoder.readBits(5); // constraint_set[0-4]_flag, u(5)
|
|
105
127
|
decoder.skipBits(3); // reserved_zero_3bits u(3),
|
|
@@ -191,7 +213,7 @@ export class H264Parser {
|
|
|
191
213
|
break;
|
|
192
214
|
}
|
|
193
215
|
}
|
|
194
|
-
if (sarRatio) {
|
|
216
|
+
if (sarRatio && sarRatio[0] > 0 && sarRatio[1] > 0) {
|
|
195
217
|
sarScale = sarRatio[0] / sarRatio[1];
|
|
196
218
|
}
|
|
197
219
|
}
|
|
@@ -212,9 +234,14 @@ export class H264Parser {
|
|
|
212
234
|
let timeScale = decoder.readUInt();
|
|
213
235
|
let fixedFrameRate = decoder.readBoolean();
|
|
214
236
|
let frameDuration = timeScale / (2 * unitsInTick);
|
|
237
|
+
|
|
238
|
+
if (fixedFrameRate) {
|
|
239
|
+
fps = frameDuration;
|
|
240
|
+
}
|
|
215
241
|
}
|
|
216
242
|
}
|
|
217
243
|
return {
|
|
244
|
+
fps: fps > 0 ? fps : undefined,
|
|
218
245
|
width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
|
|
219
246
|
height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - ((frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset)),
|
|
220
247
|
};
|
|
@@ -229,12 +256,12 @@ export class H264Parser {
|
|
|
229
256
|
constructor(remuxer) {
|
|
230
257
|
this.remuxer = remuxer;
|
|
231
258
|
this.track = remuxer.mp4track;
|
|
232
|
-
this.isSafari = remuxer.env = 'browser' && /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor);
|
|
233
259
|
}
|
|
234
260
|
|
|
235
261
|
parseSPS(sps) {
|
|
236
262
|
var config = H264Parser.readSPS(new Uint8Array(sps));
|
|
237
263
|
|
|
264
|
+
this.track.fps = config.fps;
|
|
238
265
|
this.track.width = config.width;
|
|
239
266
|
this.track.height = config.height;
|
|
240
267
|
this.track.sps = [new Uint8Array(sps)];
|
|
@@ -273,15 +300,13 @@ export class H264Parser {
|
|
|
273
300
|
push = true;
|
|
274
301
|
break;
|
|
275
302
|
case NALU.SPS:
|
|
276
|
-
if (!this.track.sps
|
|
303
|
+
if (!this.track.sps) {
|
|
277
304
|
this.parseSPS(unit.getPayload());
|
|
278
305
|
if (!this.remuxer.readyToDecode && this.track.pps && this.track.sps) {
|
|
279
306
|
this.remuxer.readyToDecode = true;
|
|
280
307
|
}
|
|
281
308
|
}
|
|
282
|
-
|
|
283
|
-
push = true;
|
|
284
|
-
}
|
|
309
|
+
push = true;
|
|
285
310
|
break;
|
|
286
311
|
case NALU.AUD:
|
|
287
312
|
debug.log('AUD - ignoing');
|
package/src/remuxer/aac.js
CHANGED
|
@@ -4,12 +4,11 @@ import { BaseRemuxer } from './base.js';
|
|
|
4
4
|
|
|
5
5
|
export class AACRemuxer extends BaseRemuxer {
|
|
6
6
|
|
|
7
|
-
constructor(timescale
|
|
7
|
+
constructor(timescale) {
|
|
8
8
|
super();
|
|
9
9
|
this.readyToDecode = false;
|
|
10
10
|
this.nextDts = 0;
|
|
11
11
|
this.dts = 0;
|
|
12
|
-
this.env = env;
|
|
13
12
|
this.mp4track = {
|
|
14
13
|
id: BaseRemuxer.getTrackID(),
|
|
15
14
|
type: 'audio',
|
|
@@ -22,7 +21,6 @@ export class AACRemuxer extends BaseRemuxer {
|
|
|
22
21
|
config: '',
|
|
23
22
|
codec: '',
|
|
24
23
|
};
|
|
25
|
-
|
|
26
24
|
this.samples = [];
|
|
27
25
|
this.aac = new AACParser(this);
|
|
28
26
|
}
|
|
@@ -33,6 +31,8 @@ export class AACRemuxer extends BaseRemuxer {
|
|
|
33
31
|
this.mp4track.channelCount = '';
|
|
34
32
|
this.mp4track.config = '';
|
|
35
33
|
this.mp4track.timescale = this.timescale;
|
|
34
|
+
this.nextDts = 0;
|
|
35
|
+
this.dts = 0;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
remux(frames) {
|
package/src/remuxer/h264.js
CHANGED
|
@@ -4,12 +4,11 @@ import { BaseRemuxer } from './base.js';
|
|
|
4
4
|
|
|
5
5
|
export class H264Remuxer extends BaseRemuxer {
|
|
6
6
|
|
|
7
|
-
constructor(timescale
|
|
7
|
+
constructor(timescale) {
|
|
8
8
|
super();
|
|
9
9
|
this.readyToDecode = false;
|
|
10
10
|
this.nextDts = 0;
|
|
11
11
|
this.dts = 0;
|
|
12
|
-
this.env = env;
|
|
13
12
|
this.mp4track = {
|
|
14
13
|
id: BaseRemuxer.getTrackID(),
|
|
15
14
|
type: 'video',
|
|
@@ -17,6 +16,7 @@ export class H264Remuxer extends BaseRemuxer {
|
|
|
17
16
|
fragmented: true,
|
|
18
17
|
sps: '',
|
|
19
18
|
pps: '',
|
|
19
|
+
fps: 30,
|
|
20
20
|
width: 0,
|
|
21
21
|
height: 0,
|
|
22
22
|
timescale: timescale,
|
|
@@ -31,6 +31,8 @@ export class H264Remuxer extends BaseRemuxer {
|
|
|
31
31
|
this.readyToDecode = false;
|
|
32
32
|
this.mp4track.sps = '';
|
|
33
33
|
this.mp4track.pps = '';
|
|
34
|
+
this.nextDts = 0;
|
|
35
|
+
this.dts = 0;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
remux(frames) {
|
|
@@ -99,7 +101,7 @@ export class H264Remuxer extends BaseRemuxer {
|
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
if (!samples.length) return null;
|
|
102
|
-
|
|
104
|
+
|
|
103
105
|
return new Uint8Array(payload.buffer, 0, this.mp4track.len);
|
|
104
106
|
}
|
|
105
107
|
}
|
package/src/util/exp-golomb.js
CHANGED