iOS 开发 AVPlayer 深入浅出

首先介绍一篇AVPlayer 的文章:

http://www.cnblogs.com/mzds/p/3711867.html

然后我再写写我在实际项目中遇到的问题

1.然后监听playerItem的status和loadedTimeRange属性,status有三种状态==>这是原文中的话,但是后面列出的属性却是AVPlayer 的status(应该是作者笔误),其实AVPlayerItem和__AVPlayer__ 都有status 属性的,而且可以使用KVO监听的。

文档中枚举类型如下:

typedef NS_ENUM(NSInteger, AVPlayerItemStatus) {

AVPlayerItemStatusUnknown,

AVPlayerItemStatusReadyToPlay,

AVPlayerItemStatusFailed

};

而AVPlayer 的status 枚举类型如下:

typedef NS_ENUM(NSInteger, AVPlayerStatus) {

AVPlayerStatusUnknown,

AVPlayerStatusReadyToPlay,

AVPlayerStatusFailed

};

看清楚啊!!!看清楚这两个之间的区别,这里我主要想说明:

AVPlayerItemStatus是代表当前播放资源item 的状态(可以理解成这url链接or视频文件。。。可以播放成功/失败)

AVPlayerStatus是代表当前播放器的状态。

我在编程的时候遇到一个问题就是AVPlayer 的status 为

AVPlayerStatusReadyToPlay,但是视频就是播放不成功,后来将KVO的监听换成了AVPlayerItem ,返回了AVPlayerItemStatusFailed。

编程的时候最好使用item 的status,会准确点。

2.addPeriodicTimeObserverForInterval

给AVPlayer 添加time Observer 有利于我们去检测播放进度

但是添加以后一定要记得移除,其实不移除程序不会崩溃,但是这个线程是不会释放的,会占用你大量的内存资源(当时发现这个问题的时候我搞了一上午,说实话当时我根本不知道是哪里出现了问题,自己对AVPlayer 根本不了解)

苹果文档中的注释:

@result

An object conforming to the NSObject protocol.  You must retain this returned value as long as you want the time observer to be invoked by the player.

Pass this object to -removeTimeObserver: to cancel time observation.

3.CMTime 结构体

连接的教程里面 给的参数是CMTimeMake(1, 1),其实就是1s调用一下block,

打个比方CMTimeMake(a, b)就是a/b秒之后调用一下block

介绍一个网站有关这个结构体的:

https://zwo28.wordpress.com/2015/03/06/%E8%A7%86%E9%A2%91%E5%90%88%E6%88%90%E4%B8%ADcmtime%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%88%A9%E7%94%A8cmtime%E5%AE%9E%E7%8E%B0%E8%BF%87%E6%B8%A1%E6%95%88%E6%9E%9C/

4.拖动slider 播放跳跃播放,要使用AVPlayer对象的seekToTime:方法,

举个最简单的例子:假如一个video视频有20s,想要跳到10s进行播放(_palyer 为AVPlayer 对象)

[_player seekToTime:CMTimeMake(10,1)];后面的参数写1,前面的参数写将要播放的秒数,我试验得出的结果,不要问我问什么,需要自己理解。

5.播放到结尾怎么回到开头呢?

[_player seekToTime:kCMTimeZero];

下面放上我的写的代码:写的不好请指正,多谢。

AVViewController.h

#import

@interface AVViewController : UIViewController

@end

AVViewController.m

#import "AVViewController.h"

#import "VideoView.h"

@interface AVViewController ()

@property (nonatomic ,strong) VideoView *videoView;

@property (nonatomic ,strong) NSMutableArray *array;

@property (nonatomic ,strong) UISlider *videoSlider;

@property (nonatomic ,strong) NSMutableArray *sliderArray;

@end

@implementation AVViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self.view setBackgroundColor:[UIColor whiteColor]];

[self initVideoView];

}

- (void)initVideoView {

//NSString *path = [[NSBundle mainBundle] pathForResource:@"some" ofType:@"mp4"];//这个时播放本地的,播放本地的时候还需要改VideoView.m中的代码

NSString *path = @"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4";

_videoView = [[VideoView alloc] initWithUrl:path delegate:self];

_videoView.someDelegate = self;

[_videoView setTranslatesAutoresizingMaskIntoConstraints:NO];

[self.view addSubview:_videoView];

[self initVideoSlider];

if (self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {

[self installLandspace];

} else {

[self installVertical];

}

}

- (void)installVertical {

if (_array != nil) {

[self.view removeConstraints:_array];

[_array removeAllObjects];

[self.view removeConstraints:_sliderArray];

[_sliderArray removeAllObjects];

} else {

_array = [NSMutableArray array];

_sliderArray = [NSMutableArray array];

}

id topGuide = self.topLayoutGuide;

NSDictionary *dic = @{@"top":@100,@"height":@180,@"edge":@20,@"space":@80};

[_array addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_videoView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_videoView)]];

[_array addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(edge)-[_videoSlider]-(edge)-|" options:0 metrics:dic views:NSDictionaryOfVariableBindings(_videoSlider)]];

[_array addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[topGuide]-(top)-[_videoView(==height)]-(space)-[_videoSlider]" options:0 metrics:dic views:NSDictionaryOfVariableBindings(_videoView,topGuide,_videoSlider)]];

[self.view addConstraints:_array];

}

- (void)installLandspace {

if (_array != nil) {

[self.view removeConstraints:_array];

[_array removeAllObjects];

[self.view removeConstraints:_sliderArray];

[_sliderArray removeAllObjects];

} else {

_array = [NSMutableArray array];

_sliderArray = [NSMutableArray array];

}

id topGuide = self.topLayoutGuide;

NSDictionary *dic = @{@"edge":@20,@"space":@30};

[_array addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_videoView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_videoView)]];

[_array addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[topGuide][_videoView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_videoView,topGuide)]];

[self.view addConstraints:_array];

[_sliderArray addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(edge)-[_videoSlider]-(edge)-|" options:0 metrics:dic views:NSDictionaryOfVariableBindings(_videoSlider)]];

[_sliderArray addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_videoSlider]-(space)-|" options:0 metrics:dic views:NSDictionaryOfVariableBindings(_videoSlider)]];

[self.view addConstraints:_sliderArray];

}

- (void)initVideoSlider {

_videoSlider = [[UISlider alloc] init];

[_videoSlider setTranslatesAutoresizingMaskIntoConstraints:NO];

[_videoSlider setThumbImage:[UIImage imageNamed:@"sliderButton"] forState:UIControlStateNormal];

[self.view addSubview:_videoSlider];

}

- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id )coordinator {

[super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];

[coordinator animateAlongsideTransition:^(id context) {

if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {

[self installLandspace];

} else {

[self installVertical];

}

[self.view setNeedsLayout];

} completion:nil];

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

}

#pragma mark -

- (void)flushCurrentTime:(NSString *)timeString sliderValue:(float)sliderValue {

_videoSlider.value = sliderValue;

}

/*

#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

// Get the new view controller using [segue destinationViewController].

// Pass the selected object to the new view controller.

}

*/

@end

VideoView.h

#import

#import

@protocol VideoSomeDelegate

@required

- (void)flushCurrentTime:(NSString *)timeString sliderValue:(float)sliderValue;

//- (void)flushVideoLength:(float)videoLength;

@end

@interface VideoView : UIView

@property (nonatomic ,strong) NSString *playerUrl;

@property (nonatomic ,readonly) AVPlayerItem *item;

@property (nonatomic ,readonly) AVPlayerLayer *playerLayer;

@property (nonatomic ,readonly) AVPlayer *player;

@property (nonatomic ,weak) id someDelegate;

- (id)initWithUrl:(NSString *)path delegate:(id)delegate;

@end

@interface VideoView  (Guester)

- (void)addSwipeView;

@end

VideoView.m

#import "VideoView.h"

#import

#import

typedef enum  {

ChangeNone,

ChangeVoice,

ChangeLigth,

ChangeCMTime

}Change;

@interface VideoView ()

@property (nonatomic ,readwrite) AVPlayerItem *item;

@property (nonatomic ,readwrite) AVPlayerLayer *playerLayer;

@property (nonatomic ,readwrite) AVPlayer *player;

@property (nonatomic ,strong)  id timeObser;

@property (nonatomic ,assign) float videoLength;

@property (nonatomic ,assign) Change changeKind;

@property (nonatomic ,assign) CGPoint lastPoint;

//Gesture

@property (nonatomic ,strong) UIPanGestureRecognizer *panGesture;

@property (nonatomic ,strong) MPVolumeView *volumeView;

@property (nonatomic ,weak) UISlider *volumeSlider;

@property (nonatomic ,strong) UIView *darkView;

@end

@implementation VideoView

- (id)initWithUrl:(NSString *)path delegate:(id)delegate {

if (self = [super init]) {

_playerUrl = path;

_someDelegate = delegate;

[self setBackgroundColor:[UIColor blackColor]];

[self setUpPlayer];

[self addSwipeView];

}

return self;

}

- (void)setUpPlayer {

//本地视频

//NSURL *rul = [NSURL fileURLWithPath:_playerUrl];

NSURL *url = [NSURL URLWithString:_playerUrl];

NSLog(@"%@",url);

_item = [[AVPlayerItem alloc] initWithURL:url];

_player = [AVPlayer playerWithPlayerItem:_item];

_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];

_playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;

[self.layer addSublayer:_playerLayer];

[self addVideoKVO];

[self addVideoTimerObserver];

[self addVideoNotic];

}

#pragma mark - KVO

- (void)addVideoKVO

{

//KVO

[_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

[_item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

[_item addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];

}

- (void)removeVideoKVO {

[_item removeObserver:self forKeyPath:@"status"];

[_item removeObserver:self forKeyPath:@"loadedTimeRanges"];

[_item removeObserver:self forKeyPath:@"playbackBufferEmpty"];

}

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context {

if ([keyPath isEqualToString:@"status"]) {

AVPlayerItemStatus status = _item.status;

switch (status) {

case AVPlayerItemStatusReadyToPlay:

{

NSLog(@"AVPlayerItemStatusReadyToPlay");

[_player play];

_videoLength = floor(_item.asset.duration.value * 1.0/ _item.asset.duration.timescale);

}

break;

case AVPlayerItemStatusUnknown:

{

NSLog(@"AVPlayerItemStatusUnknown");

}

break;

case AVPlayerItemStatusFailed:

{

NSLog(@"AVPlayerItemStatusFailed");

NSLog(@"%@",_item.error);

}

break;

default:

break;

}

} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {

} else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {

}

}

#pragma mark - Notic

- (void)addVideoNotic {

//Notification

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

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieJumped:) name:AVPlayerItemTimeJumpedNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieStalle:) name:AVPlayerItemPlaybackStalledNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backGroundPauseMoive) name:UIApplicationDidEnterBackgroundNotification object:nil];

}

- (void)removeVideoNotic {

//

[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];

[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemTimeJumpedNotification object:nil];

[[NSNotificationCenter defaultCenter] removeObserver:self];

}

- (void)movieToEnd:(NSNotification *)notic {

NSLog(@"%@",NSStringFromSelector(_cmd));

}

- (void)movieJumped:(NSNotification *)notic {

NSLog(@"%@",NSStringFromSelector(_cmd));

}

- (void)movieStalle:(NSNotification *)notic {

NSLog(@"%@",NSStringFromSelector(_cmd));

}

- (void)backGroundPauseMoive {

NSLog(@"%@",NSStringFromSelector(_cmd));

}

#pragma mark - TimerObserver

- (void)addVideoTimerObserver {

__weak typeof (self)self_ = self;

_timeObser = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {

float currentTimeValue = time.value*1.0/time.timescale/self_.videoLength;

NSString *currentString = [self_ getStringFromCMTime:time];

if ([self_.someDelegate respondsToSelector:@selector(flushCurrentTime:sliderValue:)]) {

[self_.someDelegate flushCurrentTime:currentString sliderValue:currentTimeValue];

} else {

NSLog(@"no response");

}

NSLog(@"%@",self_.someDelegate);

}];

}

- (void)removeVideoTimerObserver {

NSLog(@"%@",NSStringFromSelector(_cmd));

[_player removeTimeObserver:_timeObser];

}

#pragma mark - Utils

- (NSString *)getStringFromCMTime:(CMTime)time

{

float currentTimeValue = (CGFloat)time.value/time.timescale;//得到当前的播放时

NSDate * currentDate = [NSDate dateWithTimeIntervalSince1970:currentTimeValue];

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

NSInteger unitFlags = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond ;

NSDateComponents *components = [calendar components:unitFlags fromDate:currentDate];

if (currentTimeValue >= 3600 )

{

return [NSString stringWithFormat:@"%ld:%ld:%ld",components.hour,components.minute,components.second];

}

else

{

return [NSString stringWithFormat:@"%ld:%ld",components.minute,components.second];

}

}

- (NSString *)getVideoLengthFromTimeLength:(float)timeLength

{

NSDate * date = [NSDate dateWithTimeIntervalSince1970:timeLength];

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

NSInteger unitFlags = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond ;

NSDateComponents *components = [calendar components:unitFlags fromDate:date];

if (timeLength >= 3600 )

{

return [NSString stringWithFormat:@"%ld:%ld:%ld",components.hour,components.minute,components.second];

}

else

{

return [NSString stringWithFormat:@"%ld:%ld",components.minute,components.second];

}

}

- (void)layoutSubviews {

[super layoutSubviews];

_playerLayer.frame = self.bounds;

}

#pragma mark - release

- (void)dealloc {

NSLog(@"%@",NSStringFromSelector(_cmd));

[self removeVideoTimerObserver];

[self removeVideoNotic];

[self removeVideoKVO];

}

@end

#pragma mark - VideoView (Guester)

@implementation VideoView (Guester)

- (void)addSwipeView {

_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(swipeAction:)];

[self addGestureRecognizer:_panGesture];

[self setUpDarkView];

}

- (void)setUpDarkView {

_darkView = [[UIView alloc] init];

[_darkView setTranslatesAutoresizingMaskIntoConstraints:NO];

[_darkView setBackgroundColor:[UIColor blackColor]];

_darkView.alpha = 0.0;

[self addSubview:_darkView];

NSMutableArray *darkArray = [NSMutableArray array];

[darkArray addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_darkView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_darkView)]];

[darkArray addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_darkView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_darkView)]];

[self addConstraints:darkArray];

}

- (void)swipeAction:(UISwipeGestureRecognizer *)gesture {

switch (gesture.state) {

case UIGestureRecognizerStateBegan:

{

_changeKind = ChangeNone;

_lastPoint = [gesture locationInView:self];

}

break;

case  UIGestureRecognizerStateChanged:

{

[self getChangeKindValue:[gesture locationInView:self]];

}

break;

case UIGestureRecognizerStateEnded:

{

if (_changeKind == ChangeCMTime) {

[self changeEndForCMTime:[gesture locationInView:self]];

}

_changeKind = ChangeNone;

_lastPoint = CGPointZero;

}

default:

break;

}

}

- (void)getChangeKindValue:(CGPoint)pointNow {

switch (_changeKind) {

case ChangeNone:

{

[self changeForNone:pointNow];

}

break;

case ChangeCMTime:

{

[self changeForCMTime:pointNow];

}

break;

case ChangeLigth:

{

[self changeForLigth:pointNow];

}

break;

case ChangeVoice:

{

[self changeForVoice:pointNow];

}

break;

default:

break;

}

}

- (void)changeForNone:(CGPoint) pointNow {

if (fabs(pointNow.x - _lastPoint.x) > fabs(pointNow.y - _lastPoint.y)) {

_changeKind = ChangeCMTime;

} else {

float halfWight = self.bounds.size.width / 2;

if (_lastPoint.x < halfWight) {

_changeKind =  ChangeLigth;

} else {

_changeKind =  ChangeVoice;

}

_lastPoint = pointNow;

}

}

- (void)changeForCMTime:(CGPoint) pointNow {

float number = fabs(pointNow.x - _lastPoint.x);

if (pointNow.x > _lastPoint.x && number > 10) {

float currentTime = _player.currentTime.value / _player.currentTime.timescale;

float tobeTime = currentTime + number*0.5;

NSLog(@"forwart to  changeTo  time:%f",tobeTime);

} else if (pointNow.x < _lastPoint.x && number > 10) {

float currentTime = _player.currentTime.value / _player.currentTime.timescale;

float tobeTime = currentTime - number*0.5;

NSLog(@"back to  time:%f",tobeTime);

}

}

- (void)changeForLigth:(CGPoint) pointNow {

float number = fabs(pointNow.y - _lastPoint.y);

if (pointNow.y > _lastPoint.y && number > 10) {

_lastPoint = pointNow;

[self minLigth];

} else if (pointNow.y < _lastPoint.y && number > 10) {

_lastPoint = pointNow;

[self upperLigth];

}

}

- (void)changeForVoice:(CGPoint)pointNow {

float number = fabs(pointNow.y - _lastPoint.y);

if (pointNow.y > _lastPoint.y && number > 10) {

_lastPoint = pointNow;

[self minVolume];

} else if (pointNow.y < _lastPoint.y && number > 10) {

_lastPoint = pointNow;

[self upperVolume];

}

}

- (void)changeEndForCMTime:(CGPoint)pointNow {

if (pointNow.x > _lastPoint.x ) {

NSLog(@"end for CMTime Upper");

float length = fabs(pointNow.x - _lastPoint.x);

[self upperCMTime:length];

} else {

NSLog(@"end for CMTime min");

float length = fabs(pointNow.x - _lastPoint.x);

[self mineCMTime:length];

}

}

- (void)upperLigth {

if (_darkView.alpha >= 0.1) {

_darkView.alpha =  _darkView.alpha - 0.1;

}

}

- (void)minLigth {

if (_darkView.alpha <= 1.0) {

_darkView.alpha =  _darkView.alpha + 0.1;

}

}

- (void)upperVolume {

if (self.volumeSlider.value <= 1.0) {

self.volumeSlider.value =  self.volumeSlider.value + 0.1 ;

}

}

- (void)minVolume {

if (self.volumeSlider.value >= 0.0) {

self.volumeSlider.value =  self.volumeSlider.value - 0.1 ;

}

}

#pragma mark -CMTIME

- (void)upperCMTime:(float)length {

float currentTime = _player.currentTime.value / _player.currentTime.timescale;

float tobeTime = currentTime + length*0.5;

if (tobeTime > _videoLength) {

[_player seekToTime:_item.asset.duration];

} else {

[_player seekToTime:CMTimeMake(tobeTime, 1)];

}

}

- (void)mineCMTime:(float)length {

float currentTime = _player.currentTime.value / _player.currentTime.timescale;

float tobeTime = currentTime - length*0.5;

if (tobeTime <= 0) {

[_player seekToTime:kCMTimeZero];

} else {

[_player seekToTime:CMTimeMake(tobeTime, 1)];

}

}

- (MPVolumeView *)volumeView {

if (_volumeView == nil) {

_volumeView = [[MPVolumeView alloc] init];

_volumeView.hidden = YES;

[self addSubview:_volumeView];

}

return _volumeView;

}

- (UISlider *)volumeSlider {

if (_volumeSlider== nil) {

NSLog(@"%@",[self.volumeView subviews]);

for (UIView  *subView in [self.volumeView subviews]) {

if ([subView.class.description isEqualToString:@"MPVolumeSlider"]) {

_volumeSlider = (UISlider*)subView;

break;

}

}

}

return _volumeSlider;

}

@end

作者:SomeBoy

链接://www.greatytc.com/p/5016b72c52bd

來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容