@invintusmedia/tomp4 1.3.1 → 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.
- package/dist/tomp4.js +2 -2
- package/package.json +1 -1
- package/src/codecs/REFERENCE.md +885 -0
- package/src/codecs/h264-cabac-init.js +546 -0
- package/src/codecs/h264-cabac.js +322 -0
- package/src/codecs/h264-decoder.js +940 -0
- package/src/codecs/h264-encoder.js +577 -0
- package/src/codecs/h264-intra.js +292 -0
- package/src/codecs/h264-sps-pps.js +483 -0
- package/src/codecs/h264-tables.js +217 -0
- package/src/codecs/h264-transform.js +268 -0
- package/src/codecs/smart-render.js +169 -0
- package/src/hls-clip.js +50 -22
- package/src/index.js +1 -1
|
@@ -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
|
+
];
|