HEVC函数入门(16)——Slice编码

24 篇文章 11 订阅

本文转载整理自:http://blog.csdn.net/NB_vol_1/article/details/51152578
这篇文章很多东西还没搞懂,所以先转载放在这。
入口函数TEncSlice::compressSlice
这个函数主要是设置一些参数和初始化一些东西,然后对片中的每一个LCU调用initCU(初始化CU)和compressCU(对CU编码)和encodeCU(对CU进行熵编码,目的是选择最优参数)。
TEncSlice::compressSlice函数的详解:
(1)计算当前slice的开始CU和结束CU
(2)初始化Sbac编码器
(3)再把slice的熵编码器设置为Sbac编码器
(4)重置熵编码器(主要是上下文的重置)
(5)取得二值化编码器
(6)取得比特计数器
(7)遍历slice中的每一个LCU
①初始化LCU
②调用compressCU,用于求最优模式
③调用encodeCU,进行熵编码
下面放代码:

// 对条带编码  
Void TEncSlice::compressSlice( TComPic*& rpcPic )  
{  
    // CU的地址  
    UInt  uiCUAddr;  
    // CU的开始地址  
    UInt   uiStartCUAddr;  
    // CU的边界地址  
    UInt   uiBoundingCUAddr;  
    // 条带中当前的比特数量是0  
    rpcPic->getSlice(getSliceIdx())->setSliceSegmentBits(0);  
    TEncBinCABAC* pppcRDSbacCoder = NULL;  

    // 当前的条带  
    TComSlice* pcSlice            = rpcPic->getSlice(getSliceIdx());  

    // 得到CU(LCU)的开始和结束地址      
    xDetermineStartAndBoundingCUAddr ( uiStartCUAddr, uiBoundingCUAddr, rpcPic, false );  
    //uiStartCUAddr = 0, uiBoundingCUAddr = 2304  

    // initialize cost values  
    // 图像总的比特数  
    m_uiPicTotalBits  = 0;  
    // 率失真代价  
    m_dPicRdCost      = 0;  
    // 帧的distortion(失真)  
    m_uiPicDist       = 0;  

    // set entropy coder  
    // 初始化熵编码器  
    m_pcSbacCoder->init( m_pcBinCABAC );  
    // 设置熵编码器  
    m_pcEntropyCoder->setEntropyCoder   ( m_pcSbacCoder, pcSlice );  
    // 重置熵编码  
    // //!< 主要进行上下文模型的初始化,codILow和codIRange的初始化等  
    m_pcEntropyCoder->resetEntropy      ();  
    // 加载熵编码器SBAC  
    m_pppcRDSbacCoder[0][CI_CURR_BEST]->load(m_pcSbacCoder);  
    pppcRDSbacCoder = (TEncBinCABAC *) m_pppcRDSbacCoder[0][CI_CURR_BEST]->getEncBinIf();  
    pppcRDSbacCoder->setBinCountingEnableFlag( false );  
    pppcRDSbacCoder->setBinsCoded( 0 );  

    //------------------------------------------------------------------------------  
    //  Weighted Prediction parameters estimation.  
    //------------------------------------------------------------------------------  
    // calculate AC/DC values for current picture  
    // 不进入,因为没有使用波前前向预测  
    if( pcSlice->getPPS()->getUseWP() || pcSlice->getPPS()->getWPBiPred() )  
    {  
        xCalcACDCParamSlice(pcSlice);  
    }  

    Bool bWp_explicit = (pcSlice->getSliceType()==P_SLICE && pcSlice->getPPS()->getUseWP()) || (pcSlice->getSliceType()==B_SLICE && pcSlice->getPPS()->getWPBiPred());  

    // 不进入  
    if ( bWp_explicit )  
    {  
        //------------------------------------------------------------------------------  
        //  Weighted Prediction implemented at Slice level. SliceMode=2 is not supported yet.  
        //------------------------------------------------------------------------------  
        if ( pcSlice->getSliceMode()==2 || pcSlice->getSliceSegmentMode()==2 )  
        {  
            printf("Weighted Prediction is not supported with slice mode determined by max number of bins.\n"); exit(0);  
        }  

        xEstimateWPParamSlice( pcSlice );  
        pcSlice->initWpScaling();  

        // check WP on/off  
        xCheckWPEnable( pcSlice );  
    }  

    // 自适应量化步长  
#if ADAPTIVE_QP_SELECTION  
    // 不进入  
    if( m_pcCfg->getUseAdaptQpSelect() )  
    {  
        m_pcTrQuant->clearSliceARLCnt();  
        if(pcSlice->getSliceType()!=I_SLICE)  
        {  
            Int qpBase = pcSlice->getSliceQpBase();  
            pcSlice->setSliceQp(qpBase + m_pcTrQuant->getQpDelta(qpBase));  
        }  
    }  
#endif  

    // 获取编码器的配置  
    TEncTop* pcEncTop = (TEncTop*) m_pcCfg;  

    // 从配置中取得sbac编码器  
    TEncSbac**** ppppcRDSbacCoders    = pcEncTop->getRDSbacCoders();  
    // 从配置中获取比特计数器  
    TComBitCounter* pcBitCounters     = pcEncTop->getBitCounters();  

    // 子流的数量  
    Int  iNumSubstreams = 1;  
    // 穿过区块的个数  
    UInt uiTilesAcross  = 0;  

    iNumSubstreams = pcSlice->getPPS()->getNumSubstreams();//1  
    uiTilesAcross = rpcPic->getPicSym()->getNumColumnsMinus1()+1;//1  
    delete[] m_pcBufferSbacCoders;  
    delete[] m_pcBufferBinCoderCABACs;  
    m_pcBufferSbacCoders     = new TEncSbac    [uiTilesAcross];  
    m_pcBufferBinCoderCABACs = new TEncBinCABAC[uiTilesAcross];  
    for (Int ui = 0; ui < uiTilesAcross; ui++)  
    {  
        m_pcBufferSbacCoders[ui].init( &m_pcBufferBinCoderCABACs[ui] );  
    }  
    for (UInt ui = 0; ui < uiTilesAcross; ui++)  
    {  
        m_pcBufferSbacCoders[ui].load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  //init. state  
    }  

    for ( UInt ui = 0 ; ui < iNumSubstreams ; ui++ ) //init all sbac coders for RD optimization  
    {  
        ppppcRDSbacCoders[ui][0][CI_CURR_BEST]->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  
    }  

    delete[] m_pcBufferLowLatSbacCoders;  
    delete[] m_pcBufferLowLatBinCoderCABACs;  
    m_pcBufferLowLatSbacCoders     = new TEncSbac    [uiTilesAcross];  
    m_pcBufferLowLatBinCoderCABACs = new TEncBinCABAC[uiTilesAcross];  
    for (Int ui = 0; ui < uiTilesAcross; ui++)  
    {  
        m_pcBufferLowLatSbacCoders[ui].init( &m_pcBufferLowLatBinCoderCABACs[ui] );  
    }  
    for (UInt ui = 0; ui < uiTilesAcross; ui++)  
        m_pcBufferLowLatSbacCoders[ui].load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  //init. state  

    // 获取一帧图像中纵向可以存放多少个CU(目前是3个)  
    // 同理垂直方向上也有可以存放3个,因此,一个slice中有3*3=9个LCU  
    UInt uiWidthInLCUs  = rpcPic->getPicSym()->getFrameWidthInCU();       // 3  
    //UInt uiHeightInLCUs = rpcPic->getPicSym()->getFrameHeightInCU();  
    UInt uiCol=0, uiLin=0, uiSubStrm=0;  
    UInt uiTileCol      = 0;  
    UInt uiTileStartLCU = 0;  
    UInt uiTileLCUX     = 0;  
    // 禁用了依赖性条带片段  
    Bool depSliceSegmentsEnabled = pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag(); // false  
    uiCUAddr = rpcPic->getPicSym()->getCUOrderMap( uiStartCUAddr /rpcPic->getNumPartInCU());//0  
    uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr();//0  

    // 是否启用了条带片段功能  
    if( depSliceSegmentsEnabled )  
    {  
        if((pcSlice->getSliceSegmentCurStartCUAddr()!= pcSlice->getSliceCurStartCUAddr())&&(uiCUAddr != uiTileStartLCU))  
        {  
            if( m_pcCfg->getWaveFrontsynchro() )  
            {  
                uiTileCol = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr) % (rpcPic->getPicSym()->getNumColumnsMinus1()+1);  
                m_pcBufferSbacCoders[uiTileCol].loadContexts( CTXMem[1] );  
                Int iNumSubstreamsPerTile = iNumSubstreams/rpcPic->getPicSym()->getNumTiles();  
                uiCUAddr = rpcPic->getPicSym()->getCUOrderMap( uiStartCUAddr /rpcPic->getNumPartInCU());   
                uiLin     = uiCUAddr / uiWidthInLCUs;  
                uiSubStrm = rpcPic->getPicSym()->getTileIdxMap(rpcPic->getPicSym()->getCUOrderMap(uiCUAddr))*iNumSubstreamsPerTile  
                    + uiLin%iNumSubstreamsPerTile;  
                if ( (uiCUAddr%uiWidthInLCUs+1) >= uiWidthInLCUs  )  
                {  
                    uiTileLCUX = uiTileStartLCU % uiWidthInLCUs;  
                    uiCol     = uiCUAddr % uiWidthInLCUs;  
                    if(uiCol==uiTileStartLCU)  
                    {  
                        CTXMem[0]->loadContexts(m_pcSbacCoder);  
                    }  
                }  
            }  
            m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( CTXMem[0] );  
            ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]->loadContexts( CTXMem[0] );  
        }  
        else  
        {  
            if(m_pcCfg->getWaveFrontsynchro())  
            {  
                CTXMem[1]->loadContexts(m_pcSbacCoder);  
            }  
            CTXMem[0]->loadContexts(m_pcSbacCoder);  
        }  
    }  

    // for every CU in slice  
    // 处理条带中的每一个CU  

    int nLCUofSlice = 0;  
    // 编码CU的顺序  
    UInt uiEncCUOrder;  

    // rpcPic->getNumPartInCU()获取一个LCU分裂的个数,即它内部包含多少个CU  
    for( uiEncCUOrder = uiStartCUAddr/rpcPic->getNumPartInCU();  
        uiEncCUOrder < (uiBoundingCUAddr+(rpcPic->getNumPartInCU()-1))/rpcPic->getNumPartInCU();  
        uiCUAddr = rpcPic->getPicSym()->getCUOrderMap(++uiEncCUOrder) )  
    {  
        // initialize CU encoder  
        // 根据CU的地址取得CU(LCU)   
        TComDataCU*& pcCU = rpcPic->getCU( uiCUAddr );  

        // 初始化CU  
        pcCU->initCU( rpcPic, uiCUAddr );  

        // HM15.0中似乎没有用到区块  
        // inherit from TR if necessary, select substream to use.  
        uiTileCol = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr) % (rpcPic->getPicSym()->getNumColumnsMinus1()+1); // what column of tiles are we in?  // 0  
        uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr(); // 0  
        uiTileLCUX = uiTileStartLCU % uiWidthInLCUs; // 0  
        //UInt uiSliceStartLCU = pcSlice->getSliceCurStartCUAddr();  
        uiCol     = uiCUAddr % uiWidthInLCUs; // 0  
        uiLin     = uiCUAddr / uiWidthInLCUs; // 0  

        // 子比特流数量是1,所以没有进去  
        if (pcSlice->getPPS()->getNumSubstreams() > 1)  
        {  
            // independent tiles => substreams are "per tile".  iNumSubstreams has already been multiplied.  
            Int iNumSubstreamsPerTile = iNumSubstreams/rpcPic->getPicSym()->getNumTiles();  
            uiSubStrm = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr)*iNumSubstreamsPerTile  
                + uiLin%iNumSubstreamsPerTile;  
        }  
        else  
        {  
            // dependent tiles => substreams are "per frame".  
            uiSubStrm = uiLin % iNumSubstreams; //0  
        }  

        //  
        if ( ((pcSlice->getPPS()->getNumSubstreams() > 1) || depSliceSegmentsEnabled ) && (uiCol == uiTileLCUX) && m_pcCfg->getWaveFrontsynchro())  
        {  
            // We'll sync if the TR is available.  
            TComDataCU *pcCUUp = pcCU->getCUAbove();  
            UInt uiWidthInCU = rpcPic->getFrameWidthInCU();  
            UInt uiMaxParts = 1<<(pcSlice->getSPS()->getMaxCUDepth()<<1);  
            TComDataCU *pcCUTR = NULL;  
            if ( pcCUUp && ((uiCUAddr%uiWidthInCU+1) < uiWidthInCU)  )  
            {  
                pcCUTR = rpcPic->getCU( uiCUAddr - uiWidthInCU + 1 );  
            }  
            if ( ((pcCUTR==NULL) || (pcCUTR->getSlice()==NULL) ||  
                (pcCUTR->getSCUAddr()+uiMaxParts-1 < pcSlice->getSliceCurStartCUAddr()) ||  
                ((rpcPic->getPicSym()->getTileIdxMap( pcCUTR->getAddr() ) != rpcPic->getPicSym()->getTileIdxMap(uiCUAddr)))  
                )  
                )  
            {  
                // TR not available.  
            }  
            else  
            {  
                // TR is available, we use it.  
                ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]->loadContexts( &m_pcBufferSbacCoders[uiTileCol] );  
            }  
        }  
        m_pppcRDSbacCoder[0][CI_CURR_BEST]->load( ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST] ); //this load is used to simplify the code  

        // reset the entropy coder  
        // 重置熵编码器  
        if( uiCUAddr == rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr() &&                                   // must be first CU of tile  
            uiCUAddr!=0 &&                                                                                                                                    // cannot be first CU of picture  
            uiCUAddr!=rpcPic->getPicSym()->getPicSCUAddr(rpcPic->getSlice(rpcPic->getCurrSliceIdx())->getSliceSegmentCurStartCUAddr())/rpcPic->getNumPartInCU() &&  
            uiCUAddr!=rpcPic->getPicSym()->getPicSCUAddr(rpcPic->getSlice(rpcPic->getCurrSliceIdx())->getSliceCurStartCUAddr())/rpcPic->getNumPartInCU())     // cannot be first CU of slice  
        {  
            SliceType sliceType = pcSlice->getSliceType();  
            if (!pcSlice->isIntra() && pcSlice->getPPS()->getCabacInitPresentFlag() && pcSlice->getPPS()->getEncCABACTableIdx()!=I_SLICE)  
            {  
                sliceType = (SliceType) pcSlice->getPPS()->getEncCABACTableIdx();  
            }  
            m_pcEntropyCoder->updateContextTables ( sliceType, pcSlice->getSliceQp(), false );  
            m_pcEntropyCoder->setEntropyCoder     ( m_pppcRDSbacCoder[0][CI_CURR_BEST], pcSlice );  
            m_pcEntropyCoder->updateContextTables ( sliceType, pcSlice->getSliceQp() );  
            m_pcEntropyCoder->setEntropyCoder     ( m_pcSbacCoder, pcSlice );  
        }  

        // set go-on entropy coder  
        m_pcEntropyCoder->setEntropyCoder ( m_pcRDGoOnSbacCoder, pcSlice );  
        m_pcEntropyCoder->setBitstream( &pcBitCounters[uiSubStrm] );  

        ((TEncBinCABAC*)m_pcRDGoOnSbacCoder->getEncBinIf())->setBinCountingEnableFlag(true);  

        Double oldLambda = m_pcRdCost->getLambda();//57.9。。。  

        // 没有使用码率控制  
        if ( m_pcCfg->getUseRateCtrl() )  
        {  
            Int estQP        = pcSlice->getSliceQp();  
            Double estLambda = -1.0;  
            Double bpp       = -1.0;  

            if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )  
            {  
                estQP = pcSlice->getSliceQp();  
            }  
            else  
            {  
                bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());  
                if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE)  
                {  
                    estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);  
                }  
                else  
                {  
                    estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );  
                    estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );  
                }  

                estQP     = Clip3( -pcSlice->getSPS()->getQpBDOffsetY(), MAX_QP, estQP );  

                m_pcRdCost->setLambda(estLambda);  
#if RDOQ_CHROMA_LAMBDA  
                // set lambda for RDOQ  
                Double weight=m_pcRdCost->getChromaWeight();  
                const Double lambdaArray[3] = { estLambda, (estLambda / weight), (estLambda / weight) };  
                m_pcTrQuant->setLambdas( lambdaArray );  
#else  
                m_pcTrQuant->setLambda( estLambda );  
#endif  
            }  

            m_pcRateCtrl->setRCQP( estQP );  
#if ADAPTIVE_QP_SELECTION  
            pcCU->getSlice()->setSliceQpBase( estQP );  
#endif  
        }  

        // run CU encoder  
        // 对CU进行编码(压缩)  
        // 帧内预测,帧间预测编码还有变换编码  
        // 这里很重要  
        // 编码单元编码  
        // 最重要的部分!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  
        // 注意这个只是尝试进行,然后选出最优熵编码方案,下面的encodeCU才是真正进行熵编码的地方  
        m_pcCuEncoder->compressCU( pcCU );  
        // 最重要的部分!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  

        // restore entropy coder to an initial stage  
        // 熵编码器设置为Sbac  
        m_pcEntropyCoder->setEntropyCoder ( m_pppcRDSbacCoder[0][CI_CURR_BEST], pcSlice );  
        // 设置需要写入的比特流  
        m_pcEntropyCoder->setBitstream( &pcBitCounters[uiSubStrm] );  
        m_pcCuEncoder->setBitCounter( &pcBitCounters[uiSubStrm] );  
        // 比特计数器(用于统计熵编码器写入到比特流中的比特数)  
        m_pcBitCounter = &pcBitCounters[uiSubStrm];  
        pppcRDSbacCoder->setBinCountingEnableFlag( true );  
        m_pcBitCounter->resetBits();  
        pppcRDSbacCoder->setBinsCoded( 0 );  

        // 对CU进编码  
        // 这里是真正的进行熵编码!!!!  
        // 重要!!!!!!!!!!!!!  
        m_pcCuEncoder->encodeCU( pcCU );  
        // 重要!!!!!!!!!!!!!  

        pppcRDSbacCoder->setBinCountingEnableFlag( false );  

        // 这两个判断,是为了判断该CU是否为条带中的最后一个CU,如果是则跳出循环  
        if (m_pcCfg->getSliceMode()==FIXED_NUMBER_OF_BYTES && ( ( pcSlice->getSliceBits() + m_pcEntropyCoder->getNumberOfWrittenBits() ) ) > m_pcCfg->getSliceArgument()<<3)  
        {  
            pcSlice->setNextSlice( true );  
            break;  
        }  
        if (m_pcCfg->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceSegmentBits()+m_pcEntropyCoder->getNumberOfWrittenBits() > (m_pcCfg->getSliceSegmentArgument() << 3) &&pcSlice->getSliceCurEndCUAddr()!=pcSlice->getSliceSegmentCurEndCUAddr())  
        {  
            pcSlice->setNextSliceSegment( true );  
            break;  
        }  

        ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]->load( m_pppcRDSbacCoder[0][CI_CURR_BEST] );  
        //Store probabilties of second LCU in line into buffer  
        if ( ( uiCol == uiTileLCUX+1) && (depSliceSegmentsEnabled || (pcSlice->getPPS()->getNumSubstreams() > 1)) && m_pcCfg->getWaveFrontsynchro())  
        {  
            m_pcBufferSbacCoders[uiTileCol].loadContexts(ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]);  
        }  

        // 没有使用码率控制  
        if ( m_pcCfg->getUseRateCtrl() )  
        {  

            Int actualQP        = g_RCInvalidQPValue;  
            Double actualLambda = m_pcRdCost->getLambda();  
            Int actualBits      = pcCU->getTotalBits();  
            Int numberOfEffectivePixels    = 0;  
            for ( Int idx = 0; idx < rpcPic->getNumPartInCU(); idx++ )  
            {  
                if ( pcCU->getPredictionMode( idx ) != MODE_NONE && ( !pcCU->isSkipped( idx ) ) )  
                {  
                    numberOfEffectivePixels = numberOfEffectivePixels + 16;  
                    break;  
                }  
            }  

            if ( numberOfEffectivePixels == 0 )  
            {  
                actualQP = g_RCInvalidQPValue;  
            }  
            else  
            {  
                actualQP = pcCU->getQP( 0 );  
            }  
            m_pcRdCost->setLambda(oldLambda);  

            m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda,  
                pcCU->getSlice()->getSliceType() == I_SLICE ? 0 : m_pcCfg->getLCULevelRC() );  
        }  

        // 计算总的比特数  
        m_uiPicTotalBits += pcCU->getTotalBits();  
        // 计算运行代价  
        m_dPicRdCost     += pcCU->getTotalCost();  
        // 计算失真率  
        m_uiPicDist      += pcCU->getTotalDistortion();  
    } // for end  

    if ((pcSlice->getPPS()->getNumSubstreams() > 1) && !depSliceSegmentsEnabled)  
    {  
        pcSlice->setNextSlice( true );  
    }  
    if(m_pcCfg->getSliceMode()==FIXED_NUMBER_OF_BYTES || m_pcCfg->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES)  
    {  
        if(pcSlice->getSliceCurEndCUAddr()<=pcSlice->getSliceSegmentCurEndCUAddr())  
        {  
            pcSlice->setNextSlice( true );  
        }  
        else  
        {  
            pcSlice->setNextSliceSegment( true );  
        }  
    }  
    if( depSliceSegmentsEnabled )  
    {  
        if (m_pcCfg->getWaveFrontsynchro())  
        {  
            CTXMem[1]->loadContexts( &m_pcBufferSbacCoders[uiTileCol] );//ctx 2.LCU  
        }  
        CTXMem[0]->loadContexts( m_pppcRDSbacCoder[0][CI_CURR_BEST] );//ctx end of dep.slice  
    }  
    // 存储WP参数  
    xRestoreWPparam( pcSlice );  
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值