LINUX(CentOS)下JAVA調(diào)用 C++ SO庫(kù)實(shí)現(xiàn)錄像功能运褪,SO庫(kù)封裝FFmpeg

1.首先獲取FFmpeg惊楼,并編譯
1.1

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg

進(jìn)入ffmpeg目錄:

cd ffmpeg

1.2編譯三部曲:

./configure --extra-cflags="-O3 -fPIC" --enable-pic --disable-static --enable-shared
make -j4
make install

注意上面的參數(shù)-fPIC,--enable-pic秸讹。之前沒(méi)注意檀咙。c++調(diào)用so庫(kù)沒(méi)問(wèn)題,java死活掉不起來(lái)璃诀,找不到ffmpeg函數(shù)弧可。

2.C++封裝FFmpeg 錄像函數(shù)
直接上代碼,代碼沒(méi)有怎么整理劣欢,有點(diǎn)亂棕诵,調(diào)試部分代碼可以刪除。RtspStreamMuxTask 文件代碼轉(zhuǎn)載自https://blog.csdn.net/toshiba689/article/details/79426680氧秘,且用且珍惜年鸳。

2.1 mux.h


#pragma once

extern "C" {

//開(kāi)始錄像
//參數(shù):videoSrcAddr 視頻源地址,filePath 錄像文件路徑
//返回錄像句柄丸相,異常返回-1
int startRecordVideo(char* videoSrcAddr, char* filePath);

//停止錄像
//參數(shù):index錄像句柄
//返回傳入的句柄號(hào),異常返回-1
int stopRecordVideo(int index);

}

2.2 mux.cpp

#include "mux.h"
#include "RtspStreamMuxTask.h"
#include "TickCounter.h"
#include <memory>
#include <vector>
#include <map>
using namespace std;
#include "dlog2.h"

std::map<int, std::shared_ptr<RtspStreamMuxTask> > muxTasks;
static volatile int muxTaskNum = 0;

//開(kāi)始錄像
int startRecordVideo(char* videoSrcAddr, char* filePath)
{
Mcgi_log( "%s %s", videoSrcAddr,  filePath);
TickCounter tc(__FUNCTION__);
#if 1

std::shared_ptr<RtspStreamMuxTask> mux(new RtspStreamMuxTask);
//RtspStreamMuxTask mux;
mux.get()->SetInputUrl(videoSrcAddr);
mux.get()->SetOutputPath(filePath);
mux.get()->StartRecvStream();
muxTasks.insert(make_pair(muxTaskNum, mux)); //<int, std::shared_ptr<RtspStreamMuxTask> >

#else

RtspStreamMuxTask* mux = new RtspStreamMuxTask;
mux->SetInputUrl(videoSrcAddr);
mux->SetOutputPath(filePath);
mux->StartRecvStream();
muxTasks.insert(make_pair(muxTaskNum++, mux));

#endif

printf("**** video record index: %d\n", muxTaskNum);
Mcgi_log( "%d %s %s", muxTaskNum, videoSrcAddr, filePath);
return muxTaskNum++;

}

//停止錄像

int stopRecordVideo(int index)
{
TickCounter tc(__FUNCTION__);
printf("111111111  %d\n", muxTasks.count(index));

#if 1
if (muxTasks.count(index))
{

printf("2222222222\n");
muxTasks[index].get()->StopRecvStream();
muxTasks.erase(index);
printf("**** video record count: %d\n", muxTasks.size());
return index;
}

#else

if (muxTasks.count(index) > 0)
{
muxTasks[index]->StopRecvStream();
delete muxTasks[index];

muxTasks.erase(index);
printf("**** video record count: %d\n", muxTasks.size());
return index;
}

#endif
return -1;
}

2.3 RtspStreamMuxTask.h

#ifndef RtspStreamMuxTask_H
#define RtspStreamMuxTask_H
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <iostream>
using namespace std;

#ifdef __cplusplus 
extern "C" 
{ 
#endif 
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#ifdef __cplusplus 
}; 

#endif 

class RtspStreamMuxTask
{

public:
RtspStreamMuxTask();
virtual ~RtspStreamMuxTask();
void  SetInputUrl(string rtspUrl);
void  SetOutputPath(string outputPath);
void StartRecvStream();
void StopRecvStream();

private:
void run();
int  OpenInputStream();
void CloseInputStream();
void readAndMux();
static void* ReadingThrd(void * pParam);
int  openOutputStream();
void closeOutputStream();
void ReleaseCodecs();
void GetVideoSize(long & width, long & height)  //獲取視頻分辨率
{
width  = coded_width;
height = coded_height;
}

private:
string m_inputUrl;
string m_outputFile;

AVFormatContext* m_inputAVFormatCxt;
AVBitStreamFilterContext* m_bsfcAAC;
AVBitStreamFilterContext* m_bsfcH264;

int m_videoStreamIndex;
int m_audioStreamIndex;

AVFormatContext* m_outputAVFormatCxt;
char m_tmpErrString[64];

volatile bool m_stop_status;
pthread_t m_hReadThread;

bool  m_bInputInited;
bool  m_bOutputInited;

int    coded_width, coded_height;
int    m_frame_rate;
};

#endif // RtspStreamMuxTask_H

// https://blog.csdn.net/toshiba689/article/details/79426680

2.4 RtspStreamMuxTask.cpp

#include "RtspStreamMuxTask.h"
#include <sstream>
#include "TickCounter.h"
#include "dlog2.h"


//////////////////////////////////////////////////////////////

RtspStreamMuxTask::RtspStreamMuxTask()
{
//dLog( char* dIfomSource, char* dIfomLevel, char* dFmt, ... );
Mcgi_log( "%s", __FUNCTION__ );

    m_stop_status = false;
    m_inputAVFormatCxt = NULL;
    m_bsfcAAC = NULL;
m_bsfcH264 = NULL;
    m_videoStreamIndex = -1;
m_audioStreamIndex = -1;
    m_outputAVFormatCxt = NULL;
m_hReadThread = NULL;
m_bInputInited = false;
m_bOutputInited = false;
    coded_width = coded_height = 0;
    m_frame_rate = 25;
}

RtspStreamMuxTask::~RtspStreamMuxTask()
{
StopRecvStream();
}

void  RtspStreamMuxTask::SetInputUrl(string rtspUrl)
{
Mcgi_log( "%s", __FUNCTION__ );
m_inputUrl = rtspUrl;
}

void  RtspStreamMuxTask::SetOutputPath(string outputPath)
{

Mcgi_log( "%s", __FUNCTION__ );
m_outputFile = outputPath;
}

void RtspStreamMuxTask::StartRecvStream()
{

Mcgi_log( "%s", __FUNCTION__ );
TickCounter tc(__FUNCTION__);
if(m_inputUrl.empty())
return;

    m_videoStreamIndex = -1;
m_audioStreamIndex = -1;
m_bInputInited  = false;
m_bOutputInited = false;
coded_width = coded_height = 0;
int err = pthread_create(&m_hReadThread, NULL, ReadingThrd, this);
//pthread_detach(m_hReadThread);
//sleep(1);
}

void RtspStreamMuxTask::StopRecvStream()
{

TickCounter tc(__FUNCTION__);
    m_stop_status = true;
    printf("333333333 m_stop_status: %d\n", m_stop_status);
//sleep(1);
if (m_hReadThread != NULL)
{
        //WaitForSingleObject(m_hReadThread, INFINITE);
        void* rval1;
        pthread_join(m_hReadThread, &rval1);
//CloseHandle(m_hReadThread);
m_hReadThread = NULL;
}
    CloseInputStream();
}

void* RtspStreamMuxTask::ReadingThrd(void * pParam)
{
Mcgi_log( "%s", __FUNCTION__ );
TickCounter tc(__FUNCTION__);
RtspStreamMuxTask * pTask = (RtspStreamMuxTask *) pParam;
pTask->run();

return 0;
}

void RtspStreamMuxTask::run()
{
Mcgi_log( "%s", __FUNCTION__ );
TickCounter tc(__FUNCTION__);
    try
    {
        //m_stop_status = false;
        OpenInputStream();
openOutputStream();
        //m_stop_status = false;
        readAndMux();
        CloseInputStream();
closeOutputStream();
    }
    catch(std::exception& e)
    {
printf("%s \n", e.what());
        CloseInputStream();
    }
}

int RtspStreamMuxTask::OpenInputStream()
{
Mcgi_log( "%s start", __FUNCTION__ );
TickCounter tc(__FUNCTION__);
    if (m_inputAVFormatCxt)
    {
        string strError  = ("already has input avformat");
printf("%s \n", strError.c_str());
Mcgi_log( "%s %s", __FUNCTION__, strError.c_str());
return -1;
    }

Mcgi_log( "%s 1", __FUNCTION__ );
    m_bsfcAAC = av_bitstream_filter_init("aac_adtstoasc");
    if(!m_bsfcAAC)
    {
        string strError = ("can not create aac_adtstoasc filter");
printf("%s \n", strError.c_str());
Mcgi_log( "%s %s", __FUNCTION__, strError.c_str());
return -1;
    }

Mcgi_log( "%s 2", __FUNCTION__ );
m_bsfcH264 = av_bitstream_filter_init("h264_mp4toannexb");
    if(!m_bsfcH264)
    {
        string strError = ("can not create h264_mp4toannexb filter");
printf("%s \n", strError.c_str());
Mcgi_log( "%s %s", __FUNCTION__, strError.c_str());
return -1;
    }

///////////////////////////////////////////////////////////
Mcgi_log( "%s 3", __FUNCTION__ );
    int res = 0;
AVDictionary* options = NULL;
//av_dict_set(&options, "bufsize", "4096000", 0);
av_dict_set(&options, "rtsp_transport", "tcp", 0);              //采用tcp傳輸
    res = avformat_open_input(&m_inputAVFormatCxt, m_inputUrl.c_str(), 0, &options);
Mcgi_log( "%s 4", __FUNCTION__ );

    if(res < 0)
    {
        string strError = ("can not open file:" + m_inputUrl + ",errcode:" + to_string(res) + ",err msg:" + av_make_error_string(m_tmpErrString, AV_ERROR_MAX_STRING_SIZE, res));
printf("%s \n", strError.c_str());
Mcgi_log( "%s %s", __FUNCTION__, strError.c_str());
return -1;
    }

Mcgi_log( "%s 5", __FUNCTION__ );
    if (avformat_find_stream_info(m_inputAVFormatCxt, 0) < 0)
    {
        string strError = ("can not find stream info");
printf("%s \n", strError.c_str());
Mcgi_log( "%s %s", __FUNCTION__, strError.c_str());
return -1;
    }

Mcgi_log( "%s 6", __FUNCTION__ );
    av_dump_format(m_inputAVFormatCxt, 0, m_inputUrl.c_str(), 0);
Mcgi_log( "%s 7", __FUNCTION__ );
    for (int i = 0; i < m_inputAVFormatCxt->nb_streams; i++)
    {
        AVStream *in_stream = m_inputAVFormatCxt->streams[i];
printf("codec id: %d, URL: %s \n", in_stream->codec->codec_id, m_inputUrl.c_str());
if (in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_videoStreamIndex = i;
coded_width = in_stream->codec->width;
coded_height = in_stream->codec->height;
if(in_stream->avg_frame_rate.den != 0 && in_stream->avg_frame_rate.num != 0)
{
m_frame_rate = in_stream->avg_frame_rate.num/in_stream->avg_frame_rate.den;//每秒多少幀
}
printf("video stream index: %d, width: %d, height: %d, FrameRate: %d\n", m_videoStreamIndex, in_stream->codec->width, in_stream->codec->height, m_frame_rate);
}

else if (in_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
m_audioStreamIndex = i;
}
    }

m_bInputInited = true;
Mcgi_log( "%s end", __FUNCTION__ );
return 0;
}

void RtspStreamMuxTask::CloseInputStream()
{
TickCounter tc(__FUNCTION__);
    if (m_inputAVFormatCxt)
    {
        avformat_close_input(&m_inputAVFormatCxt);
    }

    if(m_bsfcAAC)
    {
        av_bitstream_filter_close(m_bsfcAAC);
        m_bsfcAAC = NULL;
    }
if(m_bsfcH264)
{
    av_bitstream_filter_close(m_bsfcH264);
        m_bsfcH264 = NULL;
}

m_bInputInited = false;
}

int  RtspStreamMuxTask::openOutputStream()
{
Mcgi_log( "%s start", __FUNCTION__ );
TickCounter tc(__FUNCTION__);
    if (m_outputAVFormatCxt)
    {
        printf("already has rtmp avformat \n");
return -1;
    }

    int res = 0;
    if(!m_outputFile.empty())
    {
res = avformat_alloc_output_context2(&m_outputAVFormatCxt, NULL, NULL, m_outputFile.c_str());
        if (m_outputAVFormatCxt == NULL)
        {
            printf("can not alloc output context \n");
return -1;
        }

        AVOutputFormat* fmt = m_outputAVFormatCxt->oformat;
        //fmt->audio_codec = AV_CODEC_ID_AAC;
        //fmt->video_codec = AV_CODEC_ID_H264;
        for (int i = 0; i < m_inputAVFormatCxt->nb_streams; i++)
        {
            AVStream *in_stream = m_inputAVFormatCxt->streams[i];
            AVStream *out_stream = avformat_new_stream(m_outputAVFormatCxt, in_stream->codec->codec);
            if (!out_stream)
            {
                printf("can not new out stream");
return -1;

            }
            res = avcodec_copy_context(out_stream->codec, in_stream->codec);
            if (res < 0)
            {
                string strError = "can not copy context, url: " + m_inputUrl + ",errcode:" + to_string(res) + ",err msg:" + av_make_error_string(m_tmpErrString, AV_ERROR_MAX_STRING_SIZE, res);
printf("%s \n", strError.c_str());
return -1;
            }

#define AV_CODEC_FLAG_GLOBAL_HEADER (1 << 22)
#define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER
//#define AVFMT_RAWPICTURE 0x0020

            if (m_outputAVFormatCxt->oformat->flags & AVFMT_GLOBALHEADER)
            {
                out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
            }
        }

        av_dump_format(m_outputAVFormatCxt, 0, m_outputFile.c_str(), 1);
        if (!(fmt->flags & AVFMT_NOFILE))
        {
            res = avio_open(&m_outputAVFormatCxt->pb, m_outputFile.c_str(), AVIO_FLAG_WRITE);
            if (res < 0)
            {

                string strError = "can not open output io, file:" + m_outputFile + ",errcode:" + to_string(res) + ", err msg:" + av_make_error_string(m_tmpErrString, AV_ERROR_MAX_STRING_SIZE, res);
printf("%s \n", strError.c_str());
Mcgi_log("%s", strError.c_str());
return -1;
}
        }

res = avformat_write_header(m_outputAVFormatCxt, NULL);
        if (res < 0)
        {
            string strError = "can not write outputstream header, URL:" + m_outputFile + ",errcode:" + to_string(res) + ", err msg:" + av_make_error_string(m_tmpErrString, AV_ERROR_MAX_STRING_SIZE, res);
printf("%s \n", strError.c_str());
m_bOutputInited = false;
return -1;
        }
m_bOutputInited = true;
    }

    Mcgi_log("%s end", __FUNCTION__);
return 0;
}

void RtspStreamMuxTask::closeOutputStream()
{
TickCounter tc(__FUNCTION__);
    if (m_outputAVFormatCxt)
    {
        if(m_bOutputInited)
{
          int res = av_write_trailer(m_outputAVFormatCxt);
}
        if (!(m_outputAVFormatCxt->oformat->flags & AVFMT_NOFILE))
        {
            if(m_outputAVFormatCxt->pb)
            {
                avio_close(m_outputAVFormatCxt->pb);
            }
        }

        avformat_free_context(m_outputAVFormatCxt);
        m_outputAVFormatCxt = NULL;
    }

m_bOutputInited = false;
}

void RtspStreamMuxTask::readAndMux()
{

Mcgi_log( "%s start", __FUNCTION__ );
TickCounter tc(__FUNCTION__);
int nVideoFramesNum = 0;
int64_t  first_pts_time = 0;
//DWORD start_time = GetTickCount();

    AVPacket pkt;
av_init_packet(&pkt);
    while(!m_stop_status)
    {
        int res;
        res = av_read_frame(m_inputAVFormatCxt, &pkt);
        if (res < 0)  //讀取錯(cuò)誤或流結(jié)束
        {
if(AVERROR_EOF == res)
{
printf("End of file \n");
}

else
{
    printf("av_read_frame() got error: %d \n", res);
}
break; 
        }

        AVStream *in_stream = m_inputAVFormatCxt->streams[pkt.stream_index];
        AVStream *out_stream = m_outputAVFormatCxt->streams[pkt.stream_index];
        //pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        //pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        //pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);

pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);

        pkt.pos = -1;
        if(in_stream->codec->codec_type != AVMEDIA_TYPE_VIDEO && in_stream->codec->codec_type != AVMEDIA_TYPE_AUDIO)

{
continue;
}

if(in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO)  //視頻
{
nVideoFramesNum++;
// write the compressed frame to the output format
int nError = av_interleaved_write_frame(m_outputAVFormatCxt, &pkt);
if (nError != 0)
{
char tmpErrString[AV_ERROR_MAX_STRING_SIZE] = {0};
av_make_error_string(tmpErrString, AV_ERROR_MAX_STRING_SIZE, nError);
printf("Error: %d while writing video frame, %s\n", nError, tmpErrString);
}

//int nSecs = pkt.pts*in_stream->time_base.num/in_stream->time_base.den;
//printf("Frame time: %02d:%02d \n", nSecs/60, nSecs%60);
}

else if(in_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) //音頻
{

#if 0

// write the compressed frame to the output format
int nError = av_interleaved_write_frame(m_outputAVFormatCxt, &pkt);
if (nError != 0)
{

char tmpErrString[AV_ERROR_MAX_STRING_SIZE] = {0};
av_make_error_string(tmpErrString, AV_ERROR_MAX_STRING_SIZE, nError);
printf("Error: %d while writing audio frame, %s\n", nError, tmpErrString);
}

#endif
}

if((in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) )
{
if(first_pts_time == 0)
    first_pts_time = pkt.pts;
int64_t pts_time = (pkt.pts - first_pts_time)*1000*in_stream->time_base.num/in_stream->time_base.den; //轉(zhuǎn)成毫秒

//int64_t now_time = GetTickCount() - start_time;
//if(pts_time > now_time + 10 && pts_time < now_time + 3000)
//{
//  Sleep(pts_time-now_time);
//}
//else if(pts_time == 0 && nVideoFramesNum > 1)
//{
//  Sleep(20);
//}
}

av_free_packet(&pkt);
static int frameId = 0;
frameId++;
if (frameId % 10 == 1)
printf("frameId: %d\n", frameId);
//TickCounter tc("while");
    }//while

Mcgi_log( "%s end", __FUNCTION__ );
printf("Reading ended, read %d video frames \n", nVideoFramesNum);
}

// https://blog.csdn.net/toshiba689/article/details/79426680

2.6 TickCounter.h

#pragma once
#include <iostream>
using namespace std;
class TickCounter
{
public:
TickCounter(string debug);
~TickCounter();
string debug;
};

2.7 TickCounter.cpp

#include "TickCounter.h"
#include <stdio.h>
TickCounter::TickCounter(string debug)
{
printf("%s enter <----\n", debug.c_str());
this->debug = debug;
}

TickCounter::~TickCounter()
{
printf("%s leave ---->\n", debug.c_str());
}

2.8 Makefile

#頭文件

#INCLUDE = $(shell pkg-config --cflags opencv)
INCLUDE =
INCLUDE += -I.
#擴(kuò)展庫(kù)
LIBS =
#LIBS = $(shell pkg-config --libs opencv)
LIBS += -lpthread -lavformat -lavcodec -lavdevice -lavfilter -lavutil -lswresample
#-lm -lz

CFLAGS = -O3 -Wall -std=c++11
#-fPIC -shared
# 源文件
SOURCES = RtspStreamMuxTask.cpp TickCounter.cpp  dlog2.cpp mux.cpp

#main.cpp
# 可執(zhí)行文件 
OBJECTS = $(SOURCES:.cpp=.o)
TARGET = libmux.so

$(TARGET):$(OBJECTS)
g++ -shared $(CFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET)

#-fPIC
$(OBJECTS):$(SOURCES)
g++ -fPIC $(CFLAGS) -c $(SOURCES) $(LIBS)
#

clean:
rm $(OBJECTS) $(TARGET)

# 編譯規(guī)則 $@代表目標(biāo)文件 $< 代表第一個(gè)依賴文件 
%.o:%.cpp

g++ -fPIC $(CFLAGS) $(INCLUDE) -o $@ -c $<

2.9 JAVA代碼

import com.sun.jna.Library;
import com.sun.jna.Native;
public class TestSo
{
public interface CMux extends Library {
// 調(diào)用linux下面的so文件,注意彼棍,這里只要寫mux就可以了灭忠,不要寫libmux膳算,也不要加后綴
CMux INSTANCE = (CMux) Native.loadLibrary("mux", CMux.class);
int startRecordVideo(String videoSrcAddr, String filePath);
int stopRecordVideo(int index);
}

public int startRecordVideo(String videoSrcAddr, String filePath){
return CMux.INSTANCE.startRecordVideo(videoSrcAddr, filePath);
}

public int stopRecordVideo(int index){
return CMux.INSTANCE.stopRecordVideo(index);
}

public static void main(String[] args) {
TestSo ts = new TestSo();
String videoSrcAddr = "rtsp://172.28.175.86:8554/channel=0";
String filePath = "/root/yangxm/work/rec/0.mp4";
int c = ts.startRecordVideo(videoSrcAddr, filePath);
try
{
Thread.sleep(10000);
}

catch(Exception e)
{
;
}

System.out.println(videoSrcAddr + "," + filePath);
}
}

2.9.1 編譯
javac TestSo.java

2.9.2 調(diào)用
java TestSo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弛作,隨后出現(xiàn)的幾起案子涕蜂,更是在濱河造成了極大的恐慌,老刑警劉巖映琳,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件机隙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡萨西,警方通過(guò)查閱死者的電腦和手機(jī)有鹿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谎脯,“玉大人葱跋,你說(shuō)我怎么就攤上這事≡此螅” “怎么了娱俺?”我有些...
    開(kāi)封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)废麻。 經(jīng)常有香客問(wèn)我荠卷,道長(zhǎng),這世上最難降的妖魔是什么烛愧? 我笑而不...
    開(kāi)封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任油宜,我火速辦了婚禮,結(jié)果婚禮上屑彻,老公的妹妹穿的比我還像新娘验庙。我一直安慰自己,他們只是感情好社牲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布粪薛。 她就那樣靜靜地躺著,像睡著了一般搏恤。 火紅的嫁衣襯著肌膚如雪违寿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天熟空,我揣著相機(jī)與錄音藤巢,去河邊找鬼。 笑死息罗,一個(gè)胖子當(dāng)著我的面吹牛掂咒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼绍刮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼温圆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起孩革,我...
    開(kāi)封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岁歉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后膝蜈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體锅移,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年饱搏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了十兢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片部蛇。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡模她,死狀恐怖垃沦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坤学,我是刑警寧澤疯坤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站深浮,受9級(jí)特大地震影響压怠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜飞苇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一菌瘫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧布卡,春花似錦雨让、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贸街,卻和暖如春庵寞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背薛匪。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工捐川, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逸尖。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓古沥,卻偏偏與公主長(zhǎng)得像瘸右,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渐白,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355