@yume-chan/scrcpy
Version:
TypeScript implementation of Scrcpy.
256 lines • 10.7 kB
JavaScript
// cspell: ignore golomb
// cspell: ignore qpprime
// cspell: ignore colour
import { NaluSodbBitReader, annexBSplitNalu } from "./nalu.js";
// From https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel
export var AndroidAvcProfile;
(function (AndroidAvcProfile) {
AndroidAvcProfile[AndroidAvcProfile["Baseline"] = 1] = "Baseline";
AndroidAvcProfile[AndroidAvcProfile["Main"] = 2] = "Main";
AndroidAvcProfile[AndroidAvcProfile["Extended"] = 4] = "Extended";
AndroidAvcProfile[AndroidAvcProfile["High"] = 8] = "High";
AndroidAvcProfile[AndroidAvcProfile["High10"] = 16] = "High10";
AndroidAvcProfile[AndroidAvcProfile["High422"] = 32] = "High422";
AndroidAvcProfile[AndroidAvcProfile["High444"] = 64] = "High444";
AndroidAvcProfile[AndroidAvcProfile["ConstrainedBaseline"] = 65536] = "ConstrainedBaseline";
AndroidAvcProfile[AndroidAvcProfile["ConstrainedHigh"] = 524288] = "ConstrainedHigh";
})(AndroidAvcProfile || (AndroidAvcProfile = {}));
export var AndroidAvcLevel;
(function (AndroidAvcLevel) {
AndroidAvcLevel[AndroidAvcLevel["Level1"] = 1] = "Level1";
AndroidAvcLevel[AndroidAvcLevel["Level1b"] = 2] = "Level1b";
AndroidAvcLevel[AndroidAvcLevel["Level11"] = 4] = "Level11";
AndroidAvcLevel[AndroidAvcLevel["Level12"] = 8] = "Level12";
AndroidAvcLevel[AndroidAvcLevel["Level13"] = 16] = "Level13";
AndroidAvcLevel[AndroidAvcLevel["Level2"] = 32] = "Level2";
AndroidAvcLevel[AndroidAvcLevel["Level21"] = 64] = "Level21";
AndroidAvcLevel[AndroidAvcLevel["Level22"] = 128] = "Level22";
AndroidAvcLevel[AndroidAvcLevel["Level3"] = 256] = "Level3";
AndroidAvcLevel[AndroidAvcLevel["Level31"] = 512] = "Level31";
AndroidAvcLevel[AndroidAvcLevel["Level32"] = 1024] = "Level32";
AndroidAvcLevel[AndroidAvcLevel["Level4"] = 2048] = "Level4";
AndroidAvcLevel[AndroidAvcLevel["Level41"] = 4096] = "Level41";
AndroidAvcLevel[AndroidAvcLevel["Level42"] = 8192] = "Level42";
AndroidAvcLevel[AndroidAvcLevel["Level5"] = 16384] = "Level5";
AndroidAvcLevel[AndroidAvcLevel["Level51"] = 32768] = "Level51";
AndroidAvcLevel[AndroidAvcLevel["Level52"] = 65536] = "Level52";
AndroidAvcLevel[AndroidAvcLevel["Level6"] = 131072] = "Level6";
AndroidAvcLevel[AndroidAvcLevel["Level61"] = 262144] = "Level61";
AndroidAvcLevel[AndroidAvcLevel["Level62"] = 524288] = "Level62";
})(AndroidAvcLevel || (AndroidAvcLevel = {}));
// H.264 has two standards: ITU-T H.264 and ISO/IEC 14496-10
// they have the same content, and refer themselves as "H.264".
// The name "AVC" (Advanced Video Coding) is only used in ISO spec name,
// and other ISO specs referring to H.264.
// Because this module parses H.264 Annex B format,
// it's named "h264" instead of "avc".
// 7.3.2.1.1 Sequence parameter set data syntax
// Variable names in this method uses the snake_case convention as in the spec for easier referencing.
export function h264ParseSequenceParameterSet(nalu) {
const reader = new NaluSodbBitReader(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) {
throw new Error("Invalid data");
}
if (nal_ref_idc === 0) {
throw new Error("Invalid data");
}
const profile_idc = reader.read(8);
const constraint_set = reader.peek(8);
const constraint_set0_flag = !!reader.next();
const constraint_set1_flag = !!reader.next();
const constraint_set2_flag = !!reader.next();
const constraint_set3_flag = !!reader.next();
const constraint_set4_flag = !!reader.next();
const constraint_set5_flag = !!reader.next();
// reserved_zero_2bits
if (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) {
// separate_colour_plane_flag
reader.next();
}
// bit_depth_luma_minus8
reader.decodeExponentialGolombNumber();
// bit_depth_chroma_minus8
reader.decodeExponentialGolombNumber();
// 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 = [];
for (let i = 0; i < (chroma_format_idc !== 3 ? 8 : 12); i += 1) {
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 ] )
}
}
}
}
// log2_max_frame_num_minus4
reader.decodeExponentialGolombNumber();
const pic_order_cnt_type = reader.decodeExponentialGolombNumber();
if (pic_order_cnt_type === 0) {
// log2_max_pic_order_cnt_lsb_minus4
reader.decodeExponentialGolombNumber();
}
else if (pic_order_cnt_type === 1) {
// delta_pic_order_always_zero_flag
reader.next();
// offset_for_non_ref_pic
reader.decodeExponentialGolombNumber();
// offset_for_top_to_bottom_field
reader.decodeExponentialGolombNumber();
const num_ref_frames_in_pic_order_cnt_cycle = reader.decodeExponentialGolombNumber();
const offset_for_ref_frame = [];
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i += 1) {
offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber();
}
}
// max_num_ref_frames
reader.decodeExponentialGolombNumber();
// 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) {
// mb_adaptive_frame_field_flag
reader.next();
}
// direct_8x8_inference_flag
reader.next();
const frame_cropping_flag = !!reader.next();
let frame_crop_left_offset;
let frame_crop_right_offset;
let frame_crop_top_offset;
let frame_crop_bottom_offset;
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,
};
}
/**
* Find Sequence Parameter Set (SPS) and Picture Parameter Set (PPS)
* from H.264 Annex B formatted data.
*/
export function h264SearchConfiguration(buffer) {
let sequenceParameterSet;
let pictureParameterSet;
for (const nalu of annexBSplitNalu(buffer)) {
const naluType = nalu[0] & 0x1f;
switch (naluType) {
case 7: // Sequence parameter set
sequenceParameterSet = nalu;
if (pictureParameterSet) {
return {
sequenceParameterSet,
pictureParameterSet,
};
}
break;
case 8: // Picture parameter set
pictureParameterSet = nalu;
if (sequenceParameterSet) {
return {
sequenceParameterSet,
pictureParameterSet,
};
}
break;
default:
// ignore
break;
}
}
throw new Error("Invalid data");
}
export function h264ParseConfiguration(data) {
const { sequenceParameterSet, pictureParameterSet } = h264SearchConfiguration(data);
const { profile_idc: profileIndex, constraint_set: constraintSet, level_idc: levelIndex, pic_width_in_mbs_minus1, pic_height_in_map_units_minus1, frame_mbs_only_flag, frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset, } = h264ParseSequenceParameterSet(sequenceParameterSet);
const encodedWidth = (pic_width_in_mbs_minus1 + 1) * 16;
const encodedHeight = (pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag) * 16;
const cropLeft = frame_crop_left_offset * 2;
const cropRight = frame_crop_right_offset * 2;
const cropTop = frame_crop_top_offset * 2;
const cropBottom = frame_crop_bottom_offset * 2;
const croppedWidth = encodedWidth - cropLeft - cropRight;
const croppedHeight = encodedHeight - cropTop - cropBottom;
return {
pictureParameterSet,
sequenceParameterSet,
profileIndex,
constraintSet,
levelIndex,
encodedWidth,
encodedHeight,
cropLeft,
cropRight,
cropTop,
cropBottom,
croppedWidth,
croppedHeight,
};
}
//# sourceMappingURL=h264.js.map