jmuxer 2.1.0 → 2.1.2

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,21 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import { defineConfig, globalIgnores } from "eslint/config";
4
+
5
+ export default defineConfig([
6
+ {
7
+ files: ["**/*.{js,mjs,cjs}"],
8
+ plugins: { js },
9
+ extends: ["js/recommended"],
10
+ languageOptions: { globals: { ...globals.browser, ...globals.node } },
11
+ rules: {
12
+ indent: ["error", 4, { SwitchCase: 1 }],
13
+ "no-unused-vars": "off",
14
+ "linebreak-style": ["error", "unix"],
15
+ quotes: ["error", "single"],
16
+ semi: ["error", "always"],
17
+ "no-useless-assignment": "off",
18
+ },
19
+ },
20
+ globalIgnores(["./src/util/mp4-generator.js"]),
21
+ ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jmuxer",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "jMuxer - a simple javascript mp4 muxer for non-standard streaming communications protocol",
5
5
  "main": "dist/jmuxer.min.js",
6
6
  "scripts": {
@@ -9,8 +9,7 @@
9
9
  "server": "cd example && nodemon server.mjs",
10
10
  "dev": "concurrently --names \"BUILD,SERVER\" -c \"bgBlue.bold,bgMagenta.bold\" \"npm run watch\" \"npm run server\"",
11
11
  "pro": "NODE_ENV=production rollup -c",
12
- "lint": "eslint -c .eslintrc.json ./src/",
13
- "test": "karma start karma.conf.js"
12
+ "lint": "eslint ./src/"
14
13
  },
15
14
  "keywords": [
16
15
  "h264",
@@ -28,36 +27,24 @@
28
27
  ],
29
28
  "repository": {
30
29
  "type": "git",
31
- "url": "https://github.com/samirkumardas/jmuxer.git"
30
+ "url": "git@github.com:webstream-labs/jmuxer.git"
32
31
  },
33
32
  "author": "Samir Das",
34
33
  "devDependencies": {
35
34
  "@babel/core": "^7.10.5",
36
- "@babel/eslint-parser": "^7.25.1",
37
- "@babel/plugin-external-helpers": "^7.10.4",
38
- "@babel/preset-env": "^7.10.4",
39
- "@rollup/plugin-babel": "^5.2.1",
40
- "@rollup/plugin-buble": "^0.21.3",
41
- "@rollup/plugin-eslint": "^8.0.1",
42
- "@rollup/plugin-node-resolve": "^13.0.6",
43
- "@rollup/plugin-replace": "^3.0.0",
44
- "@rollup/pluginutils": "^4.0.0",
45
- "caniuse-lite": "^1.0.30001223",
46
- "chai": "^4.2.0",
47
- "concurrently": "^9.2.1",
48
- "eslint": "^8.2.0",
35
+ "@babel/preset-env": "^7.29.0",
36
+ "@eslint/js": "^10.0.1",
37
+ "@rollup/plugin-babel": "^6.1.0",
38
+ "@rollup/plugin-replace": "^6.0.3",
39
+ "@rollup/plugin-terser": "^1.0.0",
40
+ "concurrently": "^10.0.3",
41
+ "eslint": "^10.0.2",
49
42
  "express": "^5.1.0",
50
43
  "express-ws": "^5.0.2",
51
- "karma": "^6.3.2",
52
- "karma-chai": "^0.1.0",
53
- "karma-chrome-launcher": "^3.1.0",
54
- "karma-mocha": "^2.0.1",
55
- "karma-rollup-preprocessor": "^7.0.5",
56
- "mocha": "^11.7.2",
44
+ "globals": "^17.4.0",
57
45
  "msgpack-lite": "^0.1.26",
58
46
  "nodemon": "^3.1.10",
59
- "path": "^0.12.7",
60
- "rollup": "^2.59.0",
61
- "rollup-plugin-terser": "^7.0.2"
62
- }
47
+ "rollup": "^4.59.0"
48
+ },
49
+ "type": "module"
63
50
  }
package/rollup.config.js CHANGED
@@ -1,8 +1,7 @@
1
1
  // Rollup plugins
2
2
  import babel from '@rollup/plugin-babel';
3
- import eslint from '@rollup/plugin-eslint';
4
3
  import replace from '@rollup/plugin-replace';
5
- import { terser } from 'rollup-plugin-terser';
4
+ import terser from '@rollup/plugin-terser';
6
5
  export default {
7
6
  input: 'src/jmuxer.js',
8
7
  output: [
@@ -11,6 +10,7 @@ export default {
11
10
  format: 'umd',
12
11
  name: 'JMuxer',
13
12
  sourcemap: false, // 'inline'
13
+ plugins: [terser()],
14
14
  globals: {
15
15
  stream: 'stream',
16
16
  fs: 'fs'
@@ -21,6 +21,17 @@ export default {
21
21
  format: 'umd',
22
22
  name: 'JMuxer',
23
23
  sourcemap: false,
24
+ plugins: [terser()],
25
+ globals: {
26
+ stream: 'stream',
27
+ fs: 'fs'
28
+ }
29
+ },
30
+ {
31
+ file: 'dist/jmuxer.js',
32
+ format: 'umd',
33
+ name: 'JMuxer',
34
+ sourcemap: false,
24
35
  globals: {
25
36
  stream: 'stream',
26
37
  fs: 'fs'
@@ -34,7 +45,6 @@ export default {
34
45
  console.error(message);
35
46
  },
36
47
  plugins: [
37
- eslint(),
38
48
  babel({
39
49
  exclude: 'node_modules/**',
40
50
  babelHelpers: 'bundled'
@@ -44,7 +54,6 @@ export default {
44
54
  preventAssignment: true,
45
55
  ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
46
56
  }),
47
- (process.env.NODE_ENV === 'production' && terser()),
48
57
  ],
49
58
  external: [ 'stream', 'fs' ],
50
59
  };
@@ -37,7 +37,7 @@ export default class RemuxController extends Event {
37
37
  if (type === 'audio' || type === 'both') {
38
38
  const aacRemuxer = new AACRemuxer(this.timescale, this.mediaDuration, this.frameDuration);
39
39
  this.tracks.audio = aacRemuxer;
40
- this.tracks.video.on('outOfData', () => {
40
+ this.tracks.audio.on('outOfData', () => {
41
41
  this.dispatch('missingAudioFrames');
42
42
  });
43
43
  }
@@ -123,7 +123,7 @@ export default class RemuxController extends Event {
123
123
  let remux = false;
124
124
 
125
125
  if (data.video && this.tracks.video) {
126
- remux |= this.tracks.video.feed(data.video, data.duration, data.compositionTimeOffset);
126
+ remux |= this.tracks.video.feed(data.video, data.duration, data.compositionTimeOffset, data.isLastVideoFrameComplete);
127
127
  }
128
128
  if (data.audio && this.tracks.audio) {
129
129
  remux |= this.tracks.audio.feed(data.audio, data.duration);
package/src/jmuxer.js CHANGED
@@ -324,7 +324,7 @@ export default class JMuxer extends Event {
324
324
  URL.revokeObjectURL(this.url);
325
325
  // this.createBuffer();
326
326
  if (typeof this.options.onReady === 'function') {
327
- this.options.onReady.call(null, this.isReset);
327
+ this.options.onReady.call(null, this.isReset, this.mediaSource);
328
328
  }
329
329
  }
330
330
 
@@ -221,9 +221,11 @@ export class H264Parser {
221
221
  let fixedFrameRate = decoder.readBoolean();
222
222
  let frameDuration = timeScale / (2 * unitsInTick);
223
223
 
224
- if (fixedFrameRate) {
225
- fps = frameDuration;
226
- }
224
+ // if (fixedFrameRate) {
225
+ // fps = frameDuration;
226
+ // }
227
+ // Return the fps value even if fixedFrameRate is not set
228
+ fps = frameDuration;
227
229
  }
228
230
  }
229
231
  return {
@@ -83,7 +83,7 @@ export class H265Parser {
83
83
  decoder.readUByte();
84
84
 
85
85
  decoder.readBits(4); // sps_video_parameter_set_id
86
- decoder.readBits(3); // sps_max_sub_layers_minus1
86
+ const sps_max_sub_layers_minus1 = decoder.readBits(3); // sps_max_sub_layers_minus1
87
87
  decoder.readBits(1); // sps_temporal_id_nesting_flag
88
88
 
89
89
  // --- profile_tier_level() ---
@@ -119,6 +119,96 @@ export class H265Parser {
119
119
  conf_win_bottom_offset = decoder.readUEG();
120
120
  }
121
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
+
122
212
  let fps = null;
123
213
  let vui_parameters_present_flag = decoder.readBoolean();
124
214
  if (vui_parameters_present_flag) {
@@ -154,15 +244,111 @@ export class H265Parser {
154
244
  decoder.readBoolean(); // field_seq_flag
155
245
  decoder.readBoolean(); // frame_field_info_present_flag
156
246
 
157
- // Timing info
158
- let timing_info_present_flag = decoder.readBoolean();
159
- if (timing_info_present_flag) {
160
- let num_units_in_tick = decoder.readUInt();
161
- let time_scale = decoder.readUInt();
162
- decoder.readBoolean(); // poc_proportional_to_timing_flag
163
- if (num_units_in_tick) {
164
- fps = time_scale / (2 * num_units_in_tick);
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
+ }
165
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
166
352
  }
167
353
  }
168
354
 
@@ -42,18 +42,27 @@ export class H264Remuxer extends BaseRemuxer {
42
42
  this.pendingUnits = {};
43
43
  }
44
44
 
45
- feed(data, duration, compositionTimeOffset) {
45
+ feed(data, duration, compositionTimeOffset, isLastFrameComplete = false) {
46
46
  let slices = [];
47
47
  let left;
48
48
  data = appendByteArray(this.remainingData, data);
49
49
  [slices, left] = H264Parser.extractNALu(data);
50
- this.remainingData = left || new Uint8Array();
50
+ if (left) {
51
+ if (isLastFrameComplete) {
52
+ slices.push(left);
53
+ this.remainingData = new Uint8Array();
54
+ } else {
55
+ this.remainingData = left;
56
+ }
57
+ } else {
58
+ this.remainingData = new Uint8Array();
59
+ }
51
60
 
52
61
  if (slices.length > 0) {
53
62
  this.remux(this.getVideoFrames(slices, duration, compositionTimeOffset));
54
63
  return true;
55
64
  } else {
56
- debug.error('Failed to extract any NAL units from video data:', left);
65
+ debug.log('Failed to extract any NAL units from video data:', left);
57
66
  this.dispatch('outOfData');
58
67
  return false;
59
68
  }
@@ -76,6 +85,8 @@ export class H264Remuxer extends BaseRemuxer {
76
85
  for (let nalu of nalus) {
77
86
  let unit = new NALU264(nalu);
78
87
 
88
+ if (!this.parseNAL(unit)) continue;
89
+
79
90
  // frame boundary detection
80
91
  if (units.length && vcl && (unit.isFirstSlice || !unit.isVCL)) {
81
92
  frames.push({
@@ -134,18 +145,11 @@ export class H264Remuxer extends BaseRemuxer {
134
145
 
135
146
  remux(frames) {
136
147
  for (let frame of frames) {
137
- let units = [];
138
- let size = 0;
139
- for (let unit of frame.units) {
140
- if (this.parseNAL(unit)) {
141
- units.push(unit);
142
- size += unit.getSize();
143
- }
144
- }
145
- if (units.length > 0 && this.readyToDecode) {
148
+ let size = frame.units.reduce((acc, cur) => acc + cur.getSize(), 0);
149
+ if (frame.units.length > 0 && this.readyToDecode) {
146
150
  this.mp4track.len += size;
147
151
  this.samples.push({
148
- units: units,
152
+ units: frame.units,
149
153
  size: size,
150
154
  keyFrame: frame.keyFrame,
151
155
  duration: frame.duration,
@@ -1,7 +1,7 @@
1
1
  import * as debug from '../util/debug';
2
2
  import { H265Parser, NALU265 } from '../parsers/h265.js';
3
3
  import { BaseRemuxer } from './base.js';
4
- import { appendByteArray } from '../util/utils.js';
4
+ import { appendByteArray, reverseBits, removeTrailingDotZero } from '../util/utils.js';
5
5
 
6
6
  export class H265Remuxer extends BaseRemuxer {
7
7
 
@@ -46,18 +46,27 @@ export class H265Remuxer extends BaseRemuxer {
46
46
  this.pendingUnits = {};
47
47
  }
48
48
 
49
- feed(data, duration, compositionTimeOffset) {
49
+ feed(data, duration, compositionTimeOffset, isLastFrameComplete = false) {
50
50
  let slices = [];
51
51
  let left;
52
52
  data = appendByteArray(this.remainingData, data);
53
53
  [slices, left] = H265Parser.extractNALu(data);
54
- this.remainingData = left || new Uint8Array();
54
+ if (left) {
55
+ if (isLastFrameComplete) {
56
+ slices.push(left);
57
+ this.remainingData = new Uint8Array();
58
+ } else {
59
+ this.remainingData = left;
60
+ }
61
+ } else {
62
+ this.remainingData = new Uint8Array();
63
+ }
55
64
 
56
65
  if (slices.length > 0) {
57
66
  this.remux(this.getVideoFrames(slices, duration, compositionTimeOffset));
58
67
  return true;
59
68
  } else {
60
- debug.error('Failed to extract any NAL units from video data:', left);
69
+ debug.log('Failed to extract any NAL units from video data:', left);
61
70
  this.dispatch('outOfData');
62
71
  return false;
63
72
  }
@@ -80,6 +89,8 @@ export class H265Remuxer extends BaseRemuxer {
80
89
 
81
90
  for (let nalu of nalus) {
82
91
  let unit = new NALU265(nalu);
92
+
93
+ if (!this.parseNAL(unit)) continue;
83
94
 
84
95
  // frame boundary detection
85
96
  if (units.length && vcl && (unit.isFirstSlice || !unit.isVCL)) {
@@ -139,18 +150,11 @@ export class H265Remuxer extends BaseRemuxer {
139
150
 
140
151
  remux(frames) {
141
152
  for (let frame of frames) {
142
- let units = [];
143
- let size = 0;
144
- for (let unit of frame.units) {
145
- if (this.parseNAL(unit)) {
146
- units.push(unit);
147
- size += unit.getSize();
148
- }
149
- }
150
- if (units.length > 0 && this.readyToDecode) {
153
+ let size = frame.units.reduce((acc, cur) => acc + cur.getSize(), 0);
154
+ if (frame.units.length > 0 && this.readyToDecode) {
151
155
  this.mp4track.len += size;
152
156
  this.samples.push({
153
- units: units,
157
+ units: frame.units,
154
158
  size: size,
155
159
  keyFrame: frame.keyFrame,
156
160
  duration: frame.duration,
@@ -217,9 +221,12 @@ export class H265Remuxer extends BaseRemuxer {
217
221
  this.mp4track.width = config.width;
218
222
  this.mp4track.height = config.height;
219
223
 
220
- this.mp4track.codec = `hvc1.${config.profile_idc}.${config.profile_compatibility_flags.toString(16)}`
221
- + `.L${config.level_idc}${config.tier_flag ? 'H' : 'L'}`
222
- + `.${config.constraint_indicator_flags.map(b => b.toString(16)).join('.').toUpperCase()}`;
224
+ this.mp4track.codec = 'hvc1'
225
+ + '.' + (config.profile_space ? String.fromCharCode(64 + config.profile_space) : '') // Map [0,1,2,3] to ['','A','B','C']
226
+ + config.profile_idc
227
+ + '.' + reverseBits(config.profile_compatibility_flags).toString(16)
228
+ + '.' + (config.tier_flag ? 'H' : 'L') + config.level_idc
229
+ + '.' + removeTrailingDotZero(config.constraint_indicator_flags.map(function (b) { return b.toString(16); }).join('.').toUpperCase());
223
230
 
224
231
  this.mp4track.hvcC = {
225
232
  profile_space: config.profile_space,
package/src/util/debug.js CHANGED
@@ -2,10 +2,8 @@ let logger;
2
2
  let errorLogger;
3
3
 
4
4
  export function setLogger(log, err) {
5
- /*eslint-disable */
6
5
  logger = log;
7
6
  errorLogger = err;
8
- /*eslint-enable */
9
7
  }
10
8
 
11
9
  export function isEnable() {
package/src/util/utils.js CHANGED
@@ -22,3 +22,23 @@ export function secToTime(sec) {
22
22
  result += (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
23
23
  return result;
24
24
  }
25
+
26
+ export function reverseBits(n) {
27
+ var result = 0;
28
+ for (var i = 0; i < 32; i++)
29
+ {
30
+ // Shift result left to make room
31
+ result <<= 1;
32
+ // Add the least significant bit of n
33
+ result |= (n & 1);
34
+ // Shift n right to process the next bit
35
+ n >>>= 1;
36
+ }
37
+ // Ensure result is treated as unsigned 32-bit
38
+ return result >>> 0;
39
+ }
40
+
41
+ export function removeTrailingDotZero(input) {
42
+ // Use regex to strip all trailing ".0" sequences
43
+ return input.replace(/(?:\.0)+$/, '');
44
+ }