利用FFmpeg 開發(fā)音視頻流(三)——將視頻 YUV 格式編碼成 H264

已經(jīng)好幾個月沒有寫博客了谭梗,突然來感去寫,手藝也有些生疏了题画,確實(shí)是這幾個月實(shí)在太忙了默辨,誰也知道現(xiàn)在直播火了起來,很多公司也在做直播苍息,博主也不例外呀缩幸,做起客戶端都是從 0 開始壹置,嘔心瀝血,好了表谊,廢話不說太多钞护,說說今天的主題。
首先開始的時候我們插入一張雷神大大的圖幫助大家理解一下我們今天的操作究竟屬于那一步爆办。

音視頻格式封裝層次
ps:在這里也要啰嗦一下难咕,大家看見這個圖黑白色了么?這是為啥距辆?說起這個太感慨了余佃,
一代雷神大大離我們而去,26歲的博士雷神大大跨算,一個愛分享的人就這么隕落了爆土,太可惜了。

從上圖可以看出我們要做的诸蚕,就是將像素層的 YUV 格式步势,編碼出編碼層的 h264數(shù)據(jù)。


前面講到我們已經(jīng)成功編譯出 iOS 中可用的 ffmpeg 的庫了背犯,那么我們首先熟悉一下今天我們要用到的 ffmpeg 中的函數(shù)和結(jié)構(gòu)體

AVFormatContext: 數(shù)據(jù)文件操作者坏瘩,主要是用于存儲音視頻封裝格式中包含的信息, 在工程當(dāng)中占著具足輕重的地位,因?yàn)楹芏嗪瘮?shù)都要用到它作為參數(shù)漠魏。同時倔矾,它也是我們進(jìn)行解封裝的功能結(jié)構(gòu)體。


AVOutputFormat: 輸出的格式蛉幸,包括音頻封裝格式破讨、視頻裝格式、字幕封裝格式奕纫,所有封裝格式都在 AVCodecID 這個枚舉類型上面了


AVStream: 一個裝載著視頻/音頻流信息的結(jié)構(gòu)體提陶,包括音視頻流的長度,元數(shù)據(jù)信息匹层,其中 index 屬性用于標(biāo)識視頻/音頻流隙笆。


AVCodecContext: 這個結(jié)構(gòu)體十分龐大,但它的主要是用于編碼使用的升筏,結(jié)構(gòu)體中的的 AVCodec *codec 就是編碼所采用的編碼器器, 當(dāng)然撑柔,這個結(jié)構(gòu)體中要存入視頻的基本參數(shù),例如寬高等您访,存入音頻的基本參數(shù)铅忿,聲道,采樣率等灵汪。


AVCodec:編碼器檀训,設(shè)置編碼類型柑潦,像素格式,視頻寬高峻凫,fps(每秒幀數(shù)), 用于編解碼音視頻編碼層使用渗鬼。


AVIOContext:用于管理輸入輸出結(jié)構(gòu)體。例如解碼的情況下荧琼,將一個視頻文件中的數(shù)據(jù)先從硬盤中讀入到結(jié)構(gòu)體中的 buffer 中譬胎,然后送給解碼器用于解碼,后面我們會用到命锄。


AVFrame: 結(jié)構(gòu)體一般用于存儲原始數(shù)據(jù)(即非壓縮數(shù)據(jù)堰乔,例如對視頻來說是YUV,RGB脐恩,對音頻來說是PCM)浩考,此外還包含了一些相關(guān)的信息。比如說被盈,解碼的時候存儲了宏塊類型表,QP表搭伤,運(yùn)動矢量表等數(shù)據(jù)只怎。編碼的時候也存儲了相關(guān)的數(shù)據(jù)。因此在使用FFMPEG進(jìn)行碼流分析的時候怜俐,AVFrame是一個很重要的結(jié)構(gòu)體身堡。

好了,上面就是我們這次解封裝用到的結(jié)構(gòu)體的大概解析拍鲤,那么我們就上代碼贴谎,好好分析一番。


1季稳、先取個霸氣點(diǎn)的函數(shù)名擅这,通過輸入一個 yuv 文件路徑,然后將文件數(shù)據(jù)進(jìn)行編碼景鼠,輸出 H264文件仲翎。

yuvCodecToVideoH264(const char *input_file_name)

2、打開輸入的 yuv 文件, 并設(shè)置我們 h264 文件的輸出路徑铛漓,

FILE *in_file = fopen(input_file, "rb");  
// 因?yàn)槲覀冊?iOS 工程當(dāng)中溯香,所以輸出路徑當(dāng)然要設(shè)置本機(jī)的路徑了
const char* out_file = [[NSTemporaryDirectory() stringByAppendingPathComponent:@"dash.h264"] cStringUsingEncoding:NSUTF8StringEncoding];

3、獲取 yuv 視頻中的信息

// 注冊 ffmpeg 中的所有的封裝浓恶、解封裝 和 協(xié)議等玫坛,當(dāng)然,你也可用以下兩個函數(shù)代替  
// * @see av_register_input_format()
// * @see av_register_output_format()
 av_register_all();

//  用作之后寫入視頻幀并編碼成 h264包晰,貫穿整個工程當(dāng)中
AVFormatContext* pFormatCtx;
pFormatCtx = avformat_alloc_context();

// 通過這個函數(shù)可以獲取輸出文件的編碼格式, 那么這里我們的 fmt 為 h264 格式(AVOutputFormat *)
fmt = av_guess_format(NULL, out_file, NULL);
pFormatCtx->oformat = fmt;

4湿镀、將輸出文件中的數(shù)據(jù)讀入到程序的 buffer 當(dāng)中炕吸,方便之后的數(shù)據(jù)寫入,也可以說緩存數(shù)據(jù)寫入

// 打開文件的緩沖區(qū)輸入輸出肠骆,flags 標(biāo)識為  AVIO_FLAG_READ_WRITE 算途,可讀寫
if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){
  printf("Failed to open output file! \n");
  return;
}

5、創(chuàng)建流媒體數(shù)據(jù)蚀腿,規(guī)范流媒體的編碼格式嘴瓤,設(shè)置視頻流的 fps

AVStream* video_st;
// 通過媒體文件控制者獲取輸出文件的流媒體數(shù)據(jù),這里 AVCodec * 寫 0 莉钙, 默認(rèn)會為我們計(jì)算出合適的編碼格式
video_st = avformat_new_stream(pFormatCtx, 0);

// 設(shè)置 25 幀每秒 廓脆,也就是 fps 為 25
video_st->time_base.num = 1;
video_st->time_base.den = 25;

if (video_st==NULL){
  return ;
}

6、為輸出文件設(shè)置編碼所需要的參數(shù)和格式

// 用戶存儲編碼所需的參數(shù)格式等等
AVCodecContext* pCodecCtx;

// 從媒體流中獲取到編碼結(jié)構(gòu)體磁玉,他們是一一對應(yīng)的關(guān)系停忿,一個 AVStream 對應(yīng)一個  AVCodecContext
 pCodecCtx = video_st->codec;
   
// 設(shè)置編碼器的 id,每一個編碼器都對應(yīng)著自己的 id蚊伞,例如 h264 的編碼 id 就是 AV_CODEC_ID_H264
pCodecCtx->codec_id = fmt->video_codec;

// 設(shè)置編碼類型為 視頻編碼
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;

// 設(shè)置像素格式為 yuv 格式
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;

// 設(shè)置視頻的寬高
pCodecCtx->width = 480;
pCodecCtx->height = 720;

// 設(shè)置比特率席赂,每秒傳輸多少比特數(shù) bit,比特率越高时迫,傳送速度越快颅停,也可以稱作碼率,
// 視頻中的比特是指由模擬信號轉(zhuǎn)換為數(shù)字信號后掠拳,單位時間內(nèi)的二進(jìn)制數(shù)據(jù)量癞揉。
pCodecCtx->bit_rate = 400000;

// 設(shè)置圖像組層的大小。
// 圖像組層是在 MPEG 編碼器中存在的概念溺欧,圖像組包 若干幅圖像, 組頭包 起始碼喊熟、GOP 標(biāo)志等,如視頻磁帶記錄器時間、控制碼姐刁、B 幀處理碼等;
pCodecCtx->gop_size=250;

// 設(shè)置 25 幀每秒 芥牌,也就是 fps 為 25
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;

//設(shè)置 H264 中相關(guān)的參數(shù)
//pCodecCtx->me_range = 16;
//pCodecCtx->max_qdiff = 4;
//pCodecCtx->qcompress = 0.6;
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;

// 設(shè)置 B 幀最大的數(shù)量,B幀為視頻圖片空間的前后預(yù)測幀聂使, B 幀相對于 I胳泉、P 幀來說,壓縮率比較大岩遗,也就是說相同碼率的情況下扇商,
// 越多 B 幀的視頻,越清晰宿礁,現(xiàn)在很多打視頻網(wǎng)站的高清視頻案铺,就是采用多編碼 B 幀去提高清晰度,
// 但同時對于編解碼的復(fù)雜度比較高梆靖,比較消耗性能與時間
pCodecCtx->max_b_frames=3;

// 可選設(shè)置
AVDictionary *param = 0;
//H.264
if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {
// 通過--preset的參數(shù)調(diào)節(jié)編碼速度和質(zhì)量的平衡控汉。
av_dict_set(&param, "preset", "slow", 0);

// 通過--tune的參數(shù)值指定片子的類型笔诵,是和視覺優(yōu)化的參數(shù),或有特別的情況姑子。
// zerolatency: 零延遲乎婿,用在需要非常低的延遲的情況下,比如電視電話會議的編碼
av_dict_set(&param, "tune", "zerolatency", 0);

順便說一下h264 當(dāng)中有片組的概念街佑,其中編碼片分為5種谢翎,I 片、P 片沐旨、B 片森逮、SP 片和 SI 片。

ES 碼流是 MPEG 碼流中的基本流磁携,由視頻壓縮編碼后的視頻基 碼流(Video ES)和音頻壓縮編碼后的音頻基 碼流(Audio ES)組成褒侧。
以下順帶一張 ES 碼流的結(jié)構(gòu)圖片,作為記錄學(xué)習(xí)之用


ES 碼流結(jié)構(gòu)

ES 碼流采用圖像序列(PS)谊迄、圖像組(GOP)闷供、圖像(P)、片(slice)统诺、宏塊(MB)这吻、塊(B)六層結(jié)構(gòu)。

(1)圖像序列層,圖像序列包括若干 GOP,序列頭包 起始碼和序列參數(shù),如檔次篙议、級別、彩色圖像格式怠硼、幀場選擇等等;
(2)圖像組層,圖像組包 若干幅圖像,組頭包 起始碼鬼贱、GOP 標(biāo)志等,如視頻磁帶記錄器時間、控制碼香璃、B 幀處理碼等;
(3)圖像層,一幅圖像包 若干片,頭信息中有起始碼这难、P 標(biāo)志,如時間、參考幀號葡秒、圖像類型姻乓、MV、分級等;
(4)片層,片是最小的同步單位,包 若干宏塊,片頭中有起始碼眯牧、片地址蹋岩、量化步長等;
(5)宏塊層,宏塊由 4 個 8×8 亮度塊和 2 個色度塊組成,宏塊頭包括宏塊地址、宏塊類型学少、運(yùn)動矢量等剪个。

7、printf(輸出) 一些關(guān)于輸出格式的詳細(xì)數(shù)據(jù)版确,例如時間扣囊,比特率乎折,數(shù)據(jù)流,容器侵歇,元數(shù)據(jù)骂澄,輔助數(shù)據(jù),編碼惕虑,時間戳等等

av_dump_format(pFormatCtx, 0, out_file, 1);

8坟冲、設(shè)置編碼器

// 通過 codec_id 找到對應(yīng)的編碼器
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec){
  printf("Can not find encoder! \n");
  return;
}

// 打開編碼器,并設(shè)置參數(shù) param
if (avcodec_open2(pCodecCtx, pCodec,&param) < 0){
  printf("Failed to open encoder! \n");
  return;
}

9枷遂、設(shè)置原始數(shù)據(jù) AVFrame

C
AVFrame *pFrame = av_frame_alloc();

// 通過像素格式(這里為 YUV)獲取圖片的真實(shí)大小樱衷,例如將 480 * 720 轉(zhuǎn)換成 int 類型
int picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

// 將 picture_size 轉(zhuǎn)換成字節(jié)數(shù)據(jù),byte
unsigned char *picture_buf = (uint8_t *)av_malloc(picture_size);

// 設(shè)置原始數(shù)據(jù) AVFrame 的每一個frame 的圖片大小酒唉,AVFrame 這里存儲著 YUV 非壓縮數(shù)據(jù)
avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);


10矩桂、準(zhǔn)備寫入數(shù)據(jù)之前,當(dāng)然要先寫編碼的頭部了

// 編寫 h264 封裝格式的文件頭部痪伦,基本上每種編碼都有著自己的格式的頭部侄榴,想看具體實(shí)現(xiàn)的同學(xué)可以看看 h264 的具體實(shí)現(xiàn)
int ret = avformat_write_header(pFormatCtx,NULL);
if (ret < 0) {
printf("write header is failed");
return;
}


這里順便記錄一下, h264 原始碼流网沾,又稱為原始碼流癞蚕,都是由一個一個的 NALU 組成的,結(jié)構(gòu)體如下

enum nal_unit_type_e
{
NAL_UNKNOWN = 0, // 未使用
NAL_SLICE = 1, // 不分區(qū)辉哥、非 IDR 圖像的片
NAL_SLICE_DPA = 2, // 片分區(qū) A
NAL_SLICE_DPB = 3, // 片分區(qū) B
NAL_SLICE_DPC = 4, // 片分區(qū) C
NAL_SLICE_IDR = 5, /* ref_idc != 0 / // 序列參數(shù)集
NAL_SEI = 6, /
ref_idc == 0 / // 圖像參數(shù)集
NAL_SPS = 7, // 分界符
NAL_PPS = 8, // 序列結(jié)束
NAL_AUD = 9, // 碼流結(jié)束
NAL_FILLER = 12, // 填充
/
ref_idc == 0 for 6,9,10,11,12 */
};
enum nal_priority_e // 優(yōu)先級
{
NAL_PRIORITY_DISPOSABLE = 0,
NAL_PRIORITY_LOW = 1,
NAL_PRIORITY_HIGH = 2,
NAL_PRIORITY_HIGHEST = 3,
};

typedef struct
{
int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
unsigned max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
int nal_reference_idc; //! NALU_PRIORITY_xxxx
int nal_unit_type; //! NALU_TYPE_xxxx
char *buf; //! contains the first byte followed by the EBSP
} NALU_t;

11桦山、創(chuàng)建編碼后的數(shù)據(jù) AVPacket 結(jié)構(gòu)體來存儲 AVFrame 編碼后生成的數(shù)據(jù)

AVCodec* pCodec;
av_new_packet(&pkt,picture_size);

>其實(shí)從這里看出 AVPacket 跟 AVFrame 的關(guān)系如下
編碼前:AVFrame
編碼后:AVPacket

12、寫入 yuv 數(shù)據(jù)到 AVFrame 結(jié)構(gòu)體中

// 設(shè)置 yuv 數(shù)據(jù)中 y 圖的寬高
int y_size = pCodecCtx->width * pCodecCtx->height;

for (int i=0; i<framenum; i++){
//Read raw YUV data
if (fread(picture_buf, 1, y_size3/2, in_file) <= 0){
printf("Failed to read raw data! \n");
return ;
}else if(feof(in_file)){
break;
}
pFrame->data[0] = picture_buf; // Y
pFrame->data[1] = picture_buf+ y_size; // U
pFrame->data[2] = picture_buf+ y_size
5/4; // V
//PTS
//pFrame->pts=i;
// 設(shè)置這一幀的顯示時間
pFrame->pts=i(video_st->time_base.den)/((video_st->time_base.num)25);
int got_picture=0;
// 利用編碼器進(jìn)行編碼醋旦,將 pFrame 編碼后的數(shù)據(jù)傳入 pkt 中
int ret = avcodec_encode_video2(pCodecCtx, &pkt,pFrame, &got_picture);
if(ret < 0){
printf("Failed to encode! \n");
return ;
}

// 編碼成功后寫入 AVPacket 到 輸入輸出數(shù)據(jù)操作著 pFormatCtx 中恒水,當(dāng)然,記得釋放內(nèi)存
if (got_picture==1){
printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);
framecnt++;
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
}
}


13饲齐、flush 編碼

int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){
int ret;
int got_frame;
AVPacket enc_pkt;

// 確認(rèn)如果
if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
      CODEC_CAP_DELAY))
    return 0;
while (1) {
    enc_pkt.data = NULL;
    enc_pkt.size = 0;
    av_init_packet(&enc_pkt);
    ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,
                                 NULL, &got_frame);
    av_frame_free(NULL);
    if (ret < 0)
        break;
    if (!got_frame){
        ret=0;
        break;
    }
    printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);
    /* mux encoded frame */
    ret = av_write_frame(fmt_ctx, &enc_pkt);
    if (ret < 0)
        break;
}
return ret;

}

int ret2 = flush_encoder(pFormatCtx,0);
if (ret2 < 0) {
printf("Flushing encoder failed\n");
return;
}


14钉凌、我們上面寫完了編碼頭、編碼數(shù)據(jù)捂人,當(dāng)然也要寫入編碼的尾部表示結(jié)束了啦御雕,這樣才是一個完整的編碼格式嘛

// 寫入數(shù)據(jù)流尾部到輸出文件當(dāng)中,并釋放文件的私有數(shù)據(jù)
av_write_trailer(pFormatCtx);


15滥搭、釋放我們之前創(chuàng)建的內(nèi)存

if (video_st){
// 關(guān)閉編碼器
avcodec_close(video_st->codec);
// 釋放 AVFrame
av_free(pFrame);
// 釋放圖片 buf酸纲,就是 free() 函數(shù),硬要改名字瑟匆,當(dāng)然這是跟適應(yīng)編譯環(huán)境有關(guān)系的
av_free(picture_buf);
}

// 關(guān)閉輸入數(shù)據(jù)的緩存
avio_close(pFormatCtx->pb);
// 釋放 AVFromatContext 結(jié)構(gòu)體
avformat_free_context(pFormatCtx);

// 關(guān)閉輸入文件
fclose(in_file);


----

好了福青,寫到這里,我們首先要做的就是利用就把下面這個 .yuv 文件放到工程當(dāng)中,如下圖
![工程圖片](http://upload-images.jianshu.io/upload_images/1073278-c6900f0bd6e3de7b.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

然后在 `- (void)viewDidLoad `方法中使用如下代碼

const char *input_file = [[[NSBundle mainBundle] pathForResource:@"FFmpegTest" ofType:@"yuv"] cStringUsingEncoding:NSUTF8StringEncoding];

yuvCodecToVideoH264(input_file);


然后運(yùn)行无午,瞬間媒役, 利用同步推打開我們工程的系統(tǒng),看到我們就得到我們想要的東西了

![沙盒文件結(jié)構(gòu)](http://upload-images.jianshu.io/upload_images/1073278-cfb80f2771674440.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


----

有些小伙伴可能在編譯的時候遇到錯誤宪迟,那是因?yàn)楹瘮?shù)當(dāng)中一些用到的工程庫并沒有鏈接到工程中酣衷,可以在工程的 General->Linked Frameworks and Libraries 檢查如下圖


![Linked Frameworks and Libraries](http://upload-images.jianshu.io/upload_images/1073278-a2b14540eab949a1.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



好了,先寫這么多了次泽,寫這篇博客比較趕穿仪,當(dāng)中或許有許多地方還沒有經(jīng)過細(xì)心雕琢,而且可能還存在一些錯別字意荤,容我再找我時間好好再打磨一下啊片,哈哈。
還有的就是玖像,謝謝大家一路的支持紫谷,讓我繼續(xù)有動力寫下去。


##心如止水捐寥,奮力前行
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笤昨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子握恳,更是在濱河造成了極大的恐慌瞒窒,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乡洼,死亡現(xiàn)場離奇詭異崇裁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)束昵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門拔稳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妻怎,你說我怎么就攤上這事∨⑶福” “怎么了逼侦?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腰耙。 經(jīng)常有香客問我榛丢,道長,這世上最難降的妖魔是什么挺庞? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任晰赞,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掖鱼。我一直安慰自己然走,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布戏挡。 她就那樣靜靜地躺著芍瑞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褐墅。 梳的紋絲不亂的頭發(fā)上拆檬,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機(jī)與錄音妥凳,去河邊找鬼竟贯。 笑死,一個胖子當(dāng)著我的面吹牛逝钥,可吹牛的內(nèi)容都是我干的屑那。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼晌缘,長吁一口氣:“原來是場噩夢啊……” “哼齐莲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起磷箕,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤选酗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后岳枷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芒填,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年空繁,在試婚紗的時候發(fā)現(xiàn)自己被綠了殿衰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡盛泡,死狀恐怖闷祥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情傲诵,我是刑警寧澤凯砍,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站拴竹,受9級特大地震影響悟衩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜栓拜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一座泳、第九天 我趴在偏房一處隱蔽的房頂上張望惠昔。 院中可真熱鬧,春花似錦挑势、人聲如沸镇防。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽营罢。三九已至,卻和暖如春饼齿,著一層夾襖步出監(jiān)牢的瞬間饲漾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工缕溉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留考传,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓证鸥,卻偏偏與公主長得像僚楞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枉层,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

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