基于FFmpeg進(jìn)行RTMP推流(一)

簡(jiǎn)介

  • 開(kāi)發(fā)環(huán)境
  • FFmpeg sdk下載
  • 項(xiàng)目配置
  • 代碼流程

開(kāi)發(fā)環(huán)境

vs 2017

FFmpeg sdk下載

下載地址
這里下載3.3.3 — 32bit — share和Dev

8.png

  • Shared包含運(yùn)行時(shí)的動(dòng)態(tài)庫(kù)在bin目錄下
  • Dev包含開(kāi)發(fā)是編譯需要的頭文件(include目錄下)和庫(kù)文件(lib目錄下)

項(xiàng)目配置

先看下項(xiàng)目的目錄結(jié)構(gòu)

9.png

這里的bin、include也糊、lib就是我們剛才在FFmpeg下載的相關(guān)文件炼蹦。
src是我們的項(xiàng)目源碼目錄。
新建Win32控制臺(tái)應(yīng)用程序狸剃、選擇位置掐隐、項(xiàng)目名稱钞馁。注意:去掉“為結(jié)局方案創(chuàng)建目錄”的勾選
10.png

然后選擇空項(xiàng)目虑省、去掉預(yù)編譯頭。完成項(xiàng)目的創(chuàng)建
11.png

項(xiàng)目屬性配置

右擊項(xiàng)目屬性

  • 【常規(guī)】=>【輸出目錄】 修改為..\..\bin
  • 【調(diào)試】=>【工作目錄】修改為..\..\bin
  • 【C/C++】=>【常規(guī)】=>【附加包含目錄】修改為..\..\include
    -【鏈接器】=>【常規(guī)】=>【附加庫(kù)目錄】修改為..\..\lib
    注意:這里所有的路徑都是相對(duì)路徑僧凰,相對(duì)于源碼的路徑
    這里設(shè)置輸出目錄到bin探颈。是因?yàn)閣in下運(yùn)行時(shí)會(huì)默認(rèn)在當(dāng)前運(yùn)行的目錄下尋找dll文件。而我們的dll文件放在bin目錄下训措。

開(kāi)發(fā)流程

Flow Chart.png

流程詳解

av_register_all()

該方法初始化所有的封裝和解封裝伪节。在使用FFmpeg的時(shí)候首先要調(diào)用這個(gè)方法。
找到這個(gè)方法的源碼libavformat\allformats.c

void av_register_all(void)
{
    static AVOnce control = AV_ONCE_INIT;

    ff_thread_once(&control, register_all);
}

這里控制注冊(cè)方法只被調(diào)用一次register_all是函數(shù)指針隙弛〖懿觯看到實(shí)現(xiàn)部分。

static void register_all(void)
{
    avcodec_register_all();

    /* (de)muxers */
    REGISTER_MUXER   (A64,              a64);
    REGISTER_DEMUXER (AA,               aa);
    REGISTER_DEMUXER (AAC,              aac);
//...
}

這里面就是進(jìn)行各種注冊(cè)全闷,而REGISTER_MUXER 叉寂、REGISTER_DEMUXER 是前面定義的宏。我們看到是靜態(tài)方法总珠,說(shuō)明該方法只能在所在的文件中使用屏鳍,這也防止被注冊(cè)多次勘纯。

#define REGISTER_MUXER(X, x)                                            \
    {                                                                   \
        extern AVOutputFormat ff_##x##_muxer;                           \
        if (CONFIG_##X##_MUXER)                                         \
            av_register_output_format(&ff_##x##_muxer);                 \
    }

#define REGISTER_DEMUXER(X, x)                                          \
    {                                                                   \
        extern AVInputFormat ff_##x##_demuxer;                          \
        if (CONFIG_##X##_DEMUXER)                                       \
            av_register_input_format(&ff_##x##_demuxer);                \
    }

#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)

av_register_input_format和av_register_output_format我們也可以單獨(dú)去初始化。這里內(nèi)部細(xì)節(jié)就不做過(guò)多介紹钓瞭。

avformat_network_init()

網(wǎng)絡(luò)相關(guān)初始化驳遵。如果我們使用了網(wǎng)絡(luò)拉流和推流等等,要先初始化山涡。

avformat_open_input()

聲明是

int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);

定義在libavformat\utils.c中堤结。主要功能

  • 輸入輸出結(jié)構(gòu)體AVIOContext的初始化;
  • 輸入數(shù)據(jù)的協(xié)議URLProtocol鸭丛,通過(guò)函數(shù)指針的方式竞穷,與FFMPEG關(guān)聯(lián),剩下的就是調(diào)用該URLProtocol的函數(shù)進(jìn)行open,read等操作了
avformat_find_stream_info
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

可以讀取視音頻數(shù)據(jù)并且獲得一些相關(guān)的信息鳞溉。定義在libavformat\utils.c

avformat_alloc_output_context2
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat,
                                   const char *format_name, const char *filename);

定義在libavformat\mux.c

  • ctx:函數(shù)調(diào)用成功之后創(chuàng)建的AVFormatContext結(jié)構(gòu)體瘾带。
  • oformat:指定AVFormatContext中的AVOutputFormat,用于確定輸出格式熟菲。如果指定為NULL看政,可以設(shè)定后兩個(gè)參數(shù)(format_name或者filename)由FFmpeg猜測(cè)輸出格式。
    PS:使用該參數(shù)需要自己手動(dòng)獲取AVOutputFormat抄罕,相對(duì)于使用后兩個(gè)參數(shù)來(lái)說(shuō)要麻煩一些允蚣。
  • format_name:指定輸出格式的名稱。根據(jù)格式名稱贞绵,F(xiàn)Fmpeg會(huì)推測(cè)輸出格式厉萝。輸出格式可以是“flv”,“mkv”等等榨崩。
  • filename:指定輸出文件的名稱。根據(jù)文件名稱章母,F(xiàn)Fmpeg會(huì)推測(cè)輸出格式母蛛。文件名稱可以是“xx.flv”,“yy.mkv”等等乳怎。
    函數(shù)執(zhí)行成功的話彩郊,其返回值大于等于0。

內(nèi)部流程

  • 調(diào)用avformat_alloc_context()初始化一個(gè)默認(rèn)的AVFormatContext蚪缀。
  • 如果指定了輸入的AVOutputFormat秫逝,則直接將輸入的AVOutputFormat賦值給AVOutputFormat的oformat。如果沒(méi)有指定輸入的AVOutputFormat询枚,就需要根據(jù)文件格式名稱或者文件名推測(cè)輸出的AVOutputFormat违帆。無(wú)論是通過(guò)文件格式名稱還是文件名推測(cè)輸出格式,都會(huì)調(diào)用一個(gè)函數(shù)av_guess_format()金蜀。
avio_open

打開(kāi)FFmpeg的輸入輸出文件

int avio_open2(AVIOContext **s, const char *url, int flags,
               const AVIOInterruptCB *int_cb, AVDictionary **options);
  • s:函數(shù)調(diào)用成功之后創(chuàng)建的AVIOContext結(jié)構(gòu)體刷后。
  • url:輸入輸出協(xié)議的地址(文件也是一種“廣義”的協(xié)議的畴,對(duì)于文件來(lái)說(shuō)就是文件的路徑)。
  • flags:打開(kāi)地址的方式尝胆∩ゲ茫可以選擇只讀,只寫含衔,或者讀寫煎娇。取值如下。
    AVIO_FLAG_READ:只讀贪染。
    AVIO_FLAG_WRITE:只寫缓呛。
    AVIO_FLAG_READ_WRITE:讀寫。
  • int_cb:不太清楚
  • options:不太清楚
avformat_write_header

寫視頻文件頭,av_write_trailer()用于寫視頻文件尾

av_read_frame

定義在libavformat\utils.c
讀取碼流中的音頻若干幀或者視頻一幀抑进。解碼視頻的時(shí)候强经,每解碼一個(gè)視頻幀,需要先調(diào)用 av_read_frame()獲得一幀視頻的壓縮數(shù)據(jù)寺渗,然后才能對(duì)該數(shù)據(jù)進(jìn)行解碼(例如H.264中一幀壓縮數(shù)據(jù)通常對(duì)應(yīng)一個(gè)NAL)匿情。
這里我貼上官方的注釋,很詳細(xì):

/**
 * Return the next frame of a stream.
 * This function returns what is stored in the file, and does not validate
 * that what is there are valid frames for the decoder. It will split what is
 * stored in the file into frames and return one for each call. It will not
 * omit invalid data between valid frames so as to give the decoder the maximum
 * information possible for decoding.
 *
 * If pkt->buf is NULL, then the packet is valid until the next
 * av_read_frame() or until avformat_close_input(). Otherwise the packet
 * is valid indefinitely. In both cases the packet must be freed with
 * av_packet_unref when it is no longer needed. For video, the packet contains
 * exactly one frame. For audio, it contains an integer number of frames if each
 * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
 * have a variable size (e.g. MPEG audio), then it contains one frame.
 *
 * pkt->pts, pkt->dts and pkt->duration are always set to correct
 * values in AVStream.time_base units (and guessed if the format cannot
 * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
 * has B-frames, so it is better to rely on pkt->dts if you do not
 * decompress the payload.
 *
 * @return 0 if OK, < 0 on error or end of file
 */

總結(jié)起來(lái)每段的核心意思

  • 讀取碼流中的音頻若干幀或者視頻一幀
  • 如果pkt->buf是空信殊,那么就要等待下一次av_read_frame調(diào)用炬称。否則無(wú)法確定是否有效
  • pts dts duration通常被設(shè)置為正確的值。但如果視頻幀包括Bzh幀涡拘,那么pts可以是AV_NOPTS_VALUE玲躯。所以最好依賴dts。
av_interleaved_write_frame

輸出一幀視音頻數(shù)據(jù)

核心類

AVFormatContext

AVFormatContext是一個(gè)貫穿始終的數(shù)據(jù)結(jié)構(gòu)鳄乏,很多函數(shù)都要用到它作為參數(shù)跷车。它是FFMPEG解封裝(flv,mp4橱野,rmvb朽缴,avi)功能的結(jié)構(gòu)體。
內(nèi)部的成員變量水援,大家可以查看頭文件密强。這里我們列舉下一些常用重要的成員變量:

  • struct AVInputFormat *iformat:輸入數(shù)據(jù)的封裝格式
  • AVIOContext *pb:輸入數(shù)據(jù)的緩存
  • unsigned int nb_streams:視音頻流的個(gè)數(shù)
  • AVStream **streams:視音頻流
  • char filename[1024]:文件名
  • int64_t duration:時(shí)長(zhǎng)(單位:微秒us,轉(zhuǎn)換為秒需要除以1000000)
  • int bit_rate:比特率(單位bps蜗元,轉(zhuǎn)換為kbps需要除以1000)
  • AVDictionary *metadata:元數(shù)據(jù)

視頻的原數(shù)據(jù)(metadata)信息可以通過(guò)AVDictionary獲取或渤。元數(shù)據(jù)存儲(chǔ)在AVDictionaryEntry結(jié)構(gòu)體中

typedef struct AVDictionaryEntry {
    char *key;
    char *value;
} AVDictionaryEntry;

每一條元數(shù)據(jù)分為key和value兩個(gè)屬性。
在ffmpeg中通過(guò)av_dict_get()函數(shù)獲得視頻的原數(shù)據(jù)奕扣。

    cout << endl << endl << "======元信息=======" << endl;
    string meta, key, value;
    AVDictionaryEntry *m = NULL;
    while (m = av_dict_get(ictx->metadata, "", m, AV_DICT_IGNORE_SUFFIX)) {
        key=m->key;
        value=m->value;
        meta.append(key).append("\t:").append(value).append("\r\n");
    }
    cout << meta.c_str() << endl;
AVStream

AVStream是存儲(chǔ)每一個(gè)視頻/音頻流信息的結(jié)構(gòu)體薪鹦。

  • int index:標(biāo)識(shí)該視頻/音頻流
  • AVCodecContext *codec:指向該視頻/音頻流的AVCodecContext(它們是一一對(duì)應(yīng)的關(guān)系)
  • AVRational time_base:時(shí)基。通過(guò)該值可以把PTS成畦,DTS轉(zhuǎn)化為真正的時(shí)間距芬。- FFMPEG其他結(jié)構(gòu)體中也有這個(gè)字段涝开,但是根據(jù)我的經(jīng)驗(yàn),只有AVStream中的time_base是可用的框仔。PTS*time_base=真正的時(shí)間
  • int64_t duration:該視頻/音頻流長(zhǎng)度
  • AVDictionary *metadata:元數(shù)據(jù)信息
  • AVRational avg_frame_rate:幀率(注:對(duì)視頻來(lái)說(shuō)舀武,這個(gè)挺重要的)
  • AVPacket attached_pic:附帶的圖片。比如說(shuō)一些MP3离斩,AAC音頻文件附帶的專輯封面银舱。
AVPacket

AVPacket是存儲(chǔ)壓縮編碼數(shù)據(jù)相關(guān)信息的結(jié)構(gòu)體。

  • uint8_t *data:壓縮編碼的數(shù)據(jù)跛梗。
    例如對(duì)于H.264來(lái)說(shuō)寻馏。1個(gè)AVPacket的data通常對(duì)應(yīng)一個(gè)NAL。
    注意:在這里只是對(duì)應(yīng)核偿,而不是一模一樣雇毫。他們之間有微小的差別:使用FFMPEG類庫(kù)分離出多媒體文件中的H.264碼流
    因此在使用FFMPEG進(jìn)行視音頻處理的時(shí)候胁艰,常撑璧ⅲ可以將得到的AVPacket的data數(shù)據(jù)直接寫成文件莹桅,從而得到視音頻的碼流文件。

  • int size:data的大小

  • int64_t pts:顯示時(shí)間戳

  • int64_t dts:解碼時(shí)間戳

  • int stream_index:標(biāo)識(shí)該AVPacket所屬的視頻/音頻流尼荆。

源碼

#include <iostream>
using namespace std;
//引入頭文件
extern "C"
{
#include "libavformat/avformat.h"
    //引入時(shí)間
#include "libavutil/time.h"
}
//引入庫(kù)
#pragma comment(lib,"avformat.lib")
//工具庫(kù)左腔,包括獲取錯(cuò)誤信息等
#pragma comment(lib,"avutil.lib")
//編解碼的庫(kù)
#pragma comment(lib,"avcodec.lib")

int avError(int errNum);

static double r2d(AVRational r)
{
    return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
}
int main() {
    //所有代碼執(zhí)行之前要調(diào)用av_register_all和avformat_network_init
    //初始化所有的封裝和解封裝 flv mp4 mp3 mov。不包含編碼和解碼
    av_register_all();

    //初始化網(wǎng)絡(luò)庫(kù)
    avformat_network_init();

    //使用的相對(duì)路徑捅儒,執(zhí)行文件在bin目錄下液样。test.mp4放到bin目錄下即可
    const char *inUrl = "test.flv";
    //輸出的地址
    const char *outUrl = "rtmp://192.168.136.131/live/test";

    //////////////////////////////////////////////////////////////////
    //                   輸入流處理部分
    /////////////////////////////////////////////////////////////////
    //打開(kāi)文件,解封裝 avformat_open_input
    //AVFormatContext **ps  輸入封裝的上下文巧还。包含所有的格式內(nèi)容和所有的IO鞭莽。如果是文件就是文件IO,網(wǎng)絡(luò)就對(duì)應(yīng)網(wǎng)絡(luò)IO
    //const char *url  路徑
    //AVInputFormt * fmt 封裝器
    //AVDictionary ** options 參數(shù)設(shè)置
    AVFormatContext *ictx = NULL;

    //打開(kāi)文件麸祷,解封文件頭
    int ret = avformat_open_input(&ictx, inUrl, 0, NULL);
    if (ret < 0) {
        return avError(ret);
    }
    cout << "avformat_open_input success!" << endl;
    //獲取音頻視頻的信息 .h264 flv 沒(méi)有頭信息
    ret = avformat_find_stream_info(ictx, 0);
    if (ret != 0) {
        return avError(ret);
    }
    //打印視頻視頻信息
    //0打印所有  inUrl 打印時(shí)候顯示撮抓,
    av_dump_format(ictx, 0, inUrl, 0);

    //////////////////////////////////////////////////////////////////
    //                   輸出流處理部分
    /////////////////////////////////////////////////////////////////
    AVFormatContext * octx = NULL;
    //如果是輸入文件 flv可以不傳,可以從文件中判斷摇锋。如果是流則必須傳
    //創(chuàng)建輸出上下文
    ret = avformat_alloc_output_context2(&octx, NULL, "flv", outUrl);
    if (ret < 0) {
        return avError(ret);
    }
    cout << "avformat_alloc_output_context2 success!" << endl;
    //配置輸出流
    //AVIOcontext *pb  //IO上下文
    //AVStream **streams  指針數(shù)組,存放多個(gè)輸出流  視頻音頻字幕流
    //int nb_streams;
    //duration ,bit_rate

    //AVStream
    //AVRational time_base
    //AVCodecParameters *codecpar 音視頻參數(shù)
    //AVCodecContext *codec
    //遍歷輸入的AVStream
    for (int i = 0; i < ictx->nb_streams; i++) {
        //創(chuàng)建一個(gè)新的流到octx中
        AVStream *out = avformat_new_stream(octx, ictx->streams[i]->codec->codec);
        if (!out) {
            return avError(0);
        }
        //復(fù)制配置信息 用于mp4 過(guò)時(shí)的方法
        //ret=avcodec_copy_context(out->codec, ictx->streams[i]->codec);
        ret = avcodec_parameters_copy(out->codecpar, ictx->streams[i]->codecpar);
        if (ret < 0) {
            return avError(ret);
        }
        out->codec->codec_tag = 0;
    }
    av_dump_format(octx, 0, outUrl, 1);

    //////////////////////////////////////////////////////////////////
    //                   準(zhǔn)備推流
    /////////////////////////////////////////////////////////////////

    //打開(kāi)IO
    ret = avio_open(&octx->pb, outUrl, AVIO_FLAG_WRITE);
    if (ret < 0) {
        avError(ret);
    }

    //寫入頭部信息
    ret = avformat_write_header(octx, 0);
    if (ret < 0) {
        avError(ret);
    }
    cout << "avformat_write_header Success!" << endl;

    //推流每一幀數(shù)據(jù)
    //int64_t pts  [ pts*(num/den)  第幾秒顯示]
    //int64_t dts  解碼時(shí)間 [P幀(相對(duì)于上一幀的變化) I幀(關(guān)鍵幀站超,完整的數(shù)據(jù)) B幀(上一幀和下一幀的變化)]  有了B幀壓縮率更高荸恕。
    //uint8_t *data    
    //int size
    //int stream_index
    //int flag
    AVPacket avPacket;
    //獲取當(dāng)前的時(shí)間戳  微妙
    long long startTime = av_gettime();
    while (true)
    {
        ret = av_read_frame(ictx, &avPacket);
        if (ret < 0) {
            break;
        }
        cout << avPacket.pts << " " << flush;
        //計(jì)算轉(zhuǎn)換時(shí)間戳 pts dts
        //獲取時(shí)間基數(shù)
        AVRational itime = ictx->streams[avPacket.stream_index]->time_base;
        AVRational otime = octx->streams[avPacket.stream_index]->time_base;
        avPacket.pts = av_rescale_q_rnd(avPacket.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
        avPacket.dts = av_rescale_q_rnd(avPacket.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
        //到這一幀時(shí)候經(jīng)歷了多長(zhǎng)時(shí)間
        avPacket.duration = av_rescale_q_rnd(avPacket.duration, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
        avPacket.pos = -1;
        //視頻幀推送速度
        if (ictx->streams[avPacket.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            AVRational tb = ictx->streams[avPacket.stream_index]->time_base;
            //已經(jīng)過(guò)去的時(shí)間
            long long now = av_gettime() - startTime;
            long long dts = 0;
            dts = avPacket.dts * (1000 * 1000 * r2d(tb));
            if (dts > now)
                av_usleep(dts - now);
            else {
                cout << "sss";
            }
        }
        //推送  會(huì)自動(dòng)釋放空間 不需要調(diào)用av_packet_unref
        ret = av_interleaved_write_frame(octx, &avPacket);
        if (ret < 0) {
            break;
        }
        //視頻幀推送速度
        //if (avPacket.stream_index == 0)
        //  av_usleep(30 * 1000);
        //釋放空間。內(nèi)部指向的視頻空間和音頻空間
        //av_packet_unref(&avPacket);
    }
    return 0;
}

int avError(int errNum) {
    char buf[1024];
    //獲取錯(cuò)誤信息
    av_strerror(errNum, buf, sizeof(buf));
    cout << " failed! " << buf << endl;
    return -1;
}

彩蛋

上面的代碼在推送flv格式文件時(shí)候可能沒(méi)問(wèn)題死相,當(dāng)換成mp4或者rmvb時(shí)候可能出現(xiàn)各種問(wèn)題融求。如果你是在無(wú)法解開(kāi)這個(gè)問(wèn)題,請(qǐng)看下節(jié)基于FFmpeg進(jìn)行RTMP推流(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末算撮,一起剝皮案震驚了整個(gè)濱河市生宛,隨后出現(xiàn)的幾起案子县昂,更是在濱河造成了極大的恐慌,老刑警劉巖陷舅,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倒彰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡莱睁,警方通過(guò)查閱死者的電腦和手機(jī)待讳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)仰剿,“玉大人创淡,你說(shuō)我怎么就攤上這事∧纤保” “怎么了琳彩?”我有些...
    開(kāi)封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)部凑。 經(jīng)常有香客問(wèn)我露乏,道長(zhǎng),這世上最難降的妖魔是什么砚尽? 我笑而不...
    開(kāi)封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任施无,我火速辦了婚禮,結(jié)果婚禮上必孤,老公的妹妹穿的比我還像新娘猾骡。我一直安慰自己,他們只是感情好敷搪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布兴想。 她就那樣靜靜地躺著,像睡著了一般赡勘。 火紅的嫁衣襯著肌膚如雪嫂便。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天闸与,我揣著相機(jī)與錄音毙替,去河邊找鬼。 笑死践樱,一個(gè)胖子當(dāng)著我的面吹牛厂画,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拷邢,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼袱院,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起忽洛,我...
    開(kāi)封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腻惠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后欲虚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體集灌,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年苍在,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绝页。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寂恬,死狀恐怖续誉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情初肉,我是刑警寧澤酷鸦,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站牙咏,受9級(jí)特大地震影響臼隔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妄壶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一摔握、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丁寄,春花似錦氨淌、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至屑埋,卻和暖如春豪筝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摘能。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工续崖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人团搞。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓袜刷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親莺丑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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