一个解决WebRTC在iOS模拟器红屏问题的实践

概述

笔者在实现点对点视频通信过程中,遇到了 iOS 模拟器红屏问题。本文主要记录如何解决这一问题。主要对 SDP 进行解读,分享在编辑 SDP 过程中遇到的问题。

背景

随着音视频、云游戏越来越火,一个开源解决方案 WebRTC 成为了众多技术者绕不过的框架。在了解 WebRTC 基础流程后,笔者也萌生了一个想法:使用 WebRTC 实现一个点对点的视频 & 文本单聊程序。根据 WebRTC 的框架能力,视频聊天属于其基础功能。想到就做,笔者开始将想法落地。

环境搭建

WebRTC 虽然可以实现点对点通信,但是在其通道建立过程中,有一个信令交互环节,则必要无法完全脱离服务器,为此我们需要搭建一个信令服务器。

我们可以使用 node.js 来实现一个简单的 websocket 服务器,其只需要做一件事:广播收到的数据。我们还可以选择使用 swift 来实现这样一个简单的信令服务器。

总结一下,我们需要的环境:

  • 1)MacOS 设备,例如 Mac Mini
  • 2)信令服务器一台

遇到问题

在环境搭建完毕,工程实现完成后,急切点开应用程序的视频通话按钮后,看到的是如下图效果

模拟器红屏效果.png

是的,笔者遇到了模拟器上运行视频通话后红屏的问题。

这似乎是 WebRTC 在 H264 上的一个 bug,官方也发现了,所以出了一个 错误报告。该 bug 的具体原因不在本文讨论。而想要解决该问题,则有两种方案。

  • 1)修改源代码

援引 Stack Overflow 上的方案

Fix is to not set attribute 'kCVPixelBufferIOSurfacePropertiesKey' in  RTCVideoDecoderH264.mm for TARGET_IPHONE_SIMULATOR

笔者亲测有效。此方案的麻烦点在于,我们改动完源代码后,需要自行编译,重新生成 WebRTC.framework 文件。

  • 2)修改 SDP 内容

WebRTC 在 iOS 模拟器上会出现红屏问题,主要还是因为框架对 H264 的解码出现的 bug,我们可以通过在模拟器上不使用 H264 来规避此问题。因此,我们可以在应用层编辑 SDP 内容来达到禁用 H264 编码的目的。

SDP 是什么

想要编辑 SDP,我们首先需要了解它。SDP(Session Description Protocol),会话描述的协议,它不包含传输协议。

组成结构

                                                 +---------------------+
                                                 |        v=           |
                                                 +---------------------+
                 +---------------------+         +---------------------+
         ====    |   Session Metadata  |  =====  |        o=           |
         |       +---------------------+         +----------------------
         |                                       +---------------------+
         |                                       |        t=           |
         |                                       +---------------------+
         |
         |
         |                                       +---------------------+
         |                                       |        c=           |
         |                                       +---------------------+
         |       +---------------------+
         ====    | Network Description |   =====
         |       +---------------------+
         |                                       +---------------------+
         |                                       |    a=candidate      |
         |                                       +---------------------+
         |
         |
         |                                       +---------------------+
         |                                       |        m=           |
         |                                       +---------------------+
         |        +---------------------+        +---------------------+
         ====     | Stream Description  |  ===== |      a=rtpmap       |
         |        +---------------------+        +----------------------
         |                                       +---------------------+
         |                                       |      a=fmtp         |
         |                                       +---------------------+
         |                                       +---------------------+
         |                                       |      a=sendrecv..   |
         |                                       +---------------------+
 +---------------+
 |    SEMANTIC   |
 | COMPONENTS OF |
 |     SDP       |
 +---------------+
         |                                       +---------------------+
         |                                       |      a=crypto       |
         |                                       +---------------------+
         |         +---------------------+       +---------------------+
         ====      |Security Descriptions|  =====|      a=ice-frag     |
         |         +---------------------+       +----------------------
                 |                                       +---------------------+
         |                                       |      a=ice-pwd      |
         |                                       +---------------------+
         |                                       +---------------------+
         |                                       |     a=fingerprint   |
         |                                       +---------------------+
         |
         |
         |
         |                                       +---------------------+
         |                                       |      a=rtcp-fb      |
         |                                       +---------------------+
         |         +---------------------+       +---------------------+
         ====      |   Qos,Grouping      |       |                     |
                   |   Descriptions      |  =====|       a=group       |
                   +---------------------+       +----------------------
                                                 +---------------------+
                                                 |       a=rtcpmux     |
                                                 +---------------------+

从上文可以知道 SDP 有五个部分:

  • Session Metadata
  • Network Description
  • Stream Description
  • Security Descriptions
  • Qos, Grouping Descriptions

解读

下面根据 RTCPeerConnection.createOffer 产生的 SDP 进行解读(RFC4566)。

// SDP 类型,有 offer、answer
offer
// ---------------------- Session Metadata -------------------//
// v = 0  “v =”字段给出SDP的版本,默认为0。
v=0
// o = <username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address> 
// <username> 是用户在始发主机上的登录名
// <sess-id> <sess-id>是一个数字字符串,使得<username><sess-id>nettype><addrtype>和<unicast-address>的元组形成会话的全局唯一标识符。
// <sess-version>是此会话描述的版本号 
// <nettype>是一个给出网络类型的文本字符串。 “IN”被定义为具有“Internet”的含义
// <addrtype>是一个文本字符串,给出了后面的地址类型 定义了“IP4”和“IP6”
// <unicast-address>是创建会话的计算机的地址。 
o=- 4764183099742106259 2 IN IP4 127.0.0.1
// s = <会话名称>  “s =”字段是文本会话名称.每个会话描述必须有一个且只有一个“s =”字段。“s =”字段不能为空
s=-
// t =<start-time> <stop-time>“t =”行指定会话的开始和停止时间。如果<stop-time>设置为零,则会话不受限制,但在<start-time>之后才会生效。如果<start-time>也为零,则会话被视为永久会话。
t=0 0
//********************* Session Metadata ********************//

//---------------------- Security Descriptions-------------------//
//a = <attribute>:<value> 
//a = <fingerprint> SRIP 所需的DTLS指纹信息
a=fingerprint:sha-256 00:B3:00:58:25:4A:7D:C7:CB:E3:C6:63:43:03:71:63:33:73:CE:F9:CE:12:52:4C:95:2E:0E:96:FC:93:CE:11
// Negotiating Media Multiplexing Using the Session Description Protocol
// 表示需要共用一个传输通道传输的媒体,通过ssrc进行区分不同的流。如果没有这一行,音视频数据就会分别用单独udp端口来发送
a=group:BUNDLE 0 1 2
a=extmap-allow-mixed
// WebRTC MediaStream Identification in the Session Description Protocol
// 标识SDP中包含MediaStream的标识
a=msid-semantic: WMS stream
a=ice-ufrag:9gy+
a=ice-pwd:FlgstiDMt4ffZ2NumjV7UZPP
// Trickle ICE:Incremental Provisioning of Candidates for the Interactive Connectivity Establishment (ICE) Protocol
// ICE建立候选时 采用增量设置的方式
a=ice-options:trickle renomination
// DTLS 握手方式 "active" / "passive" / "actpass"/ "holdconn"   'actpass': 连接或启动传出连接。
a=setup:actpass
a=mid:0
//********************* Security Descriptions ********************//

//---------------------- Stream Description -------------------//
// m=<media> <port> <proto> <fmt> ...
// <media>是媒体类型 当前定义的媒体 "audio","video", "text", "application", and "message"
// <port>是传输媒体流的传输端口。在相关的“c =”字段中指定,以及在媒体字段的<proto>子字段中定义的传输协议。
// <proto>是传输协议。传输协议的含义取决于相关“c =”字段中的地址类型字段。
// <fmt>是媒体格式描述(编码类型)。第四个和任何后续 如果<proto>子字段是“RTP / AVP”或“RTP / SAVP”,则<fmt> 子字段包含RTP有效载荷类型号。
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 104 9 102 0 8 106 105 13 110 112 113 126
// c=<nettype> <addrtype> <connection-address>
// 会话描述必须包含 每个媒体描述中的至少一个“c =”字段或会话级别的单个“c =”字段
// <nettype>   “IN”被定义为具有“Internet”的含义
// <addrtype> 为IP4和IP6时
c=IN IP4 0.0.0.0
// The URI for declaring this header extension in an extmap attribute is "urn:ietf:params:rtp-hdrext:ssrc-audio-level".
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
//a = sendrecv 这指定应以发送和接收模式启动工具。对于具有默认为仅接收模式的工具的交互式会议,这是必需的。
a=sendrecv
a=msid:stream audio0
// "a = rtcp-mux"属性以指示需要RTP和RTCP多路复用
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=ssrc:3316495333 cname:FlAaGq79STLBNArX
a=ssrc:3316495333 msid:stream audio0
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 124 35 36 123 122 125
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:9gy+
a=ice-pwd:FlgstiDMt4ffZ2NumjV7UZPP
a=ice-options:trickle renomination
a=fingerprint:sha-256 00:B3:00:58:25:4A:7D:C7:CB:E3:C6:63:43:03:71:63:33:73:CE:F9:CE:12:52:4C:95:2E:0E:96:FC:93:CE:11
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:stream video0
a=rtcp-mux
a=rtcp-rsize
// rtpmap:<payload type> <encoding name> / <clock rate> [/ <encoding parameters>]
// <payload type> 此属性从RTP有效内容类型编号(在 “m =”行中使用)映射到表示要使用的有效载荷格式,编码类型 采样率 编码参数
// 而以下格式的编码的格式要去相应编码格式标准文档中查看 比如H264 就是RFC3984 https://tools.ietf.org/html/rfc3984
a=rtpmap:96 H264/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
// a = rtcp-fb: RTCP-FB-PT SP RTCP-FB-VAL CRLF
// rtcp-fb-pt是有效负载类型
// rtcp-fb-val定义反馈消息的类型 ack,nack,trr-int和rtcp-fb-id
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 H264/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:125 ulpfec/90000
a=ssrc-group:FID 2531124184 1348659893
a=ssrc:2531124184 cname:FlAaGq79STLBNArX
a=ssrc:2531124184 msid:stream video0
a=ssrc:1348659893 cname:FlAaGq79STLBNArX
a=ssrc:1348659893 msid:stream video0
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=ice-ufrag:9gy+
a=ice-pwd:FlgstiDMt4ffZ2NumjV7UZPP
a=ice-options:trickle renomination
a=fingerprint:sha-256 00:B3:00:58:25:4A:7D:C7:CB:E3:C6:63:43:03:71:63:33:73:CE:F9:CE:12:52:4C:95:2E:0E:96:FC:93:CE:11
a=setup:actpass
a=mid:2
a=sctp-port:5000
a=max-message-size:262144

媒体流信息

// m=<media> <port> <proto> <fmt> ...
// <media>是媒体类型 当前定义的媒体 "audio","video", "text", "application", and "message"
// <port>是传输媒体流的传输端口。在相关的“c =”字段中指定,以及在媒体字段的<proto>子字段中定义的传输协议。
// <proto>是传输协议。传输协议的含义取决于相关“c =”字段中的地址类型字段。
// <fmt>是媒体格式描述(编码类型)。第四个和任何后续 如果<proto>子字段是“RTP / AVP”或“RTP / SAVP”,则<fmt> 子字段包含RTP有效载荷类型号。
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 124 35 36 123 122 125
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:9gy+
a=ice-pwd:FlgstiDMt4ffZ2NumjV7UZPP
a=ice-options:trickle renomination
a=fingerprint:sha-256 00:B3:00:58:25:4A:7D:C7:CB:E3:C6:63:43:03:71:63:33:73:CE:F9:CE:12:52:4C:95:2E:0E:96:FC:93:CE:11
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:stream video0
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 H264/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 H264/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 VP9/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=rtpmap:124 rtx/90000
a=fmtp:124 apt=127
a=rtpmap:35 AV1/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:123 red/90000
a=rtpmap:122 rtx/90000
a=fmtp:122 apt=123
a=rtpmap:125 ulpfec/90000
a=ssrc-group:FID 2531124184 1348659893
a=ssrc:2531124184 cname:FlAaGq79STLBNArX
a=ssrc:2531124184 msid:stream video0
a=ssrc:1348659893 cname:FlAaGq79STLBNArX
a=ssrc:1348659893 msid:stream video0

解决方案

上文中提到修改源代码来解决红屏问题。另外一种方式就是修改 SDP 内容,而 SDP 的内容修改,也有两个方法。其一是修改底层源代码生成 offer 的实现,将 H264 在模拟器环境下排除。另一种则是在应用层修改 SDP 内容。

在实际修改过程中,很可能会遇到 Session Description is NULL. 报错。相信笔者,我们的实现思路是没有错的,只要再注意一些小细节即可。

  • 1)在 RTCPeerConnection.setRemoteDescription 前修改 SDP 内容。
  • 2)注意 \r\n 符号。

我们需要确保将上文示例 SDP 内容修改如下即可

m=video 9 UDP/TLS/RTP/SAVPF 100 101 127 124 35 36 123 122 125
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:9gy+
a=ice-pwd:FlgstiDMt4ffZ2NumjV7UZPP
a=ice-options:trickle renomination
a=fingerprint:sha-256 00:B3:00:58:25:4A:7D:C7:CB:E3:C6:63:43:03:71:63:33:73:CE:F9:CE:12:52:4C:95:2E:0E:96:FC:93:CE:11
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:stream video0
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 VP9/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=rtpmap:124 rtx/90000
a=fmtp:124 apt=127
a=rtpmap:35 AV1/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:123 red/90000
a=rtpmap:122 rtx/90000
a=fmtp:122 apt=123
a=rtpmap:125 ulpfec/90000
a=ssrc-group:FID 2531124184 1348659893
a=ssrc:2531124184 cname:FlAaGq79STLBNArX
a=ssrc:2531124184 msid:stream video0
a=ssrc:1348659893 cname:FlAaGq79STLBNArX
a=ssrc:1348659893 msid:stream video0

效果展示

模拟器红屏效果修复.png

文中图片若有侵权,请联系笔者及时删除

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

推荐阅读更多精彩内容