MacOS開發(fā) OpenGL渲染YUV420數(shù)據(jù)

網(wǎng)上搜索的大部分資料都是OpenGL ES渲染視頻的猿推,OpenGL渲染yuv數(shù)據(jù)的資料比較難找宅此,因此編輯本文mark下;
結(jié)合網(wǎng)上搜索的資料卒煞,實(shí)現(xiàn)了在MacOS App開發(fā)中,將接收到的yuv420視頻數(shù)據(jù)渲染到視圖上叼架;

本文并非原創(chuàng)畔裕,只是在其他作者的基礎(chǔ)上修修改改,實(shí)現(xiàn)了在MacOS App開發(fā)中的乖订,使用OpenGL渲染yuv420視頻數(shù)據(jù)扮饶;

參考資料:
1.利用Qt + OpenGL 渲染 YUV數(shù)據(jù),播放視頻 mac版
2.AVCapture之4——NSOpenGLView
3.最簡(jiǎn)單的視音頻播放示例6:OpenGL播放YUV420P(通過Texture乍构,使用Shader)

1.感謝第一篇資料作者jake2012甜无,對(duì)于不同版本OpenGL的shader不同編寫使用的有價(jià)值的分享;
3.蘋果有個(gè)Demo中有一個(gè)VideoCIView哥遮,這個(gè)類基本實(shí)現(xiàn)了將一個(gè)CIImage繪制到NSOpenGLView中岂丘;

代碼部分

頂點(diǎn)著色器--Shader3.vs:

#version 410

in vec4 vertexIn;
in vec2 textureIn;
out vec2 textureOut;

void main(void)
{
    gl_Position = vertexIn;
    textureOut = textureIn;
}

片段著色器--Shader3.frag:

#version 410

in vec2 textureOut;
out vec4 fragColor;

uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;

void main(void)
{
    vec3 yuv;
    vec3 rgb;
    
    yuv.x = texture(tex_y, textureOut).r;
    yuv.y = texture(tex_u, textureOut).r - 0.5;
    yuv.z = texture(tex_v, textureOut).r - 0.5;
    
    rgb = mat3( 1,       1,         1,
               0,       -0.21482,  2.12798,
               1.28033, -0.38059,  0) * yuv;
    
    fragColor = vec4(rgb, 1);
}

OpenGLRenderer.h

#import <Foundation/Foundation.h>
#include "glUtil.h"
#import <AVFoundation/AVFoundation.h>

@interface OpenGLRenderer : NSObject

@property (nonatomic) GLuint defaultFBOName;

- (instancetype)initWithDefaultFBO:(id)asbs;
- (void) resizeWithWidth:(GLuint)width AndHeight:(GLuint)height;
- (void) render;
- (void) dealloc;

- (void)setImage:(CVImageBufferRef)pixelBuffer;
- (void)presentYUVData:(NSData*)yuvdata width:(GLuint)width height:(GLuint)height;

@end

OpenGLRenderer.mm

#import "OpenGLRenderer.h"
#include "glUtil.h"
#include "imageUtil.h"
#include "sourceUtil.h"


#ifndef NULL
#define NULL 0
#endif


@interface OpenGLRenderer ()
{
    GLuint m_program;
    GLuint m_vertexBuffer;
    GLuint textureUniformY;
    GLuint textureUniformU;
    GLuint textureUniformV;
    GLuint vertexBuffer;
    GLuint vertextAttribute;
    GLuint textureAttribute;
        
    GLuint id_y;
    GLuint id_u;
    GLuint id_v;
        
    int m_nVideoW;
    int m_nVideoH;
    int m_nViewW;
    int m_nViewH;
    unsigned char* m_pBufYuv420p;
    unsigned char* m_pBuffer;
    
}
@end

@implementation OpenGLRenderer

- (void)setImage:(CVImageBufferRef)pixelBuffer {
    
    //上鎖
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    {
        //寬高
        GLuint width = (GLuint)CVPixelBufferGetWidth(pixelBuffer);
        GLuint height = (GLuint)CVPixelBufferGetHeight(pixelBuffer);
        
        //調(diào)整寬高
        [self resizeWithWidth:width AndHeight:height];
        
        //yuv420視頻數(shù)據(jù)
        m_pBufYuv420p = NULL;
        m_pBufYuv420p = (unsigned char*)CVPixelBufferGetBaseAddress(pixelBuffer);
        
        //渲染yuv420
        [self rendeYUVData:m_pBufYuv420p];
    }
    
    //解鎖
    CVPixelBufferUnlockBaseAddress(pixelBuffer,0);
}

- (void)presentYUVData:(NSData*)yuvdata width:(GLuint)width height:(GLuint)height {
    
    @synchronized (self) {
        //調(diào)整寬高
        m_nVideoW = width;
        m_nVideoH = height;
        
        //yuv420視頻數(shù)據(jù)
        m_pBufYuv420p = NULL;
        m_pBufYuv420p = (unsigned char*)[yuvdata bytes];
        
        //渲染yuv420
        [self rendeYUVData:m_pBufYuv420p];
    }
}

- (instancetype)initWithDefaultFBO:(id)asbs {
    if((self = [super init])) {
        iLog(@"Render: %s; Version:%s", glGetString(GL_RENDERER), glGetString(GL_VERSION));
        [self initializeGL];
        
        //清除緩存
        [self clearRenderBuffer];
    }
    return self;
}

- (void)resizeWithWidth:(GLuint)width AndHeight:(GLuint)height {
    
    glViewport(0, 0, width, height);
    
    m_nViewW = width;
    m_nViewH = height;
    if (m_nViewW==0) {
        m_nViewW = 2*iScreenWidth/3;
    }
    if (m_nViewH==0) {
        m_nViewH = 2*iScreenHeight/3;
    }
    
    //清除緩存
    [self clearRenderBuffer];
}

- (void) render {
    
}
- (void) dealloc {
    
}
-(void)clearRenderBuffer {
    
    //清除緩存
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}

#pragma mark 渲染數(shù)據(jù)
-(void)rendeYUVData:(unsigned char*)yuv420data {
    
    //清除緩存
    [self clearRenderBuffer];
        
    //
    float x,y;
    float wRatio = (float)m_nViewW/m_nVideoW;
    float hRatio = (float)m_nViewH/m_nVideoH;
    float minRatio = wRatio<hRatio ? wRatio : hRatio;
    y = m_nVideoH * minRatio/m_nViewH;
    x = m_nVideoW * minRatio/m_nViewW;
        
    float vertexPoints[] ={
        -x, -y,  0.0f,  1.0f,
         x, -y,  1.0f,  1.0f,
        -x,  y,  0.0f,  0.0f,
         x,  y,  1.0f,  0.0f,
    };
    glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(float), vertexPoints, GL_STATIC_DRAW);
        
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, id_y);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW, m_nVideoH, 0, GL_RED, GL_UNSIGNED_BYTE, yuv420data);
    glUniform1i(textureUniformY, 0);
        
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, id_u);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW / 2, m_nVideoH / 2, 0, GL_RED, GL_UNSIGNED_BYTE, (char*)yuv420data + m_nVideoW*m_nVideoH);
    glUniform1i(textureUniformU, 1);
        
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, id_v);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW / 2, m_nVideoH / 2, 0, GL_RED, GL_UNSIGNED_BYTE, (char*)yuv420data + m_nVideoW*m_nVideoH * 5 / 4);
    glUniform1i(textureUniformV, 2);
    
    // Draw stuff
    glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
    //∫glCheckError();
}


#pragma mark init methdos
-(void)initializeGL {
    
    // 準(zhǔn)備 著色器程序
    [self prepareShaderProgram];
    
    textureUniformY = glGetUniformLocation(m_program, "tex_y");
    textureUniformU = glGetUniformLocation(m_program, "tex_u");
    textureUniformV = glGetUniformLocation(m_program, "tex_v");
    
    // Create a interleaved triangle (vec3 position, vec3 color)
    float vertexPoints[] ={
        -1.0f, -1.0f,  0.0f, 1.0f,
         1.0f, -1.0f,  1.0f, 1.0f,
        -1.0f,  1.0f,  0.0f, 0.0f,
         1.0f,  1.0f,  1.0f, 0.0f,
    };
        
    glGenVertexArrays(1, &m_vertexBuffer);
    glBindVertexArray(m_vertexBuffer);
    
    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(float), vertexPoints, GL_STATIC_DRAW);
    vertextAttribute = glGetAttribLocation(m_program, "vertexIn");
    textureAttribute = glGetAttribLocation(m_program, "textureIn");
    glEnableVertexAttribArray(vertextAttribute);
    glVertexAttribPointer(vertextAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (const GLvoid *)0);
    glEnableVertexAttribArray(textureAttribute);
    glVertexAttribPointer(textureAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (const GLvoid *)(sizeof(float)*2));
        
    //Init Texture
    glGenTextures(1, &id_y);
    glBindTexture(GL_TEXTURE_2D, id_y);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        
    glGenTextures(1, &id_u);
    glBindTexture(GL_TEXTURE_2D, id_u);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        
    glGenTextures(1, &id_v);
    glBindTexture(GL_TEXTURE_2D, id_v);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
-(void)prepareShaderProgram {
    
    //讀取文件路徑
    NSString* vertFile = [[NSBundle mainBundle] pathForResource:@"Shader3" ofType:@"vs"];
    NSString* fragFile = [[NSBundle mainBundle] pathForResource:@"Shader3" ofType:@"frag"];
    
    //加載shader
    m_program = [self loadShaders:vertFile frag:fragFile];
    
    //鏈接
    glBindFragDataLocation(m_program, 0, "fragColor");
    glLinkProgram(m_program);
    
    GLint  linked;
    glGetProgramiv(m_program, GL_LINK_STATUS, &linked );
    if ( !linked ) {
        NSLog(@"Shader program failed to link");
        GLint  logSize;
        glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &logSize);
        char* logMsg = new char[logSize];
        glGetProgramInfoLog(m_program, logSize, NULL, logMsg );
        NSLog(@"Link Error: %s", logMsg);
        delete [] logMsg;
            
        exit( EXIT_FAILURE );
    }
        
    //use program object
    glUseProgram(m_program);
}
- (GLuint)loadShaders:(NSString *)vert frag:(NSString *)frag {
    GLuint verShader, fragShader;
    GLint program = glCreateProgram();
    
    //編譯
    [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
    [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
    
    glAttachShader(program, verShader);
    glAttachShader(program, fragShader);
    
    
    //釋放不需要的shader
    glDeleteShader(verShader);
    glDeleteShader(fragShader);
    
    return program;
}

- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file {
    //讀取字符串
    NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
    const GLchar* source = (GLchar *)[content UTF8String];
    
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    
    //錯(cuò)誤分析
    GLint  compiled;
    glGetShaderiv( *shader, GL_COMPILE_STATUS, &compiled );
    if ( !compiled ) {
        GLint  logSize;
        glGetShaderiv( *shader, GL_INFO_LOG_LENGTH, &logSize );
        char* logMsg = new char[logSize];
        glGetShaderInfoLog( *shader, logSize, NULL, logMsg );
        NSLog(@"Shader compile log:%s\n", logMsg);
        delete [] logMsg;
        exit(EXIT_FAILURE);
    }
}

@end

VideoGLView.h

#import <Cocoa/Cocoa.h>
#import <QuartzCore/CVDisplayLink.h>

@interface VideoGLView : NSOpenGLView {
    CVDisplayLinkRef displayLink;
}

- (void)setImage:(CVImageBufferRef)img;
-(void)presentYUVData:(NSData*)yuvdata width:(CGFloat)width height:(CGFloat)height;
@end

VideoGLView.m

#import "VideoGLView.h"
#import "OpenGLRenderer.h"

//#define SUPPORT_RETINA_RESOLUTION 1

@interface VideoGLView()
{
    OpenGLRenderer* _renderer;
}
@end

@implementation VideoGLView


-(instancetype)init {
    if (self=[super init]) {
        [self awakeFromNib];
    }
    return self;
}

- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
{
    // There is no autorelease pool when this method is called
    // because it will be called from a background thread.
    // It's important to create one or app can leak objects.
    @autoreleasepool {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self drawView];
        });
    }
    return kCVReturnSuccess;
}

// This is the renderer output callback function
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink,
                                      const CVTimeStamp* now,
                                      const CVTimeStamp* outputTime,
                                      CVOptionFlags flagsIn,
                                      CVOptionFlags* flagsOut,
                                      void* displayLinkContext)
{
    CVReturn result = [(__bridge VideoGLView*)displayLinkContext getFrameForTime:outputTime];
    return result;
}

- (void) awakeFromNib
{
    NSOpenGLPixelFormatAttribute attrs[] =
    {
        NSOpenGLPFADoubleBuffer,
        NSOpenGLPFADepthSize, 24,
        // Must specify the 3.2 Core Profile to use OpenGL 3.2
#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
        NSOpenGLPFAOpenGLProfile,
        NSOpenGLProfileVersion3_2Core,
#endif
        0
    };
    
    NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
    
    if (!pf)
    {
        NSLog(@"No OpenGL pixel format");
    }
       
    NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
    
#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 && defined(DEBUG)
    // When we're using a CoreProfile context, crash if we call a legacy OpenGL function
    // This will make it much more obvious where and when such a function call is made so
    // that we can remove such calls.
    // Without this we'd simply get GL_INVALID_OPERATION error for calling legacy functions
    // but it would be more difficult to see where that function was called.
    CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
#endif
    
    [self setPixelFormat:pf];
    
    [self setOpenGLContext:context];
    
#if SUPPORT_RETINA_RESOLUTION
    // Opt-In to Retina resolution
    [self setWantsBestResolutionOpenGLSurface:YES];
#endif // SUPPORT_RETINA_RESOLUTION
}

- (void) prepareOpenGL
{
    [super prepareOpenGL];
    
    // Make all the OpenGL calls to setup rendering
    //  and build the necessary rendering objects
    [self initGL];
    
    // Create a display link capable of being used with all active displays
    CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
    
    // Set the renderer output callback function
    CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, (__bridge void*)self);
    
    // Set the display link for the current renderer
    CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
    CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
    CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
    
    // Activate the display link
    CVDisplayLinkStart(displayLink);
    
    // Register to be notified when the window closes so we can stop the displaylink
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(windowWillClose:)
                                                 name:NSWindowWillCloseNotification
                                               object:[self window]];
}

- (void) windowWillClose:(NSNotification*)notification
{
    // Stop the display link when the window is closing because default
    // OpenGL render buffers will be destroyed.  If display link continues to
    // fire without renderbuffers, OpenGL draw calls will set errors.
    
    CVDisplayLinkStop(displayLink);
}

- (void) initGL
{
    // The reshape function may have changed the thread to which our OpenGL
    // context is attached before prepareOpenGL and initGL are called.  So call
    // makeCurrentContext to ensure that our OpenGL context current to this
    // thread (i.e. makeCurrentContext directs all OpenGL calls on this thread
    // to [self openGLContext])
    [[self openGLContext] makeCurrentContext];
    
    // Synchronize buffer swaps with vertical refresh rate
    GLint swapInt = 1;
    [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
    
    // Init our renderer.  Use 0 for the defaultFBO which is appropriate for
    // OSX (but not iOS since iOS apps must create their own FBO)
    _renderer = [[OpenGLRenderer alloc] initWithDefaultFBO:0];
}

- (void)reshape
{
    [super reshape];
    
    // We draw on a secondary thread through the display link. However, when
    // resizing the view, -drawRect is called on the main thread.
    // Add a mutex around to avoid the threads accessing the context
    // simultaneously when resizing.
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    // Get the view size in Points
    NSRect viewRectPoints = [self bounds];
    
#if SUPPORT_RETINA_RESOLUTION
    
    // Rendering at retina resolutions will reduce aliasing, but at the potential
    // cost of framerate and battery life due to the GPU needing to render more
    // pixels.
    
    // Any calculations the renderer does which use pixel dimentions, must be
    // in "retina" space.  [NSView convertRectToBacking] converts point sizes
    // to pixel sizes.  Thus the renderer gets the size in pixels, not points,
    // so that it can set it's viewport and perform and other pixel based
    // calculations appropriately.
    // viewRectPixels will be larger than viewRectPoints for retina displays.
    // viewRectPixels will be the same as viewRectPoints for non-retina displays
    NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints];
    
#else //if !SUPPORT_RETINA_RESOLUTION
    
    // App will typically render faster and use less power rendering at
    // non-retina resolutions since the GPU needs to render less pixels.
    // There is the cost of more aliasing, but it will be no-worse than
    // on a Mac without a retina display.
    
    // Points:Pixels is always 1:1 when not supporting retina resolutions
    NSRect viewRectPixels = viewRectPoints;
    
#endif // !SUPPORT_RETINA_RESOLUTION
    
    // Set the new dimensions in our renderer
    [_renderer resizeWithWidth:viewRectPixels.size.width
                     AndHeight:viewRectPixels.size.height];
    
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}


- (void)renewGState
{
    // Called whenever graphics state updated (such as window resize)
    
    // OpenGL rendering is not synchronous with other rendering on the OSX.
    // Therefore, call disableScreenUpdatesUntilFlush so the window server
    // doesn't render non-OpenGL content in the window asynchronously from
    // OpenGL content, which could cause flickering.  (non-OpenGL content
    // includes the title bar and drawing done by the app with other APIs)
    [[self window] disableScreenUpdatesUntilFlush];
    
    [super renewGState];
}

- (void) drawRect: (NSRect) theRect
{
    // Called during resize operations
    
    // Avoid flickering during resize by drawiing
    [self drawView];
}

- (void) drawView
{
    [[self openGLContext] makeCurrentContext];
    
    // We draw on a secondary thread through the display link
    // When resizing the view, -reshape is called automatically on the main
    // thread. Add a mutex around to avoid the threads accessing the context
    // simultaneously when resizing
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    [_renderer render];
    
    CGLFlushDrawable([[self openGLContext] CGLContextObj]);
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

- (void) dealloc
{
    // Stop the display link BEFORE releasing anything in the view
    // otherwise the display link thread may call into the view and crash
    // when it encounters something that has been release
    CVDisplayLinkStop(displayLink);
    
    CVDisplayLinkRelease(displayLink);
}

- (void)setImage:(CVImageBufferRef)img {
    dispatch_sync(dispatch_get_main_queue(), ^{
        [_renderer setImage:img];
    });
}
-(void)presentYUVData:(NSData*)yuvdata width:(CGFloat)width height:(CGFloat)height {
    
    [_renderer presentYUVData:yuvdata width:width height:height];
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市眠饮,隨后出現(xiàn)的幾起案子奥帘,更是在濱河造成了極大的恐慌,老刑警劉巖仪召,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寨蹋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡扔茅,警方通過查閱死者的電腦和手機(jī)钥庇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咖摹,“玉大人,你說我怎么就攤上這事难述∮┣纾” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵胁后,是天一觀的道長(zhǎng)店读。 經(jīng)常有香客問我,道長(zhǎng)攀芯,這世上最難降的妖魔是什么屯断? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上殖演,老公的妹妹穿的比我還像新娘氧秘。我一直安慰自己,他們只是感情好趴久,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布丸相。 她就那樣靜靜地躺著,像睡著了一般彼棍。 火紅的嫁衣襯著肌膚如雪灭忠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天座硕,我揣著相機(jī)與錄音弛作,去河邊找鬼。 笑死华匾,一個(gè)胖子當(dāng)著我的面吹牛映琳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瘦真,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼刊头,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了诸尽?” 一聲冷哼從身側(cè)響起原杂,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎您机,沒想到半個(gè)月后穿肄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡际看,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年咸产,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仲闽。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脑溢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赖欣,到底是詐尸還是另有隱情屑彻,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布顶吮,位于F島的核電站社牲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏悴了。R本人自食惡果不足惜搏恤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一违寿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧熟空,春花似錦藤巢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至阱当,卻和暖如春俏扩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弊添。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工录淡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人油坝。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓嫉戚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親澈圈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彬檀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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

  • “凌晨?jī)牲c(diǎn)半 你還在我身旁 關(guān)上電話 我不想和誰(shuí)再多說話 愛著你的我 認(rèn)真聽你說的每句話” …… 伴隨著熟悉...
    黑化后的亞瑟閱讀 645評(píng)論 0 0
  • 大家好,我是呂英麗报慕,今天是日精進(jìn)第808天深浮,和大家分享今天的總結(jié)覺察,感恩見證終身成長(zhǎng)眠冈,我們互相勉勵(lì)飞苇,修...
    呂You閱讀 244評(píng)論 0 0
  • 好不容易等到了天亮,腦海中編織了無(wú)數(shù)個(gè)美麗的場(chǎng)景; 我依舊精神抖擻早起洗漱蜗顽,化了一個(gè)淡妝玄柠,穿著一身極其休閑...
    伊麗莎兒閱讀 367評(píng)論 0 0
  • 河濱街道彭莊小學(xué) 彭桃利 記得魏書生老師曾說過這樣一句話:“如果你認(rèn)為你的學(xué)生是魔鬼,那么你就生活...
    夢(mèng)想起航ptl閱讀 170評(píng)論 0 1