mpegts.js
Version:
HTML5 MPEG2-TS Stream Player
629 lines (575 loc) • 24.5 kB
text/typescript
/*
* Copyright (C) 2022 もにょてっく. All Rights Reserved.
*
* @author もにょ〜ん <monyone.teihen@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ExpGolomb from './exp-golomb.js';
type OperatingPoint = {
operating_point_idc: number,
level: number,
tier: number,
decoder_model_present_for_this_op?: boolean
};
type SequenceHeaderDetails = {
frame_id_numbers_present_flag: boolean,
additional_frame_id_length_minus_1?: number;
delta_frame_id_length_minus_2?: number;
reduced_still_picture_header: boolean;
decoder_model_info_present_flag: boolean;
operating_points_cnt_minus_1?: number;
operating_points: OperatingPoint[];
buffer_removal_time_length_minus_1: number;
equal_picture_interval: boolean;
seq_force_screen_content_tools: number;
seq_force_integer_mv: number;
enable_order_hint: boolean;
order_hint_bits: number;
enable_superres: boolean;
frame_width_bit: number;
frame_height_bit: number;
max_frame_width: number;
max_frame_height: number;
}
type FrameResolutions = {
UpscaledWidth: number;
FrameWidth: number;
FrameHeight: number;
RenderWidth: number;
RenderHeight: number;
}
type AV1Metadata = {
codec_mimetype: string,
level: number,
level_string: string,
tier: number,
profile_idc: number,
profile_string: string,
bit_depth: number,
ref_frames: number;
chroma_format: number;
chroma_format_string: string;
sequence_header: SequenceHeaderDetails;
sequence_header_data: Uint8Array;
keyframe?: boolean;
frame_rate: {
fixed: boolean
fps: number;
fps_den: number;
fps_num: number;
},
sar_ratio?: {
width: number;
height: number;
},
codec_size?: {
width: number,
height: number;
},
present_size?: {
width: number,
height: number,
}
}
class AV1OBUParser {
static parseOBUs(uint8array: Uint8Array, meta?: AV1Metadata | null) {
for (let i = 0; i < uint8array.byteLength; ) {
let first = i;
let forbidden_bit = (uint8array[i] & 0x80) >> 7;
let type = (uint8array[i] & 0x78) >> 3;
let extension_flag = (uint8array[i] & 0x04) !== 0;
let has_size_field = (uint8array[i] & 0x02) !== 0;
let reserved_1bit = (uint8array[i] & 0x01) !== 0;
i += 1;
let temporal_id = 0, spatial_id = 0;
if (extension_flag) { i += 1; }
let size = Number.POSITIVE_INFINITY;
if (has_size_field) {
size = 0;
for (let j = 0; ; j++) {
let value = uint8array[i++]
size |= (value & 0x7F) << (j * 7);
if ((value & 0x80) === 0) { break; }
}
}
console.log(type);
if (type === 1) { // OBU_SEQUENCE_HEADER
meta = {
... AV1OBUParser.parseSeuqneceHeader(uint8array.subarray(i, i + size)),
sequence_header_data: uint8array.subarray(first, i + size),
}
} else if (type == 3 && meta) { // OBU_FRAME_HEADER
meta = AV1OBUParser.parseOBUFrameHeader(uint8array.subarray(i, i + size), temporal_id, spatial_id, meta);
} else if (type == 6 && meta) { // OBU_FRAME
meta = AV1OBUParser.parseOBUFrameHeader(uint8array.subarray(i, i + size), temporal_id, spatial_id, meta);
}
i += size;
}
return meta;
}
static parseSeuqneceHeader(uint8array: Uint8Array): Omit<AV1Metadata, 'sequence_header_data'> {
let gb = new ExpGolomb(uint8array);
let seq_profile = gb.readBits(3);
let still_picture = gb.readBool();
let reduced_still_picture_header = gb.readBool();
let fps = 0, fps_fixed = true, fps_num = 0, fps_den = 1;
let decoder_model_info_present_flag = false;
let decoder_model_present_for_this_op = false;
let buffer_delay_length_minus_1: number | undefined = undefined;
let buffer_removal_time_length_minus_1: number | undefined = undefined;
let operating_points: OperatingPoint[] = [];
if (reduced_still_picture_header) {
operating_points.push({
operating_point_idc: 0,
level: gb.readBits(5),
tier: 0,
});
} else {
let timing_info_present_flag = gb.readBool();
if (timing_info_present_flag) {
// timing_info
let num_units_in_display_tick = gb.readBits(32);
let time_scale = gb.readBits(32);
let equal_picture_interval = gb.readBool();
let num_ticks_per_picture_minus_1 = 0;
if (equal_picture_interval) {
let leading = 0;
while (true) {
let value = gb.readBits(1);
if (value !== 0) { break; }
leading += 1;
}
if (leading >= 32) {
num_ticks_per_picture_minus_1 = 0xFFFFFFFF;
} else {
num_ticks_per_picture_minus_1 = ((1 << leading) - 1) + gb.readBits(leading);
}
}
fps_den = num_units_in_display_tick;
fps_num = time_scale;
fps = fps_num / fps_den;
fps_fixed = equal_picture_interval;
let decoder_model_info_present_flag = gb.readBool();
if (decoder_model_info_present_flag) {
// decoder_model_info
buffer_delay_length_minus_1 = gb.readBits(5);
let num_units_in_decoding_tick = gb.readBits(32);
buffer_removal_time_length_minus_1 = gb.readBits(5);
let frame_presentation_time_length_minus_1 = gb.readBits(5);
}
}
let initial_display_delay_present_flag = gb.readBool();
let operating_points_cnt_minus_1 = gb.readBits(5);
for (let i = 0; i <= operating_points_cnt_minus_1; i++) {
let operating_point_idc = gb.readBits(12);
let level = gb.readBits(5);
let tier = level > 7 ? gb.readBits(1) : 0;
operating_points.push({
operating_point_idc,
level,
tier
});
if (decoder_model_info_present_flag) {
let decoder_model_present_for_this_op = gb.readBool();
operating_points[operating_points.length - 1].decoder_model_present_for_this_op = decoder_model_present_for_this_op;
if (decoder_model_present_for_this_op) {
// operating_parameters_info
let decoder_buffer_delay = gb.readBits(buffer_delay_length_minus_1 + 1);
let encoder_buffer_delay = gb.readBits(buffer_delay_length_minus_1 + 1);
let low_delay_mode_flag = gb.readBool();
}
}
if (initial_display_delay_present_flag) {
let initial_display_delay_present_for_this_op = gb.readBool();
if (initial_display_delay_present_for_this_op) {
let initial_display_delay_minus_1 = gb.readBits(4);
}
}
}
}
let operating_point = 0;
let { level, tier } = operating_points[operating_point];
let frame_width_bits_minus_1 = gb.readBits(4);
let frame_height_bits_minus_1 = gb.readBits(4);
let max_frame_width = gb.readBits(frame_width_bits_minus_1 + 1) + 1;
let max_frame_height = gb.readBits(frame_height_bits_minus_1 + 1) + 1;
let frame_id_numbers_present_flag = false;
if (!reduced_still_picture_header) {
frame_id_numbers_present_flag = gb.readBool();
}
let delta_frame_id_length_minus_2: number | undefined = undefined;
let additional_frame_id_length_minus_1: number | undefined = undefined;
if (frame_id_numbers_present_flag) {
let delta_frame_id_length_minus_2 = gb.readBits(4);
let additional_frame_id_length_minus_1 = gb.readBits(4);
}
let SELECT_SCREEN_CONTENT_TOOLS = 2;
let SELECT_INTEGER_MV = 2;
let use_128x128_superblock = gb.readBool();
let enable_filter_intra = gb.readBool();
let enable_intra_edge_filter = gb.readBool();
let enable_interintra_compound = false;
let enable_masked_compound = false;
let enable_warped_motion = false;
let enable_dual_filter = false;
let enable_order_hint = false;
let enable_jnt_comp = false;
let enable_ref_frame_mvs = false;
let seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS;
let seq_force_integer_mv = SELECT_INTEGER_MV;
let OrderHintBits = 0;
if (!reduced_still_picture_header) {
enable_interintra_compound = gb.readBool();
enable_masked_compound = gb.readBool();
enable_warped_motion = gb.readBool();
enable_dual_filter = gb.readBool();
enable_order_hint = gb.readBool();
if (enable_order_hint) {
let enable_jnt_comp = gb.readBool();
let enable_ref_frame_mvs = gb.readBool();
}
let seq_choose_screen_content_tools = gb.readBool();
if (seq_choose_screen_content_tools) {
seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS;
} else {
seq_force_screen_content_tools = gb.readBits(1);
}
if (seq_force_screen_content_tools) {
let seq_choose_integer_mv = gb.readBool();
if (seq_choose_integer_mv) {
seq_force_integer_mv = SELECT_INTEGER_MV;
} else {
seq_force_integer_mv = gb.readBits(1);
}
} else {
seq_force_integer_mv = SELECT_INTEGER_MV;
}
if (enable_order_hint) {
let order_hint_bits_minus_1 = gb.readBits(3);
OrderHintBits = order_hint_bits_minus_1 + 1;
} else {
OrderHintBits = 0;
}
}
let enable_superres = gb.readBool();
let enable_cdef = gb.readBool();
let enable_restoration = gb.readBool();
// color_config
let high_bitdepth = gb.readBool();
let bitDepth = 8;
if (seq_profile === 2 && high_bitdepth) {
let twelve_bit = gb.readBool();
bitDepth = twelve_bit ? 12 : 10;
} else {
bitDepth = high_bitdepth ? 10 : 8;
}
let mono_chrome = false;
if (seq_profile !== 1) {
mono_chrome = gb.readBool();
}
let numPlanes = mono_chrome ? 1 : 3;
let color_description_present_flag = gb.readBool();
let CP_BT_709 = 1, CP_UNSPECIFIED = 2;
let TC_UNSPECIFIED = 2, TC_SRGB = 13;
let MC_UNSPECIFIED = 2, MC_IDENTITY = 0;
let color_primaries = CP_UNSPECIFIED;
let transfer_characteristics = TC_UNSPECIFIED;
let matrix_coefficients = MC_UNSPECIFIED;
if (color_description_present_flag) {
let color_primaries = gb.readBits(8);
let transfer_characteristics = gb.readBits(8);
let matrix_coefficients = gb.readBits(8);
}
let color_range = 1;
let subsampling_x = 1
let subsampling_y = 1;
if (mono_chrome) {
color_range = gb.readBits(1);
subsampling_x = 1
subsampling_y = 1;
let chroma_sample_position = 0; /* CSP_UNKNOWN */
let separate_uv_delta_q = 0
} else {
let color_range = 1;
if (color_primaries === CP_BT_709 && transfer_characteristics === TC_SRGB && matrix_coefficients === MC_IDENTITY) {
color_range = 1;
subsampling_x = 1
subsampling_y = 1
} else {
color_range = gb.readBits(1);
if (seq_profile == 0) {
subsampling_x = 1
subsampling_y = 1
} else if (seq_profile == 1) {
subsampling_x = 0
subsampling_y = 0
} else {
if (bitDepth == 12) {
let subsampling_x = gb.readBits(1);
if (subsampling_x) {
let subsampling_y = gb.readBits(1);
} else {
let subsampling_y = 0;
}
} else {
subsampling_x = 1
subsampling_y = 0
}
}
if (subsampling_x && subsampling_y) {
let chroma_sample_position = gb.readBits(2)
}
let separate_uv_delta_q = gb.readBits(1);
}
}
//
let film_grain_params_present = gb.readBool();
gb.destroy();
gb = null;
let codec_mimetype = `av01.${seq_profile}.${AV1OBUParser.getLevelString(level, tier)}.${bitDepth.toString(10).padStart(2, '0')}`;
let sar_width = 1, sar_height = 1, sar_scale = 1;
return {
codec_mimetype,
level: level,
tier: tier,
level_string: AV1OBUParser.getLevelString(level, tier),
profile_idc: seq_profile,
profile_string: `${seq_profile}`,
bit_depth: bitDepth,
ref_frames: 1, // FIXME!!!
chroma_format: AV1OBUParser.getChromaFormat(mono_chrome, subsampling_x, subsampling_y),
chroma_format_string: AV1OBUParser.getChromaFormatString(mono_chrome, subsampling_x, subsampling_y),
sequence_header: {
frame_id_numbers_present_flag,
additional_frame_id_length_minus_1,
delta_frame_id_length_minus_2,
reduced_still_picture_header,
decoder_model_info_present_flag,
operating_points,
buffer_removal_time_length_minus_1,
equal_picture_interval: fps_fixed,
seq_force_screen_content_tools,
seq_force_integer_mv,
enable_order_hint,
order_hint_bits: OrderHintBits,
enable_superres,
frame_width_bit: frame_width_bits_minus_1 + 1,
frame_height_bit: frame_height_bits_minus_1 + 1,
max_frame_width,
max_frame_height,
},
keyframe: undefined,
frame_rate: {
fixed: fps_fixed,
fps: fps_num / fps_den,
fps_den: fps_den,
fps_num: fps_num,
},
};
}
static parseOBUFrameHeader(uint8array: Uint8Array, temporal_id: number, spatial_id: number, meta: AV1Metadata) {
let { sequence_header } = meta;
let gb = new ExpGolomb(uint8array);
// obu_type is OBU_FRAME_HEADER, SeenFrameHeader = 0, OBU_REDUNDANT_FRAME_HEADER 1
let NUM_REF_FRAMES = 8;
let KEY_FRAME = 0;
let INTER_FRAME = 1;
let INTRA_ONLY_FRAME = 2;
let SWITCH_FRAME = 3;
let SELECT_SCREEN_CONTENT_TOOLS = 2;
let SELECT_INTEGER_MV = 2;
let PRIMARY_REF_NONE = 7;
let FrameWidth = sequence_header.max_frame_width;
let FrameHeight = sequence_header.max_frame_height;
let RenderWidth = FrameWidth; // Stub
let RenderHeight = FrameHeight; // Stub
let idLen = 0;
if (sequence_header.frame_id_numbers_present_flag) {
idLen = sequence_header.additional_frame_id_length_minus_1! + sequence_header.delta_frame_id_length_minus_2! + 3;
}
let allFrames = (1 << NUM_REF_FRAMES) - 1;
let show_existing_frame = false;
let frame_type = 0;
let keyframe = true;
let show_frame = true;
let showable_frame = false;
let error_resilient_mode = false;
if (!sequence_header.reduced_still_picture_header) {
show_existing_frame = gb.readBool();
if (show_existing_frame) {
// it does not contain frame data. ignored
return meta;
}
frame_type = gb.readBits(2);
keyframe = frame_type === INTRA_ONLY_FRAME || frame_type === KEY_FRAME;
show_frame = gb.readBool();
if (show_frame && sequence_header.decoder_model_info_present_flag && !sequence_header.equal_picture_interval) {
// decoder model info
}
if (!show_frame) {
showable_frame = frame_type !== KEY_FRAME;
} else {
showable_frame = gb.readBool();
}
if (frame_type === SWITCH_FRAME || (frame_type === KEY_FRAME && show_frame)) {
error_resilient_mode = true;
} else {
error_resilient_mode = gb.readBool();
}
}
meta.keyframe = keyframe;
let disable_cdf_update = gb.readBool();
let allow_screen_content_tools = sequence_header.seq_force_screen_content_tools;
if (sequence_header.seq_force_screen_content_tools === SELECT_SCREEN_CONTENT_TOOLS) {
allow_screen_content_tools = gb.readBits(1);
}
let force_integer_mv = keyframe ? 1 : 0;
if (allow_screen_content_tools) {
force_integer_mv = sequence_header.seq_force_integer_mv;
if (sequence_header.seq_force_integer_mv == SELECT_INTEGER_MV) {
force_integer_mv = gb.readBits(1);
}
}
let current_frame_id = 0;
if (sequence_header.frame_id_numbers_present_flag) {
current_frame_id = gb.readBits(idLen);
}
let frame_size_override_flag = false;
if (frame_type == SWITCH_FRAME) {
frame_size_override_flag = true;
} else if (sequence_header.reduced_still_picture_header) {
frame_size_override_flag = false;
} else {
frame_size_override_flag = gb.readBool();
}
let order_hint = gb.readBits(sequence_header.order_hint_bits);
let primary_ref_frame = PRIMARY_REF_NONE;
if (!(keyframe || error_resilient_mode)) {
primary_ref_frame = gb.readBits(3);
}
if (sequence_header.decoder_model_info_present_flag) {
let buffer_removal_time_present_flag = gb.readBool();
if (buffer_removal_time_present_flag) {
for (let opNum = 0; opNum <= sequence_header.operating_points_cnt_minus_1; opNum++) {
if (sequence_header.operating_points[opNum].decoder_model_present_for_this_op[opNum]) {
let opPtIdc = sequence_header.operating_points[opNum].operating_point_idc;
let inTemporalLayer = (opPtIdc >> temporal_id ) & 1
let inSpatialLayer = (opPtIdc >> (spatial_id + 8)) & 1
if (opPtIdc === 0 || (inTemporalLayer && inSpatialLayer)) {
gb.readBits(sequence_header.buffer_removal_time_length_minus_1 + 1);
}
}
}
}
}
let allow_high_precision_mv = 0;
let use_ref_frame_mvs = 0;
let allow_intrabc = 0;
let refresh_frame_flags = allFrames;
if (!(frame_type === SWITCH_FRAME || (frame_type == KEY_FRAME && show_frame))) {
refresh_frame_flags = gb.readBits(8);
}
if (keyframe || refresh_frame_flags !== allFrames) {
if (error_resilient_mode && sequence_header.enable_order_hint) {
for (let i = 0; i < NUM_REF_FRAMES; i++) {
gb.readBits(sequence_header.order_hint_bits);
}
}
}
if (keyframe){
const resolution = AV1OBUParser.frameSizeAndRenderSize(gb, frame_size_override_flag, sequence_header);
meta.codec_size = {
width: resolution.FrameWidth,
height: resolution.FrameHeight,
}
meta.present_size = {
width: resolution.RenderWidth,
height: resolution.RenderHeight,
}
meta.sar_ratio = {
width: resolution.RenderWidth / resolution.FrameWidth,
height: resolution.RenderHeight / resolution.FrameHeight,
}
}
// fmp4 can't support reference frame resolution change, so ignored
gb.destroy();
gb = null;
return meta;
}
static frameSizeAndRenderSize(gb: ExpGolomb, frame_size_override_flag: boolean, sequence_header: SequenceHeaderDetails): FrameResolutions {
let FrameWidth = sequence_header.max_frame_width;
let FrameHeight = sequence_header.max_frame_height;
if (frame_size_override_flag) {
FrameWidth = gb.readBits(sequence_header.frame_width_bit) + 1;
FrameHeight = gb.readBits(sequence_header.frame_height_bit) + 1;
}
let use_superress = false;
if (sequence_header.enable_superres) {
use_superress = gb.readBool();
}
let SuperresDenom = 8 /* SUPERRES_NUM */;
if (use_superress) {
let coded_denom = gb.readBits(3 /* SUPERRES_DENOM_BITS */);
SuperresDenom = coded_denom + 9; /* SUPERRES_DENOM_MIN */
}
let UpscaledWidth = FrameWidth;
FrameWidth = Math.floor((UpscaledWidth * 8 /* SUPERRES_NUM */ + (SuperresDenom / 2)) / SuperresDenom)
let render_and_frame_size_different = gb.readBool();
let RenderWidth = UpscaledWidth;
let RenderHeight = FrameHeight;
if (render_and_frame_size_different) {
let render_width_bits = gb.readBits(16) + 1;
let render_height_bits = gb.readBits(16) + 1;
RenderWidth = gb.readBits(render_width_bits) + 1;
RenderHeight = gb.readBits(render_height_bits) + 1;
}
return {
UpscaledWidth,
FrameWidth,
FrameHeight,
RenderWidth,
RenderHeight
};
}
static getLevelString(level: number, tier: number): string {
return `${level.toString(10).padStart(2, '0')}${tier === 0 ? 'M' : 'H'}`;
}
static getChromaFormat(mono_chrome: boolean, subsampling_x: number, subsampling_y: number): number {
if (mono_chrome) {
return 0;
} else if (subsampling_x === 0 && subsampling_y === 0) {
return 3;
} else if (subsampling_x === 1 && subsampling_y === 0) {
return 2;
} else if (subsampling_x === 1 && subsampling_y === 1) {
return 1;
} else {
return Number.NaN;
}
}
static getChromaFormatString(mono_chrome: boolean, subsampling_x: number, subsampling_y: number): string {
if (mono_chrome) {
return '4:0:0';
} else if (subsampling_x === 0 && subsampling_y === 0) {
return '4:4:4';
} else if (subsampling_x === 1 && subsampling_y === 0) {
return '4:2:2';
} else if (subsampling_x === 1 && subsampling_y === 1) {
return '4:2:0';
} else {
return 'Unknown';
}
}
}
export default AV1OBUParser;