AVPlayer封装

60920ddb4ccdca50074568b5ea54679f.jpg

最近在写关于音视频播放的案例,所以就趁机会研究了一下AVPlayer的内容。我封装的目前只能播放网络音视频。还未添加缓存,以后找机会研究一下再更新。代码中提供了音视频的上一曲、下一曲、暂停、开始、停止、单曲播放、顺序播放、随机播放等功能。代码写的不好,仅供参考~

代码接口文件

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

NS_ASSUME_NONNULL_BEGIN

// 当前播放器的播放形式
typedef NS_ENUM(NSInteger, MediaPlayType) {
    MediaPlayTypeCycle,   ///< 顺序
    MediaPlayTypeSingle,  ///< 单曲循环
    MediaPlayTypeRandom   ///< 随机播放
};

// 当前播放器的播放状态
typedef NS_ENUM(NSInteger, MediaPlayStatus) {
    MediaPlayStatusStop,        ///< 停止播放
    MediaPlayStatusPause,       ///< 暂停播放
    MediaPlayStatusPlaying      ///< 正在播放
};

// 媒体加载状态
typedef NS_ENUM(NSInteger, MediaLoadStatus) {
    MediaLoadStatusReadyToPlay,  ///< 准备播放
    MediaLoadStatusUnknown,      ///< 未知
    MediaPlayStatusFailed        ///< 失败
};

@class MediaPlyerManager;

@protocol MediaPlyerManagerDelegate <NSObject>
@optional
// 数据加载状态 根据状态进行播放或其他操作
- (void)MediaPlayer:(MediaPlyerManager *)playerManager playerItemStatus:(MediaLoadStatus)status;
// 缓冲进度
- (void)MediaPlayer:(MediaPlyerManager *)playerManager netBufferValue:(CGFloat)value;
// 缓冲是否足够播放
- (void)MediaPlayer:(MediaPlyerManager *)playerManager bufferHasEnough:(BOOL)enough;
// 当前播放的时间
- (void)MediaPlayer:(MediaPlyerManager *)playerManager currentPlayTime:(NSString *)time currentPlayTimeValue:(CGFloat)value;
// 播放总时间
- (void)MediaPlayer:(MediaPlyerManager *)playerManager mediaEndTime:(NSString *)time mediaEndTimeValue:(CGFloat)value;
// 播放结束
- (void)MediaPlayerCurrentMediaPlayFinish:(MediaPlyerManager *)playerManager;
// 播放状态
- (void)MediaPlayer:(MediaPlyerManager *)playerManager playeStatus:(MediaPlayStatus)status;
// 获取数据切换时获取正在播放的URL和当前的index
- (void)MediaPlayer:(MediaPlyerManager *)playerManager currentUrl:(NSString *)url currentIndex:(NSInteger)index;
// 为了配合手机后台播放 实时获取播放的进度,总的时间,当前的index<通过index获取图片等信息>
- (void)MediaPlayer:(MediaPlyerManager *)playerManager currentProgressValue:(CGFloat)value totalValue:(CGFloat)totalValue currentIndex:(NSInteger)index;
@end

typedef MediaPlyerManager *(^playerCurrentTime)(NSString *time);

@interface MediaPlyerManager : NSObject
@property (nonatomic, strong, readonly) AVPlayer                  *mediaPlayer;          ///< 播放器
@property (nonatomic, strong, readonly) AVPlayerItem              *meidaPlayerItem;      ///< 播放器的CurrentItem
@property (nonatomic, strong, readonly) NSMutableArray<NSString*> *dataUrlArray;         ///< 正在播放的列表数据
@property (nonatomic, assign, readonly) MediaPlayType              playType;             ///< 当前播放类型
@property (nonatomic, assign, readonly) MediaPlayStatus            playStatus;           ///< 当前播放状态
@property (nonatomic, assign, readonly) NSInteger                  currentIndex;         ///< 当前播放的索引
@property (nonatomic, assign, readonly) BOOL                       isPlaying;            ///< 是否在播放
@property (nonatomic, assign, readonly) CGFloat                    curentPlayTimeValue;  ///< 当前播放时间值
@property (nonatomic, copy, readonly) NSString                    *curentPlayTime;       ///< 当前播放时间
@property (nonatomic, assign, readonly) CGFloat                    endPlayTimeValue;     ///< 当前播放时间值
@property (nonatomic, copy, readonly) NSString                    *endPlayTime;          ///< 当前播放时间
+ (instancetype)defaultManager;

/**
 列表播放 ⚠️<默认不自动播放>

 @param urls 文件路径数组
 @param delegate 回调代理
 @return MediaPlyerManager
 */
- (MediaPlyerManager *)playerWithUrls:(NSArray<NSString *> *)urls actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate;

/**
 单个音视频播放 ⚠️<默认不自动播放>

 @param url 文件路径
 @param delegate 回调代理
 @return MediaPlyerManager
 */
- (MediaPlyerManager *)playerWithUrl:(NSString *)url actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate;

/**
    开始播放
 */
- (void)play;

/**
    暂停播放
 */
- (void)pause;

/**
    停止播放
 */
- (void)stop;

/**
    下一曲
 */
- (void)next;

/**
    上一曲
 */
- (void)previous;

/**
 指定进度开始播放

 @param progress 进度百分比
 */
- (void)setupPlayerSeekToProgress:(CGFloat)progress;

/**
 制定播放类型

 @param type 类型
 */
- (void)setupMediaPlayerType:(MediaPlayType)type;

/**
 指定播放的index

 @param index 索引
 */
- (void)setupPlayerIndex:(NSInteger)index;

/**
 添加数据

 @param files 文件数组
 @param index 索引
 */
- (void)insertMediaFile:(NSArray<NSString *> *)files atIndex:(NSInteger)index;

/**
    移除全部数据
 */
- (void)removeAllFiles;

/**
 移除索引中的单个数据

 @param index 索引
 */
- (void)removeObjectAtIndex:(NSInteger)index;

/**
 设置锁屏样式

 @param coverImage 专辑图片
 @param size 显示大小
 @param title 标题
 @param author 专辑作者
 @param album 专辑名称
 @param currentTime 当前播放时间
 @param duration 播放总时长
 */
- (void)setupLockScreenPlayInfo:(UIImage *)coverImage
                      imageSize:(CGSize)size
                          title:(NSString *)title
                         ahthor:(NSString *)author
                         album:(NSString *)album
                currentPlayTime:(CGFloat)currentTime
                       duration:(CGFloat)duration;
@end

NS_ASSUME_NONNULL_END

代码实现文件

#import "MediaPlyerManager.h"
#import <MediaPlayer/MediaPlayer.h>

@interface MediaPlyerManager ()
@property (nonatomic, strong, readwrite) NSMutableArray<NSString *> *dataUrlArray;
@property (nonatomic, strong, readwrite) AVPlayer                  *mediaPlayer;
@property (nonatomic, assign, readwrite) BOOL                      isPlaying;
@property (nonatomic, strong, readwrite) AVPlayerItem              *meidaPlayerItem;
@property (nonatomic, assign, readwrite) MediaPlayType             playeType;
@property (nonatomic, assign, readwrite) NSInteger                 currentIndex;
@property (nonatomic, assign, readwrite) MediaPlayStatus           playStatus;
@property (nonatomic, assign, readwrite) CGFloat                   curentPlayTimeValue;
@property (nonatomic, copy, readwrite  ) NSString                  *curentPlayTime;
@property (nonatomic, assign, readwrite) CGFloat                   endPlayTimeValue;
@property (nonatomic, copy, readwrite  ) NSString                  *endPlayTime;
@property (nonatomic, weak             ) id <MediaPlyerManagerDelegate> delegate;
@end

@implementation MediaPlyerManager

+ (instancetype)defaultManager {
    static dispatch_once_t onceToken;
    static MediaPlyerManager *manger;
    dispatch_once(&onceToken, ^{
        manger = [[MediaPlyerManager alloc] init];
    });
    return manger;
}

#pragma mark - 初始化

- (MediaPlyerManager *)playerWithUrl:(NSString *)url actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate {
    [self playerWithUrls:@[url] actionWithDelegate:delegate];
    return self;
}

- (MediaPlyerManager *)playerWithUrls:(NSArray<NSString *> *)urls actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate {
    self.delegate         = delegate;
    self.currentIndex     = 0;
    self.dataUrlArray     = [NSMutableArray array];
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:urls.count];
    for (NSString *urlStr in urls) {
        [array addObject:[self createPlayerItemWithUrl:urlStr]];
        [self.dataUrlArray addObject:urlStr];
    }
    self.playeType = MediaPlayTypeCycle;
    self.mediaPlayer = [[AVPlayer alloc] initWithPlayerItem:array.firstObject];
    self.meidaPlayerItem = self.mediaPlayer.currentItem;
    [self getCurrentIndex:self.currentIndex];
    [self addObserver];
    
    __weak typeof(self) weakself = self;
    [self.mediaPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
        NSString *currentString = [weakself getStringFromCMTime:time];
        weakself.curentPlayTime = currentString;
        weakself.curentPlayTimeValue = (CGFloat)time.value/time.timescale;        
#pragma mark - 获取当前播放时间
        if (weakself.delegate && [weakself.delegate respondsToSelector:@selector(MediaPlayer:currentPlayTime:currentPlayTimeValue:)]) {
            [weakself.delegate MediaPlayer:weakself currentPlayTime:currentString currentPlayTimeValue:(CGFloat)time.value/time.timescale];
        }
#pragma mark - 实时获取播放信息
        if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:currentProgressValue:totalValue:currentIndex:)]) {
            [weakself.delegate MediaPlayer:weakself currentProgressValue:weakself.curentPlayTimeValue totalValue:weakself.endPlayTimeValue currentIndex:weakself.currentIndex];
        }
    }];
    
    return self;
}

#pragma mark - 播放结束

- (void)playFinish:(NSNotification *)notification {
    if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayerCurrentMediaPlayFinish:)]) {
        [self.delegate MediaPlayerCurrentMediaPlayFinish:self];
    }

    if (self.playeType == MediaPlayTypeSingle) {
        [self.mediaPlayer seekToTime:kCMTimeZero];
        [self play];
    } else {
        if (self.currentIndex < self.dataUrlArray.count - 1) {
            self.currentIndex += 1;
        } else {
            self.currentIndex = 0;
        }
        [self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];
        self.meidaPlayerItem = self.mediaPlayer.currentItem;
        [self getCurrentIndex:self.currentIndex];
        [self play];
    }
}

#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    
    AVPlayerItem *playerItem = object;
    if ([keyPath isEqualToString:@"status"]) {
        MediaLoadStatus status = [change[@"new"] integerValue];
#pragma mark - 获取媒体加载状态
        if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:playerItemStatus:)]) {
            [self.delegate MediaPlayer:self playerItemStatus:status];
        }
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        NSArray * timeRanges         = playerItem.loadedTimeRanges;
        CMTimeRange timeRange        = [timeRanges.firstObject CMTimeRangeValue];
        NSTimeInterval totalLoadTime = CMTimeGetSeconds(timeRange.start) \
                                                + CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval duration      = CMTimeGetSeconds(playerItem.duration);
        NSTimeInterval scale         = totalLoadTime/duration;
        
#pragma mark - 获取媒体总时间
        if ((CGFloat)duration/scale >= 0) {
            self.endPlayTime = [self getStringFromCMTime:playerItem.duration];
            self.endPlayTimeValue = (CGFloat)duration/scale;
            if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:mediaEndTime:mediaEndTimeValue:)]) {
                [self.delegate MediaPlayer:self mediaEndTime:[self getStringFromCMTime:playerItem.duration] mediaEndTimeValue:(CGFloat)duration/scale];
            }
        }
        
#pragma mark - 缓冲百分比
        if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:netBufferValue:)]) {
            [self.delegate MediaPlayer:self netBufferValue:scale];
        }
    } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {
        
#pragma mark - 缓冲不足够播放
        if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:bufferHasEnough:)]) {
            [self.delegate MediaPlayer:self bufferHasEnough:false];
        }
    } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
        
#pragma mark - 缓冲足够播放
        if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:bufferHasEnough:)]) {
            [self.delegate MediaPlayer:self bufferHasEnough:true];
        }
    }
}


#pragma mark - 开始播放

- (void)play {
    [self.mediaPlayer play];
    [self getPlayStatus:MediaPlayStatusPlaying];
}

#pragma mark - 暂停播放

- (void)pause {
    [self.mediaPlayer pause];
    [self getPlayStatus:MediaPlayStatusPause];
}

#pragma mark - 停止播放

- (void)stop {
    [self.mediaPlayer replaceCurrentItemWithPlayerItem:nil];
    [self getPlayStatus:MediaPlayStatusStop];
    [self removeObserver];
}

#pragma mark - 下一个

- (void)next {
    if (self.playeType == MediaPlayTypeRandom) {
        self.currentIndex = (NSInteger)arc4random_uniform((int32_t)(self.dataUrlArray.count - 1));
    } else {
        if (self.currentIndex == self.dataUrlArray.count - 1) {
            self.currentIndex = 0;
        } else {
            self.currentIndex += 1;
        }
    }
    [self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];
    self.meidaPlayerItem = self.mediaPlayer.currentItem;
    [self getCurrentIndex:self.currentIndex];
    [self addObserver];
}

#pragma mark - 上一个

- (void)previous {
    if (self.playeType == MediaPlayTypeRandom) {
        self.currentIndex = (NSInteger)arc4random_uniform((int32_t)(self.dataUrlArray.count - 1));
    } else {
        if (self.currentIndex == 0) {
            self.currentIndex = self.dataUrlArray.count - 1;
        } else {
            self.currentIndex -= 1;
        }
    }
    [self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];
    self.meidaPlayerItem = self.mediaPlayer.currentItem;
    [self getCurrentIndex:self.currentIndex];
    [self addObserver];
}

#pragma mark - 播放状态

- (void)getPlayStatus:(MediaPlayStatus)status {
    if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:playeStatus:)]) {
        [self.delegate MediaPlayer:self playeStatus:status];
    }
    self.playStatus = status;

    if (status == MediaPlayStatusPlaying) {
        self.isPlaying = true;
    } else {
        self.isPlaying = false;
    }
}

#pragma mark - 根据index进行回调

- (void)getCurrentIndex:(NSInteger)index {
    if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:currentUrl:currentIndex:)]) {
        [self.delegate MediaPlayer:self currentUrl:self.dataUrlArray[index] currentIndex:index];
    }
}

#pragma mark - 设置播放进度百分比

- (void)setupPlayerSeekToProgress:(CGFloat)progress {
    float timeValue = progress * CMTimeGetSeconds(self.mediaPlayer.currentItem.duration);
    [self.mediaPlayer seekToTime:CMTimeMake(timeValue, 1)];
}

#pragma mark - 设置播放形式

- (void)setupMediaPlayerType:(MediaPlayType)type {
    self.playeType = type;
}

#pragma mark - 播放指定index的媒体

- (void)setupPlayerIndex:(NSInteger)index {
    if (index > (self.dataUrlArray.count - 1)) {
        @throw [NSException exceptionWithName:@"越界错误" reason:@"index 不能超出URL数组的长度" userInfo:nil];
        return;
    }
    self.currentIndex = index;
    [self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];
    self.meidaPlayerItem = self.mediaPlayer.currentItem;
    [self getCurrentIndex:self.currentIndex];
}

#pragma mark - 插入数据

- (void)insertMediaFile:(NSArray<NSString *> *)files atIndex:(NSInteger)index {
    for (NSString *urlStr in files) {
        NSInteger i = [files indexOfObject:urlStr];
        [self.dataUrlArray insertObject:urlStr atIndex:index + i];
    }
    if (index < self.currentIndex) {
        self.currentIndex += 1;
    }
}

#pragma mark - 删除数据

- (void)removeAllFiles {
    [self stop];
    [self.dataUrlArray removeAllObjects];
    self.dataUrlArray = [NSMutableArray array];
    self.currentIndex = 0;
}

- (void)removeObjectAtIndex:(NSInteger)index {
    if (self.dataUrlArray.count == 1) {
        [self removeAllFiles];
    } else {
        [self.dataUrlArray removeObjectAtIndex:index];
        if (index == self.currentIndex) {
            if (index == 0) {
                self.currentIndex = 0;
                [self next];
            } else {
                self.currentIndex -= 1;
            }
        } else {
            if (self.currentIndex > index) {
                self.currentIndex -= 1;
            } 
        }
    }
}

#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:@"%02ld:%02ld:%02ld", (long)components.hour, (long)components.minute, (long)components.second];
    } else {
        return [NSString stringWithFormat:@"%02ld:%02ld", (long)components.minute, (long)components.second];
    }
}

- (void)addObserver {
    // 监控状态属性
    [self.meidaPlayerItem addObserver:self
                           forKeyPath:@"status"
                              options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew)
                              context:nil];
    
    // 监控缓冲加载情况属性
    [self.meidaPlayerItem addObserver:self
                           forKeyPath:@"loadedTimeRanges"
                              options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew)
                              context:nil];
    
    // 监听缓冲不足够播放
    [self.meidaPlayerItem addObserver:self
                           forKeyPath:@"playbackBufferEmpty"
                              options:NSKeyValueObservingOptionNew
                              context:nil];
    
    // 监听缓冲足够播放
    [self.meidaPlayerItem addObserver:self
                           forKeyPath:@"playbackLikelyToKeepUp"
                              options:NSKeyValueObservingOptionNew
                              context:nil];
    
    // 获取是否播放结束
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playFinish:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:self.meidaPlayerItem];
}

- (void)removeObserver {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    @try {
        [self.meidaPlayerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
        [self.meidaPlayerItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
        [self.meidaPlayerItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
        [self.meidaPlayerItem removeObserver:self forKeyPath:@"status"];
    }
    @catch(NSException *exception) {
        NSLog(@"%@", exception);
    } 
}

#pragma mark - 设置锁屏样式

- (void)setupLockScreenPlayInfo:(UIImage *)coverImage
                      imageSize:(CGSize)size
                          title:(NSString *)title
                         ahthor:(NSString *)author
                          album:(NSString *)album
                currentPlayTime:(CGFloat)currentTime
                       duration:(CGFloat)duration {
    
    Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
    if (playingInfoCenter) {
        NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithBoundsSize:size requestHandler:^UIImage * _Nonnull(CGSize size) {
            return coverImage;
        }];
        [songInfo setObject:title forKey:MPMediaItemPropertyTitle];
        [songInfo setObject:author forKey:MPMediaItemPropertyArtist];
        [songInfo setObject:album forKey:MPMediaItemPropertyAlbumTitle];
        [songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
        [songInfo setObject:[NSNumber numberWithDouble:currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
        [songInfo setObject:[NSNumber numberWithDouble:duration] forKey:MPMediaItemPropertyPlaybackDuration];
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
    }
}

- (AVPlayerItem *)createPlayerItemWithUrl:(NSString *)url {
    return [AVPlayerItem playerItemWithURL:[NSURL URLWithString:url]];
}

@end

提问

本来相同使用AVQueuePlayer来进行列表播放的,但是当做单曲循环的时候遇到问题:通过通知监听播放完成,在通知的方法里进行具体操作,但是设置无效,直接播放的还是下一个文件。如果有人知道如何解决,帮忙回复一下。

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

推荐阅读更多精彩内容

  • 要是世界上 有一个词语来描述 我跟你的距离 那大概就是温柔了吧 夜晚的风没了白天的焦躁与不安 多了月光的柔情 你是...
    牛奶喔喔糖阅读 118评论 1 1
  • 把你存在过去, 我怕有天我走着走着就会弄丢你。 可是, 我还是弄丢了你。 呵呵! 我的奶奶, 我把你弄丢在那片池塘...
    lily空间设计阅读 219评论 0 4
  • 我们争吵 因为你喜欢杜康 人们说酒是美的 而你说酒是圆的 你的谎言和人们的谎言 交织在黑暗的碰杯声中 最后你赤祼的...
    老韩0201阅读 462评论 4 24
  • 人总是要有点遗憾的 比如说不停补课的夏天 比如说有酒没故事的旧人 比如说计划会去的城市 比如说还没机会开口的勇气 ...
    _任我行阅读 393评论 0 2
  • 拜访金堂隆盛分会 青山绿水好气象 周氏宗亲聚一堂 推心置腹为发展 树立正气显榜样 祠堂修缮德无量 民风纯朴满家乡 ...
    琮淯阅读 609评论 4 9