《AV1 Bitstream & Decoding Process Specification》,译名:AV1比特流及解码规范-Chapter 05-语法结构-Section 11~12

本文档深入介绍了AV1视频编解码标准中的Tile组OBU和Tile列表OBU的语法结构,包括tile_group_obu、decode_tile、mode_info等58个关键部分,涵盖了从解码基本单元到预测、残差、变换等多个解码步骤的详细过程。
摘要由CSDN通过智能技术生成

Table of Contents

5.11 Tile组OBU语法

5.11.1 tile_group_obu语法

5.11.2 decode_tile语法

5.11.3 clear_block_decoded_flags函数

5.11.4 decode_partition语法

5.11.5 decode_block语法

5.11.6 mode_info语法

5.11.7 intra_frame_mode_info语法

5.11.8 intra_segment_id语法

5.11.9 read_segment_id语法

5.11.10 read_skip_mode语法

5.11.11 read_skip语法

5.11.12 read_delta_qindex语法

5.11.13 read_delta_lf语法

5.11.14 seg_feature_active_idx函数

5.11.15 read_tx_size语法

5.11.16 read_block_tx_size语法

5.11.17 read_var_tx_size语法

5.11.18 inter_frame_mode_info语法

5.11.19 inter_segment_id语法

5.11.20 read_is_inter语法

5.11.21 get_segment_id函数

5.11.22 intra_block_mode_info语法

5.11.23 inter_block_mode_info语法

5.11.24 filter_intra_mode_info语法

5.11.25 read_ref_frames语法

5.11.26 assign_mv语法

5.11.27 read_motion_mode语法

5.11.28 read_interintra_mode语法

5.11.29 read_compound_type语法

5.11.30 get_mode函数

5.11.31 read_mv语法

5.11.32 read_mv_component语法

5.11.33 compute_prediction语法

5.11.34 residual语法

5.11.35 transform_block语法

5.11.36 transform_tree语法

5.11.37 get_tx_size函数

5.11.38 get_plane_residual_size函数

5.11.39 coeffs语法

5.11.40 compute_tx_type函数

5.11.41 get_mrow_scan函数

5.11.42 intra_angle_info_y语法

5.11.43 intra_angle_info_uv语法

5.11.44 is_directional_mode语法

5.11.45 read_cfl_alphas语法

5.11.46 palette_mode_info语法

5.11.47 transform_type语法

5.11.48 get_tx_set语法

5.11.49 palette_tokens语法

5.11.50 get_palette_color_context函数

5.11.51 is_inside函数

5.11.52 is_inside_filter_region函数

5.11.53 clamp_mv_row函数

5.11.54 clamp_mv_col函数

5.11.55 clear_cdef函数

5.11.56 read_cdef语法

5.11.57 read_Ir语法

5.11.58 read_lr_unit语法

5.12 Tile列表OBU语法

5.12.1 tile_list_obu语法

5.12.2 tile_list_entry语法


5.11 Tile组OBU语法

5.11.1 tile_group_obu语法

tile_group_obu( sz ) 
{
    NumTiles = TileCols * TileRows
    startBitPos = get_position( )
    tile_start_and_end_present_flag = 0
    if ( NumTiles > 1 )
        tile_start_and_end_present_flag           // Type f(1)
    if ( NumTiles == 1 || !tile_start_and_end_present_flag ) 
    {
        tg_start = 0
        tg_end = NumTiles - 1
    } 
    else 
    {
        tileBits = TileColsLog2 + TileRowsLog2
        tg_start            // Type f(tileBits)
        tg_end            // Type 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            // Type 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
    }
}

5.11.2 decode_tile语法

decode_tile( ) 
{
    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 )
        }
    }
}

其中的常量查找表Sgrproj_Xqd_Mid和Wiener_Taps_Mid定义如下,

Wiener_Taps_Mid[3] = { 3, -7, 15 }
Sgrproj_Xqd_Mid[2] = { -32, 31 }

5.11.3 clear_block_decoded_flags函数

clear_block_decoded_flags( r, c, sbSize4 )
{
    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
    }
}

5.11.4 decode_partition语法

decode_partition( r, c, bSize )
{
    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            // Type S()
    } 
    else if ( hasCols ) 
    {
        split_or_horz            // Type S()
        partition = split_or_horz ? PARTITION_SPLIT : PARTITION_HORZ
    } 
    else if ( hasRows ) 
    {
        split_or_vert            // Type 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 )
    }
}

5.11.5 decode_block语法

decode_block( r, c, subSize )
{
    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 ]
        }
    }
}

其中的reset_block_context()定义如下,

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
        }
    }
}

5.11.6 mode_info语法

mode_info( )
{
    if ( FrameIsIntra )
        intra_frame_mode_info( )
    else
        inter_frame_mode_info( )
}

5.11.7 intra_frame_mode_info语法

intra_frame_mode_info( ) 
{
    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            // Type 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            // Type S()
        YMode = intra_frame_y_mode
        intra_angle_info_y( )
        if ( HasChroma ) 
        {
            uv_mode            // Type 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( )
    }
}

5.11.8 intra_segment_id语法

intra_segment_id( )
{
    if ( segmentation_enabled )
        read_segment_id( )
    else
        segment_id = 0
    Lossless = LosslessArray[ segment_id ]
}

5.11.9 read_segment_id语法

read_segment_id( )
{
    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            // Type S()
        segment_id = neg_deinterleave( segment_id, pred, LastActiveSegId + 1 )
    }
}

其中的neg_deinterleave()定义如下,

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)
    }
}

5.11.10 read_skip_mode语法

read_skip_mode()
{
    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            // Type S()
    }
}

5.11.11 read_skip语法

read_skip() 
{
    if ( SegIdPreSkip && seg_feature_active( SEG_LVL_SKIP ) ) 
    {
        skip = 1
    } 
    else 
    {
        skip            // Type S()
    }
}

5.11.12 read_delta_qindex语法

read_delta_qindex( ) 
{
    sbSize = use_128x128_superblock ? BLOCK_128X128 : BLOCK_64X64
    if ( MiSize == sbSize && skip )
        return
    if ( ReadDeltas ) 
    {
        delta_q_abs            // Type S()
        if ( delta_q_abs == DELTA_Q_SMALL ) 
        {
            delta_q_rem_bits            // Type L(3)
            delta_q_rem_bits++
            delta_q_abs_bits            // Type 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            // Type L(1)
            reducedDeltaQIndex = delta_q_sign_bit ? -delta_q_abs : delta_q_abs
            CurrentQIndex = Clip3(1, 255, CurrentQIndex + (reducedDeltaQIndex << delta_q_res))
        }
    }
}

5.11.13 read_delta_lf语法

read_delta_lf( )
{
    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            // Type S()
            if ( delta_lf_abs == DELTA_LF_SMALL ) 
            {
                delta_lf_rem_bits            // Type L(3)
                n = delta_lf_rem_bits + 1
                delta_lf_abs_bits            // Type L(n)
                deltaLfAbs = delta_lf_abs_bits + ( 1 << n ) + 1
            } 
            else 
            {
                deltaLfAbs = delta_lf_abs
            }
            if ( deltaLfAbs ) 
            {
                delta_lf_sign_bit            // Type L(1)
                reducedDeltaLfLevel = delta_lf_sign_bit ? -deltaLfAbs : deltaLfAbs
                DeltaLF[ i ] = Clip3( -MAX_LOOP_FILTER, MAX_LOOP_FILTER, DeltaLF[ i ] + (reducedDeltaLfLevel << delta_lf_res) )
            }
        }
    }
}

5.11.14 seg_feature_active_idx函数

seg_feature_active_idx( idx, feature ) 
{
    return segmentation_enabled && FeatureEnabled[ idx ][ feature ]
}

seg_feature_active( feature ) 
{
    return seg_feature_active_idx( segment_id, feature )
}

5.11.15 read_tx_size语法

read_tx_size( allowSelect ) 
{
    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            // Type S()
        for ( i = 0; i < tx_depth; i++ )
            TxSize = Split_Tx_Size[ TxSize ]
    }
}

其中Max_Tx_Depth定义了每个块尺寸的最大变换深度,

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
}

注意:Max_Tx_Depth包含必须拆分变换以达到4x4变换大小的次数。它可能大于MAX_TX_DEPTH。但是,因为tx_depth只能编码0到2范围内的值,所以无法进行变换深度大于MAX_TX_DEPTH的编码。

5.11.16 read_block_tx_size语法

read_block_tx_size( ) 
{
    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
    }
}

5.11.17 read_var_tx_size语法

用于读取变换尺寸树。

read_var_tx_size( row, col, txSz, depth) 
{
    if ( row >= MiRows || col >= MiCols )
        return
    if ( txSz == TX_4X4 || depth == MAX_VARTX_DEPTH ) 
    {
        txfm_split = 0
    } 
    else 
    {
        txfm_split            // Type 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
    }
}

5.11.18 inter_frame_mode_info语法

inter_frame_mode_info( ) 
{ 
    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( )
}

5.11.19 inter_segment_id语法

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 )
 {
    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            // Type 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
    }
}

5.11.20 read_is_inter语法

read_is_inter( ) 
{
    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            // Type S()
    }
}

5.11.21 get_segment_id函数

segment_id是在当前块覆盖的分割图的屏幕中找到的最小值。

get_segment_id( ) 
{
    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
}

5.11.22 intra_block_mode_info语法

intra_block_mode_info( ) 
{
    RefFrame[ 0 ] = INTRA_FRAME
    RefFrame[ 1 ] = NONE
    y_mode           // Type S()
    YMode = y_mode
    intra_angle_info_y( )
    if ( HasChroma ) 
    {
        uv_mode          // Type 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( )
}

5.11.23 inter_block_mode_info语法

inter_block_mode_info( ) 
{
    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           // Type S()
        YMode = NEAREST_NEARESTMV + compound_mode
    } 
    else 
    {
        new_mv           // Type S()
        if ( new_mv == 0 ) 
        {
            YMode = NEWMV
        } 
        else 
        {
            zero_mv           // Type S()
            if ( zero_mv == 0 ) 
            {
                YMode = GLOBALMV
            } 
            else 
            {
                ref_mv           // Type 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           // Type 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           // Type 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 ]           // Type 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
    }
}

其中的has_nearmv函数和needs_interp_filter函数定义为,

has_nearmv( ) 
{
    return (YMode == NEARMV || YMode == NEAR_NEARMV || YMode == NEAR_NEWMV || YMode ==     NEW_NEARMV)
}

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
    }
}

5.11.24 filter_intra_mode_info语法

filter_intra_mode_info( ) 
{
    use_filter_intra = 0
    if ( enable_filter_intra 
        && YMode == DC_PRED 
        && PaletteSizeY == 0 
        && Max( Block_Width[ MiSize ], Block_Height[ MiSize ] ) <= 32 ) 
    {
        use_filter_intra            // Type S()
        if ( use_filter_intra ) 
        {
            filter_intra_mode            // Type S()
        }
    }
}

5.11.25 read_ref_frames语法

read_ref_frames( ) 
{
    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           // Type S()
        else
            comp_mode = SINGLE_REFERENCE
        if ( comp_mode == COMPOUND_REFERENCE ) 
        {
            comp_ref_type           // Type S()
            if ( comp_ref_type == UNIDIR_COMP_REFERENCE ) 
            {
                uni_comp_ref           // Type S()
                if ( uni_comp_ref ) 
                {
                    RefFrame[0] = BWDREF_FRAME
                    RefFrame[1] = ALTREF_FRAME
                } 
                else 
                {
                    uni_comp_ref_p1           // Type S()
                    if ( uni_comp_ref_p1 ) 
                    {
                        uni_comp_ref_p2           // Type 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           // Type S()
                if ( comp_ref == 0 ) 
                {
                    comp_ref_p1           // Type S()
                    RefFrame[ 0 ] = comp_ref_p1 ? LAST2_FRAME : LAST_FRAME
                } 
                else 
                {
                    comp_ref_p2           // Type S()
                    RefFrame[ 0 ] = comp_ref_p2 ? GOLDEN_FRAME : LAST3_FRAME
                }
                comp_bwdref           // Type S()
                if ( comp_bwdref == 0 ) 
                {
                    comp_bwdref_p1           // Type S()
                    RefFrame[ 1 ] = comp_bwdref_p1 ? ALTREF2_FRAME : BWDREF_FRAME
                } 
                else 
                {
                    RefFrame[ 1 ] = ALTREF_FRAME
                }
            }
        } 
        else 
        {
            single_ref_p1           // Type S()
            if ( single_ref_p1 ) 
            {
                single_ref_p2           // Type S()
                if ( single_ref_p2 == 0 ) 
                {
                    single_ref_p6          // Type S()
                    RefFrame[ 0 ] = single_ref_p6 ? ALTREF2_FRAME : BWDREF_FRAME
                } 
                else 
                {
                    RefFrame[ 0 ] = ALTREF_FRAME
                }
            } 
            else 
            {
                single_ref_p3           // Type S()
                if ( single_ref_p3 ) 
                {
                    single_ref_p5           // Type S()
                    RefFrame[ 0 ] = single_ref_p5 ? GOLDEN_FRAME : LAST3_FRAME
                } 
                else 
                {
                    single_ref_p4           // Type S()
                    RefFrame[ 0 ] = single_ref_p4 ? LAST2_FRAME : LAST_FRAME
                }
            }
            RefFrame[ 1 ] = NONE
        }
    }
}

5.11.26 assign_mv语法

assign_mv( isCompound ) 
{
    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 ]
        }
    }
}

5.11.27 read_motion_mode语法

read_motion_mode( isCompound ) 
{
    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            // Type S()
        motion_mode = use_obmc ? OBMC : SIMPLE
    } 
    else 
    {
        motion_mode
    }
}

其中的is_scaled函数定义为,

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
}

5.11.28 read_interintra_mode语法

read_interintra_mode( isCompound ) 
{
    if ( !skip_mode 
        && enable_interintra_compound 
        && !isCompound 
        && MiSize >= BLOCK_8X8 
        && MiSize <= BLOCK_32X32) 
    {
        interintra            // Type S()
        if ( interintra ) 
        {
            interintra_mode            // Type S()
            RefFrame[1] = INTRA_FRAME
            AngleDeltaY = 0
            AngleDeltaUV = 0
            use_filter_intra = 0
            wedge_interintra            // Type S()
            if ( wedge_interintra ) 
            {
                wedge_index            // Type S()
                wedge_sign = 0
            }
        }
    } 
    else 
    {
        interintra = 0
    }
}

5.11.29 read_compound_type语法

read_compound_type( isCompound ) 
{
    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            // Type S()
        }
        if ( comp_group_idx == 0 ) 
        {
            if ( enable_jnt_comp ) 
            {
                compound_idx            // Type 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            // Type S()
            }
        }
        if ( compound_type == COMPOUND_WEDGE ) 
        {
            wedge_index            // Type S()
            wedge_sign           // Type L(1)
        } 
        else if ( compound_type == COMPOUND_DIFFWTD ) 
        {
            mask_type            // Type L(1)
        }
    } 
    else 
    {
        if ( interintra ) 
        {
            compound_type = wedge_interintra ? COMPOUND_WEDGE : COMPOUND_INTRA
        } 
        else 
        {
            compound_type = COMPOUND_AVERAGE
        }
    }
}

5.11.30 get_mode函数

get_mode( refList ) 
{
    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
}

5.11.31 read_mv语法

read_mv( ref ) 
{
    diffMv[ 0 ] = 0
    diffMv[ 1 ] = 0
    if ( use_intrabc ) 
    {
        MvCtx = MV_INTRABC_CONTEXT
    } 
    else 
    {
        MvCtx = 0
    }
    mv_joint           // Type 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 ]
}

5.11.32 read_mv_component语法

read_mv_component( comp ) 
{
    mv_sign            // Type S()
    mv_class            // Type S()
    if ( mv_class == MV_CLASS_0 ) 
    {
        mv_class0_bit            // Type S()
        if ( force_integer_mv )
            mv_class0_fr = 3
        else
            mv_class0_fr            // Type S()
        if ( allow_high_precision_mv )
            mv_class0_hp            // Type 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            // Type S()
            d |= mv_bit << i
        }
        mag = CLASS0_SIZE << ( mv_class + 2 )
        if ( force_integer_mv )
            mv_fr = 3
        else
            mv_fr            // Type S()
        if ( allow_high_precision_mv )
            mv_hp            // Type S()
        else
            mv_hp = 1
        mag += ( ( d << 3 ) | ( mv_fr << 1 ) | mv_hp ) + 1
    }
    return mv_sign ? -mag : mag
}

5.11.33 compute_prediction语法

compute_prediction() 
{
    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++
            }
        }
    }
}

5.11.34 residual语法

residual( ) 
{
    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 ) )
                }
            }
        }
    }
}

5.11.35 transform_block语法

transform_block(plane, baseX, baseY, txSz, x, y) 
{
    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
        }
    }
}

5.11.36 transform_tree语法

用于读取一系列的转换块。

transform_tree( startX, startY, w, h )
{
    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 )
        }
    }
}

其中的find_tx_size函数定义为,

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
}

5.11.37 get_tx_size函数

get_tx_size( plane, txSz )
{
    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
}

5.11.38 get_plane_residual_size函数

get_plane_residual_size返回指定平面的残留块的大小。(残留块的宽度和高度始终至少等于4)

get_plane_residual_size( subsize, plane )
{
    subx = plane > 0 ? subsampling_x : 0
    suby = plane > 0 ? subsampling_y : 0
    return Subsampled_Size[ subsize ][ subx ][ suby ]
}

其中的Subsampled_Size定义如下,

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} },
}

5.11.39 coeffs语法

coeffs( plane, startX, startY, txSz ) 
{
    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            // Type 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            // Type S()
            eobPt = eob_pt_16 + 1
        } 
        else if ( eobMultisize == 1 ) 
        {
            eob_pt_32            // Type S()
            eobPt = eob_pt_32 + 1
        } 
        else if ( eobMultisize == 2 ) 
        {
            eob_pt_64            // Type S()
            eobPt = eob_pt_64 + 1
        } 
        else if ( eobMultisize == 3 ) 
        {
            eob_pt_128            // Type S()
            eobPt = eob_pt_128 + 1
        } 
        else if ( eobMultisize == 4 ) 
        {
            eob_pt_256            // Type S()
            eobPt = eob_pt_256 + 1
        } 
        else if ( eobMultisize == 5 ) 
        {
            eob_pt_512            // Type S()
            eobPt = eob_pt_512 + 1
        } 
        else 
        {
            eob_pt_1024            // Type S()
            eobPt = eob_pt_1024 + 1
        }
        eob = ( eobPt < 2 ) ? eobPt : ( ( 1 << ( eobPt - 2 ) ) + 1 )
        eobShift = Max( -1, eobPt - 3 )
        if ( eobShift >= 0 ) 
        {
            eob_extra            // Type S()
            if ( eob_extra ) 
            {
                eob += ( 1 << eobShift )
            }
            for ( i = 1; i < Max( 0, eobPt - 2 ); i++ ) 
            {
                eobShift = Max( 0, eobPt - 2 ) - 1 – i
                if ( eob_extra_bit ) 
                {
                    eob += ( 1 << eobShift )
                }
            }
        }
        for ( c = eob - 1; c >= 0; c-- )
        {
            pos = scan[ c ]
            if ( c == ( eob - 1 ) ) 
            {
                coeff_base_eob            // Type S()
                level = coeff_base_eob + 1
            } 
            else 
            {
                coeff_base            // Type S()
                level = coeff_base
            }
            if ( level > NUM_BASE_LEVELS ) 
            {
                for ( idx = 0; idx < COEFF_BASE_RANGE / ( BR_CDF_SIZE - 1 ); idx++ ) 
                {
                    coeff_br            // Type 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            // Type S()
                    sign = dc_sign
                } 
                else 
                {
                    sign_bit            // Type L(1)
                    sign = sign_bit
                }
            } 
            else 
            {
                sign = 0
            }
            if ( Quant[ pos ] > ( NUM_BASE_LEVELS + COEFF_BASE_RANGE ) ) 
            {
                length = 0
                do 
                {
                    length++
                    golomb_length_bit            // Type L(1)
                } while ( !golomb_length_bit )
                x = 1
                for ( i = length - 2; i >= 0; i-- ) 
                {
                    golomb_data_bit            // Type 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
}

5.11.40 compute_tx_type函数

compute_tx_type( plane, txSz, blockX, blockY ) 
{
    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 ]
}

其中的常量查找表定义为,

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,}
}

5.11.41 get_mrow_scan函数

get_mrow_scan( txSz ) 
{
    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 )
}

5.11.42 intra_angle_info_y语法

intra_angle_info_y( ) 
{
    AngleDeltaY = 0
    if ( MiSize >= BLOCK_8X8 ) 
    {
        if ( is_directional_mode( YMode ) ) 
        {
            angle_delta_y            // Type S()
            AngleDeltaY = angle_delta_y - MAX_ANGLE_DELTA
        }
    }
}

5.11.43 intra_angle_info_uv语法

intra_angle_info_uv( ) 
{
    AngleDeltaUV = 0
    if ( MiSize >= BLOCK_8X8 ) 
    {
        if ( is_directional_mode( UVMode ) ) 
        {
            angle_delta_uv            // Type S()
            AngleDeltaUV = angle_delta_uv - MAX_ANGLE_DELTA
        }
    }
}

5.11.44 is_directional_mode语法

is_directional_mode( mode ) 
{
    if ( ( mode >= V_PRED ) && ( mode <= D67_PRED ) ) 
    {
        return 1
    }
    return 0
}

5.11.45 read_cfl_alphas语法

read_cfl_alphas() 
{
    cfl_alpha_signs            // Type S()
    signU = (cfl_alpha_signs + 1 ) / 3
    signV = (cfl_alpha_signs + 1 ) % 3
    if ( signU != CFL_SIGN_ZERO ) 
    {
        cfl_alpha_u            // Type S()
        CflAlphaU = 1 + cfl_alpha_u
        if ( signU == CFL_SIGN_NEG )
            CflAlphaU = -CflAlphaU
    } 
    else 
    {
        CflAlphaU = 0
    }
    if ( signV != CFL_SIGN_ZERO ) 
    {
        cfl_alpha_v            // Type S()
        CflAlphaV = 1 + cfl_alpha_v
        if ( signV == CFL_SIGN_NEG )
            CflAlphaV = -CflAlphaV
    } 
    else 
    {
        CflAlphaV = 0
    }
}

5.11.46 palette_mode_info语法

palette_mode_info( )
{
    bsizeCtx = Mi_Width_Log2[ MiSize ] + Mi_Height_Log2[ MiSize ] - 2
    if ( YMode == DC_PRED ) 
    {
        has_palette_y            // Type S()
        if ( has_palette_y ) 
        {
            palette_size_y_minus_2            // Type 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           // Type L(1)
                if ( use_palette_color_cache_y ) 
                {
                    palette_colors_y[ idx ] = PaletteCache[ i ]
                    idx++
                }
            }
            if ( idx < PaletteSizeY ) 
            {
                palette_colors_y[ idx ]            // Type L(BitDepth)
                idx++
            }
            if ( idx < PaletteSizeY ) 
            {
                minBits = BitDepth - 3
                palette_num_extra_bits_y           // Type L(2)
                paletteBits = minBits + palette_num_extra_bits_y
            }
            while ( idx < PaletteSizeY ) 
            {
                palette_delta_y            // Type 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            // Type S()
        if ( has_palette_uv ) 
        {
            palette_size_uv_minus_2           // Type 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           // Type L(1)
                if ( use_palette_color_cache_u ) 
                {
                    palette_colors_u[ idx ] = PaletteCache[ i ]
                    idx++
                }
            }
            if ( idx < PaletteSizeUV ) 
            {
                palette_colors_u[ idx ]            // Type L(BitDepth)
                idx++
            }
            if ( idx < PaletteSizeUV ) 
            {
                minBits = BitDepth - 3
                palette_num_extra_bits_u            // Type L(2)
                paletteBits = minBits + palette_num_extra_bits_u
            }
            while ( idx < PaletteSizeUV ) 
            {
                palette_delta_u           // Type 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            // Type L(1)
            if ( delta_encode_palette_colors_v ) 
            {
                minBits = BitDepth - 4
                maxVal = 1 << BitDepth
                palette_num_extra_bits_v            // Type L(2)
                paletteBits = minBits + palette_num_extra_bits_v
                palette_colors_v[ 0 ]           // Type L(BitDepth)
                for ( idx = 1; idx < PaletteSizeUV; idx++ ) 
                {
                    palette_delta_v           // Type L(paletteBits)
                    if ( palette_delta_v ) 
                    {
                        palette_delta_sign_bit_v            // Type 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 ]            // Type L(BitDepth)
                }
            }
        }
    }
}

函数sort(arr,i1,i2)按顺序对数组arr的子数组进行就地排序,要排序的子阵列在索引i1和i2之间。

注意:调色板颜色按升序生成,调色板缓存也按升序排列。这意味着可以在实现中通过合并两个排序列表替换sort函数。

其中的get_palette_cache函数定义如下,

get_palette_cache( plane ) 
{
    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
}

注意:get_palette_cache相当于从上方和左侧排列可用的调色板颜色并删除重复项。

5.11.47 transform_type语法

transform_type( x4, y4, txSz ) 
{
    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            // 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            // 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
        }
    }
}

其中使用转换表定义如下,

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, LIPADST_ADST }
Tx_Type_Inter_Inv_Set3[ 2 ] = { IDTX, DCT_DCT }

5.11.48 get_tx_set语法

get_tx_set( txSz ) 
{
    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
    }
}

5.11.49 palette_tokens语法

palette_tokens( ) 
{
    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            // Type 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            // Type 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            // Type 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            // Type 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 ]
            }
        }
    }
}

5.11.50 get_palette_color_context函数

get_palette_color_context( colorMap, r, c, n ) 
{
    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 ]
    }
}

5.11.51 is_inside函数

is_inside( candidateR, candidateC ) 
{
    return ( candidateC >= MiColStart && candidateC < MiColEnd && candidateR >= MiRowStart && candidateR < MiRowEnd )
}

5.11.52 is_inside_filter_region函数

is_inside_filter_region( candidateR, candidateC ) 
{
    colStart = 0
    colEnd = MiCols
    rowStart = 0
    rowEnd = MiRows
    return (candidateC >= colStart && candidateC < colEnd && candidateR >= rowStart && candidateR < rowEnd)
}

5.11.53 clamp_mv_row函数

clamp_mv_row( mvec, border ) 
{
    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 )
}

5.11.54 clamp_mv_col函数

clamp_mv_col( mvec, border ) 
{
    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 )
}

5.11.55 clear_cdef函数

clear_cdef( r, c ) 
{
    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
    }
}

5.11.56 read_cdef语法

read_cdef( ) 
{
    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 ]            // Type 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 ]
            }
        }
    }
}

5.11.57 read_Ir语法

read_lr( r, c, bSize ) 
{
    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)
                }
            }
        }
    }
}

其中的count_units_in_frame函数定义如下,

count_units_in_frame(unitSize, frameSize) 
{
    return Max((frameSize + (unitSize >> 1)) / unitSize, 1)
}

5.11.58 read_lr_unit语法

read_lr_unit(plane, unitRow, unitCol) 
{
    if ( FrameRestorationType[ plane ] == RESTORE_WIENER ) 
    {
        use_wiener            // Type S()
        restoration_type = use_wiener ? RESTORE_WIENER : RESTORE_NONE
    } 
    else if ( FrameRestorationType[ plane ] == RESTORE_SGRPROJ ) 
    {
        use_sgrproj            // Type S()
        restoration_type = use_sgrproj ? RESTORE_SGRPROJ : RESTORE_NONE
    } 
    else 
    {
        restoration_type            // 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            // Type 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
        }
    }
}

其中的常量查找表和函数定义如下,

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 }

decode_signed_subexp_with_ref_bool( low, high, k, r )
{
    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           // Type NS(numSyms - mk)
            return subexp_unif_bools + mk
        } 
        else 
        {
            subexp_more_bools           // Type L(1)
            if ( subexp_more_bools ) 
            {
                i++
                mk += a
            } 
            else 
            {
                subexp_bools            // Type L(b2)
                return subexp_bools + mk
            }
        }
    }
}

注意:decode_signed_subexp_with_ref_bool函数与decode_signed_subexp_with_ref函数相同,除了用于表示符号的比特是算术编码而不是直接从比特流读取。

5.12 Tile列表OBU语法

5.12.1 tile_list_obu语法

tile_list_obu( ) 
{
    output_frame_width_in_tiles_minus_1            // Type f(8)
    output_frame_height_in_tiles_minus_1            // Type f(8)
    tile_count_minus_1            // Type f(16)
    for ( tile = 0; tile <= tile_count_minus_1; tile++ )
        tile_list_entry( )
}

5.12.2 tile_list_entry语法

tile_list_entry( ) 
{
    anchor_frame_idx           // Type f(8)
    anchor_tile_row            // Type f(8)
    anchor_tile_col           // Type f(8)
    tile_data_size_minus_1            // Type f(16)
    N = 8 * (tile_data_size_minus_1 + 1)
    coded_tile_data           // Type f(N)
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值