@invintusmedia/tomp4 1.3.0 → 1.4.0

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,483 @@
1
+ /**
2
+ * H.264 SPS and PPS Parsers
3
+ *
4
+ * Full parsing of Sequence Parameter Set and Picture Parameter Set
5
+ * NAL units, extracting all fields needed for decoding.
6
+ *
7
+ * Reference: ITU-T H.264, Section 7.3.2.1 (SPS) and 7.3.2.2 (PPS)
8
+ *
9
+ * @module codecs/h264-sps-pps
10
+ */
11
+
12
+ import { BitstreamReader, removeEmulationPrevention } from './h264-cabac.js';
13
+
14
+ // ══════════════════════════════════════════════════════════
15
+ // SPS Parser
16
+ // ══════════════════════════════════════════════════════════
17
+
18
+ /**
19
+ * Parse a full Sequence Parameter Set.
20
+ * @param {Uint8Array} nalUnit - SPS NAL unit data (including NAL header byte)
21
+ * @returns {object} Parsed SPS fields
22
+ */
23
+ export function parseSPSFull(nalUnit) {
24
+ const rbsp = removeEmulationPrevention(nalUnit);
25
+ const bs = new BitstreamReader(rbsp, 8); // skip NAL header
26
+
27
+ const sps = {};
28
+ sps.profile_idc = bs.readBits(8);
29
+ sps.constraint_set0_flag = bs.readBit();
30
+ sps.constraint_set1_flag = bs.readBit();
31
+ sps.constraint_set2_flag = bs.readBit();
32
+ sps.constraint_set3_flag = bs.readBit();
33
+ sps.constraint_set4_flag = bs.readBit();
34
+ sps.constraint_set5_flag = bs.readBit();
35
+ bs.readBits(2); // reserved_zero_2bits
36
+ sps.level_idc = bs.readBits(8);
37
+ sps.seq_parameter_set_id = bs.readUE();
38
+
39
+ // High profile extensions
40
+ sps.chroma_format_idc = 1; // default
41
+ sps.separate_colour_plane_flag = 0;
42
+ sps.bit_depth_luma_minus8 = 0;
43
+ sps.bit_depth_chroma_minus8 = 0;
44
+ sps.qpprime_y_zero_transform_bypass_flag = 0;
45
+ sps.seq_scaling_matrix_present_flag = 0;
46
+ sps.scalingLists4x4 = null;
47
+ sps.scalingLists8x8 = null;
48
+
49
+ const isHigh = [100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134].includes(sps.profile_idc);
50
+ if (isHigh) {
51
+ sps.chroma_format_idc = bs.readUE();
52
+ if (sps.chroma_format_idc === 3) {
53
+ sps.separate_colour_plane_flag = bs.readBit();
54
+ }
55
+ sps.bit_depth_luma_minus8 = bs.readUE();
56
+ sps.bit_depth_chroma_minus8 = bs.readUE();
57
+ sps.qpprime_y_zero_transform_bypass_flag = bs.readBit();
58
+ sps.seq_scaling_matrix_present_flag = bs.readBit();
59
+
60
+ if (sps.seq_scaling_matrix_present_flag) {
61
+ const numLists = sps.chroma_format_idc !== 3 ? 8 : 12;
62
+ sps.scalingLists4x4 = [];
63
+ sps.scalingLists8x8 = [];
64
+ for (let i = 0; i < numLists; i++) {
65
+ const present = bs.readBit();
66
+ if (present) {
67
+ if (i < 6) {
68
+ sps.scalingLists4x4.push(parseScalingList(bs, 16));
69
+ } else {
70
+ sps.scalingLists8x8.push(parseScalingList(bs, 64));
71
+ }
72
+ } else {
73
+ if (i < 6) sps.scalingLists4x4.push(null);
74
+ else sps.scalingLists8x8.push(null);
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ sps.log2_max_frame_num_minus4 = bs.readUE();
81
+ sps.MaxFrameNum = 1 << (sps.log2_max_frame_num_minus4 + 4);
82
+
83
+ sps.pic_order_cnt_type = bs.readUE();
84
+ if (sps.pic_order_cnt_type === 0) {
85
+ sps.log2_max_pic_order_cnt_lsb_minus4 = bs.readUE();
86
+ sps.MaxPicOrderCntLsb = 1 << (sps.log2_max_pic_order_cnt_lsb_minus4 + 4);
87
+ } else if (sps.pic_order_cnt_type === 1) {
88
+ sps.delta_pic_order_always_zero_flag = bs.readBit();
89
+ sps.offset_for_non_ref_pic = bs.readSE();
90
+ sps.offset_for_top_to_bottom_field = bs.readSE();
91
+ sps.num_ref_frames_in_pic_order_cnt_cycle = bs.readUE();
92
+ sps.offset_for_ref_frame = [];
93
+ for (let i = 0; i < sps.num_ref_frames_in_pic_order_cnt_cycle; i++) {
94
+ sps.offset_for_ref_frame.push(bs.readSE());
95
+ }
96
+ }
97
+
98
+ sps.max_num_ref_frames = bs.readUE();
99
+ sps.gaps_in_frame_num_value_allowed_flag = bs.readBit();
100
+
101
+ sps.pic_width_in_mbs_minus1 = bs.readUE();
102
+ sps.pic_height_in_map_units_minus1 = bs.readUE();
103
+
104
+ sps.frame_mbs_only_flag = bs.readBit();
105
+ if (!sps.frame_mbs_only_flag) {
106
+ sps.mb_adaptive_frame_field_flag = bs.readBit();
107
+ } else {
108
+ sps.mb_adaptive_frame_field_flag = 0;
109
+ }
110
+
111
+ sps.direct_8x8_inference_flag = bs.readBit();
112
+
113
+ sps.frame_cropping_flag = bs.readBit();
114
+ sps.frame_crop_left_offset = 0;
115
+ sps.frame_crop_right_offset = 0;
116
+ sps.frame_crop_top_offset = 0;
117
+ sps.frame_crop_bottom_offset = 0;
118
+ if (sps.frame_cropping_flag) {
119
+ sps.frame_crop_left_offset = bs.readUE();
120
+ sps.frame_crop_right_offset = bs.readUE();
121
+ sps.frame_crop_top_offset = bs.readUE();
122
+ sps.frame_crop_bottom_offset = bs.readUE();
123
+ }
124
+
125
+ sps.vui_parameters_present_flag = bs.readBit();
126
+ // We skip VUI parsing — not needed for decoding
127
+
128
+ // Derived values
129
+ sps.PicWidthInMbs = sps.pic_width_in_mbs_minus1 + 1;
130
+ sps.PicHeightInMapUnits = sps.pic_height_in_map_units_minus1 + 1;
131
+ sps.FrameHeightInMbs = (2 - sps.frame_mbs_only_flag) * sps.PicHeightInMapUnits;
132
+ sps.PicHeightInMbs = sps.FrameHeightInMbs; // frame mode only for now
133
+ sps.PicSizeInMbs = sps.PicWidthInMbs * sps.PicHeightInMbs;
134
+
135
+ const cropUnitX = sps.chroma_format_idc === 0 ? 1 : 2;
136
+ const cropUnitY = (sps.chroma_format_idc === 0 ? 1 : 2) * (2 - sps.frame_mbs_only_flag);
137
+ sps.width = sps.PicWidthInMbs * 16 - (sps.frame_crop_left_offset + sps.frame_crop_right_offset) * cropUnitX;
138
+ sps.height = sps.PicHeightInMbs * 16 - (sps.frame_crop_top_offset + sps.frame_crop_bottom_offset) * cropUnitY;
139
+
140
+ // ChromaArrayType
141
+ sps.ChromaArrayType = sps.separate_colour_plane_flag ? 0 : sps.chroma_format_idc;
142
+ sps.SubWidthC = sps.chroma_format_idc === 1 || sps.chroma_format_idc === 2 ? 2 : 1;
143
+ sps.SubHeightC = sps.chroma_format_idc === 1 ? 2 : 1;
144
+ sps.MbWidthC = 16 / sps.SubWidthC;
145
+ sps.MbHeightC = 16 / sps.SubHeightC;
146
+
147
+ sps.BitDepthY = 8 + sps.bit_depth_luma_minus8;
148
+ sps.BitDepthC = 8 + sps.bit_depth_chroma_minus8;
149
+ sps.QpBdOffsetY = 6 * sps.bit_depth_luma_minus8;
150
+ sps.QpBdOffsetC = 6 * sps.bit_depth_chroma_minus8;
151
+
152
+ return sps;
153
+ }
154
+
155
+ function parseScalingList(bs, size) {
156
+ const list = new Int32Array(size);
157
+ let lastScale = 8;
158
+ let nextScale = 8;
159
+ for (let i = 0; i < size; i++) {
160
+ if (nextScale !== 0) {
161
+ const deltaScale = bs.readSE();
162
+ nextScale = (lastScale + deltaScale + 256) % 256;
163
+ }
164
+ list[i] = nextScale === 0 ? lastScale : nextScale;
165
+ lastScale = list[i];
166
+ }
167
+ return list;
168
+ }
169
+
170
+ // ══════════════════════════════════════════════════════════
171
+ // PPS Parser
172
+ // ══════════════════════════════════════════════════════════
173
+
174
+ /**
175
+ * Parse a full Picture Parameter Set.
176
+ * @param {Uint8Array} nalUnit - PPS NAL unit data (including NAL header byte)
177
+ * @param {object} sps - The associated SPS (needed for some fields)
178
+ * @returns {object} Parsed PPS fields
179
+ */
180
+ export function parsePPSFull(nalUnit, sps) {
181
+ const rbsp = removeEmulationPrevention(nalUnit);
182
+ const bs = new BitstreamReader(rbsp, 8); // skip NAL header
183
+
184
+ const pps = {};
185
+ pps.pic_parameter_set_id = bs.readUE();
186
+ pps.seq_parameter_set_id = bs.readUE();
187
+ pps.entropy_coding_mode_flag = bs.readBit(); // 0=CAVLC, 1=CABAC
188
+ pps.bottom_field_pic_order_in_frame_present_flag = bs.readBit();
189
+
190
+ pps.num_slice_groups_minus1 = bs.readUE();
191
+ if (pps.num_slice_groups_minus1 > 0) {
192
+ // Slice group map — rarely used, skip for now
193
+ pps.slice_group_map_type = bs.readUE();
194
+ // TODO: parse slice group map if needed
195
+ throw new Error('Slice groups not supported');
196
+ }
197
+
198
+ pps.num_ref_idx_l0_default_active_minus1 = bs.readUE();
199
+ pps.num_ref_idx_l1_default_active_minus1 = bs.readUE();
200
+ pps.weighted_pred_flag = bs.readBit();
201
+ pps.weighted_bipred_idc = bs.readBits(2);
202
+ pps.pic_init_qp_minus26 = bs.readSE();
203
+ pps.pic_init_qs_minus26 = bs.readSE();
204
+ pps.chroma_qp_index_offset = bs.readSE();
205
+ pps.deblocking_filter_control_present_flag = bs.readBit();
206
+ pps.constrained_intra_pred_flag = bs.readBit();
207
+ pps.redundant_pic_cnt_present_flag = bs.readBit();
208
+
209
+ // High profile extensions
210
+ pps.transform_8x8_mode_flag = 0;
211
+ pps.pic_scaling_matrix_present_flag = 0;
212
+ pps.second_chroma_qp_index_offset = pps.chroma_qp_index_offset;
213
+
214
+ if (bs.bitsLeft > 8) {
215
+ // More RBSP data → High profile extensions
216
+ pps.transform_8x8_mode_flag = bs.readBit();
217
+ pps.pic_scaling_matrix_present_flag = bs.readBit();
218
+ if (pps.pic_scaling_matrix_present_flag) {
219
+ const numLists = 6 + (sps?.chroma_format_idc !== 3 ? 2 : 6) * pps.transform_8x8_mode_flag;
220
+ for (let i = 0; i < numLists; i++) {
221
+ const present = bs.readBit();
222
+ if (present) {
223
+ parseScalingList(bs, i < 6 ? 16 : 64);
224
+ }
225
+ }
226
+ }
227
+ pps.second_chroma_qp_index_offset = bs.readSE();
228
+ }
229
+
230
+ // Derived
231
+ pps.SliceQPY = 26 + pps.pic_init_qp_minus26;
232
+
233
+ return pps;
234
+ }
235
+
236
+ // ══════════════════════════════════════════════════════════
237
+ // Slice Header Parser
238
+ // ══════════════════════════════════════════════════════════
239
+
240
+ /**
241
+ * Parse a slice header from a slice NAL unit.
242
+ * @param {Uint8Array} nalUnit - Slice NAL unit (type 1 or 5)
243
+ * @param {object} sps - Active SPS
244
+ * @param {object} pps - Active PPS
245
+ * @returns {object} Parsed slice header + bit position where data begins
246
+ */
247
+ export function parseSliceHeader(nalUnit, sps, pps) {
248
+ const rbsp = removeEmulationPrevention(nalUnit);
249
+ const bs = new BitstreamReader(rbsp, 0);
250
+
251
+ // NAL header
252
+ const forbidden_zero_bit = bs.readBit();
253
+ const nal_ref_idc = bs.readBits(2);
254
+ const nal_unit_type = bs.readBits(5);
255
+
256
+ const sh = {};
257
+ sh.nal_ref_idc = nal_ref_idc;
258
+ sh.nal_unit_type = nal_unit_type;
259
+ sh.isIDR = nal_unit_type === 5;
260
+
261
+ sh.first_mb_in_slice = bs.readUE();
262
+ sh.slice_type = bs.readUE();
263
+ // Normalize slice type (0-4 and 5-9 map to the same types)
264
+ sh.slice_type_mod5 = sh.slice_type % 5;
265
+ sh.isI = sh.slice_type_mod5 === 2;
266
+ sh.isP = sh.slice_type_mod5 === 0;
267
+ sh.isB = sh.slice_type_mod5 === 1;
268
+
269
+ sh.pic_parameter_set_id = bs.readUE();
270
+
271
+ if (sps.separate_colour_plane_flag) {
272
+ sh.colour_plane_id = bs.readBits(2);
273
+ }
274
+
275
+ sh.frame_num = bs.readBits(sps.log2_max_frame_num_minus4 + 4);
276
+
277
+ sh.field_pic_flag = 0;
278
+ sh.bottom_field_flag = 0;
279
+ if (!sps.frame_mbs_only_flag) {
280
+ sh.field_pic_flag = bs.readBit();
281
+ if (sh.field_pic_flag) {
282
+ sh.bottom_field_flag = bs.readBit();
283
+ }
284
+ }
285
+
286
+ if (sh.isIDR) {
287
+ sh.idr_pic_id = bs.readUE();
288
+ }
289
+
290
+ sh.pic_order_cnt_lsb = 0;
291
+ sh.delta_pic_order_cnt_bottom = 0;
292
+ sh.delta_pic_order_cnt = [0, 0];
293
+
294
+ if (sps.pic_order_cnt_type === 0) {
295
+ sh.pic_order_cnt_lsb = bs.readBits(sps.log2_max_pic_order_cnt_lsb_minus4 + 4);
296
+ if (pps.bottom_field_pic_order_in_frame_present_flag && !sh.field_pic_flag) {
297
+ sh.delta_pic_order_cnt_bottom = bs.readSE();
298
+ }
299
+ } else if (sps.pic_order_cnt_type === 1 && !sps.delta_pic_order_always_zero_flag) {
300
+ sh.delta_pic_order_cnt[0] = bs.readSE();
301
+ if (pps.bottom_field_pic_order_in_frame_present_flag && !sh.field_pic_flag) {
302
+ sh.delta_pic_order_cnt[1] = bs.readSE();
303
+ }
304
+ }
305
+
306
+ sh.redundant_pic_cnt = 0;
307
+ if (pps.redundant_pic_cnt_present_flag) {
308
+ sh.redundant_pic_cnt = bs.readUE();
309
+ }
310
+
311
+ // P/B slice specific
312
+ sh.direct_spatial_mv_pred_flag = 0;
313
+ sh.num_ref_idx_active_override_flag = 0;
314
+ sh.num_ref_idx_l0_active_minus1 = pps.num_ref_idx_l0_default_active_minus1;
315
+ sh.num_ref_idx_l1_active_minus1 = pps.num_ref_idx_l1_default_active_minus1;
316
+
317
+ if (sh.isB) {
318
+ sh.direct_spatial_mv_pred_flag = bs.readBit();
319
+ }
320
+
321
+ if (sh.isP || sh.isB) {
322
+ sh.num_ref_idx_active_override_flag = bs.readBit();
323
+ if (sh.num_ref_idx_active_override_flag) {
324
+ sh.num_ref_idx_l0_active_minus1 = bs.readUE();
325
+ if (sh.isB) {
326
+ sh.num_ref_idx_l1_active_minus1 = bs.readUE();
327
+ }
328
+ }
329
+ }
330
+
331
+ // Reference picture list modification
332
+ sh.ref_pic_list_modification_l0 = null;
333
+ sh.ref_pic_list_modification_l1 = null;
334
+ if (!sh.isI) {
335
+ // ref_pic_list_modification()
336
+ const l0_flag = bs.readBit();
337
+ if (l0_flag) {
338
+ sh.ref_pic_list_modification_l0 = [];
339
+ while (true) {
340
+ const op = bs.readUE();
341
+ if (op === 3) break;
342
+ const val = bs.readUE();
343
+ sh.ref_pic_list_modification_l0.push({ op, val });
344
+ }
345
+ }
346
+ if (sh.isB) {
347
+ const l1_flag = bs.readBit();
348
+ if (l1_flag) {
349
+ sh.ref_pic_list_modification_l1 = [];
350
+ while (true) {
351
+ const op = bs.readUE();
352
+ if (op === 3) break;
353
+ const val = bs.readUE();
354
+ sh.ref_pic_list_modification_l1.push({ op, val });
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ // Weighted prediction
361
+ sh.luma_weight_l0 = null;
362
+ sh.chroma_weight_l0 = null;
363
+ sh.luma_weight_l1 = null;
364
+ sh.chroma_weight_l1 = null;
365
+ if ((pps.weighted_pred_flag && sh.isP) || (pps.weighted_bipred_idc === 1 && sh.isB)) {
366
+ // pred_weight_table()
367
+ sh.luma_log2_weight_denom = bs.readUE();
368
+ if (sps.ChromaArrayType !== 0) {
369
+ sh.chroma_log2_weight_denom = bs.readUE();
370
+ }
371
+ sh.luma_weight_l0 = [];
372
+ sh.chroma_weight_l0 = [];
373
+ for (let i = 0; i <= sh.num_ref_idx_l0_active_minus1; i++) {
374
+ const luma_flag = bs.readBit();
375
+ if (luma_flag) {
376
+ sh.luma_weight_l0.push({ weight: bs.readSE(), offset: bs.readSE() });
377
+ } else {
378
+ sh.luma_weight_l0.push({ weight: 1 << sh.luma_log2_weight_denom, offset: 0 });
379
+ }
380
+ if (sps.ChromaArrayType !== 0) {
381
+ const chroma_flag = bs.readBit();
382
+ if (chroma_flag) {
383
+ sh.chroma_weight_l0.push([
384
+ { weight: bs.readSE(), offset: bs.readSE() },
385
+ { weight: bs.readSE(), offset: bs.readSE() },
386
+ ]);
387
+ } else {
388
+ sh.chroma_weight_l0.push([
389
+ { weight: 1 << sh.chroma_log2_weight_denom, offset: 0 },
390
+ { weight: 1 << sh.chroma_log2_weight_denom, offset: 0 },
391
+ ]);
392
+ }
393
+ }
394
+ }
395
+ if (sh.isB) {
396
+ sh.luma_weight_l1 = [];
397
+ sh.chroma_weight_l1 = [];
398
+ for (let i = 0; i <= sh.num_ref_idx_l1_active_minus1; i++) {
399
+ const luma_flag = bs.readBit();
400
+ if (luma_flag) {
401
+ sh.luma_weight_l1.push({ weight: bs.readSE(), offset: bs.readSE() });
402
+ } else {
403
+ sh.luma_weight_l1.push({ weight: 1 << sh.luma_log2_weight_denom, offset: 0 });
404
+ }
405
+ if (sps.ChromaArrayType !== 0) {
406
+ const chroma_flag = bs.readBit();
407
+ if (chroma_flag) {
408
+ sh.chroma_weight_l1.push([
409
+ { weight: bs.readSE(), offset: bs.readSE() },
410
+ { weight: bs.readSE(), offset: bs.readSE() },
411
+ ]);
412
+ } else {
413
+ sh.chroma_weight_l1.push([
414
+ { weight: 1 << sh.chroma_log2_weight_denom, offset: 0 },
415
+ { weight: 1 << sh.chroma_log2_weight_denom, offset: 0 },
416
+ ]);
417
+ }
418
+ }
419
+ }
420
+ }
421
+ }
422
+
423
+ // Decoded reference picture marking
424
+ if (nal_ref_idc !== 0) {
425
+ // dec_ref_pic_marking()
426
+ if (sh.isIDR) {
427
+ sh.no_output_of_prior_pics_flag = bs.readBit();
428
+ sh.long_term_reference_flag = bs.readBit();
429
+ } else {
430
+ sh.adaptive_ref_pic_marking_mode_flag = bs.readBit();
431
+ if (sh.adaptive_ref_pic_marking_mode_flag) {
432
+ sh.mmco = [];
433
+ while (true) {
434
+ const op = bs.readUE();
435
+ if (op === 0) break;
436
+ const entry = { op };
437
+ if (op === 1 || op === 3) entry.difference_of_pic_nums_minus1 = bs.readUE();
438
+ if (op === 2) entry.long_term_pic_num = bs.readUE();
439
+ if (op === 3 || op === 6) entry.long_term_frame_idx = bs.readUE();
440
+ if (op === 4) entry.max_long_term_frame_idx_plus1 = bs.readUE();
441
+ sh.mmco.push(entry);
442
+ }
443
+ }
444
+ }
445
+ }
446
+
447
+ // CABAC init
448
+ sh.cabac_init_idc = 0;
449
+ if (pps.entropy_coding_mode_flag && !sh.isI) {
450
+ sh.cabac_init_idc = bs.readUE();
451
+ }
452
+
453
+ sh.slice_qp_delta = bs.readSE();
454
+ sh.SliceQPY = 26 + pps.pic_init_qp_minus26 + sh.slice_qp_delta;
455
+
456
+ // SP/SI slice specific (rarely used)
457
+ if (sh.slice_type_mod5 === 3 || sh.slice_type_mod5 === 4) {
458
+ if (sh.slice_type_mod5 === 3) {
459
+ sh.sp_for_switch_flag = bs.readBit();
460
+ }
461
+ sh.slice_qs_delta = bs.readSE();
462
+ }
463
+
464
+ // Deblocking filter
465
+ sh.disable_deblocking_filter_idc = 0;
466
+ sh.slice_alpha_c0_offset_div2 = 0;
467
+ sh.slice_beta_offset_div2 = 0;
468
+ if (pps.deblocking_filter_control_present_flag) {
469
+ sh.disable_deblocking_filter_idc = bs.readUE();
470
+ if (sh.disable_deblocking_filter_idc !== 1) {
471
+ sh.slice_alpha_c0_offset_div2 = bs.readSE();
472
+ sh.slice_beta_offset_div2 = bs.readSE();
473
+ }
474
+ }
475
+
476
+ // Record where slice data begins (for CABAC or CAVLC)
477
+ sh.headerBitLength = bs.bitPos;
478
+ sh._rbsp = rbsp; // keep for CABAC init
479
+
480
+ return sh;
481
+ }
482
+
483
+ export default { parseSPSFull, parsePPSFull, parseSliceHeader };
@@ -0,0 +1,217 @@
1
+ /**
2
+ * H.264 Constant Tables
3
+ *
4
+ * Lookup tables from the H.264/AVC specification (ITU-T H.264).
5
+ * Used by both the decoder and encoder.
6
+ *
7
+ * @module codecs/h264-tables
8
+ */
9
+
10
+ // ══════════════════════════════════════════════════════════
11
+ // CABAC Range Table (Table 9-48)
12
+ // rangeTabLPS[pStateIdx][qCodIRangeIdx]
13
+ // ══════════════════════════════════════════════════════════
14
+
15
+ export const rangeTabLPS = [
16
+ [128,176,208,240],[128,167,197,227],[128,158,187,216],[123,150,178,205],
17
+ [116,142,169,195],[111,135,160,185],[105,128,152,175],[100,122,144,166],
18
+ [ 95,116,137,158],[ 90,110,130,150],[ 85,104,123,142],[ 81, 99,117,135],
19
+ [ 77, 94,111,128],[ 73, 89,105,122],[ 69, 85,100,116],[ 66, 80, 95,110],
20
+ [ 62, 76, 90,104],[ 59, 72, 86, 99],[ 56, 69, 81, 94],[ 53, 65, 77, 89],
21
+ [ 51, 62, 73, 85],[ 48, 59, 69, 80],[ 46, 56, 66, 76],[ 43, 53, 63, 72],
22
+ [ 41, 50, 59, 69],[ 39, 48, 56, 65],[ 37, 45, 54, 62],[ 35, 43, 51, 59],
23
+ [ 33, 41, 48, 56],[ 32, 39, 46, 53],[ 30, 37, 43, 50],[ 29, 35, 41, 48],
24
+ [ 27, 33, 39, 45],[ 26, 31, 37, 43],[ 24, 30, 35, 41],[ 23, 28, 33, 39],
25
+ [ 22, 27, 32, 37],[ 21, 26, 30, 35],[ 20, 24, 29, 33],[ 19, 23, 27, 31],
26
+ [ 18, 22, 26, 30],[ 17, 21, 25, 28],[ 16, 20, 23, 27],[ 15, 19, 22, 25],
27
+ [ 14, 18, 21, 24],[ 14, 17, 20, 23],[ 13, 16, 19, 22],[ 12, 15, 18, 21],
28
+ [ 12, 14, 17, 20],[ 11, 14, 16, 19],[ 11, 13, 15, 18],[ 10, 12, 15, 17],
29
+ [ 10, 12, 14, 16],[ 9, 11, 13, 15],[ 9, 11, 12, 14],[ 8, 10, 12, 14],
30
+ [ 8, 9, 11, 13],[ 7, 9, 11, 12],[ 7, 9, 10, 12],[ 7, 8, 10, 11],
31
+ [ 6, 8, 9, 11],[ 6, 7, 9, 10],[ 6, 7, 8, 9],[ 2, 2, 2, 2],
32
+ ];
33
+
34
+ // ══════════════════════════════════════════════════════════
35
+ // CABAC State Transition Tables (Table 9-49, 9-50)
36
+ // Original tables (pStateIdx only, 64 entries each)
37
+ // ══════════════════════════════════════════════════════════
38
+
39
+ export const transIdxLPS = [
40
+ 0, 0, 1, 2, 2, 4, 4, 5, 6, 7, 8, 9, 9,11,11,12,
41
+ 13,13,15,15,16,16,18,18,19,19,21,21,22,22,23,24,
42
+ 24,25,26,26,27,27,28,29,29,30,30,30,31,32,32,33,
43
+ 33,33,34,34,35,35,35,36,36,36,37,37,37,38,38,63,
44
+ ];
45
+
46
+ export const transIdxMPS = [
47
+ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,
48
+ 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
49
+ 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
50
+ 49,50,51,52,53,54,55,56,57,58,59,60,61,62,62,63,
51
+ ];
52
+
53
+ // ══════════════════════════════════════════════════════════
54
+ // CABAC Context Initialization Values (Table 9-12 to 9-23)
55
+ // Each entry is [m, n] where initValue = m * SliceQPy + n
56
+ // Contexts 0-10: mb_type, sub_mb_type, etc.
57
+ // ══════════════════════════════════════════════════════════
58
+
59
+ // I-slice context init values (cabac_init_idc not used for I-slices)
60
+ // Format: array of [m, n] pairs, indexed by ctxIdx
61
+ export const cabacInitI = [
62
+ // 0-10: mb_type SI
63
+ [20,-15],[2,54],[3,74],[20,-15],[2,54],[3,74],[-28,127],[-23,104],[-6,53],[-1,54],[7,51],
64
+ // 11-23: mb_type I
65
+ [23,33],[23,2],[21,0],
66
+ // We need ~460 contexts. The full table is very large.
67
+ // For practical purposes, we'll initialize from the spec tables at runtime.
68
+ ];
69
+
70
+ // P/B-slice context init values depend on cabac_init_idc (0, 1, or 2)
71
+ // These are used for the majority of syntax elements in P/B slices.
72
+ // The full tables are in the H.264 spec Annex, Tables 9-12 through 9-23.
73
+
74
+ // ══════════════════════════════════════════════════════════
75
+ // Inverse Zigzag Scan (4x4)
76
+ // Maps coefficient index → [row, col] in the 4x4 block
77
+ // ══════════════════════════════════════════════════════════
78
+
79
+ export const scanZigzag4x4 = [
80
+ [0,0],[0,1],[1,0],[2,0],[1,1],[0,2],[0,3],[1,2],
81
+ [2,1],[3,0],[3,1],[2,2],[1,3],[2,3],[3,2],[3,3],
82
+ ];
83
+
84
+ // Flat index version: position[i] = row * 4 + col
85
+ export const scanOrder4x4 = scanZigzag4x4.map(([r,c]) => r * 4 + c);
86
+
87
+ // ══════════════════════════════════════════════════════════
88
+ // Inverse Zigzag Scan (8x8)
89
+ // ══════════════════════════════════════════════════════════
90
+
91
+ export const scanZigzag8x8 = [
92
+ [0,0],[0,1],[1,0],[2,0],[1,1],[0,2],[0,3],[1,2],
93
+ [2,1],[3,0],[4,0],[3,1],[2,2],[1,3],[0,4],[0,5],
94
+ [1,4],[2,3],[3,2],[4,1],[5,0],[6,0],[5,1],[4,2],
95
+ [3,3],[2,4],[1,5],[0,6],[0,7],[1,6],[2,5],[3,4],
96
+ [4,3],[5,2],[6,1],[7,0],[7,1],[6,2],[5,3],[4,4],
97
+ [3,5],[2,6],[1,7],[2,7],[3,6],[4,5],[5,4],[6,3],
98
+ [7,2],[7,3],[6,4],[5,5],[4,6],[3,7],[4,7],[5,6],
99
+ [6,5],[7,4],[7,5],[6,6],[5,7],[6,7],[7,6],[7,7],
100
+ ];
101
+
102
+ export const scanOrder8x8 = scanZigzag8x8.map(([r,c]) => r * 8 + c);
103
+
104
+ // ══════════════════════════════════════════════════════════
105
+ // Quantization
106
+ // ══════════════════════════════════════════════════════════
107
+
108
+ // LevelScale for 4x4 inverse quantization (Table 8-13)
109
+ // levelScale[qp%6][i][j]
110
+ export const levelScale4x4 = [
111
+ [10,13,10,13,13,16,13,16,10,13,10,13,13,16,13,16],
112
+ [11,14,11,14,14,18,14,18,11,14,11,14,14,18,14,18],
113
+ [13,16,13,16,16,20,16,20,13,16,13,16,16,20,16,20],
114
+ [14,18,14,18,18,23,18,23,14,18,14,18,18,23,18,23],
115
+ [16,20,16,20,20,25,20,25,16,20,16,20,20,25,20,25],
116
+ [18,23,18,23,23,29,23,29,18,23,18,23,23,29,23,29],
117
+ ];
118
+
119
+ // Quantization step sizes for encoder (forward quantization)
120
+ // MF[qp%6] values for 4x4 blocks
121
+ export const quantMF4x4 = [
122
+ [13107,8066,13107,8066,8066,5243,8066,5243,13107,8066,13107,8066,8066,5243,8066,5243],
123
+ [11916,7490,11916,7490,7490,4660,7490,4660,11916,7490,11916,7490,7490,4660,7490,4660],
124
+ [10082,6554,10082,6554,6554,4194,6554,4194,10082,6554,10082,6554,6554,4194,6554,4194],
125
+ [ 9362,5825, 9362,5825,5825,3647,5825,3647, 9362,5825, 9362,5825,5825,3647,5825,3647],
126
+ [ 8192,5243, 8192,5243,5243,3355,5243,3355, 8192,5243, 8192,5243,5243,3355,5243,3355],
127
+ [ 7282,4559, 7282,4559,4559,2893,4559,2893, 7282,4559, 7282,4559,4559,2893,4559,2893],
128
+ ];
129
+
130
+ // ══════════════════════════════════════════════════════════
131
+ // Intra Prediction Modes
132
+ // ══════════════════════════════════════════════════════════
133
+
134
+ export const INTRA_4x4_V = 0; // Vertical
135
+ export const INTRA_4x4_H = 1; // Horizontal
136
+ export const INTRA_4x4_DC = 2; // DC
137
+ export const INTRA_4x4_DDL = 3; // Diagonal Down-Left
138
+ export const INTRA_4x4_DDR = 4; // Diagonal Down-Right
139
+ export const INTRA_4x4_VR = 5; // Vertical-Right
140
+ export const INTRA_4x4_HD = 6; // Horizontal-Down
141
+ export const INTRA_4x4_VL = 7; // Vertical-Left
142
+ export const INTRA_4x4_HU = 8; // Horizontal-Up
143
+
144
+ export const INTRA_16x16_V = 0; // Vertical
145
+ export const INTRA_16x16_H = 1; // Horizontal
146
+ export const INTRA_16x16_DC = 2; // DC
147
+ export const INTRA_16x16_PLANE = 3; // Plane
148
+
149
+ // ══════════════════════════════════════════════════════════
150
+ // Sub-pixel interpolation filter (6-tap)
151
+ // For half-pel motion compensation
152
+ // ══════════════════════════════════════════════════════════
153
+
154
+ export const SUBPEL_FILTER_TAPS = [1, -5, 20, 20, -5, 1];
155
+
156
+ // ══════════════════════════════════════════════════════════
157
+ // Macroblock type tables
158
+ // ══════════════════════════════════════════════════════════
159
+
160
+ // I-slice macroblock types (Table 7-11)
161
+ export const MB_TYPE_I_NxN = 0; // Intra_4x4 or Intra_8x8
162
+ export const MB_TYPE_I_16x16_BASE = 1; // Intra_16x16 (types 1-24)
163
+ export const MB_TYPE_I_PCM = 25;
164
+
165
+ // P-slice macroblock types (Table 7-13)
166
+ export const MB_TYPE_P_L0_16x16 = 0;
167
+ export const MB_TYPE_P_L0_L0_16x8 = 1;
168
+ export const MB_TYPE_P_L0_L0_8x16 = 2;
169
+ export const MB_TYPE_P_8x8 = 3;
170
+ export const MB_TYPE_P_8x8ref0 = 4;
171
+
172
+ // Mapping from I_16x16 mb_type to prediction mode, CBP luma, CBP chroma
173
+ // mb_type 1-24: [intra16x16PredMode, CodedBlockPatternLuma, CodedBlockPatternChroma]
174
+ export const i16x16TypeMap = [
175
+ // mb_type 1-4: CBP_luma=0, CBP_chroma=0, pred_mode 0-3
176
+ [0,0,0],[1,0,0],[2,0,0],[3,0,0],
177
+ // mb_type 5-8: CBP_luma=0, CBP_chroma=1
178
+ [0,0,1],[1,0,1],[2,0,1],[3,0,1],
179
+ // mb_type 9-12: CBP_luma=0, CBP_chroma=2
180
+ [0,0,2],[1,0,2],[2,0,2],[3,0,2],
181
+ // mb_type 13-16: CBP_luma=15, CBP_chroma=0
182
+ [0,15,0],[1,15,0],[2,15,0],[3,15,0],
183
+ // mb_type 17-20: CBP_luma=15, CBP_chroma=1
184
+ [0,15,1],[1,15,1],[2,15,1],[3,15,1],
185
+ // mb_type 21-24: CBP_luma=15, CBP_chroma=2
186
+ [0,15,2],[1,15,2],[2,15,2],[3,15,2],
187
+ ];
188
+
189
+ // ══════════════════════════════════════════════════════════
190
+ // CAVLC Tables (for encoder)
191
+ // ══════════════════════════════════════════════════════════
192
+
193
+ // coeff_token mapping: given (TotalCoeff, TrailingOnes, nC_range),
194
+ // returns the VLC codeword. Tables 9-5 through 9-8 in the spec.
195
+ // We'll generate these at init time to keep the file manageable.
196
+
197
+ // Total zeros tables (Table 9-7, 9-8)
198
+ // run_before tables (Table 9-10)
199
+ // These will be implemented in the encoder module.
200
+
201
+ // ══════════════════════════════════════════════════════════
202
+ // CBP mapping (Table 9-4)
203
+ // Maps codeNum to (CodedBlockPatternLuma, CodedBlockPatternChroma)
204
+ // For Inter and Intra macroblocks
205
+ // ══════════════════════════════════════════════════════════
206
+
207
+ export const cbpIntraMapping = [
208
+ 47, 31, 15, 0, 23, 27, 29, 30, 7, 11, 13, 14, 39, 43, 45, 46,
209
+ 16, 3, 5, 10, 12, 19, 21, 26, 28, 35, 37, 42, 44, 1, 2, 4,
210
+ 8, 17, 18, 20, 24, 6, 9, 22, 25, 32, 33, 34, 36, 40, 38, 41,
211
+ ];
212
+
213
+ export const cbpInterMapping = [
214
+ 0, 16, 1, 2, 4, 8, 32, 3, 5, 10, 12, 15, 47, 7, 11, 13,
215
+ 14, 6, 9, 31, 35, 37, 42, 44, 33, 34, 36, 40, 39, 43, 45, 46,
216
+ 17, 18, 20, 24, 19, 21, 26, 28, 23, 27, 29, 30, 22, 25, 38, 41,
217
+ ];