HEVC/H265 HM10.0 分析(三)TAppDecTop.cpp .

H265视频码流结构分析 本文会对H265编码后的视频流做一个详细的介绍 阅读详情

在TAppDecTop.cpp  ,最重要的是decode 函数,下面将对其进行分析,是解码上层的一个重要函数。

代码如下,代码后将进行分析。

  1. Void TAppDecTop::decode()  
  2. {  
  3.   Int                 poc;  
  4.   TComList<TComPic*>* pcListPic = NULL;  
  5.   
  6.   ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary);  
  7.   if (!bitstreamFile)  
  8.   {  
  9.     fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_pchBitstreamFile);  
  10.     exit(EXIT_FAILURE);  
  11.   }  
  12.   
  13.   InputByteStream bytestream(bitstreamFile);  
  14.   
  15.   // create & initialize internal classes   
  16.   xCreateDecLib();  
  17.   xInitDecLib  ();  
  18.   m_iPOCLastDisplay += m_iSkipFrame;      // set the last displayed POC correctly for skip forward.   
  19.   
  20.   // main decoder loop   
  21.   Bool recon_opened = false; // reconstruction file not yet opened. (must be performed after SPS is seen)   
  22.   
  23.   while (!!bitstreamFile)  
  24.   {  
  25.     /* location serves to work around a design fault in the decoder, whereby 
  26.      * the process of reading a new slice that is the first slice of a new frame 
  27.      * requires the TDecTop::decode() method to be called again with the same 
  28.      * nal unit. */  
  29.     streampos location = bitstreamFile.tellg();  
  30.     AnnexBStats stats = AnnexBStats();  
  31.     Bool bPreviousPictureDecoded = false;  
  32.   
  33.     vector<uint8_t> nalUnit;  
  34.     InputNALUnit nalu;  
  35.     byteStreamNALUnit(bytestream, nalUnit, stats);  
  36.   
  37.     // call actual decoding function   
  38.     Bool bNewPicture = false;  
  39.     if (nalUnit.empty())  
  40.     {  
  41.       /* this can happen if the following occur: 
  42.        *  - empty input file 
  43.        *  - two back-to-back start_code_prefixes 
  44.        *  - start_code_prefix immediately followed by EOF 
  45.        */  
  46.       fprintf(stderr, "Warning: Attempt to decode an empty NAL unit\n");  
  47.     }  
  48.     else  
  49.     {  
  50.       read(nalu, nalUnit);  
  51.       if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu)  )  
  52.       {  
  53.         if(bPreviousPictureDecoded)  
  54.         {  
  55.           bNewPicture = true;  
  56.           bPreviousPictureDecoded = false;  
  57.         }  
  58.         else  
  59.         {  
  60.           bNewPicture = false;  
  61.         }  
  62.       }  
  63.       else  
  64.       {  
  65.         bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay);  
  66.           
  67.         if (bNewPicture)  
  68.         {  
  69.           bitstreamFile.clear();  
  70.           /* location points to the current nalunit payload[1] due to the 
  71.            * need for the annexB parser to read three extra bytes. 
  72.            * [1] except for the first NAL unit in the file 
  73.            *     (but bNewPicture doesn't happen then) */  
  74.           bitstreamFile.seekg(location-streamoff(3));  
  75.           bytestream.reset();  
  76.         }  
  77.         bPreviousPictureDecoded = true;   
  78.       }  
  79.     }  
  80.     if (bNewPicture || !bitstreamFile)  
  81.     {  
  82.           
  83.       m_cTDecTop.executeLoopFilters(poc, pcListPic);  
  84.       printf("\npoc =%d\n",poc);  
  85.     }  
  86.   
  87.     if( pcListPic )  
  88.     {  
  89.         printf("\nnaluType =%d\n",nalu.m_nalUnitType);  
  90.       if ( m_pchReconFile && !recon_opened )  
  91.       {  
  92.         if (!m_outputBitDepthY) { m_outputBitDepthY = g_bitDepthY; }  
  93.         if (!m_outputBitDepthC) { m_outputBitDepthC = g_bitDepthC; }  
  94.   
  95.         m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepthY, m_outputBitDepthC, g_bitDepthY, g_bitDepthC ); // write mode   
  96.         recon_opened = true;  
  97.       }  
  98.       if ( bNewPicture &&   
  99.            (   nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR  
  100.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP  
  101.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA_N_LP  
  102.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLANT  
  103.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA ) )  
  104.       {  
  105.         xFlushOutput( pcListPic );  
  106.       }  
  107.       // write reconstruction to file   
  108.       if(bNewPicture)  
  109.       {  
  110.         xWriteOutput( pcListPic, nalu.m_temporalId );  
  111.       }  
  112.     }  
  113.   }  
  114.     
  115.   xFlushOutput( pcListPic );  
  116.   // delete buffers   
  117.   m_cTDecTop.deletePicBuffer();  
  118.     
  119.   // destroy internal classes   
  120.   xDestroyDecLib();  
  121. }  
Void TAppDecTop::decode()
{
  Int                 poc;
  TComList<TComPic*>* pcListPic = NULL;

  ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary);
  if (!bitstreamFile)
  {
    fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_pchBitstreamFile);
    exit(EXIT_FAILURE);
  }

  InputByteStream bytestream(bitstreamFile);

  // create & initialize internal classes
  xCreateDecLib();
  xInitDecLib  ();
  m_iPOCLastDisplay += m_iSkipFrame;      // set the last displayed POC correctly for skip forward.

  // main decoder loop
  Bool recon_opened = false; // reconstruction file not yet opened. (must be performed after SPS is seen)

  while (!!bitstreamFile)
  {
    /* location serves to work around a design fault in the decoder, whereby
     * the process of reading a new slice that is the first slice of a new frame
     * requires the TDecTop::decode() method to be called again with the same
     * nal unit. */
    streampos location = bitstreamFile.tellg();
    AnnexBStats stats = AnnexBStats();
    Bool bPreviousPictureDecoded = false;

    vector<uint8_t> nalUnit;
    InputNALUnit nalu;
    byteStreamNALUnit(bytestream, nalUnit, stats);

    // call actual decoding function
    Bool bNewPicture = false;
    if (nalUnit.empty())
    {
      /* this can happen if the following occur:
       *  - empty input file
       *  - two back-to-back start_code_prefixes
       *  - start_code_prefix immediately followed by EOF
       */
      fprintf(stderr, "Warning: Attempt to decode an empty NAL unit\n");
    }
    else
    {
      read(nalu, nalUnit);
      if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu)  )
      {
        if(bPreviousPictureDecoded)
        {
          bNewPicture = true;
          bPreviousPictureDecoded = false;
        }
        else
        {
          bNewPicture = false;
        }
      }
      else
      {
        bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay);
		
        if (bNewPicture)
        {
          bitstreamFile.clear();
          /* location points to the current nalunit payload[1] due to the
           * need for the annexB parser to read three extra bytes.
           * [1] except for the first NAL unit in the file
           *     (but bNewPicture doesn't happen then) */
          bitstreamFile.seekg(location-streamoff(3));
          bytestream.reset();
        }
        bPreviousPictureDecoded = true; 
      }
    }
    if (bNewPicture || !bitstreamFile)
    {
		
      m_cTDecTop.executeLoopFilters(poc, pcListPic);
	  printf("\npoc =%d\n",poc);
    }

    if( pcListPic )
    {
		printf("\nnaluType =%d\n",nalu.m_nalUnitType);
      if ( m_pchReconFile && !recon_opened )
      {
        if (!m_outputBitDepthY) { m_outputBitDepthY = g_bitDepthY; }
        if (!m_outputBitDepthC) { m_outputBitDepthC = g_bitDepthC; }

        m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepthY, m_outputBitDepthC, g_bitDepthY, g_bitDepthC ); // write mode
        recon_opened = true;
      }
      if ( bNewPicture && 
           (   nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA_N_LP
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLANT
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA ) )
      {
        xFlushOutput( pcListPic );
      }
      // write reconstruction to file
      if(bNewPicture)
      {
        xWriteOutput( pcListPic, nalu.m_temporalId );
      }
    }
  }
  
  xFlushOutput( pcListPic );
  // delete buffers
  m_cTDecTop.deletePicBuffer();
  
  // destroy internal classes
  xDestroyDecLib();
}



代码xCreateDecLib 和 xInitDecLib 重要是初始化四叉树和解码需要的全局变量和申请内存。

当提供一个码流文件的文件名后,进行ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary); 以二进制方式打开文件名,码流以字节方式InputByteStream bytestream(bitstreamFile);进行读操作。


我们都知道,HEVC/H265 是以NAL方式组织数据的,解析VPS,SPS,PPS,SEI,SEI_SUFFIX 后,其他的是一个个slice的NAL数据,而Deblocking & SAO Filters 等滤波是对整个picuture进行滤波操作,出现从第二帧开始,每帧的第一个slice两次进行解析,这是参考软件的一个bug或不好的地方,其实完全可以知道是否是最后一个slice,不必进行两次打开。

所以出现:

bitstreamFile.clear();

bitstreamFile.seekg(location-streamoff(3));
bytestream.reset();


大家有兴趣,可以先增加一个变量,判断是否是最后一个slice,就不需要执行上面代码了。


如下代码是从来不会执行,因为HEVC/H265没有cavlc,也就不会有slice part A ,slice part B,slice part C ,是现实的编解码告诉了设计者,slice part A ,slice part B,slice part C 没有人使用,就被抛弃了,实际的编解码从来没有实现slice part A ,slice part B,slice part C 等编解码的。

 if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu)  )
      {
        if(bPreviousPictureDecoded)
        {
          bNewPicture = true;
          bPreviousPictureDecoded = false;
        }
        else
        {
          bNewPicture = false;
        }
      }


解码和滤波后当然就是输出重构数据,xWriteOutput( pcListPic, nalu.m_temporalId ), 如果slice是NAL_UNIT_CODED_SLICE_IDR,NAL_UNIT_CODED_SLICE_IDR_N_LP,NAL_UNIT_CODED_SLICE_BLA_N_LP,NAL_UNIT_CODED_SLICE_BLANT,NAL_UNIT_CODED_SLICE_BLA中的一种,将解码产生的picture全部清空,没有任何参考帧,相当于一个新的sequence进行解码了。


其他的代码很简单,请自己分析。

HEVC/H.265编码HM码率控制 HEVC/H.265编码标准HM平台码率控制流程 研究生期间了解过HM平台的码率控制过程,现在时隔2年多又回顾了一下HM平台的码控流程,发现相较之前多了CpbSaturationEnabled这么个机制,其开关由参数m_CpbSaturationEnabled控制。粗略了解了一下改工具类似于x265的VBV机制,目的应该是在保证码控精度的情况下防止码率波动过大,也就是buffer的上、下溢,对这个原理不太了解的小伙伴可以看看我的另外一篇博客x265码率控制-VBV更新过程 关于码率控制分类,回顾一下 阅读详情

相关推荐

[小梅哥FPGA] 如何通过动态重配置实现FPGA时钟的实时频率/相位/占空比调整?

PLL和MMCM是实际存在的硬件电路,用以对输入时钟的负面变化,如抖动、频率变化、相位偏移、占空比失真等进行处理。基于该表,用户在初始化Clk_Wiz后,只需要计算好所需的频率、相位、占空比对应的设置值,使用Xil_Out32函数配置对应寄存器即可。通过上述步骤,在平时的开发调试过程中,亦或是一些对于时钟可能有着实时性要求的设计(如数据采集、图像处理)里,我们便可以在无需重新修改设计的前提下,通过AXI4总线来完成对时钟频率、相位的调控,从而达到缩短设计周期以及让设计适应更多场景的目的。

zgmxs的专栏 2495

HEVC/H265 HM10.0 分析(三)TAppDecTop.cpp

在TAppDecTop.cpp  ,最重要的是decode 函数,下面将对其进行分析,是解码上层的一个重要函数。 代码如下,代码后将进行分析。 Void TAppDecTop::decode() { Int poc; TComList* pcListPic = NULL; ifstream bitstreamFile(m_pchBitstr

szu030606的专栏 8876

基于轻量级神经网络MobileNet V2的水果识别种类算法研究

本文阐述了一种基于轻量级卷积神经网络MobileNet V2的水果分类与识别算法研究。为了最大限度地促进机械收获,需要一个检测系统,它可以让我们识别不同类别的水果。一个相对未开发的想法是使用深度学习和人工智能算法,不仅可以检测水果的种类,还可以检测水果是否成熟。这可以证明是有利可图的,因为它可以提供作物生长状况以及成熟和未成熟果实的个体数量,以及决定是否是收获时间的有用信息。第一步对数据进行了预处理,数据预处理技术在众多的深度学习计算中起到了重要作用。

qq_43556680的博客 1640

H.266/VVC代码学习32:VTM5.0解码端最上层函数

解码部分的研究不像编码端那样需要精雕细琢,但如果想研究一个内容划分或选择模式等的最终结果是怎样,那么应该从解码端入手。下面让我们来学习解码端的框架及最上层的三个函数。 文章目录1 main()2 DecApp::decode()3 DecLib::decode() 1 main() 输出时间等信息,调用pcDecApp->decode()开始解码过程。 int main(int argc,...

时间有泪 2508

【亲测有效】OpenClaw Gateway 出现 “(no output)“ 卡死问题完整解决指南:从手动重启到 systemd Watchdog 自动恢复

最近在使用 OpenClaw(自托管 AI 助手框架,前身 Clawdbot/Moltbot)时,遇到了一个非常常见的顽疾:TUI(终端界面)或 Telegram 等通道经常显示 "(no output)",消息发出去后机器人完全没反应,看起来像是卡死了。

hampeter的博客 8609

西门子 标准程序解读---> 机器手抓取和涂胶控制程序

摘要 该程序实现机械手在OP70/OP90工位的自动化控制,核心功能包括: 流程控制:分段(Seg1-Seg11)管理抓件、涂胶、放件等动作,结合DB1050/DB1250数据块监控工位状态及变位机位置反馈。 安全机制:配置干涉区(FC124)、实时检测安全光/急停信号,防止碰撞;服务程序(FB191)支持维修位触发与安全隔离。 参数配置:通过DB块关联机器人速度、节拍(FB27)、车型编码(如空过=2)及手自动模式(DB70.DBX21.2)。 应用场景:适用于汽车焊接/装配线

工控智联驿站 1239

HM解码(一) :提取一个NALU信息

HM解码(一) :提取一个NALU信息

DuoKingg的博客 582

3台虚拟机部署Doris详细文档(含校验步骤)

摘要:本文详细介绍了Apache Doris在3台虚拟机上的集群部署方案。采用1主FE+1从FE+1观察者FE+3BE的混合部署模式,确保元数据高可用性。硬件配置建议测试环境4核16GB内存,生产环境8核64GB内存。部署步骤包括系统环境配置(关闭防火墙、内核参数优化)、FE/BE节点配置启动、集群初始化验证等。重点配置了IP白名单(172.30.0.115)和数据目录(/data/doris)。通过MySQL客户端和WebUI可监控集群状态,实现千万级数据的实时分析能力,后续可通过增加BE节点线性扩展。

weixin_41026747的博客 934

hm编码工具使用_HM安装和使用方法

此文为学习JVET参考了HM的安装方法,转载自岳麓吹雪大牛的博客,膜拜。早期的HM解决方案包含了7个工程:1. TAppCommon 2. TAppDecoder 3. TAppEncoder4. TLibCommon 5. TLibDecoder 6. TLibEncoder 7. TLibVideoIO后来又增加了2个工程:1.TAppDecoderAnalyser 2.TLibDecode...

weixin_29911569的博客 1398

基于FPGA的相控阵雷达波束控制系统设计(2)

也就是在计算波控码时0,按2.8125递增,取遍-90

qq_43416206的博客 1812

Debezium系列之:使用指定值替代指定字段的值

Debezium系列之:使用指定值替代指定字段的值

zhengzaifeidelushang的博客 306

2.H.265/HEVC —— 帧内预测

在H.265/HEVC中,35种预测模式是在PU的基础上定义的,而具体帧内预测过程的实现则是以TU为单位的。编撰规定PU可以以四叉树的形式划分TU,且一个PU内所有TU共享同一种预测模式的形式划分TU,且一个PU内的所有TU共享同一种预测模式。H.265/HEVC帧内预测可分为以下3个步骤: ** 1.判断当前TU相邻参考像素是否可用并做相应的处理 2.对参考像素进行滤波 3.根据滤波后的参考像素...

VS2021的专栏 1127

【图像去噪】基于加权核范数最小化算法实现图像去噪附matlab代码

1 简介 WNNM是一种约束矩阵奇异值稀疏性的低秩算法,在NNM理论基础上增加了核范数权重,有效利用了自然图像的先验信息并提高了核范数的灵活性. 2 部分代码 ​clcclear allclose allnSig = 100;O_Img = double(imread('Monarch.png'));​randn('seed', 0);N_Img = O_Img + nSig* randn(size(O_Img)); ..

qq_59747472的博客 3117

encode函数解读

Void TAppEncTop::encode() {   fstream bitstreamFile(m_pchBitstreamFile, fstream::binary | fstream::out);   if (!bitstreamFile)   {     fprintf(stderr, "\nfailed to open bitstream file `%s' for wr

Villa_7的博客 3793

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

HEVC官方软件HM源代码简单分析-编码器TAppEncoder

上一篇文章总结了HEVC官方参考软件HM中的视频解码器TAppDecoder的结构,本文继续分析HM中的视频编码器TAppEncoder。函数调用关系图HM中的HEVC视频编码器TAppEncoder的函数调用关系图如下所示。单击查看更清晰的大图下面解释一下图中关键标记的含义。函数背景色函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:白色背景的函数:不加区分的普通内部函数。黄色

雷霄骅(leixiaohua1020)的专栏 8万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

HEVC代码追踪(十三):解码之decode

// ==================================================================================================================== // Public member functions // ==================================================

成长Bar的博客专栏 2102

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

从像素到OpenCV实战

本文系统介绍了数字图像处理的基础知识和OpenCV实战技巧。主要内容包括:1)像素作为图像基本单元的特性与分辨率概念;2)RGB和HSV色彩空间的原理及适用场景;3)OpenCV环境配置与核心操作(图像读取、显示、保存、色彩转换);4)像素访问修改、颜色检测等实用技巧;5)常见问题解决方案和性能优化建议。通过代码示例展示了如何获取图像信息、处理不同色彩空间以及开发图像分析工具,为计算机视觉入门提供了实用指南。文章强调实践是掌握图像处理技术的关键,建议读者通过修改参数和实验来加深理解。

程序猿老樊的博客 6771

H.265码流解析

H.265(hevc)码流解析

青山渺渺 4510

HM笔记_1_下载调试

文章目录HM下载HM运行 本文主要参考了 HEVC之路0:HM16.18的运行+码流分析 - 只因有你pi - 博客园 (cnblogs.com) 在此表示感谢。 HM下载 HM不能直接从网页下载,而是采用SVN管理代码的——首先,下载合适版本的TortoiseSVN,这里我们下载的是最新版本1.14.1-64bit: 下载后双击安装,一直点确定就行。安装完成后在要下载HM的文件夹(此处为D:\HM)右键,选择Repo-browser: 在弹出的URL框中输入HM的下载地址:https://hevc.h

weixin_43721070的博客 1019

H265码流分析(一)

H.265标准围绕H.264编码标准,保留原有的某些技术,同时对一些技术进行改进,编码结构大致上和H.264的架构类似。这里着重讲一下两者编码格式的区别。 同H.264一样,H.265也是以NALU的形式组织起来。而在NALU header上,H.264的HALU header是一个字节,而H.265则是两个字节。我们同样假定一个头信息为0x4001作为例子: ...

程序猿老樊的博客 2273

HEVC批处理

bat文本内容: TAppEncoder.exe -c encoder_intra_main.cfg --BitstreamFile=str1.bin --ReconFile=rec1.yuv -c bitsream.cfg --InputFile=C:\Users\Administrator.DESKTOP-FG3J9CU\Desktop\630\TODO\DICOM-JPG-YUV\0001.yuv >1out.txt TAppEncoder.exe -c encoder_intra_main.

weixin_38427663的博客 578

HM编解码

目前还没有学会用批处理的方法编译HM,这里仅介绍使用visual studio直接对HM进行编码的过程,后面批处理的方法学会了再更新。 1.仅使用visual studio进行编码 我使用的编译器是visual studio2019 1.1首先是下载HM,我直接在官网上下载最新版本的HM HM官网:https://hevc.hhi.fraunhofer.de/ 在官网中点击“HM sof...

Tracytang97的博客 3061

音视频之H.265码流分析及解析

 H.265又称为HEVC(全称High Efficiency Video Coding,高效率视频编码,本文统称为H.265),是 ITU-T H.264/MPEG-4 AVC标准的继任者。2004年由ISO/IEC Moving Picture Experts Group(MPEG)和 ITU-T Video Coding Experts Group(VCEG)作为ISO/IEC 23008-2 MPEG-H Part 2或称作ITU-T H.265开 始制定。第一版的HEVC/H.265视频压缩标准在

wangyongyao1989的博客 3434

H265码率解析

Descriptorf(1)u(6)u(6)u(3)H264的NALU type是首字节&0x1f,H265的NALU type是(首字节&0x7E)>>1;/***/

chen_jianjian的专栏 2729

h.265音视频开发,H265码音视频流解析

Descriptorf(1)u(6)u(6)u(3)H264的NALU type是首字节&0x1f,H265的NALU type是(首字节&0x7E)>>1;/***/

liveweb视频汇聚平台 1264
上一篇: HEVC/H265 HM10.0 分析(二)TComDataCU.cpp .
下一篇: 深深筋骨操
NJU-HEVC-SML
博客等级 码龄15年 30粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值