jmuxer 2.0.7 → 2.1.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.
@@ -0,0 +1,489 @@
1
+ import { ExpGolomb } from '../util/exp-golomb.js';
2
+ import * as debug from '../util/debug';
3
+
4
+ // spec https://www.itu.int/rec/T-REC-H.265/
5
+
6
+ export class H265Parser {
7
+
8
+ static extractNALu(buffer) {
9
+ let i = 0,
10
+ length = buffer.byteLength,
11
+ result = [],
12
+ lastIndex = 0,
13
+ zeroCount = 0;
14
+
15
+ while (i < length) {
16
+ let value = buffer[i++];
17
+
18
+ if (value === 0) {
19
+ zeroCount++;
20
+ } else if (value === 1 && zeroCount >= 2) {
21
+ let startCodeLength = zeroCount + 1;
22
+
23
+ if (lastIndex !== i - startCodeLength) {
24
+ result.push(buffer.subarray(lastIndex, i - startCodeLength));
25
+ }
26
+
27
+ lastIndex = i;
28
+ zeroCount = 0;
29
+ } else {
30
+ zeroCount = 0;
31
+ }
32
+ }
33
+
34
+ // Remaining data after last start code
35
+ let left = null;
36
+ if (lastIndex < length) {
37
+ left = buffer.subarray(lastIndex, length);
38
+ }
39
+
40
+ return [result, left];
41
+ }
42
+
43
+ static removeEmulationPreventionBytes(nal) {
44
+ const rbsp = [];
45
+ let zeroCount = 0;
46
+
47
+ for (let i = 0; i < nal.length; i++) {
48
+ const value = nal[i];
49
+
50
+ if (zeroCount === 2 && value === 0x03) {
51
+ // Skip this emulation prevention byte
52
+ zeroCount = 0;
53
+ continue;
54
+ }
55
+
56
+ rbsp.push(value);
57
+
58
+ if (value === 0x00) {
59
+ zeroCount++;
60
+ } else {
61
+ zeroCount = 0;
62
+ }
63
+ }
64
+
65
+ return new Uint8Array(rbsp);
66
+ }
67
+
68
+
69
+ /**
70
+ * Read a sequence parameter set and return some interesting video
71
+ * properties. A sequence parameter set is the H265 metadata that
72
+ * describes the properties of upcoming video frames.
73
+ * @param data {Uint8Array} the bytes of a sequence parameter set
74
+ * @return {object} an object with configuration parsed from the
75
+ * sequence parameter set, including the dimensions of the
76
+ * associated video frames.
77
+ */
78
+ static readSPS(data) {
79
+ let decoder = new ExpGolomb(data);
80
+
81
+ // Skip NALU header (2 bytes for HEVC)
82
+ decoder.readUByte();
83
+ decoder.readUByte();
84
+
85
+ decoder.readBits(4); // sps_video_parameter_set_id
86
+ const sps_max_sub_layers_minus1 = decoder.readBits(3); // sps_max_sub_layers_minus1
87
+ decoder.readBits(1); // sps_temporal_id_nesting_flag
88
+
89
+ // --- profile_tier_level() ---
90
+ let profile_space = decoder.readBits(2);
91
+ let tier_flag = decoder.readBits(1);
92
+ let profile_idc = decoder.readBits(5);
93
+
94
+ let profile_compatibility_flags = decoder.readUInt(); // 32 bits
95
+ let constraint_indicator_flags = new Uint8Array(6);
96
+ for (let i = 0; i < 6; i++) {
97
+ constraint_indicator_flags[i] = decoder.readUByte();
98
+ }
99
+
100
+ let level_idc = decoder.readUByte();
101
+
102
+ decoder.readUEG(); // seq_parameter_set_id
103
+
104
+ let chroma_format_idc = decoder.readUEG();
105
+ if (chroma_format_idc === 3) {
106
+ decoder.readBits(1); // separate_colour_plane_flag
107
+ }
108
+
109
+ let pic_width_in_luma_samples = decoder.readUEG();
110
+ let pic_height_in_luma_samples = decoder.readUEG();
111
+
112
+ // Cropping
113
+ let conformance_window_flag = decoder.readBoolean();
114
+ let conf_win_left_offset = 0, conf_win_right_offset = 0, conf_win_top_offset = 0, conf_win_bottom_offset = 0;
115
+ if (conformance_window_flag) {
116
+ conf_win_left_offset = decoder.readUEG();
117
+ conf_win_right_offset = decoder.readUEG();
118
+ conf_win_top_offset = decoder.readUEG();
119
+ conf_win_bottom_offset = decoder.readUEG();
120
+ }
121
+
122
+ const bit_depth_luma_minus8 = decoder.readUEG();
123
+ const bit_depth_chroma_minus8 = decoder.readUEG();
124
+
125
+ const log2_max_pic_order_cnt_lsb_minus4 = decoder.readUEG();
126
+
127
+ const ordering_flag = decoder.readBits(1); // sps_sub_layer_ordering_info_present_flag
128
+
129
+ for (let i = (ordering_flag ? 0 : sps_max_sub_layers_minus1); i <= sps_max_sub_layers_minus1; i++) {
130
+ const max_dec = decoder.readUEG();
131
+ const max_num = decoder.readUEG();
132
+ const max_latency = decoder.readUEG();
133
+ }
134
+
135
+ decoder.readUEG(); // log2_min_luma_coding_block_size_minus3
136
+ decoder.readUEG(); // log2_diff_max_min_luma_coding_block_size
137
+ decoder.readUEG(); // log2_min_luma_transform_block_size_minus2
138
+ decoder.readUEG(); // log2_diff_max_min_luma_transform_block_size
139
+ decoder.readUEG(); // max_transform_hierarchy_depth_inter
140
+ decoder.readUEG(); // max_transform_hierarchy_depth_intra
141
+
142
+ const scaling_list_enabled_flag = decoder.readBits(1);
143
+ if (scaling_list_enabled_flag) {
144
+ const sps_scaling_list_data_present_flag = decoder.readBits(1);
145
+ if (sps_scaling_list_data_present_flag) {
146
+ // inline skipScalingListH265
147
+ for (let sizeId = 0; sizeId < 4; sizeId++) {
148
+ for (let matrixId = 0; matrixId < (sizeId === 3 ? 2 : 6); matrixId++) {
149
+ const flag = decoder.readBits(1);
150
+ if (!flag) {
151
+ decoder.readUEG(); // scaling_list_pred_matrix_id_delta
152
+ } else {
153
+ const coefNum = Math.min(64, 1 << (4 + (sizeId << 1)));
154
+ if (sizeId > 1) decoder.readEG(); // scaling_list_dc_coef_minus8
155
+ for (let k = 0; k < coefNum; k++) decoder.readEG();
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ const amp_enabled_flag = decoder.readBits(1);
163
+ const sample_adaptive_offset_enabled_flag = decoder.readBits(1);
164
+
165
+ const pcm_enabled_flag = decoder.readBits(1);
166
+ if (pcm_enabled_flag) {
167
+ decoder.readBits(4); // pcm_sample_bit_depth_luma_minus1
168
+ decoder.readBits(4); // pcm_sample_bit_depth_chroma_minus1
169
+ decoder.readUEG(); // log2_min_pcm_luma_coding_block_size_minus3
170
+ decoder.readUEG(); // log2_diff_max_min_pcm_luma_coding_block_size
171
+ decoder.readBits(1); // pcm_loop_filter_disabled_flag
172
+ }
173
+
174
+ const num_short_term_ref_pic_sets = decoder.readUEG();
175
+ // inline simplified short-term RPS parsing
176
+ for (let stIdx = 0; stIdx < num_short_term_ref_pic_sets; stIdx++) {
177
+ let inter_ref_pic_set_prediction_flag = false;
178
+ if (stIdx !== 0) {
179
+ inter_ref_pic_set_prediction_flag = decoder.readBoolean();
180
+ }
181
+ if (inter_ref_pic_set_prediction_flag) {
182
+ decoder.readUEG(); // delta_idx_minus1 or related
183
+ decoder.readBits(1); // delta_rps_sign
184
+ decoder.readUEG(); // abs_delta_rps_minus1
185
+ // fine-grained details omitted for brevity
186
+ } else {
187
+ const num_negative_pics = decoder.readUEG();
188
+ const num_positive_pics = decoder.readUEG();
189
+ for (let i = 0; i < num_negative_pics; i++) {
190
+ decoder.readUEG(); // delta_poc_s0_minus1[i]
191
+ decoder.readBits(1); // used_by_curr_pic_s0_flag[i]
192
+ }
193
+ for (let i = 0; i < num_positive_pics; i++) {
194
+ decoder.readUEG(); // delta_poc_s1_minus1[i]
195
+ decoder.readBits(1); // used_by_curr_pic_s1_flag[i]
196
+ }
197
+ }
198
+ }
199
+
200
+ const long_term_ref_pics_present_flag = decoder.readBits(1);
201
+ if (long_term_ref_pics_present_flag) {
202
+ const num_long_term_ref_pics_sps = decoder.readUEG();
203
+ for (let i = 0; i < num_long_term_ref_pics_sps; i++) {
204
+ decoder.readUEG(); // lt_ref_pic_poc_lsb_sps[i]
205
+ decoder.readBits(1); // used_by_curr_pic_lt_sps_flag[i]
206
+ }
207
+ }
208
+
209
+ const sps_temporal_mvp_enabled_flag = decoder.readBits(1);
210
+ const strong_intra_smoothing_enabled_flag = decoder.readBits(1);
211
+
212
+ let fps = null;
213
+ let vui_parameters_present_flag = decoder.readBoolean();
214
+ if (vui_parameters_present_flag) {
215
+ let aspect_ratio_info_present_flag = decoder.readBoolean();
216
+ if (aspect_ratio_info_present_flag) {
217
+ let aspect_ratio_idc = decoder.readUByte();
218
+ if (aspect_ratio_idc === 255) { // Extended_SAR
219
+ decoder.readUShort(); // sar_width
220
+ decoder.readUShort(); // sar_height
221
+ }
222
+ }
223
+ let overscan_info_present_flag = decoder.readBoolean();
224
+ if (overscan_info_present_flag) {
225
+ decoder.readBoolean(); // overscan_appropriate_flag
226
+ }
227
+ let video_signal_type_present_flag = decoder.readBoolean();
228
+ if (video_signal_type_present_flag) {
229
+ decoder.readBits(3); // video_format
230
+ decoder.readBoolean(); // video_full_range_flag
231
+ let colour_description_present_flag = decoder.readBoolean();
232
+ if (colour_description_present_flag) {
233
+ decoder.readUByte(); // colour_primaries
234
+ decoder.readUByte(); // transfer_characteristics
235
+ decoder.readUByte(); // matrix_coeffs
236
+ }
237
+ }
238
+ let chroma_loc_info_present_flag = decoder.readBoolean();
239
+ if (chroma_loc_info_present_flag) {
240
+ decoder.readUEG(); // chroma_sample_loc_type_top_field
241
+ decoder.readUEG(); // chroma_sample_loc_type_bottom_field
242
+ }
243
+ decoder.readBoolean(); // neutral_chroma_indication_flag
244
+ decoder.readBoolean(); // field_seq_flag
245
+ decoder.readBoolean(); // frame_field_info_present_flag
246
+
247
+ let default_display_window_flag = decoder.readBoolean();
248
+ if (default_display_window_flag) {
249
+ decoder.readUEG(); // def_disp_win_left_offset
250
+ decoder.readUEG(); // def_disp_win_right_offset
251
+ decoder.readUEG(); // def_disp_win_top_offset
252
+ decoder.readUEG(); // def_disp_win_bottom_offset
253
+ }
254
+
255
+ let vui_timing_info_present_flag = decoder.readBoolean();
256
+ if (vui_timing_info_present_flag) {
257
+ let vui_num_units_in_tick = decoder.readUInt();
258
+ let vui_time_scale = decoder.readUInt();
259
+ const vui_poc_proportional_to_timing_flag = decoder.readBoolean();
260
+ if (vui_poc_proportional_to_timing_flag) {
261
+ decoder.readUEG(); // vui_num_ticks_poc_diff_one_minus1
262
+ }
263
+
264
+ const vui_hrd_parameters_present_flag = decoder.readBoolean();
265
+ if (vui_hrd_parameters_present_flag) {
266
+ // inline simplified HRD parsing
267
+ const commonInfPresentFlag = true;
268
+ let nal_hrd_parameters_present_flag = false;
269
+ let vcl_hrd_parameters_present_flag = false;
270
+ let sub_pic_hrd_params_present_flag = false;
271
+
272
+ if (commonInfPresentFlag) {
273
+ nal_hrd_parameters_present_flag = decoder.readBoolean();
274
+ vcl_hrd_parameters_present_flag = decoder.readBoolean();
275
+ if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
276
+ sub_pic_hrd_params_present_flag = decoder.readBoolean();
277
+ if (sub_pic_hrd_params_present_flag) {
278
+ decoder.readBits(8); // tick_divisor_minus2
279
+ decoder.readBits(5); // du_cpb_removal_delay_increment_length_minus1
280
+ decoder.readBits(1); // sub_pic_cpb_params_in_pic_timing_sei_flag
281
+ decoder.readBits(5); // dpb_output_delay_du_length_minus1
282
+ }
283
+ decoder.readBits(4); // bit_rate_scale
284
+ decoder.readBits(4); // cpb_size_scale
285
+ if (sub_pic_hrd_params_present_flag) {
286
+ decoder.readBits(4); // cpb_size_du_scale
287
+ }
288
+ decoder.readBits(5); // initial_cpb_removal_delay_length_minus1
289
+ decoder.readBits(5); // au_cpb_removal_delay_length_minus1
290
+ decoder.readBits(5); // dpb_output_delay_length_minus1
291
+ }
292
+ }
293
+
294
+ for (let i = 0; i <= sps_max_sub_layers_minus1; i++) {
295
+ const fixed_pic_rate_general_flag = decoder.readBoolean();
296
+ let fixed_pic_rate_within_cvs_flag = false;
297
+ let low_delay_hrd_flag = false;
298
+ let cpb_cnt_minus1 = 0;
299
+ if (!fixed_pic_rate_general_flag) {
300
+ fixed_pic_rate_within_cvs_flag = decoder.readBoolean();
301
+ } else {
302
+ fixed_pic_rate_within_cvs_flag = true;
303
+ }
304
+ if (fixed_pic_rate_within_cvs_flag) {
305
+ decoder.readUEG(); // elemental_duration_in_tc_minus1
306
+ } else {
307
+ low_delay_hrd_flag = decoder.readBoolean();
308
+ }
309
+ if (!low_delay_hrd_flag) {
310
+ cpb_cnt_minus1 = decoder.readUEG();
311
+ }
312
+ if (nal_hrd_parameters_present_flag) {
313
+ for (let j = 0; j <= cpb_cnt_minus1; j++) {
314
+ decoder.readUEG(); // bit_rate_value_minus1
315
+ decoder.readUEG(); // cpb_size_value_minus1
316
+ if (sub_pic_hrd_params_present_flag) {
317
+ decoder.readUEG(); // cpb_size_du_value_minus1
318
+ decoder.readUEG(); // bit_rate_du_value_minus1
319
+ }
320
+ decoder.readBits(1); // cbr_flag
321
+ }
322
+ }
323
+ if (vcl_hrd_parameters_present_flag) {
324
+ for (let j = 0; j <= cpb_cnt_minus1; j++) {
325
+ decoder.readUEG(); // bit_rate_value_minus1
326
+ decoder.readUEG(); // cpb_size_value_minus1
327
+ if (sub_pic_hrd_params_present_flag) {
328
+ decoder.readUEG(); // cpb_size_du_value_minus1
329
+ decoder.readUEG(); // bit_rate_du_value_minus1
330
+ }
331
+ decoder.readBits(1); // cbr_flag
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ if (vui_num_units_in_tick > 0 && vui_time_scale > 0) {
338
+ fps = vui_time_scale / vui_num_units_in_tick;
339
+ }
340
+ }
341
+
342
+ const bitstream_restriction_flag = decoder.readBoolean();
343
+ if (bitstream_restriction_flag) {
344
+ decoder.readBoolean(); // tiles_fixed_structure_flag
345
+ decoder.readBoolean(); // motion_vectors_over_pic_boundaries_flag
346
+ decoder.readBoolean(); // restricted_ref_pic_lists_flag
347
+ decoder.readUEG(); // min_spatial_segmentation_idc
348
+ decoder.readUEG(); // max_bytes_per_pic_denom
349
+ decoder.readUEG(); // max_bits_per_min_cu_denom
350
+ decoder.readUEG(); // log2_max_mv_length_horizontal
351
+ decoder.readUEG(); // log2_max_mv_length_vertical
352
+ }
353
+ }
354
+
355
+ // Final width/height
356
+ let sub_width_c = (chroma_format_idc === 1 || chroma_format_idc === 2) ? 2 : 1;
357
+ let sub_height_c = (chroma_format_idc === 1) ? 2 : 1;
358
+ let width = pic_width_in_luma_samples - sub_width_c * (conf_win_right_offset + conf_win_left_offset);
359
+ let height = pic_height_in_luma_samples - sub_height_c * (conf_win_top_offset + conf_win_bottom_offset);
360
+
361
+ return {
362
+ width,
363
+ height,
364
+ profile_space,
365
+ tier_flag,
366
+ profile_idc,
367
+ profile_compatibility_flags,
368
+ constraint_indicator_flags,
369
+ level_idc,
370
+ chroma_format_idc,
371
+ fps,
372
+ };
373
+ }
374
+
375
+ }
376
+
377
+ export class NALU265 {
378
+ static get TRAIL_N() { return 0; }
379
+ static get TRAIL_R() { return 1; }
380
+ static get IDR_W_RADL() { return 19; }
381
+ static get IDR_N_LP() { return 20; }
382
+ static get CRA() { return 21; }
383
+ static get VPS() { return 32; }
384
+ static get SPS() { return 33; }
385
+ static get PPS() { return 34; }
386
+ static get AUD() { return 35; }
387
+ static get SEI() { return 39; }
388
+ static get SEI2() { return 40; }
389
+
390
+ static get TYPES() {
391
+ return {
392
+ [NALU265.TRAIL_N]: 'TRAIL_N',
393
+ [NALU265.TRAIL_R]: 'TRAIL_R',
394
+ [NALU265.IDR_W_RADL]: 'IDR',
395
+ [NALU265.IDR_N_LP]: 'IDR2',
396
+ [NALU265.CRA]: 'CRA',
397
+ [NALU265.VPS]: 'VPS',
398
+ [NALU265.SPS]: 'SPS',
399
+ [NALU265.PPS]: 'PPS',
400
+ [NALU265.AUD]: 'AUD',
401
+ [NALU265.SEI]: 'SEI',
402
+ [NALU265.SEI2]: 'SEI2',
403
+ };
404
+ }
405
+
406
+ constructor(data) {
407
+ this.payload = data;
408
+ this.nalUnitType = (data[0] & 0b01111110) >> 1;
409
+ this.nuhLayerId = ((data[0] & 0b00000001) << 5) | ((data[1] & 0b11111000) >> 3);
410
+ this.nuhTemporalIdPlus1 = data[1] & 0b00000111;
411
+ this._isFirstSlice = null;
412
+ this._sliceType = null;
413
+ }
414
+
415
+ toString() {
416
+ return `${NALU265.TYPES[this.type()] || 'UNKNOWN (' + this.type() + ')'}: Layer: ${this.nuhLayerId}, Temporal Id: ${this.nuhTemporalIdPlus1}`;
417
+ }
418
+
419
+ type() {
420
+ return this.nalUnitType;
421
+ }
422
+
423
+ get isKeyframe() {
424
+ return [
425
+ NALU265.IDR_W_RADL,
426
+ NALU265.IDR_N_LP,
427
+ NALU265.CRA
428
+ ].includes(this.nalUnitType);
429
+ }
430
+
431
+ get isVCL() {
432
+ return this.nalUnitType <=31;
433
+ }
434
+
435
+ parseHeader() {
436
+ let decoder = new ExpGolomb(this.getPayload());
437
+ // skip NALu type
438
+ decoder.readUByte();
439
+ decoder.readUByte();
440
+
441
+ // first_slice_segment_in_pic_flag
442
+ this._isFirstSlice = decoder.readBoolean();
443
+
444
+ // if NALU is not IDR/CRA/other IRAP, next comes no_output_of_prior_pics_flag
445
+ if (this.isKeyframe) {
446
+ decoder.readBits(1); // no_output_of_prior_pics_flag
447
+ }
448
+
449
+ // slice_pic_parameter_set_id
450
+ decoder.readUEG();
451
+
452
+ // slice_type (only for some NALU types, but useful for debugging)
453
+ this._sliceType = decoder.readUEG();
454
+ }
455
+
456
+ get isFirstSlice() {
457
+ if (!this._isFirstSlice) {
458
+ this.parseHeader();
459
+ }
460
+ return this._isFirstSlice;
461
+ }
462
+
463
+ get sliceType() {
464
+ if (!this._sliceType) {
465
+ this.parseHeader();
466
+ }
467
+ return this._sliceType;
468
+ }
469
+
470
+ getPayload() {
471
+ return this.payload;
472
+ }
473
+
474
+ getPayloadSize() {
475
+ return this.payload.byteLength;
476
+ }
477
+
478
+ getSize() {
479
+ return 4 + this.getPayloadSize();
480
+ }
481
+
482
+ getData() {
483
+ const result = new Uint8Array(this.getSize());
484
+ const view = new DataView(result.buffer);
485
+ view.setUint32(0, this.getSize() - 4);
486
+ result.set(this.getPayload(), 4);
487
+ return result;
488
+ }
489
+ }
@@ -4,9 +4,11 @@ import { BaseRemuxer } from './base.js';
4
4
 
5
5
  export class AACRemuxer extends BaseRemuxer {
6
6
 
7
- constructor(timescale) {
8
- super();
7
+ constructor(timescale, duration, frameDuration) {
8
+ super('AACRemuxer');
9
+ this.frameDuration = frameDuration;
9
10
  this.readyToDecode = false;
11
+ this.header = null;
10
12
  this.nextDts = 0;
11
13
  this.dts = 0;
12
14
  this.mp4track = {
@@ -16,17 +18,17 @@ export class AACRemuxer extends BaseRemuxer {
16
18
  len: 0,
17
19
  fragmented: true,
18
20
  timescale: timescale,
19
- duration: timescale,
21
+ duration: duration,
20
22
  samples: [],
21
23
  config: '',
22
24
  codec: '',
23
25
  };
24
26
  this.samples = [];
25
- this.aac = new AACParser(this);
26
27
  }
27
28
 
28
29
  resetTrack() {
29
30
  this.readyToDecode = false;
31
+ this.header = null;
30
32
  this.mp4track.codec = '';
31
33
  this.mp4track.channelCount = '';
32
34
  this.mp4track.config = '';
@@ -35,6 +37,39 @@ export class AACRemuxer extends BaseRemuxer {
35
37
  this.dts = 0;
36
38
  }
37
39
 
40
+ feed(data, duration) {
41
+ const { valid, header, slices } = AACParser.extractAAC(data);
42
+ if (!this.header) this.header = header;
43
+ if (valid && slices.length > 0) {
44
+ this.remux(this.getAudioFrames(slices, duration));
45
+ return true;
46
+ } else {
47
+ debug.error('Failed to extract audio data from:', data);
48
+ this.dispatch('outOfData');
49
+ return false;
50
+ }
51
+ }
52
+
53
+ getAudioFrames(aacFrames, duration) {
54
+ let frames = [],
55
+ fd = 0,
56
+ tt = 0;
57
+
58
+ for (let units of aacFrames) {
59
+ frames.push({ units });
60
+ }
61
+ fd = duration ? duration / frames.length | 0 : this.frameDuration;
62
+ tt = duration ? (duration - (fd * frames.length)) : 0;
63
+ frames.map((frame) => {
64
+ frame.duration = fd;
65
+ if (tt > 0) {
66
+ frame.duration++;
67
+ tt--;
68
+ }
69
+ });
70
+ return frames;
71
+ }
72
+
38
73
  remux(frames) {
39
74
  if (frames.length > 0) {
40
75
  for (let i = 0; i < frames.length; i++) {
@@ -48,7 +83,7 @@ export class AACRemuxer extends BaseRemuxer {
48
83
  });
49
84
  this.mp4track.len += size;
50
85
  if (!this.readyToDecode) {
51
- this.aac.setAACConfig();
86
+ this.setAACConfig();
52
87
  }
53
88
  }
54
89
  }
@@ -103,7 +138,28 @@ export class AACRemuxer extends BaseRemuxer {
103
138
  return new Uint8Array(payload.buffer, 0, this.mp4track.len);
104
139
  }
105
140
 
106
- getAacParser() {
107
- return this.aac;
141
+ setAACConfig() {
142
+ let objectType,
143
+ sampleIndex,
144
+ channelCount,
145
+ config = new Uint8Array(2);
146
+
147
+ if (!this.header) return;
148
+
149
+ objectType = ((this.header[2] & 0xC0) >>> 6) + 1;
150
+ sampleIndex = ((this.header[2] & 0x3C) >>> 2);
151
+ channelCount = ((this.header[2] & 0x01) << 2);
152
+ channelCount |= ((this.header[3] & 0xC0) >>> 6);
153
+
154
+ /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config */
155
+ config[0] = objectType << 3;
156
+ config[0] |= (sampleIndex & 0x0E) >> 1;
157
+ config[1] |= (sampleIndex & 0x01) << 7;
158
+ config[1] |= channelCount << 3;
159
+
160
+ this.mp4track.codec = 'mp4a.40.' + objectType;
161
+ this.mp4track.channelCount = channelCount;
162
+ this.mp4track.config = config;
163
+ this.readyToDecode = true;
108
164
  }
109
165
  }
@@ -1,7 +1,8 @@
1
1
  import * as debug from '../util/debug';
2
+ import Event from '../util/event';
2
3
 
3
4
  let track_id = 1;
4
- export class BaseRemuxer {
5
+ export class BaseRemuxer extends Event {
5
6
 
6
7
  static getTrackID() {
7
8
  return track_id++;