序列参数集Sequence Paramater Set(SPS)解析

github项目地址:https://github.com/VioletDream-SXZ/H.264AnalysisProject
最好使用linux的,我不知道window那个能不能用。


我们先看一下《新一代视频压缩编码标准--H.264/AVC》第七章里面句法表。(虽然我表示这个表并不全)


sps句法

先理解一下后面那个u,ue,se是什么意思先。

u(v) 读进连续的v个比特,并将它们解释为无符号整数
ue(v) 无符号指数哥伦布熵编码。
se(v) 有符号指数哥伦布熵编码。

其实就算这个说你们也可能是一脸蒙逼。之后再来介绍一下哥伦布编码,你们记住这个东西怎么运算就可以了。

uint32_t x264_analysis_t::Ue(uint8_t * pBuff, uint32_t nLen, uint32_t & nStartBit)
{
  uint32_t nZeroNum = 0;
  while (nStartBit < nLen * 8)
  {
    if(pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
    {
       break;
    }

    nZeroNum ++;
    nStartBit ++;
  }

  nStartBit++;

  uint64_t dwRet = 0;
  for(uint32_t i=0; i<nZeroNum; ++i)
  {
    dwRet <<= 1;
    if(pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
    {
      dwRet += 1;
    }
    nStartBit ++;
  }

  return (1 << nZeroNum) - 1 + dwRet;
}

int x264_analysis_t::Se(uint8_t * pBuff, uint32_t nLen, uint32_t & nStartBit)
{
  int UeVal = Ue(pBuff, nLen,nStartBit);
  double k  = UeVal;
  
  int nValue = ceil(k / 2);
  if(UeVal % 2 == 0)
    nValue = -nValue;
  
  return nValue;
}

uint64_t x264_analysis_t::U(uint8_t * pBuff, uint32_t BitCount, uint32_t & nStartBit)
{
  uint64_t dwRet = 0;
  for(uint32_t i=0; i<BitCount; ++i)
  {
    dwRet <<= 1;
    if(pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
    {
      dwRet += 1;
    }
    nStartBit++;
  }
  
  return dwRet;
}

先给你们一组数据,这是在吃鸡游戏直播里面抓出来的sps数据:

uint8_t buff[] = {
  0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50,
  0x05, 0xba, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10,
  0x00, 0x00, 0x03, 0x03, 0x20, 0xf1, 0x83, 0x19,
  0x60
};

源码里面都有,可以参考一下。
然后就是对这个表里面的元素一一解析,解析......然后就出错了,这个表其实还缺少一些东西,需要加上的数据位于seq_parameter_set_id之后,需要加入以下东西:

seq_parameter_set_rbsp() C Descriptor
if(sps_data->profile_idc == 100 or sps_data->profile_idc == 110 or sps_data->profile_idc == 122 or sps_data->profile_idc == 144)
  chroma_format_idc 0 Ue(v)
if( sps_data->chroma_format_idc == 3 )
  residual_colour_transform_flag 0 U(1)
bit_depth_luma_minus8 0 Ue(v)
bit_depth_chroma_minus8 0 Ue(v)
qpprime_y_zero_transform_bypass_flag 0 U(1)
seq_scaling_matrix_present_flag 0 U(1)
if(sps_data->seq_scaling_matrix_present_flag)
for(int i=0; i<8; i++ )
  seq_scaling_list_present_flag[i] 0 U(1)

之后就可以按照上面的表一步一步解析了。

关于一些变量的含义在下面说明一下:

  1. profile_idc: 指明其所用的档次。
    0x66 = 基本档次
    0x77 = 主要档次
    0x88 = 扩展档次
  2. constraint_setN_flag: 设置需要遵从的规约条件。
  3. seq_parameter_set_id: 指示所引用的序列参数集id。
  4. log2_max_frame_num_minus4: 可以求出frame_num的最大值。其求法如下:
    MaxFrameNum = 2^(log2_max_frame_num_minus4 + 4)
  5. num_ref_frames:指示参考帧队列可能达到的最大长度。
  6. pic_width_in_mbs_minus1:用来求一个图像的宽度。也可以算是分辨率的宽度,求法如下:
    Width = (pic_width_in_mbs_minus1 + 1) * 16
  7. pic_height_in_mbs_minus1:用来求一个图像的长度。也可以算是分辨率的长度,求法如下:
    Height = (pic_height_in_mbs_minus1 + 1) * 16

关于X264源码初始化SPS部分:

void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param )
{
    int csp = param->i_csp & X264_CSP_MASK;

    sps->i_id = i_id;
    sps->i_mb_width = ( param->i_width + 15 ) / 16;
    sps->i_mb_height= ( param->i_height + 15 ) / 16;
    sps->i_chroma_format_idc = csp >= X264_CSP_I444 ? CHROMA_444 :
                               csp >= X264_CSP_I422 ? CHROMA_422 :
                               csp >= X264_CSP_I420 ? CHROMA_420 : CHROMA_400;

    sps->b_qpprime_y_zero_transform_bypass = param->rc.i_rc_method == X264_RC_CQP && param->rc.i_qp_constant == 0;
    if( sps->b_qpprime_y_zero_transform_bypass || sps->i_chroma_format_idc == CHROMA_444 )
        sps->i_profile_idc  = PROFILE_HIGH444_PREDICTIVE;
    else if( sps->i_chroma_format_idc == CHROMA_422 )
        sps->i_profile_idc  = PROFILE_HIGH422;
    else if( BIT_DEPTH > 8 )
        sps->i_profile_idc  = PROFILE_HIGH10;
    else if( param->analyse.b_transform_8x8 || param->i_cqm_preset != X264_CQM_FLAT || sps->i_chroma_format_idc == CHROMA_400 )
        sps->i_profile_idc  = PROFILE_HIGH;
    else if( param->b_cabac || param->i_bframe > 0 || param->b_interlaced || param->b_fake_interlaced || param->analyse.i_weighted_pred > 0 )
        sps->i_profile_idc  = PROFILE_MAIN;
    else
        sps->i_profile_idc  = PROFILE_BASELINE;

    sps->b_constraint_set0  = sps->i_profile_idc == PROFILE_BASELINE;
    /* x264 doesn't support the features that are in Baseline and not in Main,
     * namely arbitrary_slice_order and slice_groups. */
    sps->b_constraint_set1  = sps->i_profile_idc <= PROFILE_MAIN;
    /* Never set constraint_set2, it is not necessary and not used in real world. */
    sps->b_constraint_set2  = 0;
    sps->b_constraint_set3  = 0;

    sps->i_level_idc = param->i_level_idc;
    if( param->i_level_idc == 9 && ( sps->i_profile_idc == PROFILE_BASELINE || sps->i_profile_idc == PROFILE_MAIN ) )
    {
        sps->b_constraint_set3 = 1; /* level 1b with Baseline or Main profile is signalled via constraint_set3 */
        sps->i_level_idc      = 11;
    }
    /* Intra profiles */
    if( param->i_keyint_max == 1 && sps->i_profile_idc >= PROFILE_HIGH )
        sps->b_constraint_set3 = 1;

    sps->vui.i_num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;
    /* extra slot with pyramid so that we don't have to override the
     * order of forgetting old pictures */
    sps->vui.i_max_dec_frame_buffering =
    sps->i_num_ref_frames = X264_MIN(X264_REF_MAX, X264_MAX4(param->i_frame_reference, 1 + sps->vui.i_num_reorder_frames,
                            param->i_bframe_pyramid ? 4 : 1, param->i_dpb_size));
    sps->i_num_ref_frames -= param->i_bframe_pyramid == X264_B_PYRAMID_STRICT;
    if( param->i_keyint_max == 1 )
    {
        sps->i_num_ref_frames = 0;
        sps->vui.i_max_dec_frame_buffering = 0;
    }

    /* number of refs + current frame */
    int max_frame_num = sps->vui.i_max_dec_frame_buffering * (!!param->i_bframe_pyramid+1) + 1;
    /* Intra refresh cannot write a recovery time greater than max frame num-1 */
    if( param->b_intra_refresh )
    {
        int time_to_recovery = X264_MIN( sps->i_mb_width - 1, param->i_keyint_max ) + param->i_bframe - 1;
        max_frame_num = X264_MAX( max_frame_num, time_to_recovery+1 );
    }

    sps->i_log2_max_frame_num = 4;
    while( (1 << sps->i_log2_max_frame_num) <= max_frame_num )
        sps->i_log2_max_frame_num++;

    sps->i_poc_type = param->i_bframe || param->b_interlaced || param->i_avcintra_class ? 0 : 2;
    if( sps->i_poc_type == 0 )
    {
        int max_delta_poc = (param->i_bframe + 2) * (!!param->i_bframe_pyramid + 1) * 2;
        sps->i_log2_max_poc_lsb = 4;
        while( (1 << sps->i_log2_max_poc_lsb) <= max_delta_poc * 2 )
            sps->i_log2_max_poc_lsb++;
    }

    sps->b_vui = 1;

    sps->b_gaps_in_frame_num_value_allowed = 0;
    sps->b_frame_mbs_only = !(param->b_interlaced || param->b_fake_interlaced);
    if( !sps->b_frame_mbs_only )
        sps->i_mb_height = ( sps->i_mb_height + 1 ) & ~1;
    sps->b_mb_adaptive_frame_field = param->b_interlaced;
    sps->b_direct8x8_inference = 1;

    x264_sps_init_reconfigurable( sps, param );

    sps->vui.b_overscan_info_present = param->vui.i_overscan > 0 && param->vui.i_overscan <= 2;
    if( sps->vui.b_overscan_info_present )
        sps->vui.b_overscan_info = ( param->vui.i_overscan == 2 ? 1 : 0 );

    sps->vui.b_signal_type_present = 0;
    sps->vui.i_vidformat = ( param->vui.i_vidformat >= 0 && param->vui.i_vidformat <= 5 ? param->vui.i_vidformat : 5 );
    sps->vui.b_fullrange = ( param->vui.b_fullrange >= 0 && param->vui.b_fullrange <= 1 ? param->vui.b_fullrange :
                           ( csp >= X264_CSP_BGR ? 1 : 0 ) );
    sps->vui.b_color_description_present = 0;

    sps->vui.i_colorprim = ( param->vui.i_colorprim >= 0 && param->vui.i_colorprim <= 12 ? param->vui.i_colorprim : 2 );
    sps->vui.i_transfer  = ( param->vui.i_transfer  >= 0 && param->vui.i_transfer  <= 18 ? param->vui.i_transfer  : 2 );
    sps->vui.i_colmatrix = ( param->vui.i_colmatrix >= 0 && param->vui.i_colmatrix <= 14 ? param->vui.i_colmatrix :
                           ( csp >= X264_CSP_BGR ? 0 : 2 ) );
    if( sps->vui.i_colorprim != 2 || sps->vui.i_transfer != 2 || sps->vui.i_colmatrix != 2 )
        sps->vui.b_color_description_present = 1;

    if( sps->vui.i_vidformat != 5 || sps->vui.b_fullrange || sps->vui.b_color_description_present )
        sps->vui.b_signal_type_present = 1;

    /* FIXME: not sufficient for interlaced video */
    sps->vui.b_chroma_loc_info_present = param->vui.i_chroma_loc > 0 && param->vui.i_chroma_loc <= 5 &&
                                         sps->i_chroma_format_idc == CHROMA_420;
    if( sps->vui.b_chroma_loc_info_present )
    {
        sps->vui.i_chroma_loc_top = param->vui.i_chroma_loc;
        sps->vui.i_chroma_loc_bottom = param->vui.i_chroma_loc;
    }

    sps->vui.b_timing_info_present = param->i_timebase_num > 0 && param->i_timebase_den > 0;

    if( sps->vui.b_timing_info_present )
    {
        sps->vui.i_num_units_in_tick = param->i_timebase_num;
        sps->vui.i_time_scale = param->i_timebase_den * 2;
        sps->vui.b_fixed_frame_rate = !param->b_vfr_input;
    }

    sps->vui.b_vcl_hrd_parameters_present = 0; // we don't support VCL HRD
    sps->vui.b_nal_hrd_parameters_present = !!param->i_nal_hrd;
    sps->vui.b_pic_struct_present = param->b_pic_struct;

    // NOTE: HRD related parts of the SPS are initialised in x264_ratecontrol_init_reconfigurable

    sps->vui.b_bitstream_restriction = !(sps->b_constraint_set3 && sps->i_profile_idc >= PROFILE_HIGH);
    if( sps->vui.b_bitstream_restriction )
    {
        sps->vui.b_motion_vectors_over_pic_boundaries = 1;
        sps->vui.i_max_bytes_per_pic_denom = 0;
        sps->vui.i_max_bits_per_mb_denom = 0;
        sps->vui.i_log2_max_mv_length_horizontal =
        sps->vui.i_log2_max_mv_length_vertical = (int)log2f( X264_MAX( 1, param->analyse.i_mv_range*4-1 ) ) + 1;
    }

    sps->b_avcintra = !!param->i_avcintra_class;
    sps->i_cqm_preset = param->i_cqm_preset;
}

从源码中,我们也可以反过来解析nalu单元里面的SPS内容。

解析SPS相关函数:

bool x264_analysis_t::h264_decode_sps(uint8_t * buf, uint32_t nLen, GY::x264_SPS_t * sps_data)
{
  uint32_t startBit = 0;
  de_emulation_prevention(buf, &nLen);
  
  sps_data->forbidden_zero_bit = U(buf, 1, startBit);
  sps_data->nal_ref_idc        = U(buf, 2, startBit);
  sps_data->nal_uint_type      = U(buf, 5, startBit);
  
  if(sps_data->nal_uint_type == 7)
  {
    sps_data->profile_idc          = U(buf, 8, startBit);
    sps_data->constraint_set0_flag = U(buf, 1, startBit);
    sps_data->constraint_set1_flag = U(buf, 1, startBit);
    sps_data->constraint_set2_flag = U(buf, 1, startBit);
    sps_data->reserved_zero_5bits  = U(buf, 5, startBit);
    sps_data->level_idx            = U(buf, 8, startBit);
    
    sps_data->seq_parameter_set_id      = Ue(buf, nLen, startBit);
    
    if(sps_data->profile_idc == 100 || sps_data->profile_idc == 110 ||
       sps_data->profile_idc == 122 || sps_data->profile_idc == 244 ||
       sps_data->profile_idc == 44  || sps_data->profile_idc == 83  ||
       sps_data->profile_idc == 86  || sps_data->profile_idc == 118 ||  
       sps_data->profile_idc ==128)
    {
      sps_data->chroma_format_idc=Ue(buf, nLen, startBit);
      if( sps_data->chroma_format_idc == 3 )
        sps_data->residual_colour_transform_flag=U(buf, 1, startBit);
      sps_data->bit_depth_luma_minus8                = Ue(buf,nLen,startBit);
      sps_data->bit_depth_chroma_minus8              = Ue(buf,nLen,startBit);
      sps_data->qpprime_y_zero_transform_bypass_flag = U(buf, 1, startBit);
      sps_data->seq_scaling_matrix_present_flag      = U(buf, 1,startBit);
      if(sps_data->seq_scaling_matrix_present_flag)
      {
        for(int i=0; i<8; i++ )
        {
          sps_data->seq_scaling_list_present_flag[i] = U(buf, 1, startBit);
        }
      }
    }
    
    sps_data->log2_max_frame_num_minus4 = Ue(buf, nLen, startBit);
    sps_data->pic_order_cnt_type        = Ue(buf, nLen, startBit);
    
    if(sps_data->pic_order_cnt_type == 0)
      sps_data->log2_max_pic_order_cnt_lsb_minus4 = Ue(buf, nLen, startBit);
    else if(sps_data->pic_order_cnt_type == 1)
    {
      sps_data->delta_pic_order_always_zero_flag = U(buf, 1, startBit);
      sps_data->offset_for_non_ref_pic           = Se(buf, nLen, startBit);
      sps_data->offset_for_top_to_bottom_field   = Se(buf, nLen, startBit);
      sps_data->num_ref_frames_int_pic_order_cnt_cycle = Ue(buf, nLen, startBit);
      for(int i=0; i<sps_data->num_ref_frames_int_pic_order_cnt_cycle; ++i)
        sps_data->offset_for_ref_frame[i] = Se(buf, nLen, startBit);
    }
    
    sps_data->num_ref_frames                       = Ue(buf, nLen, startBit);
    sps_data->gaps_in_frame_num_value_allowed_flag = U(buf, 1, startBit);
    sps_data->pic_width_in_mbs_minus1              = Ue(buf, nLen, startBit);
    sps_data->pic_height_in_mbs_minus1             = Ue(buf, nLen, startBit);
    sps_data->frame_mbs_only_flag                  = U(buf, 1, startBit);
    
    if(!sps_data->frame_mbs_only_flag)
      sps_data->mb_adaptive_frame_field_flag = U(buf, 1, startBit);
    sps_data->direct_8x8_inference_flag = U(buf, 1, startBit);
    sps_data->frame_cropping_flag       = U(buf, 1, startBit);
    
    if(sps_data->frame_cropping_flag)
    {
      sps_data->frame_crop_left_offset  = Ue(buf, nLen, startBit);
      sps_data->frame_crop_right_offset = Ue(buf, nLen, startBit);
      sps_data->frame_crop_top_offset   = Ue(buf, nLen, startBit);
      sps_data->frame_crop_bottom_offset = Ue(buf, nLen, startBit);
    }
    sps_data->vui_parameters_present_flag = U(buf, 1, startBit);
    
    if(sps_data->vui_parameters_present_flag)
    {
      sps_data->aspect_ratio_info_present_flag = U(buf, 1, startBit);
      if(sps_data->aspect_ratio_info_present_flag)
      {
        sps_data->aspect_ratio_idc = U(buf, 8, startBit);
        if(sps_data->aspect_ratio_idc == 255)
        {
          sps_data->sar_width  = U(buf, 16, startBit);
          sps_data->sar_height = U(buf, 16, startBit);
        }
      }
      
      sps_data->overscan_info_present_flag = U(buf, 1,startBit);
      if(sps_data->overscan_info_present_flag)
        sps_data->overscan_appropriate_flagu = U(buf, 1, startBit);
      
      sps_data->video_signal_type_present_flag = U(buf, 1, startBit);
      if (sps_data->video_signal_type_present_flag)
      {
        sps_data->video_format = U(buf, 3, startBit);
        sps_data->video_full_range_flag = U(buf, 1, startBit);
        sps_data->colour_description_present_flag = U(buf, 1, startBit);
        if (sps_data->colour_description_present_flag)
        {
          sps_data->colour_primaries=U(buf, 8, startBit);
          sps_data->transfer_characteristics=U(buf, 8, startBit);
          sps_data->matrix_coefficients=U(buf, 8, startBit);
        }
      }
      
      sps_data->chroma_loc_info_present_flag = U(buf, 1, startBit);
      if(sps_data->chroma_loc_info_present_flag)
      {
        sps_data->chroma_sample_loc_type_top_field    = Ue(buf, nLen, startBit);
        sps_data->chroma_sample_loc_type_bottom_field = Ue(buf, nLen, startBit);
      }
      
      sps_data->timing_info_present_flag = U(buf, 1, startBit);
      if(sps_data->timing_info_present_flag)
      {
        sps_data->num_units_in_tick = U(buf, 32, startBit);
        sps_data->time_scale = U(buf, 32, startBit);
        sps_data->fixed_frame_rate_flag = U(buf, 1, startBit);
      }
    }

    return true;
  }
  else
  {
    return false;
  }
}

关于该函数的定义:

  /**
   * 该函数用于解析h.264中的序列参数集的变量
   * @param buf : 需要解析的h.264码流数据
   * @param nLen: 需要解析的h.264码流数据的总长度
   * @param sps_data: 解析的结果放于该结构体中。
   */
  static bool h264_decode_sps(uint8_t *buf,uint32_t nLen, GY::x264_SPS_t *sps_data);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • 参考《新一代视频压缩编码标准 毕厚杰 第7章 H.264的句法和语义》【H.264/AVC视频编解码技术详解】 九...
    合肥黑阅读 3,271评论 0 1
  • 超高速音视频编码器用法: ffmpeg [options] [[infile options] -i infile...
    吉凶以情迁阅读 4,516评论 0 4
  • 使用RTP传输H264的时候,需要用到sdp协议描述,其中有两项:Sequence Parameter Sets ...
    rogerwu1228阅读 4,172评论 0 8
  • 世界上总有那么多笑话,比如我爱你你爱她… 没有人懂得我,就像我不懂这个世界一样。没有人会懂鲸的孤寂,而...
    兮言柒阅读 285评论 0 0
  • “这是哪的赠书,能否给我也来一本!”帅哥羡慕地问。 “这是奖品,读写20天挑战赛成功践行者的奖...
    甘肃倚窗听雨阅读 565评论 1 4