iOS完美實現(xiàn)微信朋友圈視頻截取

序言

微信現(xiàn)在這么普及怒竿,功能也做的越來越強大涩搓,不知大家對于微信朋友圈發(fā)視頻截取的功能或者蘋果拍視頻對視頻編輯的功能有沒有了解(作者這里也猜測他挎,微信的這個功能也是仿蘋果的)。感覺這個功能確實很方便實用牺陶,近來作者也在研究音視頻功能,所以就實現(xiàn)了一下這個功能辣之。

功能其實看著挺簡單掰伸,實現(xiàn)過程也踩了不少坑。一方面記錄一下怀估;另一方面也算是對實現(xiàn)過程的再一次梳理狮鸭,這樣大家看代碼也會比較明白。

效果

我們先看看我實現(xiàn)的效果

image

實現(xiàn)

實現(xiàn)過程分析

整個功能可以分為三部分:

  • 視頻播放
    這部分我們單獨封裝一個視頻播放器即可
  • 下邊的滑動視圖
    這部分實現(xiàn)過程比較復雜多搀,一共分成了4部分歧蕉。灰色遮蓋康铭、左右把手滑塊惯退、滑塊中間上下兩條線、圖片管理視圖
  • 控制器視圖邏輯組裝和功能實現(xiàn)

視頻播放器的封裝

這里使用AVPlayer麻削、playerLayer蒸痹、AVPlayerItem這三個類實現(xiàn)了視頻播放功能春弥;由于整個事件都是基于KVO監(jiān)聽的呛哟,所以增加了Block代碼提供了對外監(jiān)聽使用。

#import "FOFMoviePlayer.h"
@interface FOFMoviePlayer()
{
    AVPlayerLooper *_playerLooper;
    AVPlayerItem *_playItem;
    BOOL _loop;
}
@property(nonatomic,strong)NSURL *url;

@property(nonatomic,strong)AVPlayer *player;
@property(nonatomic,strong)AVPlayerLayer *playerLayer;
@property(nonatomic,strong)AVPlayerItem *playItem;

@property (nonatomic,assign) CMTime duration;
@end
@implementation FOFMoviePlayer

-(instancetype)initWithFrame:(CGRect)frame url:(NSURL *)url superLayer:(CALayer *)superLayer{
    self = [super init];
    if (self) {
        [self initplayers:superLayer];
        _playerLayer.frame = frame;
        self.url = url;
    }
    return self;
}
-(instancetype)initWithFrame:(CGRect)frame url:(NSURL *)url superLayer:(CALayer *)superLayer loop:(BOOL)loop{
    self = [self initWithFrame:frame url:url superLayer:superLayer];
    if (self) {
        _loop = loop;
    }
    return self;
}
- (void)initplayers:(CALayer *)superLayer{
    self.player = [[AVPlayer alloc] init];
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    self.playerLayer.videoGravity = AVLayerVideoGravityResize;
    [superLayer addSublayer:self.playerLayer];
}
- (void)initLoopPlayers:(CALayer *)superLayer{
    self.player = [[AVQueuePlayer alloc] init];
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    self.playerLayer.videoGravity = AVLayerVideoGravityResize;
    [superLayer addSublayer:self.playerLayer];
}
-(void)fof_play{
    [self.player play];
}
-(void)fof_pause{
    [self.player pause];
}

#pragma mark - Observe
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerItem *item = (AVPlayerItem *)object;
        AVPlayerItemStatus status = [[change objectForKey:@"new"] intValue]; // 獲取更改后的狀態(tài)
        if (status == AVPlayerItemStatusReadyToPlay) {
            _duration = item.duration;//只有在此狀態(tài)下才能獲取匿沛,不能在AVPlayerItem初始化后馬上獲取
            NSLog(@"準備播放");
            if (self.blockStatusReadyPlay) {
                self.blockStatusReadyPlay(item);
            }
        } else if (status == AVPlayerItemStatusFailed) {
            if (self.blockStatusFailed) {
                self.blockStatusFailed();
            }
            AVPlayerItem *item = (AVPlayerItem *)object;
            NSLog(@"%@",item.error);
            NSLog(@"AVPlayerStatusFailed");
        } else {
            self.blockStatusUnknown();
            NSLog(@"%@",item.error);
            NSLog(@"AVPlayerStatusUnknown");
        }
    }else if ([keyPath isEqualToString:@"tracking"]){
        NSInteger status = [change[@"new"] integerValue];
        if (self.blockTracking) {
            self.blockTracking(status);
        }
        if (status) {//正在拖動
            [self.player pause];
        }else{//停止拖動

        }
    }else if ([keyPath isEqualToString:@"loadedTimeRanges"]){
        NSArray *array = _playItem.loadedTimeRanges;
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次緩沖時間范圍
        CGFloat startSeconds = CMTimeGetSeconds(timeRange.start);
        CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩沖總長度
        double progress = totalBuffer/CMTimeGetSeconds(_duration);
        if (self.blockLoadedTimeRanges) {
            self.blockLoadedTimeRanges(progress);
        }
        NSLog(@"當前緩沖時間:%f",totalBuffer);
    }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]){
        NSLog(@"緩存不夠扫责,不能播放!");
    }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]){
        if (self.blockPlaybackLikelyToKeepUp) {
            self.blockPlaybackLikelyToKeepUp([change[@"new"] boolValue]);
        }
    }

}

-(void)setUrl:(NSURL *)url{
    _url = url;
    [self.player replaceCurrentItemWithPlayerItem:self.playItem];
}

-(AVPlayerItem *)playItem{
    _playItem = [[AVPlayerItem alloc] initWithURL:_url];
    //監(jiān)聽播放器的狀態(tài)逃呼,準備好播放鳖孤、失敗、未知錯誤
    [_playItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //    監(jiān)聽緩存的時間
    [_playItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    //    監(jiān)聽獲取當緩存不夠抡笼,視頻加載不出來的情況:
    [_playItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
    //    用于監(jiān)聽緩存足夠播放的狀態(tài)
    [_playItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(private_playerMovieFinish) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

    return _playItem;
}
- (void)private_playerMovieFinish{
    NSLog(@"播放結束");
    if (self.blockPlayToEndTime) {
        self.blockPlayToEndTime();
    }
    if (_loop) {//默認提供一個循環(huán)播放的功能
        [self.player pause];
        CMTime time = CMTimeMake(1, 1);
        __weak typeof(self)this = self;
        [self.player seekToTime:time completionHandler:^(BOOL finished) {
            [this.player play];
        }];
    }
}
-(void)dealloc{
    NSLog(@"-----銷毀-----");
}
@end

視頻播放器就不重點講了苏揣,作者計劃單獨寫一篇有關視頻播放器的。

下邊的滑動視圖

灰色遮蓋

灰色遮蓋比較簡單這里作者只是用了UIView

self.leftMaskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, height)];
self.leftMaskView.backgroundColor = [UIColor grayColor];
self.leftMaskView.alpha = 0.8;
[self addSubview:self.leftMaskView];
self.rightMaskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, height)];
self.rightMaskView.backgroundColor = [UIColor grayColor];
self.rightMaskView.alpha = 0.8;

滑塊中間上下兩條線

這兩根線單獨封裝了一個視圖Line,一開始也想到用一個UIView就好了推姻,但是發(fā)現(xiàn)一個問題平匈,就是把手的滑動與線的滑動速度不匹配,線比較慢。

@implementation Line

-(void)setBeginPoint:(CGPoint)beginPoint{
    _beginPoint = beginPoint;
    [self setNeedsDisplay];
}
-(void)setEndPoint:(CGPoint)endPoint{
    _endPoint = endPoint;
    [self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 3);
    CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:0.9 alpha:1].CGColor);
    CGContextMoveToPoint(context, self.beginPoint.x, self.beginPoint.y);
    CGContextAddLineToPoint(context, self.endPoint.x, self.endPoint.y);
    CGContextStrokePath(context);
}

圖片管理視圖

這里封裝了一個VideoPieces藏古,用來組裝把手增炭、線、遮蓋的邏輯拧晕,并且用來顯示圖片隙姿。由于圖片只有10張,所以這里緊緊是一個for循環(huán)厂捞,增加了10個UIImageView

@interface VideoPieces()
{
    CGPoint _beginPoint;
}
@property(nonatomic,strong) Haft *leftHaft;
@property(nonatomic,strong) Haft *rightHaft;
@property(nonatomic,strong) Line *topLine;
@property(nonatomic,strong) Line *bottomLine;
@property(nonatomic,strong) UIView *leftMaskView;
@property(nonatomic,strong) UIView *rightMaskView;
@end
@implementation VideoPieces
-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self initSubViews:frame];
    }
    return self;
}
- (void)initSubViews:(CGRect)frame{
    CGFloat height = CGRectGetHeight(frame);
    CGFloat width = CGRectGetWidth(frame);
    CGFloat minGap = 30;
    CGFloat widthHaft = 10;
    CGFloat heightLine = 3;
    _leftHaft = [[Haft alloc] initWithFrame:CGRectMake(0, 0, widthHaft, height)];
    _leftHaft.alpha = 0.8;
    _leftHaft.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1];
    _leftHaft.rightEdgeInset = 20;
    _leftHaft.lefEdgeInset = 5;
    __weak typeof(self) this = self;
    [_leftHaft setBlockMove:^(CGPoint point) {
        CGFloat maxX = this.rightHaft.frame.origin.x-minGap;
        if (point.x<maxX) {
            this.topLine.beginPoint = CGPointMake(point.x, heightLine/2.0);
            this.bottomLine.beginPoint = CGPointMake(point.x, heightLine/2.0);
            this.leftHaft.frame = CGRectMake(point.x, 0, widthHaft, height);
            this.leftMaskView.frame = CGRectMake(0, 0, point.x, height);
            if (this.blockSeekOffLeft) {
                this.blockSeekOffLeft(point.x);
            }
        }
    }];
    [_leftHaft setBlockMoveEnd:^{
        if (this.blockMoveEnd) {
            this.blockMoveEnd();
        }
    }];
    _rightHaft = [[Haft alloc] initWithFrame:CGRectMake(width-widthHaft, 0, widthHaft, height)];
    _rightHaft.alpha = 0.8;
    _rightHaft.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1];
    _rightHaft.lefEdgeInset = 20;
    _rightHaft.rightEdgeInset = 5;
    [_rightHaft setBlockMove:^(CGPoint point) {
        CGFloat minX = this.leftHaft.frame.origin.x+minGap+CGRectGetWidth(this.rightHaft.bounds);
        if (point.x>=minX) {
            this.topLine.endPoint = CGPointMake(point.x-widthHaft, heightLine/2.0);
            this.bottomLine.endPoint = CGPointMake(point.x-widthHaft, heightLine/2.0);
            this.rightHaft.frame = CGRectMake(point.x, 0, widthHaft, height);
            this.rightMaskView.frame = CGRectMake(point.x+widthHaft, 0, width-point.x-widthHaft, height);
            if (this.blockSeekOffRight) {
                this.blockSeekOffRight(point.x);
            }
        }
    }];
    [_rightHaft setBlockMoveEnd:^{
        if (this.blockMoveEnd) {
            this.blockMoveEnd();
        }
    }];
    _topLine = [[Line alloc] init];
    _topLine.alpha = 0.8;
    _topLine.frame = CGRectMake(widthHaft, 0, width-2*widthHaft, heightLine);
    _topLine.beginPoint = CGPointMake(0, heightLine/2.0);
    _topLine.endPoint = CGPointMake(CGRectGetWidth(_topLine.bounds), heightLine/2.0);
    _topLine.backgroundColor = [UIColor clearColor];
    [self addSubview:_topLine];

    _bottomLine = [[Line alloc] init];
    _bottomLine.alpha = 0.8;
    _bottomLine.frame = CGRectMake(widthHaft, height-heightLine, width-2*widthHaft, heightLine);
    _bottomLine.beginPoint = CGPointMake(0, heightLine/2.0);
    _bottomLine.endPoint = CGPointMake(CGRectGetWidth(_bottomLine.bounds), heightLine/2.0);
    _bottomLine.backgroundColor = [UIColor clearColor];
    [self addSubview:_bottomLine];

    [self addSubview:_leftHaft];
    [self addSubview:_rightHaft];

    self.leftMaskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, height)];
    self.leftMaskView.backgroundColor = [UIColor grayColor];
    self.leftMaskView.alpha = 0.8;
    [self addSubview:self.leftMaskView];
    self.rightMaskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, height)];
    self.rightMaskView.backgroundColor = [UIColor grayColor];
    self.rightMaskView.alpha = 0.8;
    [self addSubview:self.rightMaskView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch *touch = touches.anyObject;
    _beginPoint = [touch locationInView:self];

}

把手的實現(xiàn)

把手的實現(xiàn)這里優(yōu)化了一點输玷,就是滑動的時候比較靈敏队丝,一開始用手指滑動的時候不是非常靈敏,經(jīng)常手指滑動了欲鹏,但是把手沒有動炭玫。

增加了靈敏度的方法其實就是增加了接收事件區(qū)域的大小,重寫了-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event這個方法

@implementation Haft
-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.userInteractionEnabled = true;
    }
    return self;
}

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    CGRect rect = CGRectMake(self.bounds.origin.x-self.lefEdgeInset, self.bounds.origin.y-self.topEdgeInset, CGRectGetWidth(self.bounds)+self.lefEdgeInset+self.rightEdgeInset, CGRectGetHeight(self.bounds)+self.bottomEdgeInset+self.topEdgeInset);
    if (CGRectContainsPoint(rect, point)) {
        return YES;
    }
    return NO;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"開始");
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"Move");
    UITouch *touch = touches.anyObject;
    CGPoint point = [touch locationInView:self.superview];
    CGFloat maxX = CGRectGetWidth(self.superview.bounds)-CGRectGetWidth(self.bounds);
    if (point.x>maxX) {
        point.x = maxX;
    }
    if (point.x>=0&&point.x<=(CGRectGetWidth(self.superview.bounds)-CGRectGetWidth(self.bounds))&&self.blockMove) {
        self.blockMove(point);
    }
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (self.blockMoveEnd) {
        self.blockMoveEnd();
    }
}
- (void)drawRect:(CGRect)rect {

    CGFloat width = CGRectGetWidth(self.bounds);
    CGFloat height = CGRectGetHeight(self.bounds);
    CGFloat lineWidth = 1.5;
    CGFloat lineHeight = 12;
    CGFloat gap = (width-lineWidth*2)/3.0;
    CGFloat lineY = (height-lineHeight)/2.0;

    CGContextRef context  = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, lineWidth);
    CGContextSetStrokeColorWithColor(context, [[UIColor grayColor] colorWithAlphaComponent:0.8].CGColor);
    CGContextMoveToPoint(context, gap+lineWidth/2, lineY);
    CGContextAddLineToPoint(context, gap+lineWidth/2, lineY+lineHeight);
    CGContextStrokePath(context);

    CGContextSetLineWidth(context, lineWidth);
    CGContextSetStrokeColorWithColor(context, [[UIColor grayColor] colorWithAlphaComponent:0.8].CGColor);
    CGContextMoveToPoint(context, gap*2+lineWidth+lineWidth/2, lineY);
    CGContextAddLineToPoint(context, gap*2+lineWidth+lineWidth/2, lineY+lineHeight);
    CGContextStrokePath(context);

}

控制器視圖邏輯組裝和功能實現(xiàn)

這部分邏輯是最重要也是最復雜的。

  • 獲取10張縮略圖
- (NSArray *)getVideoThumbnail:(NSString *)path count:(NSInteger)count splitCompleteBlock:(void(^)(BOOL success, NSMutableArray *splitimgs))splitCompleteBlock {
    AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:path]];
    NSMutableArray *arrayImages = [NSMutableArray array];
    [asset loadValuesAsynchronouslyForKeys:@[@"duration"] completionHandler:^{
        AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
//        generator.maximumSize = CGSizeMake(480,136);//如果是CGSizeMake(480,136)馍资,則獲取到的圖片是{240, 136}疗杉。與實際大小成比例
        generator.appliesPreferredTrackTransform = YES;//這個屬性保證我們獲取的圖片的方向是正確的。比如有的視頻需要旋轉手機方向才是視頻的正確方向衔憨。
        /**因為有誤差,所以需要設置以下兩個屬性袄膏。如果不設置誤差有點大践图,設置了之后相差非常非常的小**/
        generator.requestedTimeToleranceAfter = kCMTimeZero;
        generator.requestedTimeToleranceBefore = kCMTimeZero;
        Float64 seconds = CMTimeGetSeconds(asset.duration);
        NSMutableArray *array = [NSMutableArray array];
        for (int i = 0; i<count; i++) {
            CMTime time = CMTimeMakeWithSeconds(i*(seconds/10.0),1);//想要獲取圖片的時間位置
            [array addObject:[NSValue valueWithCMTime:time]];
        }
        __block int i = 0;
        [generator generateCGImagesAsynchronouslyForTimes:array completionHandler:^(CMTime requestedTime, CGImageRef  _Nullable imageRef, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {

            i++;
            if (result==AVAssetImageGeneratorSucceeded) {
                UIImage *image = [UIImage imageWithCGImage:imageRef];
                [arrayImages addObject:image];
            }else{
                NSLog(@"獲取圖片失敗3凉荨B氲场!");
            }
            if (i==count) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    splitCompleteBlock(YES,arrayImages);
                });
            }
        }];
    }];
    return arrayImages;

}

10張圖片很容易獲取到斥黑,不過這里要注意一點:回調的時候要放到異步主隊列回調揖盘!要不會出現(xiàn)圖片顯示延遲比較嚴重的問題。

  • 監(jiān)聽左右滑塊事件
[_videoPieces setBlockSeekOffLeft:^(CGFloat offX) {
    this.seeking = true;
    [this.moviePlayer fof_pause];
    this.lastStartSeconds = this.totalSeconds*offX/CGRectGetWidth(this.videoPieces.bounds);
    [this.moviePlayer.player seekToTime:CMTimeMakeWithSeconds(this.lastStartSeconds, 1) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}];
[_videoPieces setBlockSeekOffRight:^(CGFloat offX) {
    this.seeking = true;
    [this.moviePlayer fof_pause];
    this.lastEndSeconds = this.totalSeconds*offX/CGRectGetWidth(this.videoPieces.bounds);
    [this.moviePlayer.player seekToTime:CMTimeMakeWithSeconds(this.lastEndSeconds, 1) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}];

這里通過監(jiān)聽左右滑塊的事件锌奴,將偏移距離轉換成時間兽狭,從而設置播放器的開始時間和結束時間。

  • 循環(huán)播放
self.timeObserverToken = [self.moviePlayer.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.5, NSEC_PER_SEC) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
    if (!this.seeking) {
        if (fabs(CMTimeGetSeconds(time)-this.lastEndSeconds)<=0.02) {
                [this.moviePlayer fof_pause];
                [this private_replayAtBeginTime:this.lastStartSeconds];
            }
    }
}];

這里有兩個注意點:

  1. addPeriodicTimeObserverForInterval要進行釋放鹿蜀,否則會有內存泄漏箕慧。
-(void)dealloc{
    [self.moviePlayer.player removeTimeObserver:self.timeObserverToken];
}
  1. 這里監(jiān)聽了播放時間,進而計算是否達到了我們右邊把手拖動的時間茴恰,如果達到了則重新播放颠焦。這個問題作者思考了很久,怎么實現(xiàn)邊播放邊截韧妗伐庭?差點進入了一個誤區(qū),真去截取視頻婉商。其實這里不用截取視頻似忧,只是控制播放時間和結束時間就可以了,最后只截取一次就行了丈秩。

總結

這次微信小視頻編輯實現(xiàn)過程中盯捌,確實遇到了挺多的小問題。不過通過仔細的研究蘑秽,最終完美實現(xiàn)了饺著,有種如釋重負的感覺箫攀。哈哈。

源碼

GitHub源碼

我的博客

FlyOceanFish

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末幼衰,一起剝皮案震驚了整個濱河市靴跛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渡嚣,老刑警劉巖梢睛,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異识椰,居然都是意外死亡绝葡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門腹鹉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藏畅,“玉大人,你說我怎么就攤上這事功咒∮溲郑” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵力奋,是天一觀的道長榜旦。 經(jīng)常有香客問我,道長刊侯,這世上最難降的妖魔是什么章办? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任锉走,我火速辦了婚禮滨彻,結果婚禮上,老公的妹妹穿的比我還像新娘挪蹭。我一直安慰自己亭饵,他們只是感情好,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布梁厉。 她就那樣靜靜地躺著辜羊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪词顾。 梳的紋絲不亂的頭發(fā)上八秃,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音肉盹,去河邊找鬼昔驱。 笑死,一個胖子當著我的面吹牛上忍,可吹牛的內容都是我干的骤肛。 我是一名探鬼主播纳本,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腋颠!你這毒婦竟也來了繁成?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤淑玫,失蹤者是張志新(化名)和其女友劉穎巾腕,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體絮蒿,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡祠墅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了歌径。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毁嗦。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖回铛,靈堂內的尸體忽然破棺而出狗准,到底是詐尸還是另有隱情,我是刑警寧澤茵肃,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布腔长,位于F島的核電站,受9級特大地震影響验残,放射性物質發(fā)生泄漏捞附。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一您没、第九天 我趴在偏房一處隱蔽的房頂上張望鸟召。 院中可真熱鬧,春花似錦氨鹏、人聲如沸欧募。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽跟继。三九已至,卻和暖如春镣丑,著一層夾襖步出監(jiān)牢的瞬間舔糖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工莺匠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留金吗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像辽聊,于是被迫代替她去往敵國和親纪挎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內容