音視頻-mrtc

近期由于項(xiàng)目需要,本來(lái)集成的TRTC切換成MRTC
現(xiàn)在就MRTC集成做個(gè)簡(jiǎn)單整理铺峭,這里主要介紹web和H5的集成

1贩绕、MRTC簡(jiǎn)介

官網(wǎng)介紹
音視頻通話組件( Mobile Real-Time Communication,簡(jiǎn)稱 MRTC)是 mPaaS 提供的音頻董朝、視頻通話組件灰瞻。該組件功能豐富腥例,提供純語(yǔ)音通話和視頻通話功能,支持 PC酝润、移動(dòng)端燎竖、IoT 設(shè)備等多終端接入。音視頻通話可實(shí)現(xiàn)一對(duì)一通話及多人會(huì)議要销,通話過(guò)程中支持屏幕錄制构回、屏幕共享、截圖等功能疏咐,同時(shí)支持即時(shí)文字消息和文件傳輸纤掸。此外,支持實(shí)時(shí)語(yǔ)音識(shí)別浑塞,能夠識(shí)別對(duì)端的語(yǔ)音確認(rèn)借跪,輔助本端判斷對(duì)端的意向;點(diǎn)播功能可實(shí)現(xiàn)在視頻通話過(guò)程中酌壕,播放視頻掏愁、PPT 等多種提示畫(huà)面。

多種參與模式:支持一對(duì)一視頻通話及多人視頻通話卵牍。

多平臺(tái):支持 iOS果港、Android、PC Web糊昙、H5 以及小程序辛掠。

多端互通:支持手機(jī)、PC释牺、IoT 設(shè)備之間互聯(lián)互通萝衩。

會(huì)話保持:網(wǎng)絡(luò)短暫異常回挽、網(wǎng)絡(luò)切換時(shí),業(yè)務(wù)流程不中斷欠气,保持會(huì)話的持續(xù)性厅各。

自定義視頻規(guī)格镜撩、自適應(yīng)視頻規(guī)格:支持自定義寬预柒、高、最大幀率袁梗、最大碼率宜鸯,并能在上限范圍內(nèi)根據(jù)網(wǎng)絡(luò)狀況自適應(yīng)調(diào)整視頻規(guī)格。

2遮怜、MRTC的集成

官網(wǎng)接入文檔

在官網(wǎng)的集成上在詳細(xì)介紹下淋袖,做個(gè)二次封裝,相關(guān)接口可查看官方文檔

官方流程圖


p432419.jpeg

思路:
1锯梁、封裝集成JS方法 (可使用mixins方式)
2即碗、封裝UI組件(呼叫組件,視頻通話組件陌凳,接聽(tīng)組件)
3剥懒、在業(yè)務(wù)的基礎(chǔ)上封裝相關(guān)業(yè)務(wù)音視頻SDK

3、具體實(shí)現(xiàn)(vue版)

1合敦、下載SDK初橘,引入項(xiàng)目

下載 artvc-web-sdk,把lib文件引入到項(xiàng)目中

項(xiàng)目按需引入對(duì)應(yīng)的js(在index.html里面)

  <script src="./lib/adapter.js"></script>
  <script src="./lib/meeting_api.js"></script>
  <script src="./lib/mcu.js"></script>
  <script src="./lib/meeting_camera_stream.js"></script>
  <script src="./lib/meeting_invite.js"></script>
2、實(shí)例化 SDK
 const test_controller = new McuController() // 實(shí)例化 SDK
    this.test_controller = test_controller
3充岛、建立連接
init() {
      const test_controller = this.test_controller
      const config_param = {}

      config_param.uid = '6189'
      config_param.biz_name = 'demo'
      config_param.sub_biz = 'default'
      config_param.workspaceId = 'default'
      config_param.room_server_url = 'wss://服務(wù)地址'
      config_param.sign = this. getSign()
      // 允許最大斷網(wǎng)時(shí)間 (超過(guò)未重連, 直接關(guān)閉)
      config_param.network_check_timeout = 120 * 1000
      test_controller.Connect(config_param)
    }

// 注意:簽名應(yīng)該是后臺(tái)返回的保檐,這是demo可寫(xiě)死
 // 簽名(通道建連/創(chuàng)建房間/加入房間需要)
    getSign(uid, isRecord = false) {
      const test_controller = this.test_controller
      test_controller.trace(`GetSign uid=${uid}`)
      return 'signature'
    },
4、初始化回調(diào)方法

所有的回調(diào)方法都在這里監(jiān)聽(tīng)

initCallback() {
      const test_controller = this.test_controller
      // 建立連接成功回調(diào)
      test_controller.OnConnectOK = () => {
        // this.initRoom()
        console.log('建立連接成功')
      }
      // 建立連接失敗回調(diào)
      test_controller.OnConnectFailed = function(code, msg) {
        console.log(code, msg)
        console.log('建立連接失敗, 請(qǐng)嘗試https修復(fù)')
      }
      // 房間初始化成功
      test_controller.OnInitRoomConfigOK = () => {
        console.log('房間初始化成功')
        if (this.role === 'created') {
          this.createRoom()
        } else if (this.role === 'join') {
          this.joinRoom()
        }
      }
      // 房間初始化失敗
      test_controller.OnInitRoomConfigFail = function(err_code, err_msg) {
        console.log(err_code, err_msg)
        console.log('房間初始化失敗')
      }
      // 創(chuàng)建房間成功回調(diào)
      test_controller.OnCreateRoomSucc = (room_id, rtoken) => {
        console.log( room_id, rtoken)

        this.isHiddenVideo = false
        this.typeState = '0'
        this.messageSend({
          toUserId: '9232131735',
          userId: '123',
          type: '1',
          roomNumber: room_id,
          passWord: rtoken
        })
        // test_controller.JoinRoom(room_id, rtoken, this.getSign())

        console.log('創(chuàng)建房間成功')
      }
      // 創(chuàng)建房間失敗回調(diào)
      test_controller.OnCreateRoomFailed = function(err_code, err_msg) {
        console.log(err_code, err_msg)
        console.log('創(chuàng)建房間失敗')
      }
      // 加入房間成功
      test_controller.OnJoinRoomSucc = () => {
        console.log('加入房間成功')
        this.isHiddenVideo = false
        this.typeState = '0'
      }
      // 加入房間失敗
      test_controller.OnJoinRoomFailed = function(err_code, err_msg) {
        console.log(err_code, err_msg)
        console.log('加入房間失敗')
      }
      test_controller.OnPublishSucc = (sid) => {
        this.timeStart()
        console.log('發(fā)布訂閱')
      }
      // 訂閱成功回調(diào)
      test_controller.OnSubscribeSucc = function(feedId, sid) {
        test_controller.trace(`~~~~~~~~~~~~~ OnSubscribeSuccess  Response  , sid=${sid},feedId=${feedId}`)
        console.log('訂閱成功回調(diào)')
      }
      // 邀請(qǐng)成功
      test_controller.OnInviteOK = function() {
        console.log('邀請(qǐng)成功回調(diào)')
      }

      // 邀請(qǐng)失敗
      test_controller.OnInviteFail = function(code, msg) {
        console.log('邀請(qǐng)失敗回調(diào)')
      }
      test_controller.OnReplyInviteOK = () => {
        console.log('回復(fù)邀請(qǐng)回調(diào)')
      }
      // 退出房間回調(diào)
      test_controller.OnLeaveRoom = (leaveType) => {
        test_controller.warning(`~~~~~~~~~~~~~ leave room! leaveType = ${leaveType}`)
        console.log('退出房間成功')
        this.onTimeReset()
        this.isHiddenVideo = true
      }
      // 退出房間回調(diào)
      test_controller.OnParticipantLeaveRoom = (participant, exitType) => {
        test_controller.warning(`~~~~~~~~~~~~~ leave room! leaveType = ${participant}${exitType}`)
        console.log('對(duì)方退出房間成功')
        this.onQuit()
      }
    }
5崔梗、初始化房間
initRoom(type) {
    // type 是區(qū)分是加入房間還是創(chuàng)建房間
      if (type) {
        this.role = type
      }
      const test_controller = this.test_controller
      const config_param = {
        auto_publish_subscribe: 3,
        media_type: 1,
        publish_device: 1,
        initSubscribe: [
          {
            subscribe_video_id: 'video0',
            subscribe_audio_id: 'audio0',
            subscribe_streamId_id: 'subscribe_streamId0',
            feedId_id: 'feedId0'
          }, {
            subscribe_video_id: 'video4',
            subscribe_audio_id: 'audio4'
          }],
        initPublish: [
          {
            publish_video_id: 'publish_video1',
            publish_streamId_id: 'publish_streamId1',
            publish_tag: 'VIDEO_SOURCE_CAMERA_1'
          }
        ]
      }
      test_controller.InitRoomConfig(config_param)
    }
6夜只、創(chuàng)建房間
createRoom() {
      const test_controller = this.test_controller
      test_controller.CreateRoom(this.getSign())
    },
7、發(fā)布訂閱

注意:如果初始化的時(shí)候是自動(dòng)發(fā)布訂閱蒜魄,則創(chuàng)建房間之后不需要手動(dòng)發(fā)布訂閱扔亥,否則需要手動(dòng)發(fā)布訂閱

onPublish() {
      const test_controller = this.test_controller
      const config_param = {
        'media_type': 1,
        'need_volume_analyser': true,
        'publish_video_id': 'publish_video1',
        'publish_streamId_id': 'publish_streamId1',
        'aspectRatioStrongDepend': false,
        'aspectRatio': '0',
        'video_profile_type': '2',
        'publish_tag': 'VIDEO_SOURCE_CAMERA',
        'enableVideo': true,
        'enableAudio': true,
        'publish_device': 1,
        'transport_': 'all',
        'defaultTurnServer': '',
        'degradationType': 1,
        'scalabilityMode': 'NONE'
      }
      test_controller.Publish(config_param)
    }
8、退出房間
 onLeaveRoom() {
      const test_controller = this.test_controller
      test_controller.LeaveRoom()
    },
9权悟、視頻UI組件
<template>
  <div id="videos" v-drag class="video-div" :class="isHiddenVideo?'display-none':'display-block'">
    <div class="publishVideo">
      <video
        id="publish_video1"
        autoplay
        muted="true"
        webkit-playsinline="true"
        playsinline="true"
        width="100%"
        height="100%"
        style="object-fit: cover;"
      />
      <div class="time">{{ time }}</div>

      <div class="video-tool">
        <img class="img-gd" src="@/assets/images/jj.png" alt="" @click="onQuit">
    
    </div>
    <div class="subscribeVideo">
      <video
        id="video0"
        autoplay
        muted
        width="100%"
        height="100%"
        webkit-playsinline="true"
        playsinline="true"
        style="object-fit: cover;"
      >
        video
      </video>
      <audio id="audio0" autoplay>音頻</audio>
      <video id="video99" autoplay muted="true" width="100%" height="480" hidden>
        video
      </video>
      <audio id="audio99" autoplay hidden>音頻</audio>
      <br>
      <label id="subscribe_feedId_text0" type="text" class="hiddenForMobile" hidden>&nbsp;&nbsp;feedId:</label>
      <label id="feedId0" class="css-text-color hiddenForMobile" type="text" />
      <br>
      <label id="subscribe_streamId_text0" type="text" hidden class="hiddenForMobile">&nbsp;&nbsp;streamId:</label>
      <label id="subscribe_streamId0" class="css-text-color hiddenForMobile" type="text" />
    </div>
  </div>
</template>

<script>
export default {
  name: 'Index',
  // 自定義指令
  directives: {
    drag: {
      // 指令的定義
      bind: function(el) {
        const oDiv = el // 獲取當(dāng)前元素
        oDiv.onmousedown = (e) => {
          console.log('onmousedown')
          // 算出鼠標(biāo)相對(duì)元素的位置
          const disX = e.clientX - oDiv.offsetLeft
          const disY = e.clientY - oDiv.offsetTop

          document.onmousemove = (e) => {
            // 用鼠標(biāo)的位置減去鼠標(biāo)相對(duì)元素的位置砸王,得到元素的位置
            const left = e.clientX - disX
            const top = e.clientY - disY

            oDiv.style.left = left + 'px'
            oDiv.style.top = top + 'px'
          }

          document.onmouseup = (e) => {
            document.onmousemove = null
            document.onmouseup = null
          }
        }
      }
    }
  },
  props: {
    isHiddenVideo: {
      type: Boolean,
      default: true
    },
    time: {
      type: String,
      default: '00:00:00'
    }
  },
  methods: {
    onQuit() {
      this.$emit('onQuit')
    },
    onInappropriate() {
      this.$emit('onInappropriate')
    },
    onLooks() {
      this.$emit('onLooks')
    },
    onOffer() {
      this.$emit('onOffer')
    }
  }
}
</script>

<style lang="scss" scoped>
.display-none {
  display: none;
}

.display-block {
  display: block;
}

.video-div {
  position: absolute;
  top: 40px;
  right: 10px;
  width: 500px;
  height: 600px;
  overflow: hidden;
  background: #001528;
  border-radius: 20px;

  .publishVideo {
    width: 100%;
    height: 100%;

    .time {
      position: absolute;
      top: 10px;
      right: 0;
      width: 100px;
      height: 40px;
      line-height: 40px;
      color: white;
    }

    .video-tool {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      height: 100px;
      background-color: rgba(0,0,0,.5);

      .img-gd {
        position: absolute;
        top: -20px;
        right: calc(50% - 20px);
        z-index: 99;
        width: 40px;
      }

      .btn-class {
        position: relative;
        width: 320px;
        height: 100px;
        margin: 0 auto;
        //background: red;
      }

      .looks {
        position: absolute;
        left: 120px;
      }

      .offer {
        position: absolute;
        left: 220px;
      }

      .inappropriate {
        position: absolute;
        left: 20px;
      }

      .btn-base {
        bottom: 25px;
        width: 90px;
        height: 40px;
        font-size: 12px;
        line-height: 40px;
        color: white;
        text-align: center;
        background: #000;
        border-radius: 20px;
      }
    }
  }

  .subscribeVideo {
    position: absolute;
    top: 50px;
    right: 10px;
    width: 200px;
    height: 200px;
    overflow: hidden;
    //background-color: #409eff;
    border-radius: 10px;
  }
}

</style>
視頻組件
10、接聽(tīng)UI組件
<template>
  <div class="invite-video">
    <div class="user-info">
      <img class="img-head" :src="callUserInfo.avatar?callUserInfo.avatar:'/company/static/images/system/user_avatar_default.png'" alt="">
      <div class="info-name"><span class="name">{{ callUserInfo.name }}</span><span> {{callUserInfo.attrs}} </span></div>
      <div class="info-text"><span>請(qǐng)求與你視頻面試</span></div>
    </div>
    <img class="img-jj" src="../../assets/image/jj.png" alt="" @click="onAction('1')">
    <img class="img-splj" src="../../assets/image/splj.png" alt="" @click="onAction('2')">

  </div>
</template>

<script>
export default {
  name: 'Index',
  props:{
    callUserInfo:{
      type:Object,
      default:()=>{
        return {
          avatar:null,
          name:null,
          attrs:null
        }
      }
    }
  },
  methods: {
    onAction(type) {
      this.$emit('onAction', type)
    }
  }
}
</script>
<style lang="scss" scoped>
.invite-video {
  position: absolute;
  top: 20px;
  right: calc(50% - 200px);
  width: 400px;
  height: 150px;
  overflow: hidden;
  background: #001528;
  border-radius: 10px;

  .user-info {
    position: absolute;
    top: 0;
    right: 0;
    width: 400px;
    height: 80px;
    //background: red;
    overflow: hidden;

    .info-name {
      position: absolute;
      top: 15px;
      left: 90px;
      width: 300px;
      height: 20px;
      font-size: 14px;
      color: #666;

      .name {
        font-size: 20px;
        color: white
      }
    }

    .info-text {
      position: absolute;
      bottom: 15px;
      left: 90px;
      width: 300px;
      height: 20px;
      font-size: 14px;
      color: white;
    }

    .img-head {
      position: absolute;
      bottom: 15px;
      left: 20px;
      width: 50px;
      border-radius: 10px;
    }
  }

  .ckjl-btn {
    position: absolute;
    left: 110px;
  }

  .kshf-btn {
    position: absolute;
    left: 20px;
  }

  .btn-base {
    bottom: 25px;
    width: 80px;
    height: 30px;
    font-size: 12px;
    line-height: 30px;
    color: white;
    text-align: center;
    background: #000;
    border-radius: 17px;
  }

  .img-jj {
    position: absolute;
    right: 80px;
    bottom: 20px;
    width: 40px;
  }

  .img-splj {
    position: absolute;
    right: 20px;
    bottom: 20px;
    width: 40px;
  }
}
</style>

接聽(tīng)組件
11峦阁、呼叫UI組件
<template>
<div class="call-class">
  <img class="img-user" :src="userInfo.avatar?userInfo.avatar:'/company/static/images/system/user_avatar_default.png'" alt="">
  <div class="name">{{ userInfo.userName }}</div>
  <div class="name-call">正在呼叫...</div>
  <img class="img-cancel" src="../../assets/image/cancel.png" alt="" @click="onCancel">
  <div class="name-cancel" @click="onCancel">取消</div>

</div>
</template>

<script>
export default {
  name: "index",
  props:{
    userInfo:{
      type:Object,
      default:()=>{
        return {
          avatar:null,
          userName:null
        }
      }
    }
  },
  methods:{
    onCancel(){
      this.$emit('onCancel')
    }

  }

}
</script>

<style scoped lang="scss">
.call-class{
  position: absolute;
  top: 100px;
  right: 0;
  width: 300px;
  height: 350px;
  background: rgba(0,0,0,0.8);
  color: white;
  border-radius: 10px 0 0 10px;
  z-index: 99999;
  .name{
    text-align: center;
    position: absolute;
    top: 120px;
    font-size: 20px;
    font-weight: bold;
    width: 100%;
  }
  .img-cancel{
    position: absolute;
    top: 230px;
    right: calc(50% - 20px);
    width: 40px;
    z-index: 99;
  }
  .name-cancel{
    text-align: center;
    position: absolute;
    top: 280px;
    font-size: 12px;
    width: 100%;
  }
  .name-call{
    text-align: center;
    position: absolute;
    top: 160px;
    font-size: 14px;
    width: 100%;
  }
}
.img-user{
  position: absolute;
  top: 30px;
  right: calc(50% - 40px);
  width: 80px;
  z-index: 99;
}

</style>

呼叫組件
12谦铃、app.vue 集成
<Video :is-hidden-video="isHiddenVideo" :time="timeStr" @onQuit="onQuit" />
    <InviteVideo v-if="typeState === '0'" @onAction="onAction" />
    <CallVideo v-if="isCallShow" @onCancel="onCancel"></CallVideo>


import InviteVideo from './components/InviteVideo'
import Video from './components/Video'
import mrtc from '@/mixins/mrtc'

components: {
    InviteVideo,
    Video
  },
  mixins: [mrtc, webSocket],

到這里基本上MRTC音視頻集成完成了。

4榔昔、WebSocket 使用

由于需求場(chǎng)景是pc和小程序互通驹闰,但由于小程序的局限性瘪菌,無(wú)法邀請(qǐng)好友加入房間,也無(wú)法監(jiān)聽(tīng)加入房間事件嘹朗。因此需要業(yè)務(wù)自行實(shí)現(xiàn)消息發(fā)送师妙。

websocket封裝,網(wǎng)上也有相應(yīng)的教程

export default {
  components: { },
  data() {
    return {
      websock: '',
      lockReconnect: false, // 是否真正建立連接
      timeout: 58 * 1000, // 58秒一次心跳
      timeoutObj: null, // 心跳倒計(jì)時(shí)
      serverTimeoutObj: null, // 心跳倒計(jì)時(shí)
      timeoutnum: null, // 斷開(kāi) 重連倒計(jì)時(shí)
      typeState: '0',
      roomID: '6693563501',
      roomKey: '123',
      userID: ''
    }
  },
  created() {
  },
  destroyed() {
    this.websock.close() // 離開(kāi)路由之后斷開(kāi)websocket連接
  },
  methods: {
    messageSend({ toUserId, userId, type, roomNumber, passWord }) {
      const actions = {
        toUserId: '9232131735487',
        userId: '123',
        type: type,
        roomNumber: roomNumber,
        passWord: passWord
      }
      this.websocketsend(JSON.stringify(actions))
    },
    currentTime() {
      setInterval(this.formatDate, 500)
    },
    initWebSocket() {
      // 初始化weosocket
      const wsuri = 'ws://域名'
      this.websock = new WebSocket(wsuri)
      // 客戶端接收服務(wù)端數(shù)據(jù)時(shí)觸發(fā)
      this.websock.onmessage = this.websocketonmessage
      // 連接建立時(shí)觸發(fā)
      this.websock.onopen = this.websocketonopen
      // 通信發(fā)生錯(cuò)誤時(shí)觸發(fā)
      this.websock.onerror = this.websocketonerror
      // 連接關(guān)閉時(shí)觸發(fā)
      this.websock.onclose = this.websocketclose
    },
    // 連接建立時(shí)觸發(fā)
    websocketonopen() {
      // 開(kāi)啟心跳
      this.start()
      // 連接建立之后執(zhí)行send方法發(fā)送數(shù)據(jù)

      // this.websocketsend(actions)
    },
    // 通信發(fā)生錯(cuò)誤時(shí)觸發(fā)
    websocketonerror() {
      console.log('出現(xiàn)錯(cuò)誤')
      this.reconnect()
    },
    // 客戶端接收服務(wù)端數(shù)據(jù)時(shí)觸發(fā)
    websocketonmessage(e) {
      console.log(e.data)
      // 收到服務(wù)器信息屹培,心跳重置
      //("1","呼叫"),
      //  ("2","被拒接"),
      //  ("3","不在線"),
      //  ("4","占線中"),
      //  ("999","心跳檢測(cè)");
  // 業(yè)務(wù)邏輯自行處理
      const data = JSON.parse(e.data)
      this.typeState = data.type
      switch (data.type) {
        case '1':
          this.roomID = data.roomNumber
          this.roomKey = data.passWord
          this.userID = data.userId
          break
      }

      this.reset()
    },
    websocketsend(Data) {
      // 數(shù)據(jù)發(fā)送
      this.websock.send(Data)
    },
    // 連接關(guān)閉時(shí)觸發(fā)
    websocketclose(e) {
      // 關(guān)閉
      console.log('斷開(kāi)連接', e)
      // 重連
      this.reconnect()
    },
    reconnect() {
      // 重新連接
      var that = this
      if (that.lockReconnect) {
        return
      }
      that.lockReconnect = true
      // 沒(méi)連接上會(huì)一直重連默穴,設(shè)置延遲避免請(qǐng)求過(guò)多
      that.timeoutnum && clearTimeout(that.timeoutnum)
      that.timeoutnum = setTimeout(function() {
        // 新連接
        that.initWebSocket()
        that.lockReconnect = false
      }, 5000)
    },
    reset() {
      // 重置心跳
      var that = this
      // 清除時(shí)間
      clearTimeout(that.timeoutObj)
      clearTimeout(that.serverTimeoutObj)
      // 重啟心跳
      that.start()
    },
    start() {
      // 開(kāi)啟心跳
      console.log('開(kāi)啟心跳')
      var self = this
      self.timeoutObj && clearTimeout(self.timeoutObj)
      self.serverTimeoutObj && clearTimeout(self.serverTimeoutObj)
      self.timeoutObj = setTimeout(function() {
        // 這里發(fā)送一個(gè)心跳,后端收到后褪秀,返回一個(gè)心跳消息蓄诽,
        if (self.websock.readyState && Number(self.websock.readyState) === 1) {
          // 如果連接正常
          const actions = {
            toUserId: '1592321317',
            userId: '123',
            type: '999',
            roomNumber: '123456',
            passWord: '123456'
          }
          self.websocketsend(JSON.stringify(actions)) // 這里可以自己跟后端約定
        } else {
          // 否則重連
          self.reconnect()
        }
        self.serverTimeoutObj = setTimeout(function() {
          // 超時(shí)關(guān)閉
          self.websock.close()
        }, self.timeout)
      }, self.timeout)
    }
  },
  mounted() {
    this.currentTime()
  },
  // 銷毀定時(shí)器
  beforeDestroy() {
    if (this.formatDate) {
      clearInterval(this.formatDate) // 在Vue實(shí)例銷毀前,清除時(shí)間定時(shí)器
    }
  }
}

到這MRTC就集成完成了媒吗,通信需要配置WebSocket實(shí)現(xiàn)仑氛。

附帶簡(jiǎn)單集成demo,不帶業(yè)務(wù)邏輯

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市闸英,隨后出現(xiàn)的幾起案子锯岖,更是在濱河造成了極大的恐慌,老刑警劉巖甫何,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件出吹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沛豌,警方通過(guò)查閱死者的電腦和手機(jī)趋箩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)加派,“玉大人叫确,你說(shuō)我怎么就攤上這事∩纸酰” “怎么了竹勉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)娄琉。 經(jīng)常有香客問(wèn)我次乓,道長(zhǎng),這世上最難降的妖魔是什么孽水? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任票腰,我火速辦了婚禮,結(jié)果婚禮上女气,老公的妹妹穿的比我還像新娘杏慰。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布缘滥。 她就那樣靜靜地躺著轰胁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朝扼。 梳的紋絲不亂的頭發(fā)上赃阀,一...
    開(kāi)封第一講書(shū)人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音擎颖,去河邊找鬼榛斯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肠仪,可吹牛的內(nèi)容都是我干的肖抱。 我是一名探鬼主播备典,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼异旧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了提佣?” 一聲冷哼從身側(cè)響起吮蛹,我...
    開(kāi)封第一講書(shū)人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拌屏,沒(méi)想到半個(gè)月后潮针,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡倚喂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年每篷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片端圈。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焦读,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舱权,到底是詐尸還是另有隱情矗晃,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布宴倍,位于F島的核電站张症,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鸵贬。R本人自食惡果不足惜俗他,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阔逼。 院中可真熱鬧兆衅,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至夕春,卻和暖如春未荒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背及志。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工片排, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人速侈。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓率寡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親倚搬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冶共,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容