This section presents the syntax structures in a tabular form. The meaning of each of the syntax elements is presented in [Section 6][].
This specification defines a low-overhead bitstream format as a sequence of the OBU syntactical elements defined in this section. When using this format, obu_has_size_field must be equal to 1. For applications requiring a format where it is easier to skip through frames or temporal units, a length-delimited bitstream format is defined in Annex B.
Derived specifications, such as container formats enabling storage of AV1 videos together with audio or subtitles, should indicate which of these formats they rely on. Other methods of packing OBUs into a bitstream format are also allowed.
| --------------------------------------------------------- | ---------------- | | open_bitstream_unit( sz ) { | Type | obu_header() | | if ( obu_has_size_field ) { | | @@obu_size | leb128() | } else { | obu_size = sz - 1 - obu_extension_flag | } | startPosition = get_position( ) | if ( obu_type != OBU_SEQUENCE_HEADER && | obu_type != OBU_TEMPORAL_DELIMITER && | OperatingPointIdc != 0 && | obu_extension_flag == 1 ) | { | inTemporalLayer = (OperatingPointIdc >> temporal_id ) & 1 | inSpatialLayer = (OperatingPointIdc >> ( spatial_id + 8 ) ) & 1 | if ( !inTemporalLayer || ! inSpatialLayer ) { | drop_obu( ) | return | } | } | if ( obu_type == OBU_SEQUENCE_HEADER ) | sequence_header_obu( ) | else if ( obu_type == OBU_TEMPORAL_DELIMITER ) | temporal_delimiter_obu( ) | else if ( obu_type == OBU_FRAME_HEADER ) | frame_header_obu( ) | else if ( obu_type == OBU_REDUNDANT_FRAME_HEADER ) | frame_header_obu( ) | else if ( obu_type == OBU_TILE_GROUP ) | tile_group_obu( obu_size ) | else if ( obu_type == OBU_METADATA ) | metadata_obu( ) | else if ( obu_type == OBU_FRAME ) | frame_obu( obu_size ) | else if ( obu_type == OBU_TILE_LIST ) | tile_list_obu( ) | else if ( obu_type == OBU_PADDING ) | padding_obu( ) | else | reserved_obu( ) | currentPosition = get_position( ) | payloadBits = currentPosition - startPosition | if ( obu_size > 0 && obu_type != OBU_TILE_GROUP && | obu_type != OBU_TILE_LIST && | obu_type != OBU_FRAME ) { | trailing_bits( obu_size * 8 - payloadBits ) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | obu_header() { | Type | @@obu_forbidden_bit | f(1) | @@obu_type | f(4) | @@obu_extension_flag | f(1) | @@obu_has_size_field | f(1) | @@obu_reserved_1bit | f(1) | if ( obu_extension_flag == 1 ) | obu_extension_header() | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | obu_extension_header() { | Type | @@temporal_id | f(3) | @@spatial_id | f(2) | @@extension_header_reserved_3bits | f(3) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | trailing_bits( nbBits ) { | Type | @@trailing_one_bit | f(1) | nbBits-- | while ( nbBits > 0 ) { | @@trailing_zero_bit | f(1) | nbBits-- | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | byte_alignment( ) { | Type | while ( get_position( ) & 7 ) | | @@zero_bit | f(1) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | reserved_obu( ) { | Type | } {:.syntax }
Note: Reserved OBUs do not have a defined syntax. The obu_type reserved values are reserved for future use. Decoders should ignore the entire OBU if they do not understand the obu_type. Ignoring the OBU can be done based on obu_size. The last byte of the valid content of the payload data for this OBU type is considered to be the last byte that is not equal to zero. This rule is to prevent the dropping of valid bytes by systems that interpret trailing zero bytes as a continuation of the trailing bits in an OBU. This implies that when any payload data is present for this OBU type, at least one byte of the payload data (including the trailing bit) shall not be equal to 0. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | sequence_header_obu( ) { | Type | @@seq_profile | f(3) | @@still_picture | f(1) | @@reduced_still_picture_header | f(1) | if ( reduced_still_picture_header ) { | timing_info_present_flag = 0 | decoder_model_info_present_flag = 0 | initial_display_delay_present_flag = 0 | operating_points_cnt_minus_1 = 0 | operating_point_idc[ 0 ] = 0 | @@seq_level_idx[ 0 ] | f(5) | seq_tier[ 0 ] = 0 | decoder_model_present_for_this_op[ 0 ] = 0 | initial_display_delay_present_for_this_op[ 0 ] = 0 | } else { | @@timing_info_present_flag | f(1) | if ( timing_info_present_flag ) { | | timing_info( ) | | @@decoder_model_info_present_flag | f(1) | if ( decoder_model_info_present_flag ) { | decoder_model_info( ) | } | } else { | decoder_model_info_present_flag = 0 | } | @@initial_display_delay_present_flag | f(1) | @@operating_points_cnt_minus_1 | f(5) | for ( i = 0; i <= operating_points_cnt_minus_1; i++ ) { | @@operating_point_idc[ i ] | f(12) | @@seq_level_idx[ i ] | f(5) | if ( seq_level_idx[ i ] > 7 ) { | @@seq_tier[ i ] | f(1) | } else { | seq_tier[ i ] = 0 | } | if ( decoder_model_info_present_flag ) { | @@decoder_model_present_for_this_op[ i ] | f(1) | if ( decoder_model_present_for_this_op[ i ] ) { | operating_parameters_info( i ) | } | } else { | decoder_model_present_for_this_op[ i ] = 0 | } | if ( initial_display_delay_present_flag ) { | @@initial_display_delay_present_for_this_op[ i ] | f(1) | if ( initial_display_delay_present_for_this_op[ i ] ) { | @@initial_display_delay_minus_1[ i ] | f(4) | } | } | } | } | operatingPoint = choose_operating_point( ) | OperatingPointIdc = operating_point_idc[ operatingPoint ] | @@frame_width_bits_minus_1 | f(4) | @@frame_height_bits_minus_1 | f(4) | n = frame_width_bits_minus_1 + 1 | | @@max_frame_width_minus_1 | f(n) | n = frame_height_bits_minus_1 + 1 | | @@max_frame_height_minus_1 | f(n) | if ( reduced_still_picture_header ) | frame_id_numbers_present_flag = 0 | else | @@frame_id_numbers_present_flag | f(1) | if ( frame_id_numbers_present_flag ) { | | @@delta_frame_id_length_minus_2 | f(4) | @@additional_frame_id_length_minus_1 | f(3) | } | | @@use_128x128_superblock | f(1) | @@enable_filter_intra | f(1) | @@enable_intra_edge_filter | f(1) | if ( reduced_still_picture_header ) { | enable_interintra_compound = 0 | enable_masked_compound = 0 | enable_warped_motion = 0 | enable_dual_filter = 0 | enable_order_hint = 0 | enable_jnt_comp = 0 | enable_ref_frame_mvs = 0 | seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS | seq_force_integer_mv = SELECT_INTEGER_MV | OrderHintBits = 0 | } else { | @@enable_interintra_compound | f(1) | @@enable_masked_compound | f(1) | @@enable_warped_motion | f(1) | @@enable_dual_filter | f(1) | @@enable_order_hint | f(1) | if ( enable_order_hint ) { | | @@enable_jnt_comp | f(1) | @@enable_ref_frame_mvs | f(1) | } else { | | enable_jnt_comp = 0 | | enable_ref_frame_mvs = 0 | | } | | @@seq_choose_screen_content_tools | f(1) | if ( seq_choose_screen_content_tools ) { | | seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS | | } else { | | @@seq_force_screen_content_tools | f(1) | } | | | | if ( seq_force_screen_content_tools > 0 ) { | | @@seq_choose_integer_mv | f(1) | if ( seq_choose_integer_mv ) { | | seq_force_integer_mv = SELECT_INTEGER_MV | | } else { | | @@seq_force_integer_mv | f(1) | } | | } else { | | seq_force_integer_mv = SELECT_INTEGER_MV | | } | | if ( enable_order_hint ) { | | @@order_hint_bits_minus_1 | f(3) | OrderHintBits = order_hint_bits_minus_1 + 1 | | } else { | | OrderHintBits = 0 | | } | | } | @@enable_superres | f(1) | @@enable_cdef | f(1) | @@enable_restoration | f(1) | color_config( ) | | @@film_grain_params_present | f(1) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | color_config( ) { | Type | @@high_bitdepth | f(1) | if ( seq_profile == 2 && high_bitdepth ) { | @@twelve_bit | f(1) | BitDepth = twelve_bit ? 12 : 10 | } else if ( seq_profile <= 2 ) { | BitDepth = high_bitdepth ? 10 : 8 | } | if ( seq_profile == 1 ) { | mono_chrome = 0 | } else { | @@mono_chrome | f(1) | } | NumPlanes = mono_chrome ? 1 : 3 | @@color_description_present_flag | f(1) | if ( color_description_present_flag ) { | @@color_primaries | f(8) | @@transfer_characteristics | f(8) | @@matrix_coefficients | f(8) | } else { | color_primaries = CP_UNSPECIFIED | transfer_characteristics = TC_UNSPECIFIED | matrix_coefficients = MC_UNSPECIFIED | } | if ( mono_chrome ) { | @@color_range | f(1) | subsampling_x = 1 | subsampling_y = 1 | chroma_sample_position = CSP_UNKNOWN | separate_uv_delta_q = 0 | return | } else if ( color_primaries == CP_BT_709 && | transfer_characteristics == TC_SRGB && | matrix_coefficients == MC_IDENTITY ) { | color_range = 1 | subsampling_x = 0 | subsampling_y = 0 | } else { | @@color_range | f(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 ) { | @@subsampling_x | f(1) | if ( subsampling_x ) | @@subsampling_y | f(1) | else | subsampling_y = 0 | } else { | subsampling_x = 1 | subsampling_y = 0 | } | } | if ( subsampling_x && subsampling_y ) { | @@chroma_sample_position | f(2) | } | } | @@separate_uv_delta_q | f(1) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | timing_info( ) { | Type | @@num_units_in_display_tick | f(32) | @@time_scale | f(32) | @@equal_picture_interval | f(1) | if ( equal_picture_interval ) | @@num_ticks_per_picture_minus_1 | uvlc() | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | decoder_model_info( ) { | Type | @@buffer_delay_length_minus_1 | f(5) | @@num_units_in_decoding_tick | f(32) | @@buffer_removal_time_length_minus_1 | f(5) | @@frame_presentation_time_length_minus_1 | f(5) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | operating_parameters_info( op ) { | Type | n = buffer_delay_length_minus_1 + 1 | @@decoder_buffer_delay[ op ] | f(n) | @@encoder_buffer_delay[ op ] | f(n) | @@low_delay_mode_flag[ op ] | f(1) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | temporal_delimiter_obu( ) { | Type | SeenFrameHeader = 0 | } {:.syntax }
Note: The temporal delimiter has an empty payload. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | padding_obu( ) { | Type | for ( i = 0; i < obu_padding_length; i++ ) | | @@obu_padding_byte | f(8) | } {:.syntax }
Note: obu_padding_length is not coded in the bitstream but can be computed based on obu_size minus the number of trailing bytes. In practice, though, since this is padding data meant to be skipped, decoders do not need to determine either that length nor the number of trailing bytes. They can ignore the entire OBU. Ignoring the OBU can be done based on obu_size. The last byte of the valid content of the payload data for this OBU type is considered to be the last byte that is not equal to zero. This rule is to prevent the dropping of valid bytes by systems that interpret trailing zero bytes as a continuation of the trailing bits in an OBU. This implies that when any payload data is present for this OBU type, at least one byte of the payload data (including the trailing bit) shall not be equal to 0. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | metadata_obu( ) { | Type | @@metadata_type | leb128() | if ( metadata_type == METADATA_TYPE_ITUT_T35 ) | metadata_itut_t35( ) | else if ( metadata_type == METADATA_TYPE_HDR_CLL ) | metadata_hdr_cll( ) | else if ( metadata_type == METADATA_TYPE_HDR_MDCV ) | metadata_hdr_mdcv( ) | else if ( metadata_type == METADATA_TYPE_SCALABILITY ) | metadata_scalability( ) | else if ( metadata_type == METADATA_TYPE_TIMECODE ) | metadata_timecode( ) | } {:.syntax }
Note: The exact syntax of metadata_obu is not defined in this specification when metadata_type is equal to a value reserved for future use or a user private value. Decoders should ignore the entire OBU if they do not understand the metadata_type. The last byte of the valid content of the data is considered to be the last byte that is not equal to zero. This rule is to prevent the dropping of valid bytes by systems that interpret trailing zero bytes as a padding continuation of the trailing bits in an OBU. This implies that when any payload data is present for this OBU type, at least one byte of the payload data (including the trailing bit) shall not be equal to 0. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | metadata_itut_t35( ) { | Type | @@itu_t_t35_country_code | f(8) | if ( itu_t_t35_country_code == 0xFF ) { | @@itu_t_t35_country_code_extension_byte | f(8) | } | @@itu_t_t35_payload_bytes | } {:.syntax }
Note: The exact syntax of itu_t_t35_payload_bytes is not defined in this specification. External specifications can define the syntax. Decoders should ignore the entire OBU if they do not understand it. The last byte of the valid content of the data is considered to be the last byte that is not equal to zero. This rule is to prevent the dropping of valid bytes by systems that interpret trailing zero bytes as a padding continuation of the trailing bits in an OBU. This implies that when any payload data is present for this OBU type, at least one byte of the payload data (including the trailing bit) shall not be equal to 0. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | metadata_hdr_cll( ) { | Type | @@max_cll | f(16) | @@max_fall | f(16) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | metadata_hdr_mdcv( ) { | Type | for ( i = 0; i < 3; i++ ) { | | @@primary_chromaticity_x[ i ] | f(16) | @@primary_chromaticity_y[ i ] | f(16) | } | @@white_point_chromaticity_x | f(16) | @@white_point_chromaticity_y | f(16) | @@luminance_max | f(32) | @@luminance_min | f(32) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | metadata_scalability( ) { | Type | @@scalability_mode_idc | f(8) | if ( scalability_mode_idc == SCALABILITY_SS ) | scalability_structure( ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | scalability_structure( ) { | Type | @@spatial_layers_cnt_minus_1 | f(2) | @@spatial_layer_dimensions_present_flag | f(1) | @@spatial_layer_description_present_flag | f(1) | @@temporal_group_description_present_flag | f(1) | @@scalability_structure_reserved_3bits | f(3) | if ( spatial_layer_dimensions_present_flag ) { | for ( i = 0; i <= spatial_layers_cnt_minus_1 ; i++ ) { | @@spatial_layer_max_width[ i ] | f(16) | @@spatial_layer_max_height[ i ] | f(16) | } | } | if ( spatial_layer_description_present_flag ) { | for ( i = 0; i <= spatial_layers_cnt_minus_1; i++ ) | @@spatial_layer_ref_id[ i ] | f(8) | } | if ( temporal_group_description_present_flag ) { | @@temporal_group_size | f(8) | for ( i = 0; i < temporal_group_size; i++ ) { | @@temporal_group_temporal_id[ i ] | f(3) | @@temporal_group_temporal_switching_up_point_flag[ i ] | f(1) | @@temporal_group_spatial_switching_up_point_flag[ i ] | f(1) | @@temporal_group_ref_cnt[ i ] | f(3) | for ( j = 0; j < temporal_group_ref_cnt[ i ]; j++ ) { | @@temporal_group_ref_pic_diff[ i ][ j ] | f(8) | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | metadata_timecode( ) { | Type | @@counting_type | f(5) | @@full_timestamp_flag | f(1) | @@discontinuity_flag | f(1) | @@cnt_dropped_flag | f(1) | @@n_frames | f(9) | if ( full_timestamp_flag ) { | @@seconds_value | f(6) | @@minutes_value | f(6) | @@hours_value | f(5) | } else { | @@seconds_flag | f(1) | if ( seconds_flag ) { | @@seconds_value | f(6) | @@minutes_flag | f(1) | if ( minutes_flag ) { | @@minutes_value | f(6) | @@hours_flag | f(1) | if ( hours_flag ) { | @@hours_value | f(5) | } | } | } | } | @@time_offset_length | f(5) | if ( time_offset_length > 0 ) { | @@time_offset_value | f(time_offset_length) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | frame_header_obu( ) { | Type | if ( SeenFrameHeader == 1 ) { | frame_header_copy() | } else { | SeenFrameHeader = 1 | uncompressed_header( ) | if ( show_existing_frame ) { | decode_frame_wrapup( ) | SeenFrameHeader = 0 | } else { | TileNum = 0 | SeenFrameHeader = 1 | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | uncompressed_header( ) { | Type | if ( frame_id_numbers_present_flag ) { | idLen = ( additional_frame_id_length_minus_1 + | delta_frame_id_length_minus_2 + 3 ) | } | allFrames = (1 << NUM_REF_FRAMES) - 1 | if ( reduced_still_picture_header ) { | show_existing_frame = 0 | frame_type = KEY_FRAME | FrameIsIntra = 1 | show_frame = 1 | showable_frame = 0 | } else { | @@show_existing_frame | f(1) | if ( show_existing_frame == 1 ) { | @@frame_to_show_map_idx | f(3) | if ( decoder_model_info_present_flag && !equal_picture_interval ) { | temporal_point_info( ) | } | refresh_frame_flags = 0 | if ( frame_id_numbers_present_flag ) { | @@display_frame_id | f(idLen) | } | frame_type = RefFrameType[ frame_to_show_map_idx ] | if ( frame_type == KEY_FRAME ) { | refresh_frame_flags = allFrames | } | if ( film_grain_params_present ) { | load_grain_params( frame_to_show_map_idx ) | } | return | } | @@frame_type | f(2) | FrameIsIntra = (frame_type == INTRA_ONLY_FRAME || | frame_type == KEY_FRAME) | @@show_frame | f(1) | if ( show_frame && decoder_model_info_present_flag && !equal_picture_interval ) { | temporal_point_info( ) | } | if ( show_frame ) { | showable_frame = frame_type != KEY_FRAME | } else { | @@showable_frame | f(1) | } | if ( frame_type == SWITCH_FRAME || | ( frame_type == KEY_FRAME && show_frame ) ) | error_resilient_mode = 1 | else | @@error_resilient_mode | f(1) | } | if ( frame_type == KEY_FRAME && show_frame ) { | for ( i = 0; i < NUM_REF_FRAMES; i++ ) { | RefValid[ i ] = 0 | RefOrderHint[ i ] = 0 | } | for ( i = 0; i < REFS_PER_FRAME; i++ ) { | OrderHints[ LAST_FRAME + i ] = 0 | } | } | @@disable_cdf_update | f(1) | if ( seq_force_screen_content_tools == SELECT_SCREEN_CONTENT_TOOLS ) { | @@allow_screen_content_tools | f(1) | } else { | allow_screen_content_tools = seq_force_screen_content_tools | } | if ( allow_screen_content_tools ) { | if ( seq_force_integer_mv == SELECT_INTEGER_MV ) { | @@force_integer_mv | f(1) | } else { | force_integer_mv = seq_force_integer_mv | } | } else { | force_integer_mv = 0 | } | if ( FrameIsIntra ) { | force_integer_mv = 1 | } | if ( frame_id_numbers_present_flag ) { | PrevFrameID = current_frame_id | @@current_frame_id | f(idLen) | mark_ref_frames( idLen ) | } else { | current_frame_id = 0 | } | if ( frame_type == SWITCH_FRAME ) | frame_size_override_flag = 1 | else if ( reduced_still_picture_header ) | frame_size_override_flag = 0 | else | @@frame_size_override_flag | f(1) | @@order_hint | f(OrderHintBits) | OrderHint = order_hint | if ( FrameIsIntra || error_resilient_mode ) { | primary_ref_frame = PRIMARY_REF_NONE | } else { | @@primary_ref_frame | f(3) | } | if ( decoder_model_info_present_flag ) { | @@buffer_removal_time_present_flag | f(1) | if ( buffer_removal_time_present_flag ) { | for ( opNum = 0; opNum <= operating_points_cnt_minus_1; opNum++ ) { | if ( decoder_model_present_for_this_op[ opNum ] ) { | opPtIdc = operating_point_idc[ opNum ] | inTemporalLayer = ( opPtIdc >> temporal_id ) & 1 | inSpatialLayer = ( opPtIdc >> ( spatial_id + 8 ) ) & 1 | if ( opPtIdc == 0 || ( inTemporalLayer && inSpatialLayer ) ) { | n = buffer_removal_time_length_minus_1 + 1 | @@buffer_removal_time[ opNum ] | f(n) | } | } | } | } | } | allow_high_precision_mv = 0 | use_ref_frame_mvs = 0 | allow_intrabc = 0 | if ( frame_type == SWITCH_FRAME || | ( frame_type == KEY_FRAME && show_frame ) ) { | refresh_frame_flags = allFrames | } else { | @@refresh_frame_flags | f(8) | } | if ( !FrameIsIntra || refresh_frame_flags != allFrames ) { | if ( error_resilient_mode && enable_order_hint ) { | for ( i = 0; i < NUM_REF_FRAMES; i++) { | @@ref_order_hint[ i ] | f(OrderHintBits) | if ( ref_order_hint[ i ] != RefOrderHint[ i ] ) { | RefValid[ i ] = 0 | } | } | } | } | if ( FrameIsIntra ) { | frame_size( ) | render_size( ) | if ( allow_screen_content_tools && UpscaledWidth == FrameWidth ) { | @@allow_intrabc | f(1) | } | } else { | if ( !enable_order_hint ) { | frame_refs_short_signaling = 0 | } else { | @@frame_refs_short_signaling | f(1) | if ( frame_refs_short_signaling ) { | @@last_frame_idx | f(3) | @@gold_frame_idx | f(3) | set_frame_refs() | } | } | for ( i = 0; i < REFS_PER_FRAME; i++ ) { | if ( !frame_refs_short_signaling ) | @@ref_frame_idx[ i ] | f(3) | if ( frame_id_numbers_present_flag ) { | n = delta_frame_id_length_minus_2 + 2 | @@delta_frame_id_minus_1 | f(n) | DeltaFrameId = delta_frame_id_minus_1 + 1 | expectedFrameId[ i ] = ((current_frame_id + (1 << idLen) - | DeltaFrameId ) % (1 << idLen)) | } | } | if ( frame_size_override_flag && !error_resilient_mode ) { | frame_size_with_refs( ) | } else { | frame_size( ) | render_size( ) | } | if ( force_integer_mv ) { | allow_high_precision_mv = 0 | } else { | @@allow_high_precision_mv | f(1) | } | read_interpolation_filter( ) | @@is_motion_mode_switchable | f(1) | if ( error_resilient_mode || !enable_ref_frame_mvs ) { | use_ref_frame_mvs = 0 | } else { | @@use_ref_frame_mvs | f(1) | } | for ( i = 0; i < REFS_PER_FRAME; i++ ) { | refFrame = LAST_FRAME + i | hint = RefOrderHint[ ref_frame_idx[ i ] ] | OrderHints[ refFrame ] = hint | if ( !enable_order_hint ) { | RefFrameSignBias[ refFrame ] = 0 | } else { | RefFrameSignBias[ refFrame ] = get_relative_dist( hint, OrderHint) > 0 | } | } | } | if ( reduced_still_picture_header || disable_cdf_update ) | disable_frame_end_update_cdf = 1 | else | @@disable_frame_end_update_cdf | f(1) | if ( primary_ref_frame == PRIMARY_REF_NONE ) { | init_non_coeff_cdfs( ) | setup_past_independence( ) | } else { | load_cdfs( ref_frame_idx[ primary_ref_frame ] ) | load_previous( ) | } | if ( use_ref_frame_mvs == 1 ) | motion_field_estimation( ) | tile_info( ) | quantization_params( ) | segmentation_params( ) | delta_q_params( ) | delta_lf_params( ) | if ( primary_ref_frame == PRIMARY_REF_NONE ) { | init_coeff_cdfs( ) | } else { | load_previous_segment_ids( ) | } | CodedLossless = 1 | for ( segmentId = 0; segmentId < MAX_SEGMENTS; segmentId++ ) { | qindex = get_qindex( 1, segmentId ) | LosslessArray[ segmentId ] = qindex == 0 && DeltaQYDc == 0 && | DeltaQUAc == 0 && DeltaQUDc == 0 && | DeltaQVAc == 0 && DeltaQVDc == 0 | if ( !LosslessArray[ segmentId ] ) | CodedLossless = 0 | if ( using_qmatrix ) { | if ( LosslessArray[ segmentId ] ) { | SegQMLevel[ 0 ][ segmentId ] = 15 | SegQMLevel[ 1 ][ segmentId ] = 15 | SegQMLevel[ 2 ][ segmentId ] = 15 | } else { | SegQMLevel[ 0 ][ segmentId ] = qm_y | SegQMLevel[ 1 ][ segmentId ] = qm_u | SegQMLevel[ 2 ][ segmentId ] = qm_v | } | } | } | AllLossless = CodedLossless && ( FrameWidth == UpscaledWidth ) | loop_filter_params( ) | cdef_params( ) | lr_params( ) | read_tx_mode( ) | frame_reference_mode( ) | skip_mode_params( ) | if ( FrameIsIntra || | error_resilient_mode || | !enable_warped_motion ) | allow_warped_motion = 0 | else | @@allow_warped_motion | f(1) | @@reduced_tx_set | f(1) | global_motion_params( ) | film_grain_params( ) | } {:.syntax }
This function computes the distance between two order hints by sign extending the result of subtracting the values.
| --------------------------------------------------------- | ---------------- | | get_relative_dist( a, b ) { | Type | if ( !enable_order_hint ) | return 0 | diff = a - b | m = 1 << (OrderHintBits - 1) | diff = (diff & (m - 1)) - (diff & m) | return diff | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | mark_ref_frames( idLen ) { | Type | diffLen = delta_frame_id_length_minus_2 + 2 | for ( i = 0; i < NUM_REF_FRAMES; i++ ) { | if ( current_frame_id > ( 1 << diffLen ) ) { | if ( RefFrameId[ i ] > current_frame_id || | RefFrameId[ i ] < ( current_frame_id - ( 1 << diffLen ) ) ) | RefValid[ i ] = 0 | } else { | if ( RefFrameId[ i ] > current_frame_id && | RefFrameId[ i ] < ( ( 1 << idLen ) + | current_frame_id - | ( 1 << diffLen ) ) ) | RefValid[ i ] = 0 | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | frame_size( ) { | Type | if ( frame_size_override_flag ) { | | n = frame_width_bits_minus_1 + 1 | | @@frame_width_minus_1 | f(n) | n = frame_height_bits_minus_1 + 1 | | @@frame_height_minus_1 | f(n) | FrameWidth = frame_width_minus_1 + 1 | FrameHeight = frame_height_minus_1 + 1 | } else { | FrameWidth = max_frame_width_minus_1 + 1 | FrameHeight = max_frame_height_minus_1 + 1 | } | superres_params( ) | compute_image_size( ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | render_size( ) { | Type | @@render_and_frame_size_different | f(1) | if ( render_and_frame_size_different == 1 ) { | | @@render_width_minus_1 | f(16) | @@render_height_minus_1 | f(16) | RenderWidth = render_width_minus_1 + 1 | RenderHeight = render_height_minus_1 + 1 | } else { | RenderWidth = UpscaledWidth | RenderHeight = FrameHeight | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | frame_size_with_refs( ) { | Type | for ( i = 0; i < REFS_PER_FRAME; i++ ) { | | @@found_ref | f(1) | if ( found_ref == 1 ) { | UpscaledWidth = RefUpscaledWidth[ ref_frame_idx[ i ] ] | FrameWidth = UpscaledWidth | FrameHeight = RefFrameHeight[ ref_frame_idx[ i ] ] | RenderWidth = RefRenderWidth[ ref_frame_idx[ i ] ] | RenderHeight = RefRenderHeight[ ref_frame_idx[ i ] ] | break | } | } | if ( found_ref == 0 ) { | frame_size( ) | render_size( ) | } else { | superres_params( ) | compute_image_size( ) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | superres_params() { | Type | if ( enable_superres ) | @@use_superres | f(1) | else | use_superres = 0 | if ( use_superres ) { | | @@coded_denom | f(SUPERRES_DENOM_BITS) | SuperresDenom = coded_denom + SUPERRES_DENOM_MIN | } else { | SuperresDenom = SUPERRES_NUM | } | UpscaledWidth = FrameWidth | FrameWidth = (UpscaledWidth * SUPERRES_NUM + | (SuperresDenom / 2)) / SuperresDenom | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | compute_image_size( ) { | Type | MiCols = 2 * ( ( FrameWidth + 7 ) >> 3 ) | MiRows = 2 * ( ( FrameHeight + 7 ) >> 3 ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_interpolation_filter( ) { | Type | @@is_filter_switchable | f(1) | if ( is_filter_switchable == 1 ) { | | interpolation_filter = SWITCHABLE | | } else { | | @@interpolation_filter | f(2) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | loop_filter_params( ) { | Type | if ( CodedLossless || allow_intrabc ) { | loop_filter_level[ 0 ] = 0 | loop_filter_level[ 1 ] = 0 | loop_filter_ref_deltas[ INTRA_FRAME ] = 1 | loop_filter_ref_deltas[ LAST_FRAME ] = 0 | loop_filter_ref_deltas[ LAST2_FRAME ] = 0 | loop_filter_ref_deltas[ LAST3_FRAME ] = 0 | loop_filter_ref_deltas[ BWDREF_FRAME ] = 0 | loop_filter_ref_deltas[ GOLDEN_FRAME ] = -1 | loop_filter_ref_deltas[ ALTREF_FRAME ] = -1 | loop_filter_ref_deltas[ ALTREF2_FRAME ] = -1 | for ( i = 0; i < 2; i++ ) { | loop_filter_mode_deltas[ i ] = 0 | } | return | } | @@loop_filter_level[ 0 ] | f(6) | @@loop_filter_level[ 1 ] | f(6) | if ( NumPlanes > 1 ) { | if ( loop_filter_level[ 0 ] || loop_filter_level[ 1 ] ) { | @@loop_filter_level[ 2 ] | f(6) | @@loop_filter_level[ 3 ] | f(6) | } | } | @@loop_filter_sharpness | f(3) | @@loop_filter_delta_enabled | f(1) | if ( loop_filter_delta_enabled == 1 ) { | @@loop_filter_delta_update | f(1) | if ( loop_filter_delta_update == 1 ) { | for ( i = 0; i < TOTAL_REFS_PER_FRAME; i++ ) { | @@update_ref_delta | f(1) | if ( update_ref_delta == 1 ) | @@loop_filter_ref_deltas[ i ] | su(1+6) | } | for ( i = 0; i < 2; i++ ) { | @@update_mode_delta | f(1) | if ( update_mode_delta == 1 ) | @@loop_filter_mode_deltas[ i ] | su(1+6) | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | quantization_params( ) { | Type | @@base_q_idx | f(8) | DeltaQYDc = read_delta_q( ) | | if ( NumPlanes > 1 ) { | | if ( separate_uv_delta_q ) | | @@diff_uv_delta | f(1) | else | diff_uv_delta = 0 | DeltaQUDc = read_delta_q( ) | DeltaQUAc = read_delta_q( ) | if ( diff_uv_delta ) { | DeltaQVDc = read_delta_q( ) | DeltaQVAc = read_delta_q( ) | } else { | DeltaQVDc = DeltaQUDc | DeltaQVAc = DeltaQUAc | } | } else { | DeltaQUDc = 0 | DeltaQUAc = 0 | DeltaQVDc = 0 | DeltaQVAc = 0 | } | @@using_qmatrix | f(1) | if ( using_qmatrix ) { | | @@qm_y | f(4) | @@qm_u | f(4) | if ( !separate_uv_delta_q ) | qm_v = qm_u | else | @@qm_v | f(4) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_delta_q( ) { | Type | @@delta_coded | f(1) | if ( delta_coded ) { | | @@delta_q | su(1+6) | } else { | delta_q = 0 | } | return delta_q | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | segmentation_params( ) { | Type | @@segmentation_enabled | f(1) | if ( segmentation_enabled == 1 ) { | if ( primary_ref_frame == PRIMARY_REF_NONE ) { | segmentation_update_map = 1 | segmentation_temporal_update = 0 | segmentation_update_data = 1 | } else { | @@segmentation_update_map | f(1) | if ( segmentation_update_map == 1 ) | | @@segmentation_temporal_update | f(1) | @@segmentation_update_data | f(1) | } | | if ( segmentation_update_data == 1 ) { | | for ( i = 0; i < MAX_SEGMENTS; i++ ) { | | for ( j = 0; j < SEG_LVL_MAX; j++ ) { | | @@feature_enabled | f(1) | FeatureEnabled[ i ][ j ] = feature_enabled | clippedValue = 0 | if ( feature_enabled == 1 ) { | bitsToRead = Segmentation_Feature_Bits[ j ] | limit = Segmentation_Feature_Max[ j ] | if ( Segmentation_Feature_Signed[ j ] == 1 ) { | @@feature_value | su(1+bitsToRead) | clippedValue = Clip3( -limit, limit, feature_value) | } else { | @@feature_value | f(bitsToRead) | clippedValue = Clip3( 0, limit, feature_value) | } | } | FeatureData[ i ][ j ] = clippedValue | } | } | } | } else { | for ( i = 0; i < MAX_SEGMENTS; i++ ) { | for ( j = 0; j < SEG_LVL_MAX; j++ ) { | FeatureEnabled[ i ][ j ] = 0 | FeatureData[ i ][ j ] = 0 | } | } | } | SegIdPreSkip = 0 | LastActiveSegId = 0 | for ( i = 0; i < MAX_SEGMENTS; i++ ) { | for ( j = 0; j < SEG_LVL_MAX; j++ ) { | if ( FeatureEnabled[ i ][ j ] ) { | LastActiveSegId = i | if ( j >= SEG_LVL_REF_FRAME ) { | SegIdPreSkip = 1 | } | } | } | } | } {:.syntax }
The constant lookup tables used in this syntax are defined as:
Segmentation_Feature_Bits[ SEG_LVL_MAX ] = { 8, 6, 6, 6, 6, 3, 0, 0 }
Segmentation_Feature_Signed[ SEG_LVL_MAX ] = { 1, 1, 1, 1, 1, 0, 0, 0 }
Segmentation_Feature_Max[ SEG_LVL_MAX ] = {
255, MAX_LOOP_FILTER, MAX_LOOP_FILTER,
MAX_LOOP_FILTER, MAX_LOOP_FILTER, 7,
0, 0 }
| --------------------------------------------------------- | ---------------- | | tile_info ( ) { | Type | sbCols = use_128x128_superblock ? ( ( MiCols + 31 ) >> 5 ) : ( ( MiCols + 15 ) >> 4 ) | sbRows = use_128x128_superblock ? ( ( MiRows + 31 ) >> 5 ) : ( ( MiRows + 15 ) >> 4 ) | sbShift = use_128x128_superblock ? 5 : 4 | sbSize = sbShift + 2 | maxTileWidthSb = MAX_TILE_WIDTH >> sbSize | maxTileAreaSb = MAX_TILE_AREA >> ( 2 * sbSize ) | minLog2TileCols = tile_log2(maxTileWidthSb, sbCols) | maxLog2TileCols = tile_log2(1, Min(sbCols, MAX_TILE_COLS)) | maxLog2TileRows = tile_log2(1, Min(sbRows, MAX_TILE_ROWS)) | minLog2Tiles = Max(minLog2TileCols, | tile_log2(maxTileAreaSb, sbRows * sbCols)) | | @@uniform_tile_spacing_flag | f(1) | if ( uniform_tile_spacing_flag ) { | | TileColsLog2 = minLog2TileCols | | while ( TileColsLog2 < maxLog2TileCols ) { | | @@increment_tile_cols_log2 | f(1) | if ( increment_tile_cols_log2 == 1 ) | TileColsLog2++ | else | break | } | tileWidthSb = (sbCols + (1 << TileColsLog2) - 1) >> TileColsLog2 | i = 0 | for ( startSb = 0; startSb < sbCols; startSb += tileWidthSb ) { | MiColStarts[ i ] = startSb << sbShift | i += 1 | } | MiColStarts[i] = MiCols | TileCols = i | | minLog2TileRows = Max( minLog2Tiles - TileColsLog2, 0) | TileRowsLog2 = minLog2TileRows | while ( TileRowsLog2 < maxLog2TileRows ) { | | @@increment_tile_rows_log2 | f(1) | if ( increment_tile_rows_log2 == 1 ) | | TileRowsLog2++ | else | break | } | tileHeightSb = (sbRows + (1 << TileRowsLog2) - 1) >> TileRowsLog2 | i = 0 | for ( startSb = 0; startSb < sbRows; startSb += tileHeightSb ) { | MiRowStarts[ i ] = startSb << sbShift | i += 1 | } | MiRowStarts[i] = MiRows | TileRows = i | } else { | widestTileSb = 0 | startSb = 0 | for ( i = 0; startSb < sbCols; i++ ) { | MiColStarts[ i ] = startSb << sbShift | maxWidth = Min(sbCols - startSb, maxTileWidthSb) | @@width_in_sbs_minus_1 | ns(maxWidth) | sizeSb = width_in_sbs_minus_1 + 1 | | widestTileSb = Max( sizeSb, widestTileSb ) | | startSb += sizeSb | } | MiColStarts[i] = MiCols | TileCols = i | TileColsLog2 = tile_log2(1, TileCols) | | if ( minLog2Tiles > 0 ) | maxTileAreaSb = (sbRows * sbCols) >> (minLog2Tiles + 1) | else | maxTileAreaSb = sbRows * sbCols | maxTileHeightSb = Max( maxTileAreaSb / widestTileSb, 1 ) | | startSb = 0 | for ( i = 0; startSb < sbRows; i++ ) { | MiRowStarts[ i ] = startSb << sbShift | maxHeight = Min(sbRows - startSb, maxTileHeightSb) | @@height_in_sbs_minus_1 | ns(maxHeight) | sizeSb = height_in_sbs_minus_1 + 1 | | startSb += sizeSb | | } | MiRowStarts[ i ] = MiRows | TileRows = i | TileRowsLog2 = tile_log2(1, TileRows) | } | if ( TileColsLog2 > 0 || TileRowsLog2 > 0 ) { | | @@context_update_tile_id | f(TileRowsLog2 + TileColsLog2) | @@tile_size_bytes_minus_1 | f(2) | TileSizeBytes = tile_size_bytes_minus_1 + 1 | } else { | context_update_tile_id = 0 | } | } {:.syntax }
tile_log2 returns the smallest value for k such that blkSize << k is greater than or equal to target.
| --------------------------------------------------------- | ---------------- | | tile_log2( blkSize, target ) { | Type | for ( k = 0; (blkSize << k) < target; k++ ) { | } | return k | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | delta_q_params( ) { | Type | delta_q_res = 0 | delta_q_present = 0 | if ( base_q_idx > 0 ) { | @@delta_q_present | f(1) | } | | if ( delta_q_present ) { | | @@delta_q_res | f(2) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | delta_lf_params( ) { | Type | delta_lf_present = 0 | | delta_lf_res = 0 | | delta_lf_multi = 0 | | if ( delta_q_present ) { | | if ( !allow_intrabc ) | @@delta_lf_present | f(1) | if ( delta_lf_present ) { | | @@delta_lf_res | f(2) | @@delta_lf_multi | f(1) | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | cdef_params( ) { | Type | if ( CodedLossless || allow_intrabc || | !enable_cdef) { | cdef_bits = 0 | cdef_y_pri_strength[0] = 0 | cdef_y_sec_strength[0] = 0 | cdef_uv_pri_strength[0] = 0 | cdef_uv_sec_strength[0] = 0 | CdefDamping = 3 | return | } | @@cdef_damping_minus_3 | f(2) | CdefDamping = cdef_damping_minus_3 + 3 | | @@cdef_bits | f(2) | for ( i = 0; i < (1 << cdef_bits); i++ ) { | | @@cdef_y_pri_strength[i] | f(4) | @@cdef_y_sec_strength[i] | f(2) | if ( cdef_y_sec_strength[i] == 3 ) | cdef_y_sec_strength[i] += 1 | if ( NumPlanes > 1 ) { | @@cdef_uv_pri_strength[i] | f(4) | @@cdef_uv_sec_strength[i] | f(2) | if ( cdef_uv_sec_strength[i] == 3 ) | cdef_uv_sec_strength[i] += 1 | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | lr_params( ) { | Type | if ( AllLossless || allow_intrabc || | !enable_restoration ) { | FrameRestorationType[0] = RESTORE_NONE | FrameRestorationType[1] = RESTORE_NONE | FrameRestorationType[2] = RESTORE_NONE | UsesLr = 0 | return | } | UsesLr = 0 | usesChromaLr = 0 | for ( i = 0; i < NumPlanes; i++ ) { | @@lr_type | f(2) | FrameRestorationType[i] = Remap_Lr_Type[lr_type] | if ( FrameRestorationType[i] != RESTORE_NONE ) { | UsesLr = 1 | if ( i > 0 ) { | usesChromaLr = 1 | } | } | } | if ( UsesLr ) { | if ( use_128x128_superblock ) { | @@lr_unit_shift | f(1) | lr_unit_shift++ | } else { | @@lr_unit_shift | f(1) | if ( lr_unit_shift ) { | @@lr_unit_extra_shift | f(1) | lr_unit_shift += lr_unit_extra_shift | } | } | LoopRestorationSize[ 0 ] = RESTORATION_TILESIZE_MAX >> (2 - lr_unit_shift) | if ( subsampling_x && subsampling_y && usesChromaLr ) { | @@lr_uv_shift | f(1) | } else { | lr_uv_shift = 0 | } | LoopRestorationSize[ 1 ] = LoopRestorationSize[ 0 ] >> lr_uv_shift | LoopRestorationSize[ 2 ] = LoopRestorationSize[ 0 ] >> lr_uv_shift | } | } {:.syntax }
where Remap_Lr_Type is a constant lookup table specified as:
Remap_Lr_Type[4] = {
RESTORE_NONE, RESTORE_SWITCHABLE, RESTORE_WIENER, RESTORE_SGRPROJ
}
| --------------------------------------------------------- | ---------------- | | read_tx_mode( ) { | Type | if ( CodedLossless == 1 ) { | TxMode = ONLY_4X4 | } else { | @@tx_mode_select | f(1) | if ( tx_mode_select ) { | TxMode = TX_MODE_SELECT | } else { | TxMode = TX_MODE_LARGEST | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | skip_mode_params( ) { | Type | if ( FrameIsIntra || !reference_select || !enable_order_hint ) { | skipModeAllowed = 0 | } else { | forwardIdx = -1 | backwardIdx = -1 | for ( i = 0; i < REFS_PER_FRAME; i++ ) { | refHint = RefOrderHint[ ref_frame_idx[ i ] ] | if ( get_relative_dist( refHint, OrderHint ) < 0 ) { | if ( forwardIdx < 0 || | get_relative_dist( refHint, forwardHint) > 0 ) { | forwardIdx = i | forwardHint = refHint | } | } else if ( get_relative_dist( refHint, OrderHint) > 0 ) { | if ( backwardIdx < 0 || | get_relative_dist( refHint, backwardHint) < 0 ) { | backwardIdx = i | backwardHint = refHint | } | } | } | if ( forwardIdx < 0 ) { | skipModeAllowed = 0 | } else if ( backwardIdx >= 0 ) { | skipModeAllowed = 1 | SkipModeFrame[ 0 ] = LAST_FRAME + Min(forwardIdx, backwardIdx) | SkipModeFrame[ 1 ] = LAST_FRAME + Max(forwardIdx, backwardIdx) | } else { | secondForwardIdx = -1 | for ( i = 0; i < REFS_PER_FRAME; i++ ) { | refHint = RefOrderHint[ ref_frame_idx[ i ] ] | if ( get_relative_dist( refHint, forwardHint ) < 0 ) { | if ( secondForwardIdx < 0 || | get_relative_dist( refHint, secondForwardHint ) > 0 ) { | secondForwardIdx = i | secondForwardHint = refHint | } | } | } | if ( secondForwardIdx < 0 ) { | skipModeAllowed = 0 | } else { | skipModeAllowed = 1 | SkipModeFrame[ 0 ] = LAST_FRAME + Min(forwardIdx, secondForwardIdx) | SkipModeFrame[ 1 ] = LAST_FRAME + Max(forwardIdx, secondForwardIdx) | } | } | } | if ( skipModeAllowed ) { | @@skip_mode_present | f(1) | } else { | skip_mode_present = 0 | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | frame_reference_mode( ) { | Type | if ( FrameIsIntra ) { | reference_select = 0 | } else { | @@reference_select | f(1) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | global_motion_params( ) { | Type | for ( ref = LAST_FRAME; ref <= ALTREF_FRAME; ref++ ) { | GmType[ ref ] = IDENTITY | for ( i = 0; i < 6; i++ ) { | gm_params[ ref ][ i ] = ( ( i % 3 == 2 ) ? | 1 << WARPEDMODEL_PREC_BITS : 0 ) | } | } | if ( FrameIsIntra ) | return | for ( ref = LAST_FRAME; ref <= ALTREF_FRAME; ref++ ) { | @@is_global | f(1) | if ( is_global ) { | | @@is_rot_zoom | f(1) | if ( is_rot_zoom ) { | | type = ROTZOOM | | } else { | | @@is_translation | f(1) | type = is_translation ? TRANSLATION : AFFINE | } | } else { | type = IDENTITY | } | GmType[ref] = type | | if ( type >= ROTZOOM ) { | read_global_param(type,ref,2) | read_global_param(type,ref,3) | if ( type == AFFINE ) { | read_global_param(type,ref,4) | read_global_param(type,ref,5) | } else { | gm_params[ref][4] = -gm_params[ref][3] | gm_params[ref][5] = gm_params[ref][2] | } | } | if ( type >= TRANSLATION ) { | read_global_param(type,ref,0) | read_global_param(type,ref,1) | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_global_param( type, ref, idx ) { | Type | absBits = GM_ABS_ALPHA_BITS | precBits = GM_ALPHA_PREC_BITS | if ( idx < 2 ) { | if ( type == TRANSLATION ) { | absBits = GM_ABS_TRANS_ONLY_BITS - !allow_high_precision_mv | precBits = GM_TRANS_ONLY_PREC_BITS - !allow_high_precision_mv | } else { | absBits = GM_ABS_TRANS_BITS | precBits = GM_TRANS_PREC_BITS | } | } | precDiff = WARPEDMODEL_PREC_BITS - precBits | round = (idx % 3) == 2 ? (1 << WARPEDMODEL_PREC_BITS) : 0 | sub = (idx % 3) == 2 ? (1 << precBits) : 0 | mx = (1 << absBits) | r = (PrevGmParams[ref][idx] >> precDiff) - sub | gm_params[ref][idx] = | (decode_signed_subexp_with_ref( -mx, mx + 1, r )<< precDiff) + round | } {:.syntax }
Note: When force_integer_mv is equal to 1, some fractional bits are still read for the translation components. However, these fractional bits will be discarded during the Setup Global MV process. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | decode_signed_subexp_with_ref( low, high, r ) { | Type | x = decode_unsigned_subexp_with_ref(high - low, r - low) | return x + low | } {:.syntax }
Note: decode_signed_subexp_with_ref will return a value in the range low to high - 1 (inclusive). {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | decode_unsigned_subexp_with_ref( mx, r ) { | Type | v = decode_subexp( mx ) | if ( (r << 1) <= mx ) { | return inverse_recenter(r, v) | } else { | return mx - 1 - inverse_recenter(mx - 1 - r, v) | } | } {:.syntax }
Note: decode_unsigned_subexp_with_ref will return a value in the range 0 to mx - 1 (inclusive). {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | decode_subexp( numSyms ) { | Type | i = 0 | mk = 0 | k = 3 | while ( 1 ) { | b2 = i ? k + i - 1 : k | a = 1 << b2 | if ( numSyms <= mk + 3 * a ) { | @@subexp_final_bits | ns(numSyms - mk) | return subexp_final_bits + mk | } else { | @@subexp_more_bits | f(1) | if ( subexp_more_bits ) { | i++ | mk += a | } else { | @@subexp_bits | f(b2) | return subexp_bits + mk | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | inverse_recenter( r, v ) { | Type | if ( v > 2 * r ) | return v | else if ( v & 1 ) | return r - ((v + 1) >> 1) | else | return r + (v >> 1) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | film_grain_params( ) { | Type | if ( !film_grain_params_present || | (!show_frame && !showable_frame) ) { | reset_grain_params() | return | } | @@apply_grain | f(1) | if ( !apply_grain ) { | reset_grain_params() | return | } | @@grain_seed | f(16) | if ( frame_type == INTER_FRAME ) | @@update_grain | f(1) | else | update_grain = 1 | if ( !update_grain ) { | @@film_grain_params_ref_idx | f(3) | tempGrainSeed = grain_seed | load_grain_params( film_grain_params_ref_idx ) | grain_seed = tempGrainSeed | return | } | @@num_y_points | f(4) | for ( i = 0; i < num_y_points; i++ ) { | @@point_y_value[ i ] | f(8) | @@point_y_scaling[ i ] | f(8) | } | if ( mono_chrome ) { | chroma_scaling_from_luma = 0 | } else { | @@chroma_scaling_from_luma | f(1) | } | if ( mono_chrome || chroma_scaling_from_luma || | ( subsampling_x == 1 && subsampling_y == 1 && | num_y_points == 0 ) | ) { | num_cb_points = 0 | num_cr_points = 0 | } else { | @@num_cb_points | f(4) | for ( i = 0; i < num_cb_points; i++ ) { | @@point_cb_value[ i ] | f(8) | @@point_cb_scaling[ i ] | f(8) | } | @@num_cr_points | f(4) | for ( i = 0; i < num_cr_points; i++ ) { | @@point_cr_value[ i ] | f(8) | @@point_cr_scaling[ i ] | f(8) | } | } | @@grain_scaling_minus_8 | f(2) | @@ar_coeff_lag | f(2) | numPosLuma = 2 * ar_coeff_lag * ( ar_coeff_lag + 1 ) | if ( num_y_points ) { | numPosChroma = numPosLuma + 1 | for ( i = 0; i < numPosLuma; i++ ) | @@ar_coeffs_y_plus_128[ i ] | f(8) | } else { | numPosChroma = numPosLuma | } | if ( chroma_scaling_from_luma || num_cb_points ) { | for ( i = 0; i < numPosChroma; i++ ) | @@ar_coeffs_cb_plus_128[ i ] | f(8) | } | if ( chroma_scaling_from_luma || num_cr_points ) { | for ( i = 0; i < numPosChroma; i++ ) | @@ar_coeffs_cr_plus_128[ i ] | f(8) | } | @@ar_coeff_shift_minus_6 | f(2) | @@grain_scale_shift | f(2) | if ( num_cb_points ) { | @@cb_mult | f(8) | @@cb_luma_mult | f(8) | @@cb_offset | f(9) | } | if ( num_cr_points ) { | @@cr_mult | f(8) | @@cr_luma_mult | f(8) | @@cr_offset | f(9) | } | @@overlap_flag | f(1) | @@clip_to_restricted_range | f(1) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | temporal_point_info( ) { | Type | n = frame_presentation_time_length_minus_1 + 1 | @@frame_presentation_time | f(n) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | frame_obu( sz ) { | Type | startBitPos = get_position( ) | frame_header_obu( ) | byte_alignment( ) | endBitPos = get_position( ) | headerBytes = (endBitPos - startBitPos) / 8 | sz -= headerBytes | tile_group_obu( sz ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | tile_group_obu( sz ) { | Type | NumTiles = TileCols * TileRows | startBitPos = get_position( ) | tile_start_and_end_present_flag = 0 | if ( NumTiles > 1 ) | @@tile_start_and_end_present_flag | f(1) | if ( NumTiles == 1 || !tile_start_and_end_present_flag ) { | tg_start = 0 | tg_end = NumTiles - 1 | } else { | tileBits = TileColsLog2 + TileRowsLog2 | @@tg_start | f(tileBits) | @@tg_end | f(tileBits) | } | byte_alignment( ) | endBitPos = get_position( ) | headerBytes = (endBitPos - startBitPos) / 8 | sz -= headerBytes | | for ( TileNum = tg_start; TileNum <= tg_end; TileNum++ ) { | tileRow = TileNum / TileCols | tileCol = TileNum % TileCols | lastTile = TileNum == tg_end | if ( lastTile ) { | tileSize = sz | } else { | @@tile_size_minus_1 | le(TileSizeBytes) | tileSize = tile_size_minus_1 + 1 | sz -= tileSize + TileSizeBytes | } | MiRowStart = MiRowStarts[ tileRow ] | MiRowEnd = MiRowStarts[ tileRow + 1 ] | MiColStart = MiColStarts[ tileCol ] | MiColEnd = MiColStarts[ tileCol + 1 ] | CurrentQIndex = base_q_idx | init_symbol( tileSize ) | decode_tile( ) | exit_symbol( ) | } | if ( tg_end == NumTiles - 1 ) { | if ( !disable_frame_end_update_cdf ) { | frame_end_update_cdf( ) | } | decode_frame_wrapup( ) | SeenFrameHeader = 0 | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | decode_tile( ) { | Type | clear_above_context( ) | for ( i = 0; i < FRAME_LF_COUNT; i++ ) | DeltaLF[ i ] = 0 | for ( plane = 0; plane < NumPlanes; plane++ ) { | for ( pass = 0; pass < 2; pass++ ) { | RefSgrXqd[ plane ][ pass ] = Sgrproj_Xqd_Mid[ pass ] | for ( i = 0; i < WIENER_COEFFS; i++ ) { | RefLrWiener[ plane ][ pass ][ i ] = Wiener_Taps_Mid[ i ] | } | } | } | sbSize = use_128x128_superblock ? BLOCK_128X128 : BLOCK_64X64 | sbSize4 = Num_4x4_Blocks_Wide[ sbSize ] | for ( r = MiRowStart; r < MiRowEnd; r += sbSize4 ) { | clear_left_context( ) | for ( c = MiColStart; c < MiColEnd; c += sbSize4 ) { | ReadDeltas = delta_q_present | clear_cdef( r, c ) | clear_block_decoded_flags( r, c, sbSize4 ) | read_lr( r, c, sbSize ) | decode_partition( r, c, sbSize ) | } | } | } {:.syntax }
where Sgrproj_Xqd_Mid and Wiener_Taps_Mid are constant lookup tables specified as:
Wiener_Taps_Mid[3] = { 3, -7, 15 }
Sgrproj_Xqd_Mid[2] = { -32, 31 }
| --------------------------------------------------------- | ---------------- | | clear_block_decoded_flags( r, c, sbSize4 ) { | Type | for ( plane = 0; plane < NumPlanes; plane++ ) { | subX = (plane > 0) ? subsampling_x : 0 | subY = (plane > 0) ? subsampling_y : 0 | sbWidth4 = ( MiColEnd - c ) >> subX | sbHeight4 = ( MiRowEnd - r ) >> subY | for ( y = -1; y <= ( sbSize4 >> subY ); y++ ) | for ( x = -1; x <= ( sbSize4 >> subX ); x++ ) { | if ( y < 0 && x < sbWidth4 ) | BlockDecoded[ plane ][ y ][ x ] = 1 | else if ( x < 0 && y < sbHeight4 ) | BlockDecoded[ plane ][ y ][ x ] = 1 | else | BlockDecoded[ plane ][ y ][ x ] = 0 | } | BlockDecoded[ plane ][ sbSize4 >> subY ][ -1 ] = 0 | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | decode_partition( r, c, bSize ) { | Type | if ( r >= MiRows || c >= MiCols ) | return 0 | AvailU = is_inside( r - 1, c ) | AvailL = is_inside( r, c - 1 ) | num4x4 = Num_4x4_Blocks_Wide[ bSize ] | halfBlock4x4 = num4x4 >> 1 | quarterBlock4x4 = halfBlock4x4 >> 1 | hasRows = ( r + halfBlock4x4 ) < MiRows | hasCols = ( c + halfBlock4x4 ) < MiCols | if ( bSize < BLOCK_8X8 ) { | partition = PARTITION_NONE | } else if ( hasRows && hasCols ) { | @@partition | S() | } else if ( hasCols ) { | @@split_or_horz | S() | partition = split_or_horz ? PARTITION_SPLIT : PARTITION_HORZ | } else if ( hasRows ) { | @@split_or_vert | S() | partition = split_or_vert ? PARTITION_SPLIT : PARTITION_VERT | } else { | partition = PARTITION_SPLIT | } | subSize = Partition_Subsize[ partition ][ bSize ] | splitSize = Partition_Subsize[ PARTITION_SPLIT ][ bSize ] | if ( partition == PARTITION_NONE ) { | decode_block( r, c, subSize ) | } else if ( partition == PARTITION_HORZ ) { | decode_block( r, c, subSize ) | if ( hasRows ) | decode_block( r + halfBlock4x4, c, subSize ) | } else if ( partition == PARTITION_VERT ) { | decode_block( r, c, subSize ) | if ( hasCols ) | decode_block( r, c + halfBlock4x4, subSize ) | } else if ( partition == PARTITION_SPLIT ) { | decode_partition( r, c, subSize ) | decode_partition( r, c + halfBlock4x4, subSize ) | decode_partition( r + halfBlock4x4, c, subSize ) | decode_partition( r + halfBlock4x4, c + halfBlock4x4, subSize ) | } else if ( partition == PARTITION_HORZ_A ) { | decode_block( r, c, splitSize ) | decode_block( r, c + halfBlock4x4, splitSize ) | decode_block( r + halfBlock4x4, c, subSize ) | } else if ( partition == PARTITION_HORZ_B ) { | decode_block( r, c, subSize ) | decode_block( r + halfBlock4x4, c, splitSize ) | decode_block( r + halfBlock4x4, c + halfBlock4x4, splitSize ) | } else if ( partition == PARTITION_VERT_A ) { | decode_block( r, c, splitSize ) | decode_block( r + halfBlock4x4, c, splitSize ) | decode_block( r, c + halfBlock4x4, subSize ) | } else if ( partition == PARTITION_VERT_B ) { | decode_block( r, c, subSize ) | decode_block( r, c + halfBlock4x4, splitSize ) | decode_block( r + halfBlock4x4, c + halfBlock4x4, splitSize ) | } else if ( partition == PARTITION_HORZ_4 ) { | decode_block( r + quarterBlock4x4 * 0, c, subSize ) | decode_block( r + quarterBlock4x4 * 1, c, subSize ) | decode_block( r + quarterBlock4x4 * 2, c, subSize ) | if ( r + quarterBlock4x4 * 3 < MiRows ) | decode_block( r + quarterBlock4x4 * 3, c, subSize ) | } else { | decode_block( r, c + quarterBlock4x4 * 0, subSize ) | decode_block( r, c + quarterBlock4x4 * 1, subSize ) | decode_block( r, c + quarterBlock4x4 * 2, subSize ) | if ( c + quarterBlock4x4 * 3 < MiCols ) | decode_block( r, c + quarterBlock4x4 * 3, subSize ) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | decode_block( r, c, subSize ) { | Type | MiRow = r | MiCol = c | MiSize = subSize | bw4 = Num_4x4_Blocks_Wide[ subSize ] | bh4 = Num_4x4_Blocks_High[ subSize ] | if ( bh4 == 1 && subsampling_y && (MiRow & 1) == 0 ) | HasChroma = 0 | else if ( bw4 == 1 && subsampling_x && (MiCol & 1) == 0 ) | HasChroma = 0 | else | HasChroma = NumPlanes > 1 | AvailU = is_inside( r - 1, c ) | AvailL = is_inside( r, c - 1 ) | AvailUChroma = AvailU | AvailLChroma = AvailL | if ( HasChroma ) { | if ( subsampling_y && bh4 == 1 ) | AvailUChroma = is_inside( r - 2, c ) | if ( subsampling_x && bw4 == 1 ) | AvailLChroma = is_inside( r, c - 2 ) | } else { | AvailUChroma = 0 | AvailLChroma = 0 | } | mode_info( ) | palette_tokens( ) | read_block_tx_size( ) | | if ( skip ) | reset_block_context( bw4, bh4 ) | isCompound = RefFrame[ 1 ] > INTRA_FRAME | for ( y = 0; y < bh4; y++ ) { | for ( x = 0; x < bw4; x++ ) { | YModes [ r + y ][ c + x ] = YMode | if ( RefFrame[ 0 ] == INTRA_FRAME && HasChroma ) | UVModes [ r + y ][ c + x ] = UVMode | for ( refList = 0; refList < 2; refList++ ) | RefFrames[ r + y ][ c + x ][ refList ] = RefFrame[ refList ] | if ( is_inter ) { | if ( !use_intrabc ) { | CompGroupIdxs[ r + y ][ c + x ] = comp_group_idx | CompoundIdxs[ r + y ][ c + x ] = compound_idx | } | for ( dir = 0; dir < 2; dir++ ) { | InterpFilters[ r + y ][ c + x ][ dir ] = interp_filter[ dir ] | } | for ( refList = 0; refList < 1 + isCompound; refList++ ) { | Mvs[ r + y ][ c + x ][ refList ] = Mv[ refList ] | } | } | } | } | compute_prediction( ) | residual( ) | for ( y = 0; y < bh4; y++ ) { | for ( x = 0; x < bw4; x++ ) { | IsInters[ r + y ][ c + x ] = is_inter | SkipModes[ r + y ][ c + x ] = skip_mode | Skips[ r + y ][ c + x ] = skip | TxSizes[ r + y ][ c + x ] = TxSize | MiSizes[ r + y ][ c + x ] = MiSize | SegmentIds[ r + y ][ c + x ] = segment_id | PaletteSizes[ 0 ][ r + y ][ c + x ] = PaletteSizeY | PaletteSizes[ 1 ][ r + y ][ c + x ] = PaletteSizeUV | for ( i = 0; i < PaletteSizeY; i++ ) | PaletteColors[ 0 ][ r + y ][ c + x ][ i ] = palette_colors_y[ i ] | for ( i = 0; i < PaletteSizeUV; i++ ) | PaletteColors[ 1 ][ r + y ][ c + x ][ i ] = palette_colors_u[ i ] | for ( i = 0; i < FRAME_LF_COUNT; i++ ) | DeltaLFs[ r + y ][ c + x ][ i ] = DeltaLF[ i ] | } | } | } {:.syntax }
where reset_block_context( ) is specified as:
reset_block_context( bw4, bh4 ) {
for ( plane = 0; plane < 1 + 2 * HasChroma; plane++ ) {
subX = (plane > 0) ? subsampling_x : 0
subY = (plane > 0) ? subsampling_y : 0
for ( i = MiCol >> subX; i < ( ( MiCol + bw4 ) >> subX ); i++) {
AboveLevelContext[ plane ][ i ] = 0
AboveDcContext[ plane ][ i ] = 0
}
for ( i = MiRow >> subY; i < ( ( MiRow + bh4 ) >> subY ); i++) {
LeftLevelContext[ plane ][ i ] = 0
LeftDcContext[ plane ][ i ] = 0
}
}
}
| --------------------------------------------------------- | ---------------- | | mode_info( ) { | Type | if ( FrameIsIntra ) | intra_frame_mode_info( ) | else | inter_frame_mode_info( ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | intra_frame_mode_info( ) { | Type | skip = 0 | if ( SegIdPreSkip ) | intra_segment_id( ) | skip_mode = 0 | read_skip( ) | if ( !SegIdPreSkip ) | intra_segment_id( ) | read_cdef( ) | read_delta_qindex( ) | read_delta_lf( ) | ReadDeltas = 0 | RefFrame[ 0 ] = INTRA_FRAME | RefFrame[ 1 ] = NONE | if ( allow_intrabc ) { | @@use_intrabc | S() | } else { | use_intrabc = 0 | } | if ( use_intrabc ) { | is_inter = 1 | YMode = DC_PRED | UVMode = DC_PRED | motion_mode = SIMPLE | compound_type = COMPOUND_AVERAGE | PaletteSizeY = 0 | PaletteSizeUV = 0 | interp_filter[ 0 ] = BILINEAR | interp_filter[ 1 ] = BILINEAR | find_mv_stack( 0 ) | assign_mv( 0 ) | } else { | is_inter = 0 | @@intra_frame_y_mode | S() | YMode = intra_frame_y_mode | intra_angle_info_y( ) | if ( HasChroma ) { | @@uv_mode | S() | UVMode = uv_mode | if ( UVMode == UV_CFL_PRED ) { | read_cfl_alphas( ) | } | intra_angle_info_uv( ) | } | PaletteSizeY = 0 | PaletteSizeUV = 0 | if ( MiSize >= BLOCK_8X8 && | Block_Width[ MiSize ] <= 64 && | Block_Height[ MiSize ] <= 64 && | allow_screen_content_tools ) { | palette_mode_info( ) | } | filter_intra_mode_info( ) | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | intra_segment_id( ) { | Type | if ( segmentation_enabled ) | read_segment_id( ) | else | segment_id = 0 | Lossless = LosslessArray[ segment_id ] | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_segment_id( ) { | Type | if ( AvailU && AvailL ) | prevUL = SegmentIds[ MiRow - 1 ][ MiCol - 1 ] | else | prevUL = -1 | if ( AvailU ) | prevU = SegmentIds[ MiRow - 1 ][ MiCol ] | else | prevU = -1 | if ( AvailL ) | prevL = SegmentIds[ MiRow ][ MiCol - 1 ] | else | prevL = -1 | if ( prevU == -1 ) | pred = (prevL == -1) ? 0 : prevL | else if ( prevL == -1 ) | pred = prevU | else | pred = (prevUL == prevU) ? prevU : prevL | if ( skip ) { | segment_id = pred | } else { | @@segment_id | S() | segment_id = neg_deinterleave( segment_id, pred, | LastActiveSegId + 1 ) | } | } {:.syntax }
where neg_deinterleave is a function defined as:
neg_deinterleave(diff, ref, max) {
if ( !ref )
return diff
if ( ref >= (max - 1) )
return max - diff - 1
if ( 2 * ref < max ) {
if ( diff <= 2 * ref ) {
if ( diff & 1 )
return ref + ((diff + 1) >> 1)
else
return ref - (diff >> 1)
}
return diff
} else {
if ( diff <= 2 * (max - ref - 1) ) {
if ( diff & 1 )
return ref + ((diff + 1) >> 1)
else
return ref - (diff >> 1)
}
return max - (diff + 1)
}
}
| --------------------------------------------------------- | ---------------- | | read_skip_mode() { | Type | if ( seg_feature_active( SEG_LVL_SKIP ) || | seg_feature_active( SEG_LVL_REF_FRAME ) || | seg_feature_active( SEG_LVL_GLOBALMV ) || | !skip_mode_present || | Block_Width[ MiSize ] < 8 || | Block_Height[ MiSize ] < 8 ) { | skip_mode = 0 | } else { | @@skip_mode | S() | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_skip() { | Type | if ( SegIdPreSkip && seg_feature_active( SEG_LVL_SKIP ) ) { | skip = 1 | } else { | @@skip | S() | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_delta_qindex( ) { | Type | sbSize = use_128x128_superblock ? BLOCK_128X128 : BLOCK_64X64 | if ( MiSize == sbSize && skip ) | return | if ( ReadDeltas ) { | @@delta_q_abs | S() | if ( delta_q_abs == DELTA_Q_SMALL ) { | @@delta_q_rem_bits | L(3) | delta_q_rem_bits++ | @@delta_q_abs_bits | L(delta_q_rem_bits) | delta_q_abs = delta_q_abs_bits + (1 << delta_q_rem_bits) + 1 | } | if ( delta_q_abs ) { | @@delta_q_sign_bit | L(1) | reducedDeltaQIndex = delta_q_sign_bit ? -delta_q_abs : delta_q_abs | CurrentQIndex = Clip3(1, 255, | CurrentQIndex + (reducedDeltaQIndex << delta_q_res)) | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_delta_lf( ) { | Type | sbSize = use_128x128_superblock ? BLOCK_128X128 : BLOCK_64X64 | if ( MiSize == sbSize && skip ) | return | if ( ReadDeltas && delta_lf_present ) { | frameLfCount = 1 | if ( delta_lf_multi ) { | frameLfCount = ( NumPlanes > 1 ) ? FRAME_LF_COUNT : ( FRAME_LF_COUNT - 2 ) | } | for ( i = 0; i < frameLfCount; i++ ) { | @@delta_lf_abs | S() | if ( delta_lf_abs == DELTA_LF_SMALL ) { | @@delta_lf_rem_bits | L(3) | n = delta_lf_rem_bits + 1 | @@delta_lf_abs_bits | L(n) | deltaLfAbs = delta_lf_abs_bits + | ( 1 << n ) + 1 | } else { | deltaLfAbs = delta_lf_abs | } | if ( deltaLfAbs ) { | @@delta_lf_sign_bit | L(1) | reducedDeltaLfLevel = delta_lf_sign_bit ? | -deltaLfAbs : | deltaLfAbs | DeltaLF[ i ] = Clip3( -MAX_LOOP_FILTER, MAX_LOOP_FILTER, DeltaLF[ i ] + | (reducedDeltaLfLevel << delta_lf_res) ) | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | seg_feature_active_idx( idx, feature ) { | Type | return segmentation_enabled && FeatureEnabled[ idx ][ feature ] | } | | seg_feature_active( feature ) { | return seg_feature_active_idx( segment_id, feature ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_tx_size( allowSelect ) { | Type | if ( Lossless ) { | TxSize = TX_4X4 | return | } | maxRectTxSize = Max_Tx_Size_Rect[ MiSize ] | maxTxDepth = Max_Tx_Depth[ MiSize ] | TxSize = maxRectTxSize | if ( MiSize > BLOCK_4X4 && allowSelect && TxMode == TX_MODE_SELECT ) { | @@tx_depth | S() | for ( i = 0; i < tx_depth; i++ ) | TxSize = Split_Tx_Size[ TxSize ] | } | } | } {:.syntax }
The Max_Tx_Depth table specifies the maximum transform depth for each block size:
Max_Tx_Depth[ BLOCK_SIZES ] = {
0, 1, 1, 1,
2, 2, 2, 3,
3, 3, 4, 4,
4, 4, 4, 4,
2, 2, 3, 3,
4, 4
}
Note: Max_Tx_Depth contains the number of times the transform must be split to reach a 4x4 transform size. This number can be greater than MAX_TX_DEPTH. However, it is impossible to encode a transform depth greater than MAX_TX_DEPTH because tx_depth can only encode values in the range 0 to 2 {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | read_block_tx_size( ) { | Type | bw4 = Num_4x4_Blocks_Wide[ MiSize ] | bh4 = Num_4x4_Blocks_High[ MiSize ] | if ( TxMode == TX_MODE_SELECT && | MiSize > BLOCK_4X4 && is_inter && | !skip && !Lossless ) { | maxTxSz = Max_Tx_Size_Rect[ MiSize ] | txW4 = Tx_Width[ maxTxSz ] / MI_SIZE | txH4 = Tx_Height[ maxTxSz ] / MI_SIZE | for ( row = MiRow; row < MiRow + bh4; row += txH4 ) | for ( col = MiCol; col < MiCol + bw4; col += txW4 ) | read_var_tx_size( row, col, maxTxSz, 0 ) | } else { | read_tx_size(!skip || !is_inter) | for ( row = MiRow; row < MiRow + bh4; row++ ) | for ( col = MiCol; col < MiCol + bw4; col++ ) | InterTxSizes[ row ][ col ] = TxSize | } | } {:.syntax }
read_var_tx_size is used to read a transform size tree.
| --------------------------------------------------------- | ---------------- | | read_var_tx_size( row, col, txSz, depth) { | Type | if ( row >= MiRows || col >= MiCols ) | return | if ( txSz == TX_4X4 || depth == MAX_VARTX_DEPTH ) { | txfm_split = 0 | } else { | @@txfm_split | S() | } | w4 = Tx_Width[ txSz ] / MI_SIZE | h4 = Tx_Height[ txSz ] / MI_SIZE | if ( txfm_split ) { | subTxSz = Split_Tx_Size[ txSz ] | stepW = Tx_Width[ subTxSz ] / MI_SIZE | stepH = Tx_Height[ subTxSz ] / MI_SIZE | for ( i = 0; i < h4; i += stepH ) | for ( j = 0; j < w4; j += stepW ) | read_var_tx_size( row + i, col + j, subTxSz, depth+1) | } else { | for ( i = 0; i < h4; i++ ) | for ( j = 0; j < w4; j++ ) | InterTxSizes[ row + i ][ col + j ] = txSz | TxSize = txSz | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | inter_frame_mode_info( ) { | Type | use_intrabc = 0 | LeftRefFrame[ 0 ] = AvailL ? RefFrames[ MiRow ][ MiCol-1 ][ 0 ] : INTRA_FRAME | AboveRefFrame[ 0 ] = AvailU ? RefFrames[ MiRow-1 ][ MiCol ][ 0 ] : INTRA_FRAME | LeftRefFrame[ 1 ] = AvailL ? RefFrames[ MiRow ][ MiCol-1 ][ 1 ] : NONE | AboveRefFrame[ 1 ] = AvailU ? RefFrames[ MiRow-1 ][ MiCol ][ 1 ] : NONE | LeftIntra = LeftRefFrame[ 0 ] <= INTRA_FRAME | AboveIntra = AboveRefFrame[ 0 ] <= INTRA_FRAME | LeftSingle = LeftRefFrame[ 1 ] <= INTRA_FRAME | AboveSingle = AboveRefFrame[ 1 ] <= INTRA_FRAME | skip = 0 | inter_segment_id( 1 ) | read_skip_mode( ) | if ( skip_mode ) | skip = 1 | else | read_skip( ) | if ( !SegIdPreSkip ) | inter_segment_id( 0 ) | Lossless = LosslessArray[ segment_id ] | read_cdef( ) | read_delta_qindex( ) | read_delta_lf( ) | ReadDeltas = 0 | read_is_inter( ) | if ( is_inter ) | inter_block_mode_info( ) | else | intra_block_mode_info( ) | } {:.syntax }
This is called before (preSkip equal to 1) and after (preSkip equal to 0) the skip syntax element has been read.
| --------------------------------------------------------- | ---------------- | | inter_segment_id( preSkip ) { | Type | if ( segmentation_enabled ) { | predictedSegmentId = get_segment_id( ) | if ( segmentation_update_map ) { | if ( preSkip && !SegIdPreSkip ) { | segment_id = 0 | return | } | if ( !preSkip ) { | if ( skip ) { | seg_id_predicted = 0 | for ( i = 0; i < Num_4x4_Blocks_Wide[ MiSize ]; i++ ) | AboveSegPredContext[ MiCol + i ] = seg_id_predicted | for ( i = 0; i < Num_4x4_Blocks_High[ MiSize ]; i++ ) | LeftSegPredContext[ MiRow + i ] = seg_id_predicted | read_segment_id( ) | return | } | } | if ( segmentation_temporal_update == 1 ) { | @@seg_id_predicted | S() | if ( seg_id_predicted ) | segment_id = predictedSegmentId | else | read_segment_id( ) | for ( i = 0; i < Num_4x4_Blocks_Wide[ MiSize ]; i++ ) | AboveSegPredContext[ MiCol + i ] = seg_id_predicted | for ( i = 0; i < Num_4x4_Blocks_High[ MiSize ]; i++ ) | LeftSegPredContext[ MiRow + i ] = seg_id_predicted | } else { | read_segment_id( ) | } | } else { | segment_id = predictedSegmentId | } | } else { | segment_id = 0 | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_is_inter( ) { | Type | if ( skip_mode ) { | is_inter = 1 | } else if ( seg_feature_active ( SEG_LVL_REF_FRAME ) ) { | is_inter = FeatureData[ segment_id ][ SEG_LVL_REF_FRAME ] != INTRA_FRAME | } else if ( seg_feature_active ( SEG_LVL_GLOBALMV ) ) { | is_inter = 1 | } else { | @@is_inter | S() | } | } {:.syntax }
The predicted segment id is the smallest value found in the on-screen region of the segmentation map covered by the current block.
| --------------------------------------------------------- | ---------------- | | get_segment_id( ) { | Type | bw4 = Num_4x4_Blocks_Wide[ MiSize ] | bh4 = Num_4x4_Blocks_High[ MiSize ] | xMis = Min( MiCols - MiCol, bw4 ) | yMis = Min( MiRows - MiRow, bh4 ) | seg = 7 | for ( y = 0; y < yMis; y++ ) | for ( x = 0; x < xMis; x++ ) | seg = Min( seg, PrevSegmentIds[ MiRow + y ][ MiCol + x ] ) | return seg | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | intra_block_mode_info( ) { | Type | RefFrame[ 0 ] = INTRA_FRAME | RefFrame[ 1 ] = NONE | @@y_mode | S() | YMode = y_mode | intra_angle_info_y( ) | if ( HasChroma ) { | @@uv_mode | S() | UVMode = uv_mode | if ( UVMode == UV_CFL_PRED ) { | read_cfl_alphas( ) | } | intra_angle_info_uv( ) | } | PaletteSizeY = 0 | PaletteSizeUV = 0 | if ( MiSize >= BLOCK_8X8 && | Block_Width[ MiSize ] <= 64 && | Block_Height[ MiSize ] <= 64 && | allow_screen_content_tools ) | palette_mode_info( ) | filter_intra_mode_info( ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | inter_block_mode_info( ) { | Type | PaletteSizeY = 0 | PaletteSizeUV = 0 | read_ref_frames( ) | isCompound = RefFrame[ 1 ] > INTRA_FRAME | find_mv_stack( isCompound ) | if ( skip_mode ) { | YMode = NEAREST_NEARESTMV | } else if ( seg_feature_active( SEG_LVL_SKIP ) || | seg_feature_active( SEG_LVL_GLOBALMV ) ) { | YMode = GLOBALMV | } else if ( isCompound ) { | @@compound_mode | S() | YMode = NEAREST_NEARESTMV + compound_mode | } else { | @@new_mv | S() | if ( new_mv == 0 ) { | YMode = NEWMV | } else { | @@zero_mv | S() | if ( zero_mv == 0 ) { | YMode = GLOBALMV | } else { | @@ref_mv | S() | YMode = (ref_mv == 0) ? NEARESTMV : NEARMV | } | } | } | RefMvIdx = 0 | if ( YMode == NEWMV || YMode == NEW_NEWMV ) { | for ( idx = 0; idx < 2; idx++ ) { | if ( NumMvFound > idx + 1 ) { | @@drl_mode | S() | if ( drl_mode == 0 ) { | RefMvIdx = idx | break | } | RefMvIdx = idx + 1 | } | } | } else if ( has_nearmv( ) ) { | RefMvIdx = 1 | for ( idx = 1; idx < 3; idx++ ) { | if ( NumMvFound > idx + 1 ) { | @@drl_mode | S() | if ( drl_mode == 0 ) { | RefMvIdx = idx | break | } | RefMvIdx = idx + 1 | } | } | } | assign_mv( isCompound ) | read_interintra_mode( isCompound ) | read_motion_mode( isCompound ) | read_compound_type( isCompound ) | if ( interpolation_filter == SWITCHABLE ) { | for ( dir = 0; dir < ( enable_dual_filter ? 2 : 1 ); dir++ ) { | if ( needs_interp_filter( ) ) { | @@interp_filter[ dir ] | S() | } else { | interp_filter[ dir ] = EIGHTTAP | } | } | if ( !enable_dual_filter ) | interp_filter[ 1 ] = interp_filter[ 0 ] | } else { | for ( dir = 0; dir < 2; dir++ ) | interp_filter[ dir ] = interpolation_filter | } | } {:.syntax }
The function has_nearmv is defined as:
has_nearmv( ) {
return (YMode == NEARMV || YMode == NEAR_NEARMV
|| YMode == NEAR_NEWMV || YMode == NEW_NEARMV)
}
The function needs_interp_filter is defined as:
needs_interp_filter( ) {
large = (Min(Block_Width[MiSize], Block_Height[MiSize]) >= 8)
if ( skip_mode || motion_mode == LOCALWARP ) {
return 0
} else if ( large && YMode == GLOBALMV ) {
return GmType[ RefFrame[ 0 ] ] == TRANSLATION
} else if ( large && YMode == GLOBAL_GLOBALMV ) {
return GmType[ RefFrame[ 0 ] ] == TRANSLATION || GmType[ RefFrame[ 1 ] ] == TRANSLATION
} else {
return 1
}
}
| --------------------------------------------------------- | ---------------- | | filter_intra_mode_info( ) { | Type | use_filter_intra = 0 | if ( enable_filter_intra && | YMode == DC_PRED && PaletteSizeY == 0 && | Max( Block_Width[ MiSize ], Block_Height[ MiSize ] ) <= 32 ) { | @@use_filter_intra | S() | if ( use_filter_intra ) { | @@filter_intra_mode | S() | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_ref_frames( ) { | Type | if ( skip_mode ) { | RefFrame[ 0 ] = SkipModeFrame[ 0 ] | RefFrame[ 1 ] = SkipModeFrame[ 1 ] | } else if ( seg_feature_active( SEG_LVL_REF_FRAME ) ) { | RefFrame[ 0 ] = FeatureData[ segment_id ][ SEG_LVL_REF_FRAME ] | RefFrame[ 1 ] = NONE | } else if ( seg_feature_active( SEG_LVL_SKIP ) || | seg_feature_active( SEG_LVL_GLOBALMV ) ) { | RefFrame[ 0 ] = LAST_FRAME | RefFrame[ 1 ] = NONE | } else { | bw4 = Num_4x4_Blocks_Wide[ MiSize ] | bh4 = Num_4x4_Blocks_High[ MiSize ] | if ( reference_select && ( Min( bw4, bh4 ) >= 2 ) ) | @@comp_mode | S() | else | comp_mode = SINGLE_REFERENCE | if ( comp_mode == COMPOUND_REFERENCE ) { | @@comp_ref_type | S() | if ( comp_ref_type == UNIDIR_COMP_REFERENCE ) { | @@uni_comp_ref | S() | if ( uni_comp_ref ) { | RefFrame[0] = BWDREF_FRAME | RefFrame[1] = ALTREF_FRAME | } else { | @@uni_comp_ref_p1 | S() | if ( uni_comp_ref_p1 ) { | @@uni_comp_ref_p2 | S() | if ( uni_comp_ref_p2 ) { | RefFrame[0] = LAST_FRAME | RefFrame[1] = GOLDEN_FRAME | } else { | RefFrame[0] = LAST_FRAME | RefFrame[1] = LAST3_FRAME | } | } else { | RefFrame[0] = LAST_FRAME | RefFrame[1] = LAST2_FRAME | } | } | } else { | @@comp_ref | S() | if ( comp_ref == 0 ) { | @@comp_ref_p1 | S() | RefFrame[ 0 ] = comp_ref_p1 ? | LAST2_FRAME : LAST_FRAME | } else { | @@comp_ref_p2 | S() | RefFrame[ 0 ] = comp_ref_p2 ? | GOLDEN_FRAME : LAST3_FRAME | } | @@comp_bwdref | S() | if ( comp_bwdref == 0 ) { | @@comp_bwdref_p1 | S() | RefFrame[ 1 ] = comp_bwdref_p1 ? | ALTREF2_FRAME : BWDREF_FRAME | } else { | RefFrame[ 1 ] = ALTREF_FRAME | } | } | } else { | @@single_ref_p1 | S() | if ( single_ref_p1 ) { | @@single_ref_p2 | S() | if ( single_ref_p2 == 0 ) { | @@single_ref_p6 | S() | RefFrame[ 0 ] = single_ref_p6 ? | ALTREF2_FRAME : BWDREF_FRAME | } else { | RefFrame[ 0 ] = ALTREF_FRAME | } | } else { | @@single_ref_p3 | S() | if ( single_ref_p3 ) { | @@single_ref_p5 | S() | RefFrame[ 0 ] = single_ref_p5 ? | GOLDEN_FRAME : LAST3_FRAME | } else { | @@single_ref_p4 | S() | RefFrame[ 0 ] = single_ref_p4 ? | LAST2_FRAME : LAST_FRAME | } | } | RefFrame[ 1 ] = NONE | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | assign_mv( isCompound ) { | Type | for ( i = 0; i < 1 + isCompound; i++ ) { | if ( use_intrabc ) { | compMode = NEWMV | } else { | compMode = get_mode( i ) | } | if ( use_intrabc ) { | PredMv[ 0 ] = RefStackMv[ 0 ][ 0 ] | if ( PredMv[ 0 ][ 0 ] == 0 && PredMv[ 0 ][ 1 ] == 0 ) { | PredMv[ 0 ] = RefStackMv[ 1 ][ 0 ] | } | if ( PredMv[ 0 ][ 0 ] == 0 && PredMv[ 0 ][ 1 ] == 0 ) { | sbSize = use_128x128_superblock ? BLOCK_128X128 : BLOCK_64X64 | sbSize4 = Num_4x4_Blocks_High[ sbSize ] | if ( MiRow - sbSize4 < MiRowStart ) { | PredMv[ 0 ][ 0 ] = 0 | PredMv[ 0 ][ 1 ] = -(sbSize4 * MI_SIZE + INTRABC_DELAY_PIXELS) * 8 | } else { | PredMv[ 0 ][ 0 ] = -(sbSize4 * MI_SIZE * 8) | PredMv[ 0 ][ 1 ] = 0 | } | } | } else if ( compMode == GLOBALMV ) { | PredMv[ i ] = GlobalMvs[ i ] | } else { | pos = ( compMode == NEARESTMV ) ? 0 : RefMvIdx | if ( compMode == NEWMV && NumMvFound <= 1 ) | pos = 0 | PredMv[ i ] = RefStackMv[ pos ][ i ] | } | if ( compMode == NEWMV ) { | read_mv( i ) | } else { | Mv[ i ] = PredMv[ i ] | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_motion_mode( isCompound ) { | Type | if ( skip_mode ) { | motion_mode = SIMPLE | return | } | if ( !is_motion_mode_switchable ) { | motion_mode = SIMPLE | return | } | if ( Min( Block_Width[ MiSize ], | Block_Height[ MiSize ] ) < 8 ) { | motion_mode = SIMPLE | return | } | if ( !force_integer_mv && | ( YMode == GLOBALMV || YMode == GLOBAL_GLOBALMV ) ) { | if ( GmType[ RefFrame[ 0 ] ] > TRANSLATION ) { | motion_mode = SIMPLE | return | } | } | if ( isCompound || RefFrame[ 1 ] == INTRA_FRAME || !has_overlappable_candidates( ) ) { | motion_mode = SIMPLE | return | } | find_warp_samples() | if ( force_integer_mv || NumSamples == 0 || | !allow_warped_motion || is_scaled( RefFrame[0] ) ) { | @@use_obmc | S() | motion_mode = use_obmc ? OBMC : SIMPLE | } else { | @@motion_mode | S() | } | } {:.syntax }
where is_scaled is a function that determines whether a reference frame uses scaling and is specified as:
is_scaled( refFrame ) {
refIdx = ref_frame_idx[ refFrame - LAST_FRAME ]
xScale = ( ( RefUpscaledWidth[ refIdx ] << REF_SCALE_SHIFT ) + ( FrameWidth / 2 ) ) / FrameWidth
yScale = ( ( RefFrameHeight[ refIdx ] << REF_SCALE_SHIFT ) + ( FrameHeight / 2 ) ) / FrameHeight
noScale = 1 << REF_SCALE_SHIFT
return xScale != noScale || yScale != noScale
}
| --------------------------------------------------------- | ---------------- | | read_interintra_mode( isCompound ) { | Type | if ( !skip_mode && enable_interintra_compound && !isCompound && | MiSize >= BLOCK_8X8 && MiSize <= BLOCK_32X32) { | @@interintra | S() | if ( interintra ) { | @@interintra_mode | S() | RefFrame[1] = INTRA_FRAME | AngleDeltaY = 0 | AngleDeltaUV = 0 | use_filter_intra = 0 | @@wedge_interintra | S() | if ( wedge_interintra ) { | @@wedge_index | S() | wedge_sign = 0 | } | } | } else { | interintra = 0 | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_compound_type( isCompound ) { | Type | comp_group_idx = 0 | compound_idx = 1 | if ( skip_mode ) { | compound_type = COMPOUND_AVERAGE | return | } | if ( isCompound ) { | n = Wedge_Bits[ MiSize ] | if ( enable_masked_compound ) { | @@comp_group_idx | S() | } | if ( comp_group_idx == 0 ) { | if ( enable_jnt_comp ) { | @@compound_idx | S() | compound_type = compound_idx ? COMPOUND_AVERAGE : | COMPOUND_DISTANCE | } else { | compound_type = COMPOUND_AVERAGE | } | } else { | if ( n == 0 ) { | compound_type = COMPOUND_DIFFWTD | } else { | @@compound_type | S() | } | } | if ( compound_type == COMPOUND_WEDGE ) { | @@wedge_index | S() | @@wedge_sign | L(1) | } else if ( compound_type == COMPOUND_DIFFWTD ) { | @@mask_type | L(1) | } | } else { | if ( interintra ) { | compound_type = wedge_interintra ? COMPOUND_WEDGE : COMPOUND_INTRA | } else { | compound_type = COMPOUND_AVERAGE | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | get_mode( refList ) { | Type | if ( refList == 0 ) { | if ( YMode < NEAREST_NEARESTMV ) | compMode = YMode | else if ( YMode == NEW_NEWMV || YMode == NEW_NEARESTMV || YMode == NEW_NEARMV ) | compMode = NEWMV | else if ( YMode == NEAREST_NEARESTMV || YMode == NEAREST_NEWMV ) | compMode = NEARESTMV | else if ( YMode == NEAR_NEARMV || YMode == NEAR_NEWMV ) | compMode = NEARMV | else | compMode = GLOBALMV | } else { | if ( YMode == NEW_NEWMV || YMode == NEAREST_NEWMV || YMode == NEAR_NEWMV ) | compMode = NEWMV | else if ( YMode == NEAREST_NEARESTMV || YMode == NEW_NEARESTMV ) | compMode = NEARESTMV | else if ( YMode == NEAR_NEARMV || YMode == NEW_NEARMV ) | compMode = NEARMV | else | compMode = GLOBALMV | } | return compMode | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_mv( ref ) { | Type | diffMv[ 0 ] = 0 | diffMv[ 1 ] = 0 | if ( use_intrabc ) { | MvCtx = MV_INTRABC_CONTEXT | } else { | MvCtx = 0 | } | @@mv_joint | S() | if ( mv_joint == MV_JOINT_HZVNZ || mv_joint == MV_JOINT_HNZVNZ ) | diffMv[ 0 ] = read_mv_component( 0 ) | if ( mv_joint == MV_JOINT_HNZVZ || mv_joint == MV_JOINT_HNZVNZ ) | diffMv[ 1 ] = read_mv_component( 1 ) | Mv[ ref ][ 0 ] = PredMv[ ref ][ 0 ] + diffMv[ 0 ] | Mv[ ref ][ 1 ] = PredMv[ ref ][ 1 ] + diffMv[ 1 ] | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_mv_component( comp ) { | Type | @@mv_sign | S() | @@mv_class | S() | if ( mv_class == MV_CLASS_0 ) { | @@mv_class0_bit | S() | if ( force_integer_mv ) | mv_class0_fr = 3 | else | @@mv_class0_fr | S() | if ( allow_high_precision_mv ) | @@mv_class0_hp | S() | else | mv_class0_hp = 1 | mag = ( ( mv_class0_bit << 3 ) | | ( mv_class0_fr << 1 ) | | mv_class0_hp ) + 1 | } else { | d = 0 | for ( i = 0; i < mv_class; i++ ) { | @@mv_bit | S() | d |= mv_bit << i | } | mag = CLASS0_SIZE << ( mv_class + 2 ) | if ( force_integer_mv ) | mv_fr = 3 | else | @@mv_fr | S() | if ( allow_high_precision_mv ) | @@mv_hp | S() | else | mv_hp = 1 | mag += ( ( d << 3 ) | ( mv_fr << 1 ) | mv_hp ) + 1 | } | return mv_sign ? -mag : mag | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | compute_prediction() { | Type | sbMask = use_128x128_superblock ? 31 : 15 | subBlockMiRow = MiRow & sbMask | subBlockMiCol = MiCol & sbMask | for ( plane = 0; plane < 1 + HasChroma * 2; plane++ ) { | planeSz = get_plane_residual_size( MiSize, plane ) | num4x4W = Num_4x4_Blocks_Wide[ planeSz ] | num4x4H = Num_4x4_Blocks_High[ planeSz ] | log2W = MI_SIZE_LOG2 + Mi_Width_Log2[ planeSz ] | log2H = MI_SIZE_LOG2 + Mi_Height_Log2[ planeSz ] | subX = (plane > 0) ? subsampling_x : 0 | subY = (plane > 0) ? subsampling_y : 0 | baseX = (MiCol >> subX) * MI_SIZE | baseY = (MiRow >> subY) * MI_SIZE | candRow = (MiRow >> subY) << subY | candCol = (MiCol >> subX) << subX | | IsInterIntra = ( is_inter && RefFrame[ 1 ] == INTRA_FRAME ) | if ( IsInterIntra ) { | if ( interintra_mode == II_DC_PRED ) mode = DC_PRED | else if ( interintra_mode == II_V_PRED ) mode = V_PRED | else if ( interintra_mode == II_H_PRED ) mode = H_PRED | else mode = SMOOTH_PRED | predict_intra( plane, baseX, baseY, | plane == 0 ? AvailL : AvailLChroma, | plane == 0 ? AvailU : AvailUChroma, | BlockDecoded[ plane ] | [ ( subBlockMiRow >> subY ) - 1 ] | [ ( subBlockMiCol >> subX ) + num4x4W ], | BlockDecoded[ plane ] | [ ( subBlockMiRow >> subY ) + num4x4H ] | [ ( subBlockMiCol >> subX ) - 1 ], | mode, | log2W, log2H ) | } | if ( is_inter ) { | predW = Block_Width[ MiSize ] >> subX | predH = Block_Height[ MiSize ] >> subY | someUseIntra = 0 | for ( r = 0; r < (num4x4H << subY); r++ ) | for ( c = 0; c < (num4x4W << subX); c++ ) | if ( RefFrames[ candRow + r ][ candCol + c ][ 0 ] == INTRA_FRAME ) | someUseIntra = 1 | if ( someUseIntra ) { | predW = num4x4W * 4 | predH = num4x4H * 4 | candRow = MiRow | candCol = MiCol | } | r = 0 | for ( y = 0; y < num4x4H * 4; y += predH ) { | c = 0 | for ( x = 0; x < num4x4W * 4; x += predW ) { | predict_inter( plane, baseX + x, baseY + y, | predW, predH, | candRow + r, candCol + c) | c++ | } | r++ | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | residual( ) { | Type | sbMask = use_128x128_superblock ? 31 : 15 | | widthChunks = Max( 1, Block_Width[ MiSize ] >> 6 ) | heightChunks = Max( 1, Block_Height[ MiSize ] >> 6 ) | | miSizeChunk = ( widthChunks > 1 || heightChunks > 1 ) ? BLOCK_64X64 : MiSize | | for ( chunkY = 0; chunkY < heightChunks; chunkY++ ) { | for ( chunkX = 0; chunkX < widthChunks; chunkX++ ) { | miRowChunk = MiRow + ( chunkY << 4 ) | miColChunk = MiCol + ( chunkX << 4 ) | subBlockMiRow = miRowChunk & sbMask | subBlockMiCol = miColChunk & sbMask | | for ( plane = 0; plane < 1 + HasChroma * 2; plane++ ) { | txSz = Lossless ? TX_4X4 : get_tx_size( plane, TxSize ) | stepX = Tx_Width[ txSz ] >> 2 | stepY = Tx_Height[ txSz ] >> 2 | planeSz = get_plane_residual_size( miSizeChunk, plane ) | num4x4W = Num_4x4_Blocks_Wide[ planeSz ] | num4x4H = Num_4x4_Blocks_High[ planeSz ] | subX = (plane > 0) ? subsampling_x : 0 | subY = (plane > 0) ? subsampling_y : 0 | baseX = (miColChunk >> subX) * MI_SIZE | baseY = (miRowChunk >> subY) * MI_SIZE | if ( is_inter && !Lossless && !plane ) { | transform_tree( baseX, baseY, num4x4W * 4, num4x4H * 4 ) | } else { | baseXBlock = (MiCol >> subX) * MI_SIZE | baseYBlock = (MiRow >> subY) * MI_SIZE | for ( y = 0; y < num4x4H; y += stepY ) | for ( x = 0; x < num4x4W; x += stepX ) | transform_block( plane, baseXBlock, baseYBlock, txSz, | x + ( ( chunkX << 4 ) >> subX ), | y + ( ( chunkY << 4 ) >> subY ) ) | } | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | transform_block(plane, baseX, baseY, txSz, x, y) { | Type | startX = baseX + 4 * x | startY = baseY + 4 * y | subX = (plane > 0) ? subsampling_x : 0 | subY = (plane > 0) ? subsampling_y : 0 | row = ( startY << subY ) >> MI_SIZE_LOG2 | col = ( startX << subX ) >> MI_SIZE_LOG2 | sbMask = use_128x128_superblock ? 31 : 15 | subBlockMiRow = row & sbMask | subBlockMiCol = col & sbMask | stepX = Tx_Width[ txSz ] >> MI_SIZE_LOG2 | stepY = Tx_Height[ txSz ] >> MI_SIZE_LOG2 | maxX = (MiCols * MI_SIZE) >> subX | maxY = (MiRows * MI_SIZE) >> subY | if ( startX >= maxX || startY >= maxY ) { | return | } | if ( !is_inter ) { | if ( ( ( plane == 0 ) && PaletteSizeY ) || | ( ( plane != 0 ) && PaletteSizeUV ) ) { | predict_palette( plane, startX, startY, x, y, txSz ) | } else { | isCfl = (plane > 0 && UVMode == UV_CFL_PRED) | if ( plane == 0 ) { | mode = YMode | } else { | mode = ( isCfl ) ? DC_PRED : UVMode | } | log2W = Tx_Width_Log2[ txSz ] | log2H = Tx_Height_Log2[ txSz ] | predict_intra( plane, startX, startY, | ( plane == 0 ? AvailL : AvailLChroma ) || x > 0, | ( plane == 0 ? AvailU : AvailUChroma ) || y > 0, | BlockDecoded[ plane ] | [ ( subBlockMiRow >> subY ) - 1 ] | [ ( subBlockMiCol >> subX ) + stepX ], | BlockDecoded[ plane ] | [ ( subBlockMiRow >> subY ) + stepY ] | [ ( subBlockMiCol >> subX ) - 1 ], | mode, | log2W, log2H ) | if ( isCfl ) { | predict_chroma_from_luma( plane, startX, startY, txSz ) | } | } | | if ( plane == 0 ) { | MaxLumaW = startX + stepX * 4 | MaxLumaH = startY + stepY * 4 | } | } | if ( !skip ) { | eob = coeffs( plane, startX, startY, txSz ) | if ( eob > 0 ) | reconstruct( plane, startX, startY, txSz ) | } | for ( i = 0; i < stepY; i++ ) { | for ( j = 0; j < stepX; j++ ) { | LoopfilterTxSizes[ plane ] | [ (row >> subY) + i ] | [ (col >> subX) + j ] = txSz | BlockDecoded[ plane ] | [ ( subBlockMiRow >> subY ) + i ] | [ ( subBlockMiCol >> subX ) + j ] = 1 | } | } | } {:.syntax }
transform_tree is used to read a number of transform blocks arranged in a transform tree.
| --------------------------------------------------------- | ---------------- | | transform_tree( startX, startY, w, h ) { | Type | maxX = MiCols * MI_SIZE | maxY = MiRows * MI_SIZE | if ( startX >= maxX || startY >= maxY ) { | return | } | row = startY >> MI_SIZE_LOG2 | col = startX >> MI_SIZE_LOG2 | lumaTxSz = InterTxSizes[ row ][ col ] | lumaW = Tx_Width[ lumaTxSz ] | lumaH = Tx_Height[ lumaTxSz ] | if ( w <= lumaW && h <= lumaH ) { | txSz = find_tx_size( w, h ) | transform_block( 0, startX, startY, txSz, 0, 0 ) | } else { | if ( w > h ) { | transform_tree( startX, startY, w/2, h ) | transform_tree( startX + w / 2, startY, w/2, h ) | } else if ( w < h ) { | transform_tree( startX, startY, w, h/2 ) | transform_tree( startX, startY + h/2, w, h/2 ) | } else { | transform_tree( startX, startY, w/2, h/2 ) | transform_tree( startX + w/2, startY, w/2, h/2 ) | transform_tree( startX, startY + h/2, w/2, h/2 ) | transform_tree( startX + w/2, startY + h/2, w/2, h/2 ) | } | } | } {:.syntax }
where find_tx_size finds the transform size matching the given dimensions and is defined as:
find_tx_size( w, h ) {
for ( txSz = 0; txSz < TX_SIZES_ALL; txSz++ )
if ( Tx_Width[ txSz ] == w && Tx_Height[ txSz ] == h )
break
return txSz
}
| --------------------------------------------------------- | ---------------- | | get_tx_size( plane, txSz ) { | Type | if ( plane == 0 ) | return txSz | uvTx = Max_Tx_Size_Rect[ get_plane_residual_size( MiSize, plane ) ] | if ( Tx_Width[ uvTx ] == 64 || Tx_Height[ uvTx ] == 64 ){ | if ( Tx_Width[ uvTx ] == 16 ) { | return TX_16X32 | } | if ( Tx_Height[ uvTx ] == 16 ) { | return TX_32X16 | } | return TX_32X32 | } | return uvTx | } {:.syntax }
The get_plane_residual_size returns the size of a residual block for the specified plane. (The residual block will always have width and height at least equal to 4.)
| --------------------------------------------------------- | ---------------- | | get_plane_residual_size( subsize, plane ) { | Type | subx = plane > 0 ? subsampling_x : 0 | suby = plane > 0 ? subsampling_y : 0 | return Subsampled_Size[ subsize ][ subx ][ suby ] | } {:.syntax }
The Subsampled_Size table is defined as:
Subsampled_Size[ BLOCK_SIZES ][ 2 ][ 2 ] = {
{ { BLOCK_4X4, BLOCK_4X4}, {BLOCK_4X4, BLOCK_4X4} },
{ { BLOCK_4X8, BLOCK_4X4}, {BLOCK_INVALID, BLOCK_4X4} },
{ { BLOCK_8X4, BLOCK_INVALID}, {BLOCK_4X4, BLOCK_4X4} },
{ { BLOCK_8X8, BLOCK_8X4}, {BLOCK_4X8, BLOCK_4X4} },
{ {BLOCK_8X16, BLOCK_8X8}, {BLOCK_INVALID, BLOCK_4X8} },
{ {BLOCK_16X8, BLOCK_INVALID}, {BLOCK_8X8, BLOCK_8X4} },
{ {BLOCK_16X16, BLOCK_16X8}, {BLOCK_8X16, BLOCK_8X8} },
{ {BLOCK_16X32, BLOCK_16X16}, {BLOCK_INVALID, BLOCK_8X16} },
{ {BLOCK_32X16, BLOCK_INVALID}, {BLOCK_16X16, BLOCK_16X8} },
{ {BLOCK_32X32, BLOCK_32X16}, {BLOCK_16X32, BLOCK_16X16} },
{ {BLOCK_32X64, BLOCK_32X32}, {BLOCK_INVALID, BLOCK_16X32} },
{ {BLOCK_64X32, BLOCK_INVALID}, {BLOCK_32X32, BLOCK_32X16} },
{ {BLOCK_64X64, BLOCK_64X32}, {BLOCK_32X64, BLOCK_32X32} },
{ {BLOCK_64X128, BLOCK_64X64}, {BLOCK_INVALID, BLOCK_32X64} },
{ {BLOCK_128X64, BLOCK_INVALID}, {BLOCK_64X64, BLOCK_64X32} },
{ {BLOCK_128X128, BLOCK_128X64}, {BLOCK_64X128, BLOCK_64X64} },
{ {BLOCK_4X16, BLOCK_4X8}, {BLOCK_INVALID, BLOCK_4X8} },
{ {BLOCK_16X4, BLOCK_INVALID}, {BLOCK_8X4, BLOCK_8X4} },
{ {BLOCK_8X32, BLOCK_8X16}, {BLOCK_INVALID, BLOCK_4X16} },
{ {BLOCK_32X8, BLOCK_INVALID}, {BLOCK_16X8, BLOCK_16X4} },
{ {BLOCK_16X64, BLOCK_16X32}, {BLOCK_INVALID, BLOCK_8X32} },
{ {BLOCK_64X16, BLOCK_INVALID}, {BLOCK_32X16, BLOCK_32X8} },
}
| --------------------------------------------------------- | ---------------- | | coeffs( plane, startX, startY, txSz ) { | Type | x4 = startX >> 2 | y4 = startY >> 2 | w4 = Tx_Width[ txSz ] >> 2 | h4 = Tx_Height[ txSz ] >> 2 | | txSzCtx = ( Tx_Size_Sqr[txSz] + Tx_Size_Sqr_Up[txSz] + 1 ) >> 1 | ptype = plane > 0 | segEob = ( txSz == TX_16X64 || txSz == TX_64X16 ) ? 512 : | Min( 1024, Tx_Width[ txSz ] * Tx_Height[ txSz ] ) | | for ( c = 0; c < segEob; c++ ) | Quant[c] = 0 | for ( i = 0; i < 64; i++ ) | for ( j = 0; j < 64; j++ ) | Dequant[ i ][ j ] = 0 | | eob = 0 | culLevel = 0 | dcCategory = 0 | | @@all_zero | S() | if ( all_zero ) { | c = 0 | if ( plane == 0 ) { | for ( i = 0; i < w4; i++ ) { | for ( j = 0; j < h4; j++ ) { | TxTypes[ y4 + j ][ x4 + i ] = DCT_DCT | } | } | } | } else { | if ( plane == 0 ) | transform_type( x4, y4, txSz ) | PlaneTxType = compute_tx_type( plane, txSz, x4, y4 ) | scan = get_scan( txSz ) | | eobMultisize = Min( Tx_Width_Log2[ txSz ], 5) + Min( Tx_Height_Log2[ txSz ], 5) - 4 | if ( eobMultisize == 0 ) { | @@eob_pt_16 | S() | eobPt = eob_pt_16 + 1 | } else if ( eobMultisize == 1 ) { | @@eob_pt_32 | S() | eobPt = eob_pt_32 + 1 | } else if ( eobMultisize == 2 ) { | @@eob_pt_64 | S() | eobPt = eob_pt_64 + 1 | } else if ( eobMultisize == 3 ) { | @@eob_pt_128 | S() | eobPt = eob_pt_128 + 1 | } else if ( eobMultisize == 4 ) { | @@eob_pt_256 | S() | eobPt = eob_pt_256 + 1 | } else if ( eobMultisize == 5 ) { | @@eob_pt_512 | S() | eobPt = eob_pt_512 + 1 | } else { | @@eob_pt_1024 | S() | eobPt = eob_pt_1024 + 1 | } | | eob = ( eobPt < 2 ) ? eobPt : ( ( 1 << ( eobPt - 2 ) ) + 1 ) | eobShift = Max( -1, eobPt - 3 ) | if ( eobShift >= 0 ) { | @@eob_extra | S() | if ( eob_extra ) { | eob += ( 1 << eobShift ) | } | | for ( i = 1; i < Max( 0, eobPt - 2 ); i++ ) { | eobShift = Max( 0, eobPt - 2 ) - 1 - i | @@eob_extra_bit | L(1) | if ( eob_extra_bit ) { | eob += ( 1 << eobShift ) | } | } | } | for ( c = eob - 1; c >= 0; c-- ) { | pos = scan[ c ] | if ( c == ( eob - 1 ) ) { | @@coeff_base_eob | S() | level = coeff_base_eob + 1 | } else { | @@coeff_base | S() | level = coeff_base | } | | if ( level > NUM_BASE_LEVELS ) { | for ( idx = 0; | idx < COEFF_BASE_RANGE / ( BR_CDF_SIZE - 1 ); | idx++ ) { | @@coeff_br | S() | level += coeff_br | if ( coeff_br < ( BR_CDF_SIZE - 1 ) ) | break | } | } | Quant[ pos ] = level | } | | for ( c = 0; c < eob; c++ ) { | pos = scan[ c ] | if ( Quant[ pos ] != 0 ) { | if ( c == 0 ) { | @@dc_sign | S() | sign = dc_sign | } else { | @@sign_bit | L(1) | sign = sign_bit | } | } else { | sign = 0 | } | if ( Quant[ pos ] > | ( NUM_BASE_LEVELS + COEFF_BASE_RANGE ) ) { | length = 0 | do { | length++ | @@golomb_length_bit | L(1) | } while ( !golomb_length_bit ) | x = 1 | for ( i = length - 2; i >= 0; i-- ) { | @@golomb_data_bit | L(1) | x = ( x << 1 ) | golomb_data_bit | } | Quant[ pos ] = x + COEFF_BASE_RANGE + NUM_BASE_LEVELS | } | if ( pos == 0 && Quant[ pos ] > 0 ) { | dcCategory = sign ? 1 : 2 | } | Quant[ pos ] = Quant[ pos ] & 0xFFFFF | culLevel += Quant[ pos ] | if ( sign ) | Quant[ pos ] = - Quant[ pos ] | } | culLevel = Min( 63, culLevel ) | } | | for ( i = 0; i < w4; i++ ) { | AboveLevelContext[ plane ][ x4 + i ] = culLevel | AboveDcContext[ plane ][ x4 + i ] = dcCategory | } | for ( i = 0; i < h4; i++ ) { | LeftLevelContext[ plane ][ y4 + i ] = culLevel | LeftDcContext[ plane ][ y4 + i ] = dcCategory | } | | return eob | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | compute_tx_type( plane, txSz, blockX, blockY ) { | Type | txSzSqrUp = Tx_Size_Sqr_Up[ txSz ] | | if ( Lossless || txSzSqrUp > TX_32X32 ) | return DCT_DCT | | txSet = get_tx_set( txSz ) | | if ( plane == 0 ) { | return TxTypes[ blockY ][ blockX ] | } | | if ( is_inter ) { | x4 = Max( MiCol, blockX << subsampling_x ) | y4 = Max( MiRow, blockY << subsampling_y ) | txType = TxTypes[ y4 ][ x4 ] | if ( !is_tx_type_in_set( txSet, txType ) ) | return DCT_DCT | return txType | } | | txType = Mode_To_Txfm[ UVMode ] | if ( !is_tx_type_in_set( txSet, txType ) ) | return DCT_DCT | return txType | } | | is_tx_type_in_set( txSet, txType ) { | return is_inter ? Tx_Type_In_Set_Inter[ txSet ][ txType ] : | Tx_Type_In_Set_Intra[ txSet ][ txType ] | } {:.syntax }
where the tables Tx_Type_In_Set_Inter and Tx_Type_In_Set_Intra are specified as follows:
Tx_Type_In_Set_Intra[ TX_SET_TYPES_INTRA ][ TX_TYPES ] = {
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
},
{
1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
},
{
1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
}
}
Tx_Type_In_Set_Inter[ TX_SET_TYPES_INTER ][ TX_TYPES ] = {
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
},
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
},
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
},
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
}
}
| --------------------------------------------------------- | ---------------- | | get_mrow_scan( txSz ) { | Type | if ( txSz == TX_4X4 ) | return Mrow_Scan_4x4 | else if ( txSz == TX_4X8 ) | return Mrow_Scan_4x8 | else if ( txSz == TX_8X4 ) | return Mrow_Scan_8x4 | else if ( txSz == TX_8X8 ) | return Mrow_Scan_8x8 | else if ( txSz == TX_8X16 ) | return Mrow_Scan_8x16 | else if ( txSz == TX_16X8 ) | return Mrow_Scan_16x8 | else if ( txSz == TX_16X16 ) | return Mrow_Scan_16x16 | else if ( txSz == TX_4X16 ) | return Mrow_Scan_4x16 | return Mrow_Scan_16x4 | } | | get_mcol_scan( txSz ) { | if ( txSz == TX_4X4 ) | return Mcol_Scan_4x4 | else if ( txSz == TX_4X8 ) | return Mcol_Scan_4x8 | else if ( txSz == TX_8X4 ) | return Mcol_Scan_8x4 | else if ( txSz == TX_8X8 ) | return Mcol_Scan_8x8 | else if ( txSz == TX_8X16 ) | return Mcol_Scan_8x16 | else if ( txSz == TX_16X8 ) | return Mcol_Scan_16x8 | else if ( txSz == TX_16X16 ) | return Mcol_Scan_16x16 | else if ( txSz == TX_4X16 ) | return Mcol_Scan_4x16 | return Mcol_Scan_16x4 | } | | get_default_scan( txSz ) { | if ( txSz == TX_4X4 ) | return Default_Scan_4x4 | else if ( txSz == TX_4X8 ) | return Default_Scan_4x8 | else if ( txSz == TX_8X4 ) | return Default_Scan_8x4 | else if ( txSz == TX_8X8 ) | return Default_Scan_8x8 | else if ( txSz == TX_8X16 ) | return Default_Scan_8x16 | else if ( txSz == TX_16X8 ) | return Default_Scan_16x8 | else if ( txSz == TX_16X16 ) | return Default_Scan_16x16 | else if ( txSz == TX_16X32 ) | return Default_Scan_16x32 | else if ( txSz == TX_32X16 ) | return Default_Scan_32x16 | else if ( txSz == TX_4X16 ) | return Default_Scan_4x16 | else if ( txSz == TX_16X4 ) | return Default_Scan_16x4 | else if ( txSz == TX_8X32 ) | return Default_Scan_8x32 | else if ( txSz == TX_32X8 ) | return Default_Scan_32x8 | return Default_Scan_32x32 | } | | get_scan( txSz ) { | if ( txSz == TX_16X64 ) { | return Default_Scan_16x32 | } | if ( txSz == TX_64X16 ) { | return Default_Scan_32x16 | } | if ( Tx_Size_Sqr_Up[ txSz ] == TX_64X64 ) { | return Default_Scan_32x32 | } | | if ( PlaneTxType == IDTX ) { | return get_default_scan( txSz ) | } | | preferRow = ( PlaneTxType == V_DCT || | PlaneTxType == V_ADST || | PlaneTxType == V_FLIPADST ) | | preferCol = ( PlaneTxType == H_DCT || | PlaneTxType == H_ADST || | PlaneTxType == H_FLIPADST ) | | if ( preferRow ) { | return get_mrow_scan( txSz ) | } else if ( preferCol ) { | return get_mcol_scan( txSz ) | } | return get_default_scan( txSz ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | intra_angle_info_y( ) { | Type | AngleDeltaY = 0 | if ( MiSize >= BLOCK_8X8 ) { | if ( is_directional_mode( YMode ) ) { | @@angle_delta_y | S() | AngleDeltaY = angle_delta_y - MAX_ANGLE_DELTA | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | intra_angle_info_uv( ) { | Type | AngleDeltaUV = 0 | if ( MiSize >= BLOCK_8X8 ) { | if ( is_directional_mode( UVMode ) ) { | @@angle_delta_uv | S() | AngleDeltaUV = angle_delta_uv - MAX_ANGLE_DELTA | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | is_directional_mode( mode ) { | Type | if ( ( mode >= V_PRED ) && ( mode <= D67_PRED ) ) { | return 1 | } | return 0 | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_cfl_alphas() { | Type | @@cfl_alpha_signs | S() | signU = (cfl_alpha_signs + 1 ) / 3 | signV = (cfl_alpha_signs + 1 ) % 3 | if ( signU != CFL_SIGN_ZERO ) { | @@cfl_alpha_u | S() | CflAlphaU = 1 + cfl_alpha_u | if ( signU == CFL_SIGN_NEG ) | CflAlphaU = -CflAlphaU | } else { | CflAlphaU = 0 | } | if ( signV != CFL_SIGN_ZERO ) { | @@cfl_alpha_v | S() | CflAlphaV = 1 + cfl_alpha_v | if ( signV == CFL_SIGN_NEG ) | CflAlphaV = -CflAlphaV | } else { | CflAlphaV = 0 | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | palette_mode_info( ) { | Type | bsizeCtx = Mi_Width_Log2[ MiSize ] + Mi_Height_Log2[ MiSize ] - 2 | if ( YMode == DC_PRED ) { | @@has_palette_y | S() | if ( has_palette_y ) { | @@palette_size_y_minus_2 | S() | PaletteSizeY = palette_size_y_minus_2 + 2 | cacheN = get_palette_cache( 0 ) | idx = 0 | for ( i = 0; i < cacheN && idx < PaletteSizeY; i++ ) { | @@use_palette_color_cache_y | L(1) | if ( use_palette_color_cache_y ) { | palette_colors_y[ idx ] = PaletteCache[ i ] | idx++ | } | } | if ( idx < PaletteSizeY ) { | @@palette_colors_y[ idx ] | L(BitDepth) | idx++ | } | if ( idx < PaletteSizeY ) { | minBits = BitDepth - 3 | @@palette_num_extra_bits_y | L(2) | paletteBits = minBits + palette_num_extra_bits_y | } | while ( idx < PaletteSizeY ) { | @@palette_delta_y | L(paletteBits) | palette_delta_y++ | palette_colors_y[ idx ] = | Clip1( palette_colors_y[ idx - 1 ] + | palette_delta_y ) | range = ( 1 << BitDepth ) - palette_colors_y[ idx ] - 1 | paletteBits = Min( paletteBits, CeilLog2( range ) ) | idx++ | } | sort( palette_colors_y, 0, PaletteSizeY - 1 ) | } | } | if ( HasChroma && UVMode == DC_PRED ) { | @@has_palette_uv | S() | if ( has_palette_uv ) { | @@palette_size_uv_minus_2 | S() | PaletteSizeUV = palette_size_uv_minus_2 + 2 | cacheN = get_palette_cache( 1 ) | idx = 0 | for ( i = 0; i < cacheN && idx < PaletteSizeUV; i++ ) { | @@use_palette_color_cache_u | L(1) | if ( use_palette_color_cache_u ) { | palette_colors_u[ idx ] = PaletteCache[ i ] | idx++ | } | } | if ( idx < PaletteSizeUV ) { | @@palette_colors_u[ idx ] | L(BitDepth) | idx++ | } | if ( idx < PaletteSizeUV ) { | minBits = BitDepth - 3 | @@palette_num_extra_bits_u | L(2) | paletteBits = minBits + palette_num_extra_bits_u | } | while ( idx < PaletteSizeUV ) { | @@palette_delta_u | L(paletteBits) | palette_colors_u[ idx ] = | Clip1( palette_colors_u[ idx - 1 ] + | palette_delta_u ) | range = ( 1 << BitDepth ) - palette_colors_u[ idx ] | paletteBits = Min( paletteBits, CeilLog2( range ) ) | idx++ | } | sort( palette_colors_u, 0, PaletteSizeUV - 1 ) | | @@delta_encode_palette_colors_v | L(1) | if ( delta_encode_palette_colors_v ) { | minBits = BitDepth - 4 | maxVal = 1 << BitDepth | @@palette_num_extra_bits_v | L(2) | paletteBits = minBits + palette_num_extra_bits_v | @@palette_colors_v[ 0 ] | L(BitDepth) | for ( idx = 1; idx < PaletteSizeUV; idx++ ) { | @@palette_delta_v | L(paletteBits) | if ( palette_delta_v ) { | @@palette_delta_sign_bit_v | L(1) | if ( palette_delta_sign_bit_v ) { | palette_delta_v = -palette_delta_v | } | } | val = palette_colors_v[ idx - 1 ] + palette_delta_v | if ( val < 0 ) val += maxVal | if ( val >= maxVal ) val -= maxVal | palette_colors_v[ idx ] = Clip1( val ) | } | } else { | for ( idx = 0; idx < PaletteSizeUV; idx++ ) { | @@palette_colors_v[ idx ] | L(BitDepth) | } | } | } | } | } {:.syntax }
The function sort( arr, i1, i2 ) sorts a subarray of the array arr in-place into ascending order. The subarray to be sorted is between indices i1 and i2 inclusive.
Note: The palette colors are generated in ascending order. The palette cache is also in ascending order. This means that the sort function can be replaced in implementations by a merge of two sorted lists. {:.alert .alert-info }
where the function get_palette_cache, which merges the above and left palettes to form a cache, is specified as follows:
| --------------------------------------------------------- | ---------------- | | get_palette_cache( plane ) { | Type | aboveN = 0 | if ( ( MiRow * MI_SIZE ) % 64 ) { | aboveN = PaletteSizes[ plane ][ MiRow - 1 ][ MiCol ] | } | leftN = 0 | if ( AvailL ) { | leftN = PaletteSizes[ plane ][ MiRow ][ MiCol - 1 ] | } | aboveIdx = 0 | leftIdx = 0 | n = 0 | while ( aboveIdx < aboveN && leftIdx < leftN ) { | aboveC = PaletteColors[ plane ][ MiRow - 1 ][ MiCol ][ aboveIdx ] | leftC = PaletteColors[ plane ][ MiRow ][ MiCol - 1 ][ leftIdx ] | if ( leftC < aboveC ) { | if ( n == 0 || leftC != PaletteCache[ n - 1 ] ) { | PaletteCache[ n ] = leftC | n++ | } | leftIdx++ | } else { | if ( n == 0 || aboveC != PaletteCache[ n - 1 ] ) { | PaletteCache[ n ] = aboveC | n++ | } | aboveIdx++ | if ( leftC == aboveC ) { | leftIdx++ | } | } | } | while ( aboveIdx < aboveN ) { | val = PaletteColors[ plane ][ MiRow - 1 ][ MiCol ][ aboveIdx ] | aboveIdx++ | if ( n == 0 || val != PaletteCache[ n - 1 ] ) { | PaletteCache[ n ] = val | n++ | } | } | while ( leftIdx < leftN ) { | val = PaletteColors[ plane ][ MiRow ][ MiCol - 1 ][ leftIdx ] | leftIdx++ | if ( n == 0 || val != PaletteCache[ n - 1 ] ) { | PaletteCache[ n ] = val | n++ | } | } | return n | } {:.syntax }
Note: get_palette_cache is equivalent to sorting the available palette colors from above and left together and removing any duplicates. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | transform_type( x4, y4, txSz ) { | Type | set = get_tx_set( txSz ) | | if ( set > 0 && | ( segmentation_enabled ? get_qindex( 1, segment_id ) : base_q_idx ) > 0 ) { | if ( is_inter ) { | @@inter_tx_type | S() | if ( set == TX_SET_INTER_1 ) | TxType = Tx_Type_Inter_Inv_Set1[ inter_tx_type ] | else if ( set == TX_SET_INTER_2 ) | TxType = Tx_Type_Inter_Inv_Set2[ inter_tx_type ] | else | TxType = Tx_Type_Inter_Inv_Set3[ inter_tx_type ] | } else { | @@intra_tx_type | S() | if ( set == TX_SET_INTRA_1 ) | TxType = Tx_Type_Intra_Inv_Set1[ intra_tx_type ] | else | TxType = Tx_Type_Intra_Inv_Set2[ intra_tx_type ] | } | } else { | TxType = DCT_DCT | } | for ( i = 0; i < ( Tx_Width[ txSz ] >> 2 ); i++ ) { | for ( j = 0; j < ( Tx_Height[ txSz ] >> 2 ); j++ ) { | TxTypes[ y4 + j ][ x4 + i ] = TxType | } | } | } {:.syntax }
where the inversion tables used in the function are specified as follows:
Tx_Type_Intra_Inv_Set1[ 7 ] = { IDTX, DCT_DCT, V_DCT, H_DCT, ADST_ADST, ADST_DCT, DCT_ADST }
Tx_Type_Intra_Inv_Set2[ 5 ] = { IDTX, DCT_DCT, ADST_ADST, ADST_DCT, DCT_ADST }
Tx_Type_Inter_Inv_Set1[ 16 ] = { IDTX, V_DCT, H_DCT, V_ADST, H_ADST, V_FLIPADST, H_FLIPADST,
DCT_DCT, ADST_DCT, DCT_ADST, FLIPADST_DCT, DCT_FLIPADST, ADST_ADST,
FLIPADST_FLIPADST, ADST_FLIPADST, FLIPADST_ADST }
Tx_Type_Inter_Inv_Set2[ 12 ] = { IDTX, V_DCT, H_DCT, DCT_DCT, ADST_DCT, DCT_ADST, FLIPADST_DCT,
DCT_FLIPADST, ADST_ADST, FLIPADST_FLIPADST, ADST_FLIPADST,
FLIPADST_ADST }
Tx_Type_Inter_Inv_Set3[ 2 ] = { IDTX, DCT_DCT }
| --------------------------------------------------------- | ---------------- | | get_tx_set( txSz ) { | Type | txSzSqr = Tx_Size_Sqr[ txSz ] | txSzSqrUp = Tx_Size_Sqr_Up[ txSz ] | if ( txSzSqrUp > TX_32X32 ) | return TX_SET_DCTONLY | if ( is_inter ) { | if ( reduced_tx_set || txSzSqrUp == TX_32X32 ) return TX_SET_INTER_3 | else if ( txSzSqr == TX_16X16 ) return TX_SET_INTER_2 | return TX_SET_INTER_1 | } else { | if ( txSzSqrUp == TX_32X32 ) return TX_SET_DCTONLY | else if ( reduced_tx_set ) return TX_SET_INTRA_2 | else if ( txSzSqr == TX_16X16 ) return TX_SET_INTRA_2 | return TX_SET_INTRA_1 | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | palette_tokens( ) { | Type | blockHeight = Block_Height[ MiSize ] | blockWidth = Block_Width[ MiSize ] | onscreenHeight = Min( blockHeight, (MiRows - MiRow) * MI_SIZE ) | onscreenWidth = Min( blockWidth, (MiCols - MiCol) * MI_SIZE ) | | if ( PaletteSizeY ) { | @@color_index_map_y | NS(PaletteSizeY) | ColorMapY[0][0] = color_index_map_y | for ( i = 1; i < onscreenHeight + onscreenWidth - 1; i++ ) { | for ( j = Min( i, onscreenWidth - 1 ); | j >= Max( 0, i - onscreenHeight + 1 ); j-- ) { | get_palette_color_context( | ColorMapY, ( i - j ), j, PaletteSizeY ) | @@palette_color_idx_y | S() | ColorMapY[ i - j ][ j ] = ColorOrder[ palette_color_idx_y ] | } | } | for ( i = 0; i < onscreenHeight; i++ ) { | for ( j = onscreenWidth; j < blockWidth; j++ ) { | ColorMapY[ i ][ j ] = ColorMapY[ i ][ onscreenWidth - 1 ] | } | } | for ( i = onscreenHeight; i < blockHeight; i++ ) { | for ( j = 0; j < blockWidth; j++ ) { | ColorMapY[ i ][ j ] = ColorMapY[ onscreenHeight - 1 ][ j ] | } | } | } | | if ( PaletteSizeUV ) { | @@color_index_map_uv | NS(PaletteSizeUV) | ColorMapUV[0][0] = color_index_map_uv | blockHeight = blockHeight >> subsampling_y | blockWidth = blockWidth >> subsampling_x | onscreenHeight = onscreenHeight >> subsampling_y | onscreenWidth = onscreenWidth >> subsampling_x | if ( blockWidth < 4 ) { | blockWidth += 2 | onscreenWidth += 2 | } | if ( blockHeight < 4 ) { | blockHeight += 2 | onscreenHeight += 2 | } | | for ( i = 1; i < onscreenHeight + onscreenWidth - 1; i++ ) { | for ( j = Min( i, onscreenWidth - 1 ); | j >= Max( 0, i - onscreenHeight + 1 ); j-- ) { | get_palette_color_context( | ColorMapUV, ( i - j ), j, PaletteSizeUV ) | @@palette_color_idx_uv | S() | ColorMapUV[ i - j ][ j ] = ColorOrder[ palette_color_idx_uv ] | } | } | for ( i = 0; i < onscreenHeight; i++ ) { | for ( j = onscreenWidth; j < blockWidth; j++ ) { | ColorMapUV[ i ][ j ] = ColorMapUV[ i ][ onscreenWidth - 1 ] | } | } | for ( i = onscreenHeight; i < blockHeight; i++ ) { | for ( j = 0; j < blockWidth; j++ ) { | ColorMapUV[ i ][ j ] = ColorMapUV[ onscreenHeight - 1 ][ j ] | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | get_palette_color_context( colorMap, r, c, n ) { | Type | for ( i = 0; i < PALETTE_COLORS; i++ ) { | scores[ i ] = 0 | ColorOrder[i] = i | } | if ( c > 0 ) { | neighbor = colorMap[ r ][ c - 1 ] | scores[ neighbor ] += 2 | } | if ( ( r > 0 ) && ( c > 0 ) ) { | neighbor = colorMap[ r - 1 ][ c - 1 ] | scores[ neighbor ] += 1 | } | if ( r > 0 ) { | neighbor = colorMap[ r - 1 ][ c ] | scores[ neighbor ] += 2 | } | for ( i = 0; i < PALETTE_NUM_NEIGHBORS; i++ ) { | maxScore = scores[ i ] | maxIdx = i | for ( j = i + 1; j < n; j++ ) { | if ( scores[ j ] > maxScore ) { | maxScore = scores[ j ] | maxIdx = j | } | } | if ( maxIdx != i ) { | maxScore = scores[ maxIdx ] | maxColorOrder = ColorOrder[ maxIdx ] | for ( k = maxIdx; k > i; k-- ) { | scores[ k ] = scores[ k - 1 ] | ColorOrder[ k ] = ColorOrder[ k - 1 ] | } | scores[ i ] = maxScore | ColorOrder[ i ] = maxColorOrder | } | } | ColorContextHash = 0 | for ( i = 0; i < PALETTE_NUM_NEIGHBORS; i++ ) { | ColorContextHash += scores[ i ] * Palette_Color_Hash_Multipliers[ i ] | } | } {:.syntax }
is_inside determines whether a candidate position is inside the current tile.
| --------------------------------------------------------- | ---------------- | | is_inside( candidateR, candidateC ) { | Type | return ( candidateC >= MiColStart && | candidateC < MiColEnd && | candidateR >= MiRowStart && | candidateR < MiRowEnd ) | } {:.syntax }
is_inside_filter_region determines whether a candidate position is inside the region that is being used for CDEF filtering.
| --------------------------------------------------------- | ---------------- | | is_inside_filter_region( candidateR, candidateC ) { | Type | colStart = 0 | colEnd = MiCols | rowStart = 0 | rowEnd = MiRows | return (candidateC >= colStart && | candidateC < colEnd && | candidateR >= rowStart && | candidateR < rowEnd) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | clamp_mv_row( mvec, border ) { | Type | bh4 = Num_4x4_Blocks_High[ MiSize ] | mbToTopEdge = -((MiRow * MI_SIZE) * 8) | mbToBottomEdge = ((MiRows - bh4 - MiRow) * MI_SIZE) * 8 | return Clip3( mbToTopEdge - border, mbToBottomEdge + border, mvec ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | clamp_mv_col( mvec, border ) { | Type | bw4 = Num_4x4_Blocks_Wide[ MiSize ] | mbToLeftEdge = -((MiCol * MI_SIZE) * 8) | mbToRightEdge = ((MiCols - bw4 - MiCol) * MI_SIZE) * 8 | return Clip3( mbToLeftEdge - border, mbToRightEdge + border, mvec ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | clear_cdef( r, c ) { | Type | cdef_idx[ r ][ c ] = -1 | if ( use_128x128_superblock ) { | cdefSize4 = Num_4x4_Blocks_Wide[ BLOCK_64X64 ] | cdef_idx[ r ][ c + cdefSize4 ] = -1 | cdef_idx[ r + cdefSize4][ c ] = -1 | cdef_idx[ r + cdefSize4][ c + cdefSize4 ] = -1 | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_cdef( ) { | Type | if ( skip || CodedLossless || !enable_cdef || allow_intrabc) { | return | } | cdefSize4 = Num_4x4_Blocks_Wide[ BLOCK_64X64 ] | cdefMask4 = ~(cdefSize4 - 1) | r = MiRow & cdefMask4 | c = MiCol & cdefMask4 | if ( cdef_idx[ r ][ c ] == -1 ) { | @@cdef_idx[ r ][ c ] | L(cdef_bits) | w4 = Num_4x4_Blocks_Wide[ MiSize ] | h4 = Num_4x4_Blocks_High[ MiSize ] | for ( i = r; i < r + h4 ; i += cdefSize4 ) { | for ( j = c; j < c + w4 ; j += cdefSize4 ) { | cdef_idx[ i ][ j ] = cdef_idx[ r ][ c ] | } | } | } | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | read_lr( r, c, bSize ) { | Type | if ( allow_intrabc ) { | return | } | w = Num_4x4_Blocks_Wide[ bSize ] | h = Num_4x4_Blocks_High[ bSize ] | for ( plane = 0; plane < NumPlanes; plane++ ) { | if ( FrameRestorationType[ plane ] != RESTORE_NONE ) { | subX = (plane == 0) ? 0 : subsampling_x | subY = (plane == 0) ? 0 : subsampling_y | unitSize = LoopRestorationSize[ plane ] | unitRows = count_units_in_frame( unitSize, Round2( FrameHeight, subY) ) | unitCols = count_units_in_frame( unitSize, Round2( UpscaledWidth, subX) ) | unitRowStart = ( r * ( MI_SIZE >> subY) + | unitSize - 1 ) / unitSize | unitRowEnd = Min( unitRows, ( (r + h) * ( MI_SIZE >> subY) + | unitSize - 1 ) / unitSize) | if ( use_superres ) { | numerator = (MI_SIZE >> subX) * SuperresDenom | denominator = unitSize * SUPERRES_NUM | } else { | numerator = MI_SIZE >> subX | denominator = unitSize | } | unitColStart = ( c * numerator + denominator - 1 ) / denominator | unitColEnd = Min( unitCols, ( (c + w) * numerator + | denominator - 1 ) / denominator) | for ( unitRow = unitRowStart; unitRow < unitRowEnd; unitRow++ ) { | for ( unitCol = unitColStart; unitCol < unitColEnd; unitCol++ ) { | read_lr_unit(plane, unitRow, unitCol) | } | } | } | } | } {:.syntax }
where count_units_in_frame is a function specified as:
count_units_in_frame(unitSize, frameSize) {
return Max((frameSize + (unitSize >> 1)) / unitSize, 1)
}
| --------------------------------------------------------- | ---------------- | | read_lr_unit(plane, unitRow, unitCol) { | Type | if ( FrameRestorationType[ plane ] == RESTORE_WIENER ) { | @@use_wiener | S() | restoration_type = use_wiener ? RESTORE_WIENER : RESTORE_NONE | } else if ( FrameRestorationType[ plane ] == RESTORE_SGRPROJ ) { | @@use_sgrproj | S() | restoration_type = use_sgrproj ? RESTORE_SGRPROJ : RESTORE_NONE | } else { | @@restoration_type | S() | } | LrType[ plane ][ unitRow ][ unitCol ] = restoration_type | if ( restoration_type == RESTORE_WIENER ) { | for ( pass = 0; pass < 2; pass++ ) { | if ( plane ) { | firstCoeff = 1 | LrWiener[ plane ] | [ unitRow ][ unitCol ][ pass ][0] = 0 | } else { | firstCoeff = 0 | } | for ( j = firstCoeff; j < 3; j++ ) { | min = Wiener_Taps_Min[ j ] | max = Wiener_Taps_Max[ j ] | k = Wiener_Taps_K[ j ] | v = decode_signed_subexp_with_ref_bool( | min, max + 1, k, RefLrWiener[ plane ][ pass ][ j ] ) | LrWiener[ plane ] | [ unitRow ][ unitCol ][ pass ][ j ] = v | RefLrWiener[ plane ][ pass ][ j ] = v | } | } | } else if ( restoration_type == RESTORE_SGRPROJ ) { | @@lr_sgr_set | L(SGRPROJ_PARAMS_BITS) | LrSgrSet[ plane ][ unitRow ][ unitCol ] = lr_sgr_set | for ( i = 0; i < 2; i++ ) { | radius = Sgr_Params[ lr_sgr_set ][ i * 2 ] | min = Sgrproj_Xqd_Min[i] | max = Sgrproj_Xqd_Max[i] | if ( radius ) { | v = decode_signed_subexp_with_ref_bool( | min, max + 1, SGRPROJ_PRJ_SUBEXP_K, | RefSgrXqd[ plane ][ i ]) | } else { | v = 0 | if ( i == 1 ) { | v = Clip3( min, max, (1 << SGRPROJ_PRJ_BITS) - | RefSgrXqd[ plane ][ 0 ] ) | } | } | LrSgrXqd[ plane ][ unitRow ][ unitCol ][ i ] = v | RefSgrXqd[ plane ][ i ] = v | } | } | } {:.syntax }
where Wiener_Taps_Min, Wiener_Taps_Max, ,Wiener_Taps_K, Sgrproj_Xqd_Min, and Sgrproj_Xqd_Max are constant lookup tables:
Wiener_Taps_Min[3] = { -5, -23, -17 }
Wiener_Taps_Max[3] = { 10, 8, 46 }
Wiener_Taps_K[3] = { 1, 2, 3 }
Sgrproj_Xqd_Min[2] = { -96, -32 }
Sgrproj_Xqd_Max[2] = { 31, 95 }
Sgr_Params is a constant lookup table defined in [section 7.17.3][] and decode_signed_subexp_with_ref_bool is a function specified as follows:
| --------------------------------------------------------- | ---------------- | | decode_signed_subexp_with_ref_bool( low, high, k, r ) { | Type | x = decode_unsigned_subexp_with_ref_bool(high - low, k, r - low) | return x + low | } | | decode_unsigned_subexp_with_ref_bool( mx, k, r ) { | v = decode_subexp_bool( mx, k ) | if ( (r << 1) <= mx ) { | return inverse_recenter(r, v) | } else { | return mx - 1 - inverse_recenter(mx - 1 - r, v) | } | } | | decode_subexp_bool( numSyms, k ) { | i = 0 | mk = 0 | while ( 1 ) { | b2 = i ? k + i - 1 : k | a = 1 << b2 | if ( numSyms <= mk + 3 * a ) { | @@subexp_unif_bools | NS(numSyms - mk) | return subexp_unif_bools + mk | } else { | @@subexp_more_bools | L(1) | if ( subexp_more_bools ) { | i++ | mk += a | } else { | @@subexp_bools | L(b2) | return subexp_bools + mk | } | } | } | } {:.syntax }
Note: The decode_signed_subexp_with_ref_bool function is the same as the decode_signed_subexp_with_ref function except that the bits used to represent the symbol are arithmetic coded instead of being read directly from the bitstream. {:.alert .alert-info }
| --------------------------------------------------------- | ---------------- | | tile_list_obu( ) { | Type | @@output_frame_width_in_tiles_minus_1 | f(8) | @@output_frame_height_in_tiles_minus_1 | f(8) | @@tile_count_minus_1 | f(16) | for ( tile = 0; tile <= tile_count_minus_1; tile++ ) | tile_list_entry( ) | } {:.syntax }
| --------------------------------------------------------- | ---------------- | | tile_list_entry( ) { | Type | @@anchor_frame_idx | f(8) | @@anchor_tile_row | f(8) | @@anchor_tile_col | f(8) | @@tile_data_size_minus_1 | f(16) | N = 8 * (tile_data_size_minus_1 + 1) | @@coded_tile_data | f(N) | } {:.syntax }