iOS-视频播放

引言

当前比较火的软件都是和视频挂钩的,所以这次打算由浅到深的学一下视频的各种操作,本篇文章主要讲解以各种的方式播放视频(侧重AVPlayer)
demo传送门:https://github.com/YJExpand/videoPlayerTest

目录

  • 一、MPPlayerController

  • 二、MPPlayerViewController

  • 三、AVPlayerViewController

  • 四、AVPlayer(本篇主讲)

    1. 增加对视频的拉伸处理(AVLayerVideoGravity)
    2. 增加对视频的旋转处理
    3. 视频进度,缓存
    4. 视频手势处理
    5. 对系统声音的操作
    6. 视频速率
    7. 切换视频

原生框架

  1. #import <MediaPlayer/MediaPlayer.h>
  2. #import <AVFoundation/AVFoundation.h>
  3. #import <AVKit/AVKit.h>

播放方式优缺点分析

视频播放方式.jpg

一、MPPlayerController

MPMoviePlayerController继承NSObject,显示视频需使用MPMoviePlayerController.view,必须设置frame

依赖

#import <MediaPlayer/MediaPlayer.h>

使用方式

    /// 初始化
    // 网络路径
    // NSURL *webUrl =[NSURL URLWithString:@"http://www.xxxx.com/x/x.mp4"];
    // 本地路径
    NSURL *localUrl = [[NSBundle mainBundle] URLForResource:@"lin" withExtension:@"mp4"];

    self.playerController = [[MPMoviePlayerController alloc] initWithContentURL:localUrl];
    // 播放控件的样式
    self.playerController.controlStyle = MPMovieControlStyleDefault;
    // 是否自动播放
    self.playerController.shouldAutoplay = NO;
    // 播放模式
    self.playerController.repeatMode = MPMovieRepeatModeOne;
    /*----------还有视频时间、大小等等。。自行查看头文件---------*/
    
    // 重点
    [self.videoView addSubview:self.playerController.view];
    // 必须要设置Frame
    self.playerController.view.frame = self.videoView.bounds;


    /*  对视频的操作 ***********/
    // 播放前,停止所有的播放
    [self.playerController prepareToPlay];
    // 播放
    [self.playerController play];
    // 暂停
    [self.playerController pause];
    // 停止
    [self.playerController stop];

注意事项

/// 如果使用临时变量保存,则会出现黑屏,必须使用全局变量缓存(iOS9.0后,已弃用)
@property (strong ,nonatomic) MPMoviePlayerController *playerController;

二、MPPlayerViewController

MPMoviePlayerViewController继承UIViewController,所以可以直接跳转控制器(push、presentViewController等),也可以直接将MPMoviePlayerViewController.moviePlayer.view添加到视图。主要使用MPMoviePlayerViewController.moviePlayer操作视频,具体可参考上面的《一、MPPlayerController》

依赖

#import <MediaPlayer/MediaPlayer.h>

使用方式

    // 网络路径
    // NSURL *webUrl =[NSURL URLWithString:@"http://www.xxxx.com/x/x.mp4"];
    // 本地路径
    NSURL *localUrl = [[NSBundle mainBundle] URLForResource:@"lin" withExtension:@"mp4"];
    
    self.playerVC = [[MPMoviePlayerViewController alloc] initWithContentURL:localUrl];
    /*
     MPMoviePlayerViewController 简单粗暴,不需要写UI,但是自定义UI不行
     视频的操作都在 MPMoviePlayerViewController.moviePlayer
     具体可以参考《MPPlayerController -> 使用》
     */
    
//    [self.videoView addSubview:self.playerVC.moviePlayer.view];
//    self.playerVC.moviePlayer.view.frame = self.videoView.bounds;
//    [self.playerVC.moviePlayer play];

注意事项

/// 若想addSubview到当前的self.view中,就必须使用全局变量,具体原因可参考《一、MPPlayerController》(iOS9.0后,已弃用)
@property (strong , nonatomic )MPMoviePlayerViewController *playerVC;

三、AVPlayerViewController

AVPlayerViewController继承UIViewController,可以说是MPMoviePlayerViewController的替代品,目前也是苹果推荐使用的。主要使用AVPlayerViewController.player操作视频,具体可参考下面的《四、AVPlayer》

依赖

#import <AVKit/AVKit.h>

使用方式

    // 本地路径
    NSURL *localUrl = [[NSBundle mainBundle] URLForResource:@"lin" withExtension:@"mp4"];
    
    AVPlayerViewController *playVC = [[AVPlayerViewController alloc] init];
    
    /// 基本操作都是player , 可参考YJAVPlayerViewController(AVPlayer的使用)
    playVC.player = [AVPlayer playerWithURL:localUrl];
    
    // 具体使用参考<AVPlayerViewControllerDelegate>
    playVC.delegate = self;
    
    [playVC.player play];
    
    /*
     可以使用控制器的方式使用,也可以[self.view addSubview:playVC.view],
     设置playVC.view.frame就好了
     */
    [self presentViewController:playVC animated:YES completion:nil];

四、AVPlayer(本篇主讲)

视频播放AVPlayer.jpg

依赖

#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>

全局缓存视频相关的属性

@property (weak, nonatomic) IBOutlet UIView *videoView;

@property (weak, nonatomic) IBOutlet UIView *videoBackgroundView;

@property (strong , nonatomic) AVPlayer *player;

@property (strong , nonatomic) AVPlayerItem *playerItem;

@property (strong , nonatomic) AVPlayerLayer *playerLayer;

使用方式

    /// 初始化
    NSURL *localUrl = [[NSBundle mainBundle] URLForResource:@"lin" withExtension:@"mp4"];
    self.playerItem = [[AVPlayerItem alloc] initWithURL:localUrl];
    
     // 在线链接
//     NSURL *webUrl =[NSURL URLWithString:@"http://www.xxxx.com/x/x.mp4"];
//     AVAsset *asset = [AVAsset assetWithURL:webURL];
//     self.playerItem = [[AVPlayerItem alloc] initWithAsset:asset];
    
    self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];
    
    /*
     AVPlayer要显示,就要结合AVPlayerLayer
     */
    self.playerLayer = [[AVPlayerLayer alloc] init];
    // 设置
    self.playerLayer.player = self.player;
    self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    self.playerLayer.contentsScale = [UIScreen mainScreen].scale;
    // 添加到页面
    [self.videoBackgroundView.layer addSublayer:self.playerLayer];
    // 必须设置frame,建议在viewDidLayoutSubviews设置
    self.playerLayer.frame = self.videoView.bounds;

    /***操作******/
    // 播放
    [self.player play];
    // 暂停
    [self.player pause];

拓展

1、增加对视频的拉伸处理(AVLayerVideoGravity)
// AVLayerVideoGravityResizeAspect : 默认原画(不拉伸,不平铺)
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
// AVLayerVideoGravityResizeAspectFill : 不拉伸,平铺
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
// AVLayerVideoGravityResize : 拉伸平铺
self.playerLayer.videoGravity = AVLayerVideoGravityResize;
2、增加对视频的旋转处理
/*
     CGAffineTransform CGAffineTransformMake (CGFloat a,CGFloat b,CGFloat c,CGFloat d,CGFloat tx,CGFloat ty);
     其中tx用来控制在x轴方向上的平移
     ty用来控制在y轴方向上的平移
     a用来控制在x轴方向上的缩放
     d用来控制在y轴方向上的缩放
     abcd共同控制旋转
     */
    
    // 正常竖屏
    CATransform3D normal = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(0));
    // 倒竖屏
    CATransform3D inverted = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(M_PI));
    // 右横屏
    CATransform3D right = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(-M_PI_2));
    // 左横屏
    CATransform3D left = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(M_PI_2));
    
    switch (self.style) {
        case videoShowStyleVertical_normal:
            self.playerLayer.transform = left;
            self.style = videoShowStyleHorizontal_left;
            break;
        case videoShowStyleHorizontal_left:
            self.playerLayer.transform = right;
            self.style = videoShowStyleHorizontal_right;
            break;
        case videoShowStyleHorizontal_right:
            self.playerLayer.transform = normal;
            self.style = videoShowStyleVertical_normal;
            break;
        default:
            break;
    }
    
    self.playerLayer.frame = self.videoView.bounds;

注释

/// 视频显示样式
typedef enum : NSUInteger {
    /// 普通竖屏
    videoShowStyleVertical_normal = 0,
    /// 左横屏
    videoShowStyleHorizontal_left = 1,
    /// 右横屏
    videoShowStyleHorizontal_right = 2,
} videoShowStyle;
/// 视频显示样式
@property (assign , nonatomic) videoShowStyle style;
3、视频进度,缓存
UI方面主要用了UIProgressView->缓存进度、UISlider->播放进度 构成。
Snip20200402_2.png
/// 缓存进度
@property (weak, nonatomic) IBOutlet UIProgressView *cacheProgressView;
/// 播放进度
@property (weak, nonatomic) IBOutlet UISlider *playProgressView;
/// 总时间
@property (weak, nonatomic) IBOutlet UILabel *totalTimeLB;
/// 播放时间
@property (weak, nonatomic) IBOutlet UILabel *currentTimeLB;
准备就绪、缓存进度使用KVO获取
    static NSString * const PlayerItemStatusContext = @"PlayerItemStatusContext";
    static NSString * const PlayerItemLoadedTimeRangesContext = @"PlayerItemLoadedTimeRangesContext";
    /*
     可以监听self.playerItem.status 判断当前媒体的状态
     AVPlayerItemStatusUnknown  -> 不在播放队列
     AVPlayerItemStatusReadyToPlay  -> 可播放
     AVPlayerItemStatusFailed   -> 播放失败
     
     // 代码
     [self.playerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
     */
    /// 增加监听 ,注意要在dealloc移除,否则崩溃
    [self.playerItem addObserver:self
                      forKeyPath:@"status"
                         options:0
                         context:(__bridge void *)(PlayerItemStatusContext)];
    
    // 监听当前视频的缓存程度
    [self.playerItem addObserver:self
                      forKeyPath:@"loadedTimeRanges"
                         options:0
                         context:(__bridge void *)PlayerItemLoadedTimeRangesContext];


//监听回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    AVPlayerItem *item = (AVPlayerItem *)object;
    
    if (context == (__bridge void *)(PlayerItemStatusContext)) {
        
        NSLog(@"item.status  -> %zd",item.status);
        
        /// 当前播放时间
        NSTimeInterval  current = CMTimeGetSeconds(item.currentTime);
        /// 总时间
        NSTimeInterval  total = CMTimeGetSeconds(item.duration);
        
        self.totalTimeLB.text = [self formatWithTime:total];
        self.currentTimeLB.text = [self formatWithTime:current];
    }
    
    
    if (context == (__bridge void *)PlayerItemLoadedTimeRangesContext) {
       [self.cacheProgressView setProgress:[self availableDurationWithplayerItem:item]];
    }
}

/// 获取当前缓存进度算法
- (NSTimeInterval)availableDurationWithplayerItem:(AVPlayerItem *)playerItem{
    
    NSArray *loadedTimeRanges = [playerItem loadedTimeRanges];
    CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
    NSTimeInterval startSeconds = CMTimeGetSeconds(timeRange.start);
    NSTimeInterval durationSeconds = CMTimeGetSeconds(timeRange.duration);
    NSTimeInterval result = startSeconds + durationSeconds;   // 计算缓存总进度
    
    return result;
}

/// 格式化时间
- (NSString *)formatWithTime:(NSTimeInterval)duration{
    int minute = 0, hour = 0, secend = duration;
    minute = (secend % 3600)/60;
    hour = secend / 3600;
    secend = secend % 60;
    return [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, secend];
}
移动控制条,调节视频进度

主要UISlider的touchDown、touchUpInside控制

/// touchDown
- (IBAction)sliderBeginClick:(UISlider *)sender {
    
    // 当可以播放视频时才能操作进度条
    if (self.playerItem.status == AVPlayerItemStatusReadyToPlay) {
       
        if (self.operationBtns.selectedSegmentIndex == 0) { // 播放状态
             [self.player pause];
        }
    }
}
/// touchUpInside
- (IBAction)sliderEndClick:(UISlider *)sender {
    // 当可以播放视频时才能操作进度条
    if (self.playerItem.status == AVPlayerItemStatusReadyToPlay) {
        /// 当前定位
        float progress = sender.value;
        NSTimeInterval currentTime = CMTimeGetSeconds(self.playerItem.duration) * progress;
        [self playVideoWithTime:currentTime];
        
    }
    
}

/// 定点播放视频
- (void)playVideoWithTime:(NSTimeInterval)currentTime{
    
    CMTime time = CMTimeMake(currentTime, 1);
    [self.player seekToTime:time completionHandler:^(BOOL finished) {
        
    }];
    
    if (self.operationBtns.selectedSegmentIndex == 0) {   // 播放状态
        [self.player play];
    }
}
视频进度影响进度条
/*
     AVPlayer提供一个block,当播放进度改变时,则会自动调取block,
     @param interval 在正常回放期间,根据播放机当前时间的进度,调用块的间隔。
     */
    __weak __typeof(self) weakSelf = self;
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
       
        /// 当前播放时间
        NSTimeInterval  current = CMTimeGetSeconds(time);
        /// 总时间
        NSTimeInterval  total = CMTimeGetSeconds(weakSelf.player.currentItem.duration);
        /// 当前进度
        float progress = current / total;
        [weakSelf.playProgressView setValue:progress animated:YES];
        
        weakSelf.totalTimeLB.text = [weakSelf formatWithTime:total];
        weakSelf.currentTimeLB.text = [weakSelf formatWithTime:current];
        
    }];
4、视频手势处理
声音、亮度UI:

主要以UILabel和UIProgressView组成


Snip20200402_3.png
/*
 ❗️注意 : 测试亮度和音量大小需使用真机
 */
/// 系统操作view
@property (weak, nonatomic) IBOutlet UIView *systemOperationView;
/// 操作名称
@property (weak, nonatomic) IBOutlet UILabel *systemOperationNameLB;
/// 操作进度
@property (weak, nonatomic) IBOutlet UIProgressView *systemOperationProgressView;
/// 亮度
@property (assign , nonatomic) float brightnessProgress;
/// 音量
@property (assign , nonatomic) float volumeProgress;
/// 系统音量控件
@property (strong , nonatomic) UISlider* volumeViewSlider;

参考了众多的播放器,都是
左右滑动->进度
左屏 上下滑动 -> 亮度
右屏 上下滑动 -> 声音

    // 初始化亮度、声音
    self.brightnessProgress = [UIScreen mainScreen].brightness;
    self.volumeProgress = [[AVAudioSession sharedInstance] outputVolume];

    // 添加手势
    UIPanGestureRecognizer *moveTag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(movePanWithGesture:)];
    self.videoView.userInteractionEnabled = YES;
    [self.videoView addGestureRecognizer:moveTag];

/// 手势滑动
- (void)movePanWithGesture:(UIPanGestureRecognizer *)tag{
    // 不可拖动
    if (self.playerItem.status != AVPlayerItemStatusReadyToPlay) return;
    
    /*
     translationInView:方法获取View的偏移量;
     setTranslation:方法设置手势的偏移量;
     velocityInView:方法获取速度;
     */
    
    // 获取view的偏移量
    CGPoint point = [tag translationInView:self.videoView];
    
    if (tag.state == UIGestureRecognizerStateBegan) {  // 开始拖动
        
        NSLog(@"Began : point.x -> %f , point.y -> %f",point.x,point.y);
        self.progress_panBeginPoint = point;
        
    } else if (tag.state == UIGestureRecognizerStateEnded) { // 停止拖动
        NSLog(@"Ended : point.x -> %f , point.y -> %f",point.x,point.y);
        
        switch (self.currentPanStyle) {
            case panGestureStyleProgress:{
                /// 当前定位,开始播放
                float progress = self.playProgressView.value;
                NSTimeInterval currentTime = CMTimeGetSeconds(self.playerItem.duration) * progress;
                [self playVideoWithTime:currentTime];
                break;
                
            }
            case panGestureStyleBrightness:
            case panGestureStyleVolume:{
                // 一秒后隐藏
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    self.systemOperationView.hidden = true;
                });
                break;
                
            }
            default:
                break;
        }
        
        // 格式化
        self.currentPanStyle = panGestureStyleNone;
        
    }else if (tag.state == UIGestureRecognizerStateChanged) { // 拖动中
        NSLog(@"Changed : point.x -> %f , point.y -> %f",point.x,point.y);
        
        if (self.currentPanStyle == panGestureStyleNone) {  // 未区分手势滑动的样式
            
            float moveX = fabs(point.x - self.progress_panBeginPoint.x);
            float moveY = fabs(point.y - self.progress_panBeginPoint.y);
            
            CGPoint currentPoint = [tag locationInView:self.videoBackgroundView];
            if (moveX >= moveY) {  // 属于进度操作
                self.currentPanStyle = panGestureStyleProgress;
                
                if (self.operationBtns.selectedSegmentIndex == 0) {   // 正在播放,暂停播放
                    [self.player pause];
                }
                
            }else if (currentPoint.x <= self.videoView.frame.size.width / 2){  // 亮度操作
                self.currentPanStyle = panGestureStyleBrightness;
            }else{// 音量操作
                self.currentPanStyle = panGestureStyleVolume;
            }
            
        }
        
        
        switch (self.currentPanStyle) {
            case panGestureStyleProgress:{   // 进度
                
                float move = point.x - self.progress_panBeginPoint.x;
                float progress = (move / self.videoView.frame.size.width);
                
                float playProgressValue = self.playProgressView.value;
                float progressEnd = playProgressValue + progress;
                if (progressEnd > 1) {  // 直接加载完
                    [self.playProgressView setValue:1 animated:YES];
                } else if (progressEnd < 0){ // 重新加载
                    [self.playProgressView setValue:0 animated:YES];
                }else{
                    [self.playProgressView setValue:progressEnd animated:YES];
                }
                
                break;
            }
            case panGestureStyleVolume:{     // 音量
                
                self.systemOperationView.hidden = false;
                float move = self.progress_panBeginPoint.y - point.y;
                self.volumeProgress = (move / self.videoView.frame.size.height) * 4 + self.volumeProgress;  // *4 -> 更容易改变音量,不需要滑动更多
                self.systemOperationNameLB.text = @"音量";
                
                if (self.volumeProgress > 1) {  // 已经是最大了
                    self.volumeProgress = 1;
                } else if (self.volumeProgress < 0){ // 已经是最小了
                    self.volumeProgress = 0;
                }
                [self.systemOperationProgressView setProgress:self.volumeProgress];
                // 调节音量
                [self.volumeViewSlider setValue:self.volumeProgress];
                
                break;
            }
            case panGestureStyleBrightness:{     // 亮度
                
                self.systemOperationView.hidden = false;
                float move = self.progress_panBeginPoint.y - point.y;
                self.brightnessProgress = (move / self.videoView.frame.size.height) * 4 + self.brightnessProgress;  // *4 -> 更容易改变亮度,不需要滑动更多
                self.systemOperationNameLB.text = @"亮度";
                if (self.brightnessProgress > 1) {  // 已经是最大了
                    self.brightnessProgress = 1;
                } else if (self.brightnessProgress < 0){ // 已经是最小了
                    self.brightnessProgress = 0;
                }
                [self.systemOperationProgressView setProgress:self.brightnessProgress];
                // 调节亮度
                [[UIScreen mainScreen] setBrightness:self.brightnessProgress];
                
                break;
            }
            default:
                break;
        }
        
        
        
        self.progress_panBeginPoint = point;
    }
}

注释

/// 当前手势操作
typedef enum : NSUInteger {
    /// 默认无
    panGestureStyleNone = 0,
    /// 进度操作
    panGestureStyleProgress = 1,
    /// 屏幕亮度操作
    panGestureStyleBrightness = 2,
    /// 音量大小操作
    panGestureStyleVolume = 3,
} panGestureStyle;

/// 当前手势操作
@property (assign , nonatomic) panGestureStyle currentPanStyle;
5、对系统声音的操作
依赖
#import <MediaPlayer/MediaPlayer.h>

若要对系统声音进行操作,必须要取出系统的MPVolumeSlider

// 设置音量控件
    MPVolumeView *volumeView = [[MPVolumeView alloc] init];
    volumeView.frame = CGRectZero;
    [self.view addSubview:volumeView];
    for (UIView *view in [volumeView subviews]){
        if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
            // 缓存
            self.volumeViewSlider = (UISlider*)view;
            break;
        }
    }
    [self.volumeViewSlider setValue:self.volumeProgress animated:YES];
    [self.volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];

    // 监听音量变化
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(systemVolumeDidChangeNoti:) name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];

/// 音量变化
- (void)systemVolumeDidChangeNoti:(NSNotification *)noti{
    float voiceSize = [[noti.userInfo valueForKey:@"AVSystemController_AudioVolumeNotificationParameter"]floatValue];
    self.volumeProgress = voiceSize;
}
6、视频速率

主要设置player.rate 属性

    // 修改速率
    self.player.rate = rate;
7、切换视频

主要是监听视频播放完成 - > 切换视频/重播

/// 监听视频是否播放完成
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoPlayEndNoti:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
/// 视频播放完毕
- (void)videoPlayEndNoti:(NSNotification *)noti{
    self.switchBackgroundView.hidden = false;
}

选择操作

- (IBAction)videoSwitchBtnClick:(UIButton *)sender {
    
    self.switchBackgroundView.hidden = true;
    
    switch (sender.tag) {
        case 1:  // 重播
            [self playVideoWithTime:0];
            break;
        case 2:     // 下一个
            // 1、先移除监听,避免崩溃
            [self playerItemRemoveObserver];
            // 2、重写playerItem
            self.playerItem = [AVPlayerItem playerItemWithURL:[YJManager getLocalVideoURL]];
            // 3、替换
            [self.player replaceCurrentItemWithPlayerItem:self.playerItem];
            // 4、添加监听
            [self playerItemAddObserver];
            // 5、播放
            [self.player play];
            self.operationBtns.selectedSegmentIndex = 0;
            break;
        default:
            break;
    }
}

总结

关于视频播放的操作基本完成,应该可以满足大多数的需求,本次主要讲解实现思路,未进行封装处理,后续有时间再封装个框架
本文修改时间2020.04.02

效果图


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

推荐阅读更多精彩内容