H.264 结构分析

H.264 定义

field:帧的交替行的集合。一帧由两个场组成,一个顶场(top field)和一个底场 (bottom field)。

frame:一个帧包含一个亮度(Y)样本数组和两个对应的色度(CrCb)样本数组。一个帧由两个 field 组成,一个顶场(top field)和一个底场(bottom field)。

  • top field:组成一帧的两个场之一。顶场的每一行在空间上位于底场的相应行的正上方。
  • bottom field:组成一帧的两个场之一。底场的每一行在空间上位于顶场的对应行的正下方。

picture:一个 field 或一个 frame 的统称。

视频信号可按完整的帧序列渐进地采样,或者是被隔行采样为隔行场序列。在交错视频序列中,两个场构成一幅视频帧,一个场包括一个完整视频帧的奇数行或者是偶数行。这种采样方法地优点是,在相同地数据率下,它在每秒钟发送地场数可以是相同渐进序列帧数的两倍,同时能给出更平滑的运动视觉效果。

slice grouppicturemacroblockmacroblock pair 的子集。将 picture 分割 slice group 是对 picture 进行分区。分区由宏块到切片组映射指定(map[macroblock][slicegroup])。

slice:在特定 slice group 内的光栅扫描中 连续排序 的整数个宏块或宏块对。

光栅扫描(RasterScan) 是指从左往右,由上往下,先扫描完一行,再移至下一行起始位置继续扫描,H.264使用的主要就是光栅扫描顺序。

连续排序指的是在同一行连续或跨行时上一行的尾和当前行的头连续,即在扫描的顺序中连续。

  • I slice:使用来自同一 slice 内的解码样本的预测进行解码的 slice,且不是 SI slice 的 slice。
  • P slice:使用来自同一 slice 内的解码样本的帧内预测 或 来自前置解码的参考图片的帧间预测来解码的 slice,最多使用一个运动矢量和参考索引来预测每个块的样本值。
  • B slice:使用来自同一 slice 内的解码样本的帧内预测或来自先前解码的参考图片的帧间预测来解码的切片,使用最多两个运动矢量和参考索引来预测每个块的样本值。
  • SI slice (switching I slice):使用来自同一 slice 内的解码样本的预测和预测样本的量化来编码的 slice。可以对 SI slice 进行编码,使 SI slice 的解码样本的构造与 SP slice 相同。
  • SP slice (switching P slice):使用来自前置解码的参考图片的帧间预测进行编码的切片,最多使用一个运动矢量和参考索引来预测每个块的样本值。可以对 SP slice 进行编码,使 SP slice 的解码样本的构造与另一个 SP 切片或 SI 切片相同。

macroblock :picture 中的一个16x16的像素块,包含一个 16x16 的亮度样本块和两个相应的色度样本块(色度样本块的数量视采样模式而定)。

  • field macroblock :一个包含的 samples 来自单个 field 的 macroblock。一个 coded field 的 macroblocks 都是 field macroblocks 。当使用 macroblock-adaptive frame/field (宏块自适应帧/场)解码时,一个 coded frame 的 macroblocks 可能是 field macroblocks 。
  • frame macroblock :一个包含的 samples 来自一个 coded frame 中两个 fields 的 macroblock 。当不使用 macroblock-adaptive frame/field (宏块自适应帧/场)解码时,coded frame 的所有宏块都是 frame macroblock 。当使用 macroblock-adaptive frame/field (宏块自适应帧/场)解码时,编码帧的一些宏块可能是frame macroblock,也可能是 field macroblocks。

macroblock pair :picture 中一对垂直连续宏块。

  • field macroblock pair :解码为两个场宏块的宏块对。
  • frame macroblock pair :解码为两个帧宏块的宏块对。

macroblock to slice group map :一种将 picture 的 **macroblock ** 映射到 slice group 的方法。macroblock 到 slice group 的映射由一个数字列表组成,每个编码宏块一个,指定每个编码宏块所属的 slice group。

skipped macroblock :没有数据被编码的 macroblock ,且指示该 macroblock 在解码时跳过。这个指示可能对几个宏块是共同的。

macroblock partition :由 帧间预测的宏块划分 产生的亮度样本块和两个对应的色度样本块。

sub-macroblock :一个宏块的四分之一样本,即一个8x8的亮度块和对应的两个色度块。

sub-macroblock partition :由 帧间预测的子宏块划分 产生的亮度样本块和两个对应的色度样本块。

top macroblock of a macroblock pair :宏块对的顶部宏块,简称 top macroblock。包含 macroblock pair 的 top row (顶行)的样本(亮度样本和色度样本)。

  • field macroblock pair ,top macroblock 表示 frame 的 top field 的 samples。
  • frame macroblock pair ,top macroblock 表示 macroblock pair 上半部分 的 samples。

coded field:field 的编码表示。

coded frame:frame 的编码表示。

coded picture:picture 的编码表示。coded picture 可以是一个 coded field 或是 一个 coded frame 。Coded picture 是主编码图片(primary coded picture)或冗余编码图片(redundant coded picture)的一个总称,但不能同时指两者。

coded video sequence:a sequence of access units,由按解码顺序排序的 IDR 访问单元(IDR access unit)及其后跟的零个或多个非 IDR 访问单元(non-IDR access units)组成。

access unit:一组 包含 primary coded picture 的 NAL 单元。除了 primary coded picture 之外,一个 access unit 也可能包含一个或多个 redundant coded pictures 或 其他不包含 slices 或 slices data partitions 的 coded picture。access unit 的解码结果是一个 decoded picture 。

  • primary coded picture:primary picture 的编码表示(符合H.264标准的可被正确解码的 bistream)。primary coded picture 包含一张 picture 的所有宏块。
  • redundant coded picture :primary picture 的 redundant picture 的编码表示 。redundant 即冗余,用于增强 primary picture的错误恢复能力。
    • redundant coded picture 通常覆盖 primary picture 的一部分,因此不需要包含 primary coded picture 中的所有宏块。
    • 当 primary coded picture 的 bitstream 在传输中出错时(即 bitstream 不符合H.264标准),才对 redundant coded picture 进行解码,并通过 redundant picture 恢复 primary picture。
    • 通常只有 IDR picture 附有 redundant picture,因为 IDR picture 的损坏可能会破坏整个GOP。
    • 顺便说一下,每个 primary coded picture 可能有多达127个 redundant pictures。因为单个access unit 最多可包含128张图片。

IDR access unitprimary coded picture 是 IDR picture 的访问单元。

IDR picture: slices 全部是 I slice 或 SI slice 类型的 coded picture 。在解码 IDR 图片后,解码进程将所有参考图片标记为"unused for reference"。在对 IDR 图片进行解码之后,可以对解码顺序中的所有后续 coded picture 进行解码,而无需从在 IDR 图片之前解码的任何图片进行帧间预测。每个 coded video sequence 的第一张图片是 IDR picture

IDR(instantaneous decoding refresh):瞬时解码刷新。

random access : 随机访问:在比特流的开始点以外的点开始解码过程的行为流。

reference field:当 coded field 采用 P, SP 和 B slices 编码 或者 coded frame 采用 field macroblocks 编码时,reference field 可被用于帧间预测。

reference frame:当 coded frame 采用 P, SP 和 B slices 编码时,reference field 可被用于帧间预测。

reference picture:nal_ref_idc 不等于 0 的 picture (reference picture 表示当前 picture 被别的 coded picture 引用)。reference picture 包含的样本可用于在解码顺序的后续图片的解码过程中进行帧间预测。

reference index:reference picture 的索引列表。


syntax element:语法元素:比特流中表示的数据元素。

syntax structure:语法结构:零个或多个语法元素以特定的顺序一起出现在比特流中。

picture parameter set :一种语法结构,包含适用于零个或多个完整编码图片的语法元素,由每个 slice header 中的 pic_parameter_set_id 确定。

sequence parameter set :一种语法结构,包含适用于零个或多个完整编码视频序列的语法元素,由每个 slice header 中的 pic_parameter_set_id 的 picture parameter set 的 seq_parameter_set_id 确定。

NAL Unit :一种语法结构,包含指示要遵循的数据类型(nal unit type 指示 RBSP的数据类型)和包含该数据的 RBSP 形式的字节数据(必要时增加防竞争字节)。

RBSP(raw byte sequence payload):一种语法结构,包含整数个字节,封装在 NAL 单元中。 RBSP 要么是空的,要么是表示一个或多个语法元素的 SODB,后跟一个 RBSP stop bit,然后是零个或多个等于 0 的后续位。

RBSP stop bit:RBSP 中位于 SODB 后的一个等于 1 的 bit 。可以通过从 RBSP 的末尾搜索 RBSP 的 stop bit 来识别 RBSP 中的 SODB 的结束。RBSP stop bit 是 RBSP 中的最后一个非零位。

SODB(string of data bits):表示一个或多个语法元素的若干位序列,SODB 存在于 RBSP 内,在 RBSP stop bit 之前。在 SODB 中,最左边的位被认为是第一个和最高有效位,最右边的位被认为是最后一个和最低有效位。


NAL unit

nal_unit( NumBytesInNALunit ) { 
    forbidden_zero_bit   /* 1 bit  */
    nal_ref_idc          /* 2 bits */
    nal_unit_type        /* 5 bits */
    NumBytesInRBSP = 0 
    for( i = 1; i < NumBytesInNALunit; i++ ) {
        if( i + 2 < NumBytesInNALunit && next_bits( 24 ) = = 0x000003 ) {
            rbsp_byte[ NumBytesInRBSP++ ] 
            rbsp_byte[ NumBytesInRBSP++ ] 
            i += 2 
            emulation_prevention_three_byte /* equal to 0x03 (跳过防竞争字节)*/
        } else {
            rbsp_byte[ NumBytesInRBSP++ ]
        }
    }
}
  • forbidden_zero_bit :应该等于 0
  • nal_ref_idc :不等于 0 意味着 NAL unit 的内容为一个 sequence parameter set 或 一个 picture parameter set 或 一个 refrence picture 的 slice 或者 一个 reference picture 的 slice data partition 。
  • nal_unit_type:指示 NAL unit 中的 RBSP data structure。
    • VCL NAL units 的 nal_unit_type 等于 1 to 5 。剩余的 NAL units 为 non-VCL NAL units 。
NAL unit type codes

RBSP

Slice layer without partitioning syntax:包含 slice_data( )
的所有类别。

slice_layer_without_partitioning_rbsp( ) { 
    slice_header( )
    slice_data( ) /* all categories of slice_data( ) syntax */ 
    rbsp_slice_trailing_bits( ) 
}

slice data partitioning:一种基于与每个语法元素相关联的类别将所选语法元素 划分 为语法结构的方法。 当使用 slice data partitioning 时,单个 slice 的编码数据被分成三个单独的分区:partition A包含类别 2 的所有语法元素,partition B 包含类别 3 的所有语法元素,partition C 包含类别 4 的所有语法元素。如果语法元素未区分类别,则所有 partiion 都包含该语法元素。partition 的区别在于 slice_data( ) 中的 macroblock_layer( ) 中的 residual( )。详见:T-REC-H.264-200305-S!!PDF-E.pdf 中的 7.3.4 Slice data syntax - 7.3.5.3.2 Residual block CABAC syntax 其中,C 列 表示 Category (类别)。

slice_data( ) {
    ...
    do {
        ... /* all */
        ...  /* category 2 */
        if( moreDataFlag ) {
            ...
            macroblock_layer( )  /* category 2, 3, 4 */
        }
        ...
    } while( moreDataFlag ) 
}

macroblock_layer( ) {
    ... /* all */
    ... /* category 2 */
    if( CodedBlockPatternLuma > 0 | | CodedBlockPatternChroma > 0 
        || MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) {
        residual( )  /* category 3, 4 */
    }
}

residual( ) { 
    ...   /* category 3 */
    ...   /* category 3, 4 */
}

Slice data partition A RBSP semantics:slice_data( ) 含类别 2 的 slice_data( ) 的语法元素。Slice data partition A RBSP syntax:

slice_data_partition_a_layer_rbsp( ) { 
    slice_header( ) 
    slice_id 
    slice_data( ) /* only category 2 parts of slice_data( ) syntax */
    rbsp_slice_trailing_bits( ) 
}

Slice data partition B RBSP semantics:slice_data( ) 含类别 3 的语法元素。Slice data partition B RBSP syntax:

slice_data_partition_b_layer_rbsp( ) { 
    slice_id 
    if( redundant_pic_cnt_present_flag ) 
        redundant_pic_cnt
    slice_data( ) /* only category 3 parts of slice_data( ) syntax */
    rbsp_slice_trailing_bits( ) 
}

Slice data partition C RBSP semantics:slice_data( ) 含类别 4 的语法元素。Slice data partition C RBSP syntax:

slice_data_partition_c_layer_rbsp( ) { 
    slice_id 
    if( redundant_pic_cnt_present_flag ) 
        redundant_pic_cnt
    slice_data( ) /* only category 4 parts of slice_data( ) syntax */
    rbsp_slice_trailing_bits( ) 
}

slice header:coded slice 的 header(包含 slice 的相关信息)。
slice header syntax elements

  • first_mb_in_slice : 指定 slice 中第一个宏块的地址。当 Annex A 中规定不允许任意 slice 顺序时,first_mb_in_slice 的值不得小于当前 picture 中解码顺序位于当前切片之前的任何其他切片的 first_mb_in_slice 的值。slice 的第一个宏块地址导出如下。

    • 如果 MbaffFrameFlag 等于 0 , first_mb_in_slice 是 slice 中第一个宏块的宏块地址,first_mb_in_slice 的范围为 0 to PicSizeInMbs - 1
    • 如果 MbaffFrameFlag 等于 1 , first_mb_in_slice * 2 是 slice 中第一个宏块的宏块地址,这是 slice 中第一个宏块对的 top macroblock,first_mb_in_slice 的范围为 0 to PicSizeInMbs/2 - 1
  • slice_type :

    slice type

  • pic_parameter_set_id:指定使用的 picture parameter set。

  • frame_num:用作 picture 的标识符,应由 bitstream 中的 log2_max_frame_num_minus4 + 4 bits 进行表示,frame_num 的约束如下:

    • 定义变量 PrevRefFrameNum
      • 如果当前 picture 是 IDR picture,则 PrevRefFrameNum = 0
      • 否则,PrevRefFrameNum 顾名思义:previous reference frame frame_num
    • frame_num 的值约束如下:
      • frame_num = 0 : picture 是 IDR picture 。
      • frame_num == PrevRefFrameNum , 以下三个条件为 true 时成立
        • 当前图片和前面的参考图片在解码顺序上属于连续的访问单元。
        • 当前图片和前面的参考图片是具有相反奇偶性的参考场
        • 以下任意条件为真
          • 前面的参考图片是 IDR 图片
          • 前面的参考图片包括一个 memory_management_control_operation 语法元素等于 5。(当前面的参考图片包括等于5的memory_management_control_operation语法元素时,PrevRefFrameNum等于0。)
          • 在前面的参考图片之前有一个 primary coded picture,并且在前面的参考图片之前的 primary coded picture 的 frame_num 不等于 PrevRefFrameNum 。
          • 在前面的参考图片之前有一个 primary coded picture,并且在前面的参考图片之前的 primary coded picture 不是参考图片 。
      • frame_num != PrevRefFrameNum
        • used for short-term reference 的 field 或 frame 的 frame_num 为 UnusedShortTermFrameNum = ( PrevRefFrameNum + 1 ) % MaxFrameNum
        • frame_num 的值约束如下:
          • 如果 gaps_in_frame_num_value_allowed_flag 等于 0,则当前图片的 frame_num 的值应等于 ( PrevRefFrameNum + 1 ) % MaxFrameNum
          • 如果 gaps_in_frame_num_value_allowed_flag 等于 1,则:
            • 如果 frame_num > PrevRefFrameNum,则在以下任一条件为真的解码顺序中,比特流中不应有任何非参考图片在前一个参考图片之后和当前图片之前进行解码。
              • 非参考图片的 frame_num 的值小于 PrevRefFrameNum。
              • 非参考图片的 frame_num 的值大于当前图片的 frame_num 的值。
            • 否则 frame_num < PrevRefFrameNum,在以下两个条件都为真的情况下,比特流中不应有任何非参考图片在前一个参考图片之后和当前图片之前的解码顺序。
              • 非参考图片的 frame_num 的值小于 PrevRefFrameNum。
              • 非参考图片的 frame_num 的值大于当前图片的 frame_num 的值。
  • field_pic_flag:等于 1 时指示 slice 是 coded field 的 slice。

  • bottom_field_flag:等于 1 时 指示 slice 是 coded bottom field 的 slice 。

  • idr_pic_id identifies:指示是 IDR picture 的 slice 。

其中, pic_parameter_set_id, frame_num, field_pic_flag, bottom_field_flag, idr_pic_id, pic_order_cnt_lsb, delta_pic_order_cnt_bottom, delta_pic_order_cnt[ 0 ], delta_pic_order_cnt[ 1 ], sp_for_switch_flag, 和 slice_group_change_cycle 的值在同一个 coded picture 的所有 slice header 中应相同。

slice_id:标识与 data partition 关联的 slice。每个 slice 在 所属的 coded picture 内具有唯一的 slice_id。当 Annex A 中规定不允许任意切片顺序时,在解码顺序中,编码图像的第一个切片的 slice_id 应等于 0,并且对于coded picture 的每个后续切片, slice_id 的值应以 1 进行递增。

H.264 视频分层结构

在H.264中,语法元素被组织成五个层次:

  • 序列 (sequence)
  • 图像 (picture => frame or filed)
  • 分片 (slice)
  • 宏块 (macroblock)
  • 子块 (sub-block)
H.264_video_structure
  • access unit: A set of NAL units always containing a primary coded picture.
  • coded video sequence: A sequence of access units that consists.
    • in decoding order, of an instantaneous decoding refresh (IDR) access unit followed by zero or more non-IDR access units including all subsequent access units up to but not including any subsequent IDR access unit.
  • IDR access unit: An access unit in which the primary coded picture is an IDR picture.
  • IDR picture: A coded picture containing only slices with I or SI slice types that causes a "reset" in the decoding process

H.264 码流分层结构

RBSP Sequence

NAL unit 和 coded picture 的顺序以及与访问单元的关联

在 primary coded picture 的最后一个 VCL NAL unit 之后,首个以下的任何 NAL unit 指定新访问单元的开始:

  1. access unit delimiter NAL unit (when present)
  2. sequence parameter set NAL unit (when present)
  3. picture parameter set NAL unit (when present)
  4. SEI NAL unit (when present)
  5. NAL units with nal_unit_type in the range of 13 to 18, inclusive
  6. first VCL NAL unit of a primary coded picture (always present)

访问单元内的 coded picture 和非 VCL NAL 单元的顺序应遵守以下约束:

  1. 当存在 access unit delimiter(间隔符) NAL unit 时,它应该是第一个 NAL 单元。任何访问单元中最多有一个 access unit delimiter NAL unit 。
  2. 当存在任何 SEI NAL units 时,它们应位于 primary coded picture 之前。
  3. 当存在包含 buffering period SEI message 的 SEI NAL unit 时,buffering period SEI message 应是该访问单元中第一个 SEI NAL unit 的第一个 SEI message payload 。
  4. primary coded picture 应在相应的 redundant coded pictures 之前。
  5. 当存在 redundant coded pictures 时,它们应按redundant_pic_cnt 值的升序排列。
  6. 当 end of sequence NAL unit 出现时,它应跟随在 primary coded picture 和所有 redundant coded pictures(如果有)之后。
  7. 当end of stream NAL unit 存在时,它应是最后一个 NAL unit。
  8. nal_unit_type 等于 0、12 或在 19 到 31 范围内(含)的 NAL unit 不得位于 primary coded picture 的第一个 VCL NAL 之前。
  9. Sequence parameter set NAL units 或 picture parameter set NAL units 可能存在于访问单元中。

H.264 封装格式

H.264 Package format

Annexb 格式

Annexb 格式主要用于实时播放(直播流),每一个NAL单元前面都有一个 StartCode (起始码)。
一共有两种起始码 start_code

  • 0x000001 3 字节,用在单帧多 slice(即单帧多个NALU)之间间隔。
  • 0x00000001 4 字节,用在帧之间,或者SPS等之前。

防字节竞争处理:用起始码定位NALU边界存在一个问题,即NALU中可能存在与起始码相同的数据。为了防止这个问题,在构建NALU时,需要在数据中的0x000000,0x000001,0x000002,0x000003中插入防竞争字节(Emulation Prevention Bytes)0x03 。

AVCC 格式

AVCC 格式主要用于存储(如存储在硬盘的文件:FLV、MP4、MKV通常用AVCC格式来存储。),AVCC格式不使用起始码作为NALU的分界,AVCC在每个NALU前都加上一个大端格式的前缀(1、2、4字节,代表NALU长度)。

在解析 AVCC 格式的时候需要将指定的前缀字节数的值保存在一个头部对象中,这个都通常称为 sequence header 。同时,SPS 和 PPS 数据也需要保存在 sequence header 中。(这些数据的存储和传输是文件容器的任务(即 FLV 中的 AVC Sequence header),超出了本文的范畴)。

虽然 AVCC 格式不使用起始码,但防竞争字节还是有的。因此在构建 NALU 时,同样在数据中的0x000000,0x000001,0x000002,0x000003中插入防竞争字节(Emulation Prevention Bytes) 0x03 。

RTP 格式

RTP 格式主要用于网络传输(例如:RTC)。而在IP网络中,当要传输的IP报文大小超过 最大传输单元MTU 时就会产生IP分片情况。若交给底层协议拆包容易出问题,因此此时需要主动拆分NALU再打包成RTP包后发送。

RTP 的 NALU

  • Single NAL Unit Packet: payload 中仅包含一个 NAL 单元。 NAL header 的 type 等于原始 NAL 单元类型。

  • Aggregation packet:用于将多个 NAL 单元聚合为单个 RTP payload 的数据包类型。

  • Fragmentation unit: 用于将单个 NAL 单元分段到多个 RTP 数据包上。

      Type   Packet    Type name                       
      -------------------------------------------------
      0      undefined                                   
      1-23   NAL unit  Single NAL unit packet per H.264 
      24     STAP-A    Single-time aggregation packet   
      25     STAP-B    Single-time aggregation packet  
      26     MTAP16    Multi-time aggregation packet    
      27     MTAP24    Multi-time aggregation packet    
      28     FU-A      Fragmentation unit           
      29     FU-B      Fragmentation unit              
      30-31  undefined                              

详见:rfc3984

思考

为什么需要多总格式?

媒体可分为本地文件和直播流:

  • 如果是本地文件,则我们只需要读取一次SPS,PPS的信息,然后就可以一直进行解码,所以将SP,PPS等信息放到文件的头部,打开文件,先读取这些信息初始化解码器,就可以顺利的解码。如 FLV,MP4,MKV 等常用的本地存储方式采用 AVCC 封装 NALU。

  • 如果是直播流,如果将SPS,PPS放到头部,那么中途播放的用户无法接收到SPS ,PPS信息,导致无法初始化解码器,所以必须每隔一段时间发送一次SPS,PPS等信息,一般是放在IDR帧之前进行发送,如 mpeg-ts 直播流采用 Annex-B 结构封装 NALU。

H.264 in FLV

在 NAL 中没有 frame 的概念,NAL 中包含的图像数据其实是 frame 的 Slice,一个 或 多个 Slice 构成 一个 frame。

H.264 在 FLV 中的 Frame 实际是一个包含 AVCPacket 的 FLV Video Tag 包含了一个 或 多个 NALU (即一个 或 多个 Slice)。比如 I Frame 由一个 或 多个 I Slice 构成。

FLV Video Tag 中的 AVCPacket 是使用 AVCC 格式封装的(AVCC在每个NALU前都加上一个大端格式的前缀代表NALU长度),因此可以知道一个 AVCPacket 中每个 NALU 的长度,从而解出每个 NALU 中的 Slice,从而得出一帧。

参考文献

T-REC-H.264-200305-S!!PDF-E.pdf

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

推荐阅读更多精彩内容