Metal 案例05:視頻采集 & 實(shí)時(shí)渲染

本案例主要是利用Metal實(shí)現(xiàn)攝像頭采集內(nèi)容的即刻渲染處理鳖敷,理解視頻采集、處理及渲染的流程

視頻實(shí)時(shí)采集并渲染的效果圖如下牛哺,以下效果是由于設(shè)置了高斯模糊濾鏡达传,其中高斯模糊濾鏡的sigma參數(shù)值越高篙耗,圖像越模糊

視頻渲染的實(shí)現(xiàn)思路主要有以下三步

  • 1、通過(guò)AVFoundation進(jìn)行視頻數(shù)據(jù)的采集宪赶,并將采集到的原始數(shù)據(jù)存儲(chǔ)到CMSampleBufferRef中宗弯,即視頻幀數(shù)據(jù)(視頻幀其實(shí)本質(zhì)也是一張圖片)
  • 2、通過(guò)CoreVideoCMSampleBufferRef中存儲(chǔ)的圖像數(shù)據(jù)搂妻,轉(zhuǎn)換為Metal可以直接使用的紋理
  • 3蒙保、將Metal紋理進(jìn)行渲染,并即刻顯示到屏幕上

在實(shí)際的開發(fā)應(yīng)用中欲主,AVFoundation提供了一個(gè)layer邓厕,即AVCaptureVideoPreviewLayer 預(yù)覽層,我們可以使用預(yù)覽層直接預(yù)覽視頻采集后的即可渲染扁瓢,用于替代思路中2详恼、3步。
根據(jù)官方文檔-AVCaptureVideoPreviewLayer說(shuō)明引几,AVCaptureVideoPreviewLayerCALayer 的子類昧互,用于在輸入設(shè)備捕獲視頻時(shí)顯示視頻,此預(yù)覽圖層與捕獲會(huì)話結(jié)合使用,主要有以下三步:

  • 創(chuàng)建預(yù)覽層對(duì)象
  • 將預(yù)覽層與captureSession鏈接
  • 將預(yù)覽層加到view的子layer中
//創(chuàng)建一個(gè)預(yù)覽層敞掘。
let previewLayer = AVCaptureVideoPreviewLayer()

//將預(yù)覽層與捕獲會(huì)話連接叽掘。
previewLayer.session = captureSession

//將預(yù)覽圖層添加到視圖的圖層層次結(jié)構(gòu)中
view.layer.addSublayer(previewLayer)

下面來(lái)說(shuō)下視頻渲染的實(shí)現(xiàn),其整體的實(shí)現(xiàn)流程如圖所示

主要分為3部分

  • viewDidLoad函數(shù):初始化Metal和視頻采集的準(zhǔn)備工作
  • MTKViewDelegate協(xié)議方法:視頻采集數(shù)據(jù)轉(zhuǎn)換為紋理
  • AVCaptureVideoDataOutputSampleBufferDelegate協(xié)議方法:將采集轉(zhuǎn)換后的紋理渲染到屏幕上

viewDidLoad函數(shù)

該函數(shù)中主要是設(shè)置Metal的相關(guān)初始化操作渐逃,以及視頻采集前的準(zhǔn)備工作够掠,函數(shù)的流程如圖所示

分為以下兩部分

  • setupMetal函數(shù)
  • setupCaptureSession函數(shù)

setupMetal函數(shù)
Metal的準(zhǔn)備工作,主要需要初始化以下3部分

  • 初始化MTKView茄菊,用于顯示視頻采集數(shù)據(jù)轉(zhuǎn)換后的紋理
self.mtkView = [[MTKView alloc] initWithFrame:self.view.bounds device:MTLCreateSystemDefaultDevice()];
[self.view insertSubview:self.mtkView atIndex:0];
self.mtkView.delegate = self;

  • 創(chuàng)建命令隊(duì)列:通過(guò)MTKView中的device創(chuàng)建
self.commandQueue = [self.mtkView.device newCommandQueue];

  • 設(shè)置MTKView的讀寫操作 & 創(chuàng)建紋理緩沖區(qū)
    • MTKView中的framebufferOnly屬性疯潭,默認(rèn)的幀緩存是只讀的即YES,由于view需要顯示紋理面殖,所以需要該屬性改為可讀寫NO
    • 通過(guò)CVMetalTextureCacheCreate方法創(chuàng)建CoreVideo中的metal紋理緩存區(qū)竖哩,因?yàn)椴杉囊曨l數(shù)據(jù)是通過(guò)CoreVideo轉(zhuǎn)換為metal紋理的,主要的用于存儲(chǔ)轉(zhuǎn)換后的metal紋理
//注意: 在初始化MTKView 的基本操作以外. 還需要多下面2行代碼.
    /*
     1\. 設(shè)置MTKView 的drawable 紋理是可讀寫的(默認(rèn)是只讀);
     2\. 創(chuàng)建CVMetalTextureCacheRef _textureCache; 這是Core Video的Metal紋理緩存
     */
    //允許讀寫操作
    self.mtkView.framebufferOnly = NO;

    /*
    CVMetalTextureCacheCreate(CFAllocatorRef  allocator,
    CFDictionaryRef cacheAttributes,
    id <MTLDevice>  metalDevice,
    CFDictionaryRef  textureAttributes,
    CVMetalTextureCacheRef * CV_NONNULL cacheOut )

    功能: 創(chuàng)建紋理緩存區(qū)
    參數(shù)1: allocator 內(nèi)存分配器.默認(rèn)即可.NULL
    參數(shù)2: cacheAttributes 緩存區(qū)行為字典.默認(rèn)為NULL
    參數(shù)3: metalDevice
    參數(shù)4: textureAttributes 緩存創(chuàng)建紋理選項(xiàng)的字典. 使用默認(rèn)選項(xiàng)NULL
    參數(shù)5: cacheOut 返回時(shí)脊僚,包含新創(chuàng)建的紋理緩存相叁。
    */
    CVMetalTextureCacheCreate(NULL, NULL, self.mtkView.device, NULL, &_textureCache);

setupCaptureSession函數(shù)
初始化視頻采集的準(zhǔn)備工作,以及開始視頻采集辽幌,主要分為以下幾步:

  • 設(shè)置AVCaptureSession & 視頻采集的分辨率
1增淹、設(shè)置AVCaptureSession & 視頻采集的分辨率
self.mCaptureSession = [[AVCaptureSession alloc] init];
self.mCaptureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
  • 創(chuàng)建串行隊(duì)列
    串行隊(duì)列創(chuàng)建的目的在于處理captureSession的交互時(shí),不會(huì)影響主隊(duì)列乌企,在蘋果官方文檔中有如下圖示虑润,表示captureSession是如何管理設(shè)備的輸入 & 輸出,以及與主隊(duì)列之間的關(guān)系

image
 self.mProcessQueue = dispatch_queue_create("mProcessQueue", DISPATCH_QUEUE_SERIAL);

  • 設(shè)置輸入設(shè)備
    • 獲取后置攝像頭設(shè)備AVCaptureDevice
      通過(guò)獲取設(shè)備數(shù)組加酵,循環(huán)判斷找到后置攝像頭拳喻,將后置攝像頭設(shè)備為當(dāng)前的輸入設(shè)備
    • 通過(guò)攝像頭設(shè)備創(chuàng)建AVCaptureDeviceInput
      將AVCaptureDevice 轉(zhuǎn)換為 AVCaptureDeviceInput,主要是因?yàn)?AVCaptureSession 無(wú)法直接使用 AVCaptureDevice猪腕,所以需要將device轉(zhuǎn)換為deviceInput
    • 輸入設(shè)備添加到captureSession中
      在添加之前冗澈,需要通過(guò)captureSessioncanAddInput函數(shù)判斷是否可以添加輸入設(shè)備,如果可以陋葡,則通過(guò)session的addInput函數(shù)添加輸入設(shè)備
//    3亚亲、獲取攝像頭設(shè)備(前置/后置攝像頭設(shè)備)
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    AVCaptureDevice *inputCamera = nil;
    //循環(huán)設(shè)備數(shù)組,找到后置攝像頭.設(shè)置為當(dāng)前inputCamera
    for (AVCaptureDevice *device in devices) {
        if ([device position] == AVCaptureDevicePositionBack) {
            //拿到后置攝像頭
            inputCamera = device;
        }
    }

//    4、將AVCaptureDevice 轉(zhuǎn)換為 AVCaptureDeviceInput脖岛,即輸入
//    AVCaptureSession 無(wú)法直接使用 AVCaptureDevice朵栖,所喲需要將device轉(zhuǎn)換為deviceInput
    self.mCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputCamera error:nil];

//    5、將設(shè)備添加到captureSession中,需要先判斷能否添加輸入
    if ([self.mCaptureSession canAddInput:self.mCaptureDeviceInput]) {
        [self.mCaptureSession addInput:self.mCaptureDeviceInput];
    }

  • 設(shè)置輸出設(shè)備
    • 創(chuàng)建AVCaptureVideoDataOutput對(duì)象柴梆,即輸出設(shè)備
    • 設(shè)置輸出設(shè)備的setAlwaysDiscardsLateVideoFrames屬性(表示視頻幀延時(shí)使是否丟棄數(shù)據(jù))為NO
      • YES:處理現(xiàn)有幀的調(diào)度隊(duì)列在captureOutput:didOutputSampleBuffer:FromConnection:Delegate方法中被阻止時(shí),對(duì)象會(huì)立即丟棄捕獲的幀
      • NO:在丟棄新幀之前终惑,允許委托有更多的時(shí)間處理舊幀绍在,但這樣可能會(huì)內(nèi)存增加
    • 設(shè)置輸出設(shè)備的setVideoSettings屬性(即像素格式),表示每一個(gè)像素點(diǎn)顏色保存的格式,且設(shè)置的格式是BGRA偿渡,而不是YUV臼寄,主要是為了避免Shader轉(zhuǎn)換,如果使用了YUV格式溜宽,就需要編寫shader來(lái)進(jìn)行顏色格式轉(zhuǎn)換
    • 設(shè)置輸出設(shè)備的視頻捕捉輸出的delegate
    • 將輸出設(shè)備添加到captureSession中
//    6吉拳、創(chuàng)建AVCaptureVideoDataOutput對(duì)象,即輸出 & 設(shè)置輸出相關(guān)屬性
    self.mCaptureDeviceOutput = [[AVCaptureVideoDataOutput alloc] init];
    /*設(shè)置視頻幀延遲到底時(shí)是否丟棄數(shù)據(jù).
    YES: 處理現(xiàn)有幀的調(diào)度隊(duì)列在captureOutput:didOutputSampleBuffer:FromConnection:Delegate方法中被阻止時(shí)适揉,對(duì)象會(huì)立即丟棄捕獲的幀留攒。
    NO: 在丟棄新幀之前,允許委托有更多的時(shí)間處理舊幀嫉嘀,但這樣可能會(huì)內(nèi)存增加.
    */
    //視頻幀延遲是否需要丟幀
    [self.mCaptureDeviceOutput setAlwaysDiscardsLateVideoFrames:NO];

    //設(shè)置像素格式:每一個(gè)像素點(diǎn)顏色保存的格式
    //這里設(shè)置格式為BGRA炼邀,而不用YUV的顏色空間,避免使用Shader轉(zhuǎn)換剪侮,如果使用YUV格式拭宁,需要編寫shade來(lái)進(jìn)行顏色格式轉(zhuǎn)換
    //注意:這里必須和后面CVMetalTextureCacheCreateTextureFromImage 保存圖像像素存儲(chǔ)格式保持一致.否則視頻會(huì)出現(xiàn)異常現(xiàn)象.
    [self.mCaptureDeviceOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];

    //設(shè)置視頻捕捉輸出的代理方法:將采集的視頻數(shù)據(jù)輸出
    [self.mCaptureDeviceOutput setSampleBufferDelegate:self queue:self.mProcessQueue];

//    7瓣俯、添加輸出杰标,即添加到captureSession中
    if ([self.mCaptureSession canAddOutput:self.mCaptureDeviceOutput]) {
        [self.mCaptureSession addOutput:self.mCaptureDeviceOutput];
    }

  • 輸入與輸出鏈接 & 設(shè)置視頻輸出方向
    通過(guò)AVCaptureConnection鏈接輸入和輸出,并設(shè)置connect的視頻輸出方向彩匕,即設(shè)置videoOrientation屬性
//    8腔剂、輸入與輸出鏈接
    AVCaptureConnection *connection = [self.mCaptureDeviceOutput connectionWithMediaType:AVMediaTypeVideo];

//    9、設(shè)置視頻輸出方向
    //注意: 一定要設(shè)置視頻方向.否則視頻會(huì)是朝向異常的.
    [connection setVideoOrientation:AVCaptureVideoOrientationPortrait];

  • 開始捕捉推掸,即開始視頻采集
    也可以通過(guò)一個(gè)按鈕來(lái)控制視頻采集的開始與停止
    • startRunning:開啟捕捉
    • stopRunning:停止捕捉
//    10桶蝎、開始捕捉
    [self.mCaptureSession startRunning];

AVCaptureVideoDataOutputSampleBufferDelegate協(xié)議方法

在視頻采集的同時(shí),采集到的視頻數(shù)據(jù)谅畅,即視頻幀會(huì)自動(dòng)回調(diào)視頻采集回調(diào)方法captureOutput:didOutputSampleBuffer:fromConnection:登渣,在該方法中處理采集到的原始視頻數(shù)據(jù),將其轉(zhuǎn)換為metal紋理

didOutputSampleBuffer代理方法
主要是獲取視頻的幀數(shù)據(jù)毡泻,將其轉(zhuǎn)換為metal紋理胜茧,函數(shù)流程如下

主要分為以下幾步:

  • sampleBuffer中獲取位圖
    通過(guò)CMSampleBufferGetImageBuffer函數(shù)從sampleBuffer形參中獲取視頻像素緩存區(qū)對(duì)象,即視頻幀數(shù)據(jù)仇味,平常所說(shuō)的位圖
//    1呻顽、從sampleBuffer 獲取視頻像素緩存區(qū)對(duì)象,即獲取位圖
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

  • 獲取捕捉視頻幀的寬高
    通過(guò)CoreVideo中的CVPixelBufferGetWidthCVPixelBufferGetHeight函數(shù)獲取寬高
size_t width = CVPixelBufferGetWidth(pixelBuffer);
size_t height = CVPixelBufferGetHeight(pixelBuffer);

  • 將位圖轉(zhuǎn)換為metal紋理
    • 通過(guò)CVMetalTextureRef創(chuàng)建臨時(shí)紋理
    • 通過(guò)CVMetalTextureCacheCreateTextureFromImage函數(shù)創(chuàng)建metal紋理緩沖區(qū)丹墨,賦值給臨時(shí)紋理
    • 判斷臨時(shí)紋理是否創(chuàng)建成功廊遍,如果臨時(shí)紋理創(chuàng)建成功,則繼續(xù)往下執(zhí)行
    • 設(shè)置MTKView中的drawableSize屬性贩挣,即表示可繪制紋理的大小
    • 通過(guò)CVMetalTextureGetTexture函數(shù)喉前,獲取紋理緩沖區(qū)的metal紋理對(duì)象
    • 釋放臨時(shí)紋理
//    3没酣、將位圖轉(zhuǎn)換為紋理
    //方法來(lái)自CoreVideo
    /*3\. 根據(jù)視頻像素緩存區(qū) 創(chuàng)建 Metal 紋理緩存區(qū)
    CVReturn CVMetalTextureCacheCreateTextureFromImage(CFAllocatorRef allocator,                         CVMetalTextureCacheRef textureCache,
    CVImageBufferRef sourceImage,
    CFDictionaryRef textureAttributes,
    MTLPixelFormat pixelFormat,
    size_t width,
    size_t height,
    size_t planeIndex,
    CVMetalTextureRef  *textureOut);

    功能: 從現(xiàn)有圖像緩沖區(qū)創(chuàng)建核心視頻Metal紋理緩沖區(qū)。
    參數(shù)1: allocator 內(nèi)存分配器,默認(rèn)kCFAllocatorDefault
    參數(shù)2: textureCache 紋理緩存區(qū)對(duì)象
    參數(shù)3: sourceImage 視頻圖像緩沖區(qū)
    參數(shù)4: textureAttributes 紋理參數(shù)字典.默認(rèn)為NULL
    參數(shù)5: pixelFormat 圖像緩存區(qū)數(shù)據(jù)的Metal 像素格式常量.注意如果MTLPixelFormatBGRA8Unorm和攝像頭采集時(shí)設(shè)置的顏色格式不一致卵迂,則會(huì)出現(xiàn)圖像異常的情況裕便;
    參數(shù)6: width,紋理圖像的寬度(像素)
    參數(shù)7: height,紋理圖像的高度(像素)
    參數(shù)8: planeIndex 顏色通道.如果圖像緩沖區(qū)是平面的,則為映射紋理數(shù)據(jù)的平面索引见咒。對(duì)于非平面圖像緩沖區(qū)忽略偿衰。
    參數(shù)9: textureOut,返回時(shí),返回創(chuàng)建的Metal紋理緩沖區(qū)改览。
    */
    //創(chuàng)建臨時(shí)紋理
    CVMetalTextureRef tmpTexture = NULL;
    CVReturn status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, self.textureCache, pixelBuffer, NULL, MTLPixelFormatBGRA8Unorm, width, height, 0, &tmpTexture);

//    4下翎、判斷tmpTexture 是否創(chuàng)建成功
    if (status == kCVReturnSuccess) {//創(chuàng)建成功
//        5、設(shè)置可繪制紋理的大小
        self.mtkView.drawableSize = CGSizeMake(width, height);

//        6恃疯、返回紋理緩沖區(qū)的metal紋理對(duì)象
        self.texture = CVMetalTextureGetTexture(tmpTexture);

//        7漏设、使用完畢,釋放tmptexture
        CFRelease(tmpTexture);
    }

MTKViewDelegate協(xié)議方法

接下來(lái)就是將獲取的metal紋理即刻渲染并顯示到屏幕上今妄,這里是通過(guò)MTKViewDelegate協(xié)議的drawInMTKView代理方法渲染并顯示

drawInMTKView代理方法
MTKView默認(rèn)的幀速率與屏幕刷新頻率一致郑口,所以每當(dāng)屏幕刷新時(shí),都會(huì)回調(diào) 視頻采集方法 和 視圖渲染方法盾鳞,以下是視圖渲染方法執(zhí)行流程

主要有以下幾步

  • 判斷紋理是否獲取成功
    即紋理不為空犬性,如果紋理為空,則沒必要執(zhí)行視圖渲染流程

  • 通過(guò)commandQueue創(chuàng)建commandBuffer命令緩存區(qū)

  • 將MTKView的紋理作為目標(biāo)渲染紋理
    即獲取view中紋理對(duì)象

  • 設(shè)置高斯模糊濾鏡

    • MetalPerformanceShaders是Metal的一個(gè)集成庫(kù)腾仅,有一些濾鏡處理的Metal實(shí)現(xiàn)乒裆,
    • 此時(shí)的濾鏡就等價(jià)于Metal中的MTLRenderCommandEncoder渲染命令編碼器,類似于GLSL中program
    • 高斯模糊濾鏡在渲染時(shí)推励,會(huì)觸發(fā)離屏渲染鹤耍,且其中的sigma值設(shè)置的越高,圖像越模糊验辞,就如文章開頭展示的效果圖
//        4稿黄、設(shè)置濾鏡(Metal封裝了一些濾鏡)
        //高斯模糊 渲染時(shí),會(huì)觸發(fā) 離屏渲染
        /*
          MetalPerformanceShaders是Metal的一個(gè)集成庫(kù)跌造,有一些濾鏡處理的Metal實(shí)現(xiàn);
          MPSImageGaussianBlur 高斯模糊處理;
          */

         //創(chuàng)建高斯濾鏡處理filter
         //注意:sigma值可以修改杆怕,sigma值越高圖像越模糊;
        MPSImageGaussianBlur *filter = [[MPSImageGaussianBlur alloc] initWithDevice:self.mtkView.device sigma:5];

//        5、MPSImageGaussianBlur以一個(gè)Metal紋理作為輸入壳贪,以一個(gè)Metal紋理作為輸出陵珍;
        //輸入:攝像頭采集的圖像 self.texture
        //輸出:創(chuàng)建的紋理 drawingTexture(其實(shí)就是view.currentDrawable.texture)
        //filter等價(jià)于Metal中的MTLRenderCommandEncoder 渲染命令編碼器,類似于GLSL中的program
        [filter encodeToCommandBuffer:commandBuffer sourceTexture:self.texture destinationTexture:drawingTexture];

  • 將獲取的紋理顯示到屏幕上
  • 將commandBuffer通過(guò)commit提交給GPU
  • 清空當(dāng)前紋理违施,為下一次紋理數(shù)據(jù)讀取做準(zhǔn)備
    如果不清空互纯,也是可以的,下一次的紋理數(shù)據(jù)會(huì)將上次的數(shù)據(jù)覆蓋
//        6磕蒲、展示顯示的內(nèi)容
        [commandBuffer presentDrawable:view.currentDrawable];

//        7伟姐、提交命令
        [commandBuffer commit];

//        8收苏、清空當(dāng)前紋理亿卤,準(zhǔn)備下一次的紋理數(shù)據(jù)讀取愤兵,
        //如果不清空,也是可以的排吴,下一次的紋理數(shù)據(jù)會(huì)將上次的數(shù)據(jù)覆蓋
        self.texture = NULL;

總結(jié)

視頻采集流程總結(jié)
根據(jù)上述流程的解析秆乳,視頻的采集主要有以下幾步:

  • 1、設(shè)置session
  • 2钻哩、創(chuàng)建串行隊(duì)列
  • 3屹堰、設(shè)置輸入設(shè)備
  • 4、設(shè)置輸出設(shè)備
  • 5街氢、輸入與輸出鏈接
  • 6扯键、設(shè)置視頻輸出方向
  • 7、開始捕捉珊肃,即開始視頻采集
  • 8荣刑、AVCaptureVideoDataOutputSampleBufferDelegate協(xié)議處理采集后的視頻數(shù)據(jù)

如何判斷采集的數(shù)據(jù)是音頻還是視頻?
主要有以下兩種判斷方式:

  • 1伦乔、通過(guò)AVCaptureConnection判斷
    • 視頻:包含視頻輸入設(shè)備 & 視頻輸出設(shè)備厉亏,通過(guò)AVCaptureConnection鏈接起來(lái)
    • 音頻:包含音頻輸入設(shè)備 & 音頻輸出設(shè)備,同樣通過(guò)AVCaptureConnection鏈接起來(lái)

如果需要判斷當(dāng)前采集的輸出是視頻還是音頻烈和,需要將connect對(duì)象設(shè)置為全局變量爱只,然后在采集回調(diào)方法captureOutput:didOutputSampleBuffer:fromConnection:中判斷全局的connection 是否等于 代理方法參數(shù)中的coneection ,如果相等招刹,就是視頻恬试,反之是音頻

  • 2、通過(guò)AVCaptureOutput判斷

在采集回調(diào)方法captureOutput:didOutputSampleBuffer:fromConnection:中判斷output形參的類型疯暑,如果是AVCaptureVideoDataOutput 類型則是視頻训柴,反之,是音頻

完整的代碼見Github :21_21_Metal_視頻渲染_OC

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缰儿,一起剝皮案震驚了整個(gè)濱河市畦粮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乖阵,老刑警劉巖宣赔,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瞪浸,居然都是意外死亡儒将,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門对蒲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)钩蚊,“玉大人贡翘,你說(shuō)我怎么就攤上這事∨槁撸” “怎么了鸣驱?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蝠咆。 經(jīng)常有香客問(wèn)我踊东,道長(zhǎng),這世上最難降的妖魔是什么刚操? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任闸翅,我火速辦了婚禮,結(jié)果婚禮上菊霜,老公的妹妹穿的比我還像新娘坚冀。我一直安慰自己,他們只是感情好鉴逞,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布记某。 她就那樣靜靜地躺著,像睡著了一般华蜒。 火紅的嫁衣襯著肌膚如雪辙纬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天叭喜,我揣著相機(jī)與錄音贺拣,去河邊找鬼。 笑死捂蕴,一個(gè)胖子當(dāng)著我的面吹牛譬涡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播啥辨,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼涡匀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了溉知?” 一聲冷哼從身側(cè)響起陨瘩,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎级乍,沒想到半個(gè)月后舌劳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玫荣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年甚淡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捅厂。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贯卦,死狀恐怖资柔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撵割,我是刑警寧澤贿堰,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站睁枕,受9級(jí)特大地震影響官边,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜外遇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望契吉。 院中可真熱鬧跳仿,春花似錦、人聲如沸捐晶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惑灵。三九已至山上,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間英支,已是汗流浹背佩憾。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留干花,地道東北人妄帘。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像池凄,于是被迫代替她去往敵國(guó)和親抡驼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

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