mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-06 03:50:18 +02:00
284 lines
9.6 KiB
TypeScript
284 lines
9.6 KiB
TypeScript
class BitReader {
|
||
private buffer: Uint8Array;
|
||
|
||
private bytePosition = 0;
|
||
|
||
private bitPosition = 0;
|
||
|
||
public constructor(buffer: Uint8Array) {
|
||
this.buffer = buffer;
|
||
}
|
||
|
||
public read(length: number): number {
|
||
let result = 0;
|
||
for (let i = 0; i < length; i += 1) {
|
||
result = (result << 1) | this.next();
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public next(): number {
|
||
const value = (this.buffer[this.bytePosition] >> (7 - this.bitPosition)) & 1;
|
||
this.bitPosition += 1;
|
||
if (this.bitPosition === 8) {
|
||
this.bytePosition += 1;
|
||
this.bitPosition = 0;
|
||
}
|
||
return value;
|
||
}
|
||
|
||
public decodeExponentialGolombNumber(): number {
|
||
let length = 0;
|
||
while (this.next() === 0) {
|
||
length += 1;
|
||
}
|
||
if (length === 0) {
|
||
return 0;
|
||
}
|
||
return (1 << length | this.read(length)) - 1;
|
||
}
|
||
}
|
||
|
||
function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> {
|
||
// -1 means we haven't found the first start code
|
||
let start = -1;
|
||
let writeIndex = 0;
|
||
|
||
// How many `0x00`s in a row we have counted
|
||
let zeroCount = 0;
|
||
|
||
let inEmulation = false;
|
||
|
||
for (const byte of buffer) {
|
||
buffer[writeIndex] = byte;
|
||
writeIndex += 1;
|
||
|
||
if (inEmulation) {
|
||
if (byte > 0x03) {
|
||
// `0x00000304` or larger are invalid
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
inEmulation = false;
|
||
continue;
|
||
}
|
||
|
||
if (byte == 0x00) {
|
||
zeroCount += 1;
|
||
continue;
|
||
}
|
||
|
||
const lastZeroCount = zeroCount;
|
||
zeroCount = 0;
|
||
|
||
if (start === -1) {
|
||
// 0x000001 is the start code
|
||
// But it can be preceded by any number of zeros
|
||
// So 2 is the minimal
|
||
if (lastZeroCount >= 2 && byte === 0x01) {
|
||
// Found start of first NAL unit
|
||
writeIndex = 0;
|
||
start = 0;
|
||
continue;
|
||
}
|
||
|
||
// Not begin with start code
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
if (lastZeroCount < 2) {
|
||
// zero or one `0x00`s are acceptable
|
||
continue;
|
||
}
|
||
|
||
if (byte === 0x01) {
|
||
// Remove all leading `0x00`s and this `0x01`
|
||
writeIndex -= lastZeroCount + 1;
|
||
|
||
// Found another NAL unit
|
||
yield buffer.subarray(start, writeIndex);
|
||
|
||
start = writeIndex;
|
||
continue;
|
||
}
|
||
|
||
if (lastZeroCount > 2) {
|
||
// Too much `0x00`s
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
switch (byte) {
|
||
case 0x02:
|
||
// Didn't find why, but 7.4.1 NAL unit semantics forbids `0x000002` appearing in NAL units
|
||
throw new Error('Invalid data');
|
||
case 0x03:
|
||
// `0x000003` is the "emulation_prevention_three_byte"
|
||
// `0x00000300`, `0x00000301`, `0x00000302` and `0x00000303` represent
|
||
// `0x000000`, `0x000001`, `0x000002` and `0x000003` respectively
|
||
|
||
// Remove current byte
|
||
writeIndex -= 1;
|
||
|
||
inEmulation = true;
|
||
break;
|
||
default:
|
||
// `0x000004` or larger are ok
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (inEmulation || zeroCount !== 0) {
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
yield buffer.subarray(start, writeIndex);
|
||
}
|
||
|
||
// 7.3.2.1.1 Sequence parameter set data syntax
|
||
export function parse_sequence_parameter_set(buffer: ArrayBuffer) {
|
||
for (const nalu of iterateNalu(new Uint8Array(buffer))) {
|
||
const reader = new BitReader(nalu);
|
||
if (reader.next() !== 0) {
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
const nal_ref_idc = reader.read(2);
|
||
const nal_unit_type = reader.read(5);
|
||
|
||
if (nal_unit_type !== 7) {
|
||
continue;
|
||
}
|
||
|
||
if (nal_ref_idc === 0) {
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
const profile_idc = reader.read(8);
|
||
const constraint_set = reader.read(8);
|
||
|
||
const constraint_set_reader = new BitReader(new Uint8Array([constraint_set]));
|
||
const constraint_set0_flag = !!constraint_set_reader.next();
|
||
const constraint_set1_flag = !!constraint_set_reader.next();
|
||
const constraint_set2_flag = !!constraint_set_reader.next();
|
||
const constraint_set3_flag = !!constraint_set_reader.next();
|
||
const constraint_set4_flag = !!constraint_set_reader.next();
|
||
const constraint_set5_flag = !!constraint_set_reader.next();
|
||
|
||
// reserved_zero_2bits
|
||
if (constraint_set_reader.read(2) !== 0) {
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
const level_idc = reader.read(8);
|
||
const seq_parameter_set_id = reader.decodeExponentialGolombNumber();
|
||
|
||
if (profile_idc === 100 || profile_idc === 110 ||
|
||
profile_idc === 122 || profile_idc === 244 || profile_idc === 44 ||
|
||
profile_idc === 83 || profile_idc === 86 || profile_idc === 118 ||
|
||
profile_idc === 128 || profile_idc === 138 || profile_idc === 139 ||
|
||
profile_idc === 134) {
|
||
const chroma_format_idc = reader.decodeExponentialGolombNumber();
|
||
if (chroma_format_idc === 3) {
|
||
const separate_colour_plane_flag = !!reader.next();
|
||
}
|
||
|
||
const bit_depth_luma_minus8 = reader.decodeExponentialGolombNumber();
|
||
const bit_depth_chroma_minus8 = reader.decodeExponentialGolombNumber();
|
||
|
||
const qpprime_y_zero_transform_bypass_flag = !!reader.next();
|
||
|
||
const seq_scaling_matrix_present_flag = !!reader.next();
|
||
if (seq_scaling_matrix_present_flag) {
|
||
const seq_scaling_list_present_flag: boolean[] = [];
|
||
for (let i = 0; i < ((chroma_format_idc !== 3) ? 8 : 12); i++) {
|
||
seq_scaling_list_present_flag[i] = !!reader.next();
|
||
if (seq_scaling_list_present_flag[i])
|
||
if (i < 6) {
|
||
// TODO
|
||
// scaling_list( ScalingList4x4[ i ], 16,
|
||
// UseDefaultScalingMatrix4x4Flag[ i ])
|
||
} else {
|
||
// TODO
|
||
// scaling_list( ScalingList8x8[ i − 6 ], 64,
|
||
// UseDefaultScalingMatrix8x8Flag[ i − 6 ] )
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const log2_max_frame_num_minus4 = reader.decodeExponentialGolombNumber();
|
||
const pic_order_cnt_type = reader.decodeExponentialGolombNumber();
|
||
if (pic_order_cnt_type === 0) {
|
||
const log2_max_pic_order_cnt_lsb_minus4 = reader.decodeExponentialGolombNumber();
|
||
} else if (pic_order_cnt_type === 1) {
|
||
const delta_pic_order_always_zero_flag = reader.next();
|
||
const offset_for_non_ref_pic = reader.decodeExponentialGolombNumber();
|
||
const offset_for_top_to_bottom_field = reader.decodeExponentialGolombNumber();
|
||
const num_ref_frames_in_pic_order_cnt_cycle = reader.decodeExponentialGolombNumber();
|
||
const offset_for_ref_frame: number[] = [];
|
||
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
|
||
offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber();
|
||
}
|
||
}
|
||
|
||
const max_num_ref_frames = reader.decodeExponentialGolombNumber();
|
||
const gaps_in_frame_num_value_allowed_flag = reader.next();
|
||
const pic_width_in_mbs_minus1 = reader.decodeExponentialGolombNumber();
|
||
const pic_height_in_map_units_minus1 = reader.decodeExponentialGolombNumber();
|
||
|
||
const frame_mbs_only_flag = reader.next();
|
||
if (!frame_mbs_only_flag) {
|
||
const mb_adaptive_frame_field_flag = !!reader.next();
|
||
}
|
||
|
||
const direct_8x8_inference_flag = reader.next();
|
||
|
||
const frame_cropping_flag = !!reader.next();
|
||
let frame_crop_left_offset: number;
|
||
let frame_crop_right_offset: number;
|
||
let frame_crop_top_offset: number;
|
||
let frame_crop_bottom_offset: number;
|
||
if (frame_cropping_flag) {
|
||
frame_crop_left_offset = reader.decodeExponentialGolombNumber();
|
||
frame_crop_right_offset = reader.decodeExponentialGolombNumber();
|
||
frame_crop_top_offset = reader.decodeExponentialGolombNumber();
|
||
frame_crop_bottom_offset = reader.decodeExponentialGolombNumber();
|
||
} else {
|
||
frame_crop_left_offset = 0;
|
||
frame_crop_right_offset = 0;
|
||
frame_crop_top_offset = 0;
|
||
frame_crop_bottom_offset = 0;
|
||
}
|
||
|
||
const vui_parameters_present_flag = !!reader.next();
|
||
if (vui_parameters_present_flag) {
|
||
// TODO
|
||
// vui_parameters( )
|
||
}
|
||
|
||
return {
|
||
profile_idc,
|
||
constraint_set,
|
||
constraint_set0_flag,
|
||
constraint_set1_flag,
|
||
constraint_set2_flag,
|
||
constraint_set3_flag,
|
||
constraint_set4_flag,
|
||
constraint_set5_flag,
|
||
level_idc,
|
||
seq_parameter_set_id,
|
||
pic_width_in_mbs_minus1,
|
||
pic_height_in_map_units_minus1,
|
||
frame_mbs_only_flag,
|
||
frame_cropping_flag,
|
||
frame_crop_left_offset,
|
||
frame_crop_right_offset,
|
||
frame_crop_top_offset,
|
||
frame_crop_bottom_offset,
|
||
};
|
||
}
|
||
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
export type SequenceParameterSet = ReturnType<typeof parse_sequence_parameter_set>;
|