AVPlayer视频播放

1,555 阅读6分钟

哈喽,我是二西,今天主要来记录AVPlayer播放视频,Emmm包括一些坑,Emmm顺便放上最近喜欢的一首歌哈哈哈
错位时空 music.163.com/song?id=180…

简单初始化

AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:url]];
_player = [[AVPlayer alloc] initWithPlayerItem:item];
//放置播放器的视图
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
_playerLayer.frame = _videoView.bounds;
[_videoView.layer addSublayer:_playerLayer];

//自定义view转屏自动布局
//[((AVPlayerLayer *)_playerView.layer) setPlayer:_player];

_player.automaticallyWaitsToMinimizeStalling = NO;//坑坑坑,要加不然会卡主第一帧很久才播放

坑1:记得加automaticallyWaitsToMinimizeStalling=NO; 不然某些视频会卡在第一帧很久才会自动播放。

坑2:如需转屏的时候自动布局,需自定义一个view,让view的layer类返回AVPlayerLayer类,不然有时候转屏frame不会改变或者错误,布局会乱

@interface DQPlayerLayerView : UIView
@end

#import <AVKit/AVKit.h>
@implementation DQPlayerLayerView
+ (Class)layerClass {
    return [AVPlayerLayer class];
}

KVO检测一些状态属性

由于我引用了FBKVOController,所以一些监听回调和系统不一样,自行修改,如未引用记得释放监听

状态status

[self.KVOController observe:item keyPath:@"status" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
    __strong typeof(weakSelf) strongSelf = weakSelf;

    AVPlayerItem *item = (AVPlayerItem *)object;
    if (item.status == AVPlayerItemStatusReadyToPlay) {
        strongSelf.isFail = NO;
        
        //开始播放
        [strongSelf.loadView stopAnimating];
        [strongSelf.player play];

        //进度条时间
        CMTime duration = item.duration;
        [strongSelf setEndTime:CMTimeGetSeconds(duration)];
    }else if(item.status == AVPlayerItemStatusFailed){
        strongSelf.isFail = YES;
        
        //视频加载失败
        [strongSelf.loadView stopAnimating];
    }
}];

缓冲loadedTimeRanges

[self.KVOController observe:item keyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
    __strong typeof(weakSelf) strongSelf = weakSelf;

    NSArray *loadedTimeRanges = [[strongSelf.player currentItem] loadedTimeRanges];
    CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
    float startSeconds = CMTimeGetSeconds(timeRange.start);
    float durationSeconds = CMTimeGetSeconds(timeRange.duration);
    NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度

    NSTimeInterval current = strongSelf.player.currentTime.value/strongSelf.player.currentTime.timescale;//当前播放进度

    if (strongSelf.isPlay && (result>current+2 || result==CMTimeGetSeconds(strongSelf.player.currentItem.duration))) {
        if (strongSelf.isBuffer) {
            strongSelf.isBuffer = NO;

            [strongSelf.loadView stopAnimating];
            [strongSelf.player play];
        }

    }
}];
//缓冲
[self.KVOController observe:item keyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
    __strong typeof(weakSelf) strongSelf = weakSelf;

    if (strongSelf.isPlay && !strongSelf.isBuffer) {
        strongSelf.isBuffer = YES;

        [strongSelf.loadView startAnimating];
        [strongSelf.player pause];
    }
}];

加载进度

// 每秒回调一次,更新进度条
_playTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
    __strong typeof(weakSelf) strongSelf = weakSelf;

    NSTimeInterval totalTime = CMTimeGetSeconds(strongSelf.player.currentItem.duration);//总时长
    NSTimeInterval currentTime = time.value / time.timescale;//当前时间进度
    strongSelf.slider.value = currentTime / totalTime;
    [strongSelf setStartTime:currentTime];
}];

- (void)dealloc {
    [_player removeTimeObserver:_playTimeObserver];
}

播放结束通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinishNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:item];

- (void)playFinishNotification:(NSNotification *)noti {
    [self stop];
}

操作

#pragma mark - 停止
- (void)stop {
    _isPlay = NO;

    if (_player) {
        [_player.currentItem cancelPendingSeeks];
        [_player.currentItem.asset cancelLoading];
        [_player pause];
        [_player.currentItem seekToTime:kCMTimeZero];
       
        [_loadView stopAnimating];
        [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
    }
}
#pragma mark - 继续
- (void)continuePlay {
    if (!_isPlay) {
        [_player play];
        _isPlay = YES;
        [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpzt"] forState:UIControlStateNormal];
    }
}
#pragma mark - 暂停
- (void)pausePlay {
    if (_isPlay) {
        [_player pause];
        _isPlay = NO;
        [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
    }
}
#

静音

#pragma mark - 静音
- (void)onMute {
    _player.muted = YES;
}
- (void)offMute {
    _player.muted = NO;
}

进度条拖拽播放

#pragma mark - 拖拽播放
_slider = [[UISlider alloc] init];
//进度条图片
[_slider setMinimumTrackImage:[UIImage imageNamed:@"app_img_jdt_2"] forState:UIControlStateNormal];
[_slider setMaximumTrackImage:[UIImage imageNamed:@"app_img_jdt_1"] forState:UIControlStateNormal];
//进度条小圆点
[_slider setThumbImage:[UIImage imageNamed:@"app_img_hk"] forState:UIControlStateNormal];
[_slider setThumbImage:[UIImage imageNamed:@"app_img_hk"] forState:UIControlStateHighlighted];
//拖拽事件
[_slider addTarget:self action:@selector(sliderTouchBeginAction) forControlEvents:UIControlEventTouchDown];
[_slider addTarget:self action:@selector(sliderChangeAction) forControlEvents:UIControlEventValueChanged];
[_slider addTarget:self action:@selector(sliderTouchEndAction) forControlEvents:UIControlEventTouchUpInside];
    

- (void)sliderTouchBeginAction {
    [_player pause];
    [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
}
- (void)sliderChangeAction {
    NSTimeInterval currentTime = _slider.value * CMTimeGetSeconds(_player.currentItem.duration);
    [self setStartTime:currentTime];
}
- (void)sliderTouchEndAction {
    NSTimeInterval changedTime = _slider.value * CMTimeGetSeconds(_player.currentItem.duration);
    if (changedTime == CMTimeGetSeconds(_player.currentItem.duration)) {
        changedTime -= 0.5;
    }
    [_player.currentItem seekToTime:CMTimeMakeWithSeconds(changedTime,NSEC_PER_SEC)];
    
    [_player play];
    [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpzt"] forState:UIControlStateNormal];
}

最后

最后贴个全代码,看看就好,根据需求来~

@interface DQPlayerView : UIView
@property (nonatomic, strong) NSString *playImageUrl;//封面
@property (nonatomic, strong) NSString *playTitle;//标题
@property (nonatomic, assign) BOOL isPlay;

@property (nonatomic, copy) dispatch_block_t closeBlock;
@property (nonatomic, copy) dispatch_block_t endBlock;

- (void)playWithUrl:(NSString *)url;
- (void)continuePlay;
- (void)pausePlay;
- (void)operaAction;

- (void)showToolView;
- (void)hideToolView;

- (void)onMute;
- (void)offMute;
#import "DQPlayerView.h"
#import <AVKit/AVKit.h>
#import "NSObject+FBKVOController.h"
#import "PublicAction.h"
#import "Masonry.h"
#import "DQPlayerLayerView.h"

@interface DQPlayerView()
@property (nonatomic, strong) UIImageView *playImageV;
@property (nonatomic, strong) DQPlayerLayerView *playerView;
@property (nonatomic, strong) UIActivityIndicatorView *loadView;
@property (nonatomic, strong) UIButton *playBtn;
@property (nonatomic, strong) UIView *topView;
@property (nonatomic, strong) UILabel *titleL;
@property (nonatomic, strong) UIView *toolView;
@property (nonatomic, strong) UIButton *operaBtn;
@property (nonatomic, strong) UILabel *startL;
@property (nonatomic, strong) UISlider *slider;
@property (nonatomic, strong) UILabel *endL;

@property (nonatomic, strong) NSString *url;
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerItem *playItem;
@property (nonatomic, strong) id playTimeObserver;

@property (nonatomic, assign) BOOL isFail;
@property (nonatomic, assign) BOOL isBuffer;

@end

@implementation DQPlayerView
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setupView];
    }
    return self;
}
- (void)layoutSubviews {
    UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
    if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) {
        if (inch_X) {
            [_topView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.top.mas_equalTo(20);
            }];
        }
    }else{
        if (inch_X) {
            [_topView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.top.mas_equalTo(NavigationBarHeight-44);
            }];
        }
    }
}
- (void)dealloc {
    [_player removeTimeObserver:_playTimeObserver];
}
- (void)setupView {
    self.backgroundColor = rgb(0, 0, 0);
    
    UIView *tapView = [[UIView alloc] init];
    [PublicAction addTapGesture:tapView number:1 Target:self Sel:@selector(tapAction)];
    
    //顶部栏
    _topView = [[UIView alloc] init];
    
    UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [backBtn setImage:[UIImage imageNamed:@"app_icon_fh_bs"] forState:UIControlStateNormal];
    [backBtn addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
    [backBtn setLargeEdge:20];
    
    _titleL = [[UILabel alloc] init];
    _titleL.font = [UIFont boldSystemFontOfSize:17];
    _titleL.textColor = kWhiteColor;
    
    //播放页
    _playerView = [[DQPlayerLayerView alloc] init];
    //封面图
    _playImageV = [[UIImageView alloc] init];
    //加载
    _loadView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
    //播放按钮
    _playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [_playBtn setImage:[UIImage imageNamed:@"app_icon_bf"] forState:UIControlStateNormal];
    [_playBtn addTarget:self action:@selector(operaAction) forControlEvents:UIControlEventTouchUpInside];
    _playBtn.hidden = YES;
    
    //工具栏
    _toolView = [[UIView alloc] init];
    
    _operaBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
    [_operaBtn addTarget:self action:@selector(operaAction) forControlEvents:UIControlEventTouchUpInside];
    [_operaBtn setLargeEdge:20];
    
    _startL = [[UILabel alloc] init];
    _startL.font = [UIFont fontWithName:numFontBoldName size:15];
    _startL.textColor = kWhiteColor;
    _startL.textAlignment = NSTextAlignmentCenter;
    _startL.text = @"00:00";
    
    _slider = [[UISlider alloc] init];
    [_slider setMinimumTrackImage:[UIImage imageNamed:@"app_img_jdt_2"] forState:UIControlStateNormal];
    [_slider setMaximumTrackImage:[UIImage imageNamed:@"app_img_jdt_1"] forState:UIControlStateNormal];
    [_slider setThumbImage:[UIImage imageNamed:@"app_img_hk"] forState:UIControlStateNormal];
    [_slider setThumbImage:[UIImage imageNamed:@"app_img_hk"] forState:UIControlStateHighlighted];
    [_slider addTarget:self action:@selector(sliderTouchBeginAction) forControlEvents:UIControlEventTouchDown];
    [_slider addTarget:self action:@selector(sliderChangeAction) forControlEvents:UIControlEventValueChanged];
    [_slider addTarget:self action:@selector(sliderTouchEndAction) forControlEvents:UIControlEventTouchUpInside];
    
    _endL = [[UILabel alloc] init];
    _endL.font = [UIFont fontWithName:numFontBoldName size:15];
    _endL.textColor = kWhiteColor;
    _endL.textAlignment = NSTextAlignmentCenter;
    _endL.text = @"00:00";
    
    UIButton *rotateBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [rotateBtn setImage:[UIImage imageNamed:@"app_icon_hspqh"] forState:UIControlStateNormal];
    [rotateBtn addTarget:self action:@selector(rotateAction) forControlEvents:UIControlEventTouchUpInside];
    [rotateBtn setLargeEdge:20];
    
    [self addSubview:_playerView];
    [_playerView addSubview:_playImageV];
    [_playerView addSubview:_loadView];
    [self addSubview:tapView];
    [tapView addSubview:_playBtn];
    [self addSubview:_topView];
    [_topView addSubview:backBtn];
    [_topView addSubview:_titleL];
    [self addSubview:_toolView];
    [_toolView addSubview:_operaBtn];
    [_toolView addSubview:_startL];
    [_toolView addSubview:_slider];
    [_toolView addSubview:_endL];
    [_toolView addSubview:rotateBtn];
    
    [_playerView mas_makeConstraints:^(MASConstraintMaker *make) {
        if(@available(iOS 11.0, *)) {
            //横屏状态
            make.left.mas_equalTo(self.mas_safeAreaLayoutGuideLeft);
            make.right.mas_equalTo(self.mas_safeAreaLayoutGuideRight);
        }else{
            make.left.right.mas_equalTo(0);
        }
        make.center.mas_equalTo(0);
        make.height.mas_equalTo(_playerView.mas_width).multipliedBy(9/16.0);
    }];
    [_playImageV mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(0);
    }];
    [_loadView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(_playerView);
    }];
    [_playBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(_playerView);
    }];
    [tapView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(0);
    }];
    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        if(@available(iOS 11.0, *)) {
            //横屏状态
            make.left.mas_equalTo(self.mas_safeAreaLayoutGuideLeft).mas_offset(12);
            make.right.mas_equalTo(self.mas_safeAreaLayoutGuideRight).mas_offset(-16);
        }else{
            make.left.mas_equalTo(12);
            make.right.mas_equalTo(-16);
        }
        make.height.mas_equalTo(44);
        if (inch_X) {
            make.top.mas_equalTo(NavigationBarHeight-44);
        }else{
            make.top.mas_equalTo(20);
        }
    }];
    [backBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(0);
        make.centerY.mas_equalTo(0);
        make.size.mas_equalTo(CGSizeMake(24, 24));
    }];
    [_titleL mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(backBtn.mas_right).mas_offset(2);
        make.centerY.mas_equalTo(backBtn);
        make.right.mas_equalTo(-100);
    }];
    [_toolView mas_makeConstraints:^(MASConstraintMaker *make) {
        if(@available(iOS 11.0, *)) {
            //横屏状态
            make.left.mas_equalTo(self.mas_safeAreaLayoutGuideLeft).mas_offset(16);
            make.right.mas_equalTo(self.mas_safeAreaLayoutGuideRight).mas_offset(-16);
        }else{
            make.left.mas_equalTo(16);
            make.right.mas_equalTo(-16);
        }
        make.height.mas_equalTo(30);
        make.bottom.mas_equalTo(-30);
    }];
    [_operaBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(0);
        make.centerY.mas_equalTo(0);
        make.size.mas_equalTo(CGSizeMake(22, 22));
    }];
    [_startL mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(_operaBtn.mas_right).mas_offset(10);
        make.centerY.mas_equalTo(0);
        make.width.mas_equalTo(36);
    }];
    [_slider mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(_startL.mas_right).mas_offset(10);
        make.centerY.mas_equalTo(0);
    }];
    [_endL mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(_slider.mas_right).mas_offset(10);
        make.centerY.mas_equalTo(0);
        make.width.mas_equalTo(36);
    }];
    [rotateBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(_endL.mas_right).mas_offset(10);
        make.right.mas_equalTo(0);
        make.centerY.mas_equalTo(0);
        make.size.mas_equalTo(CGSizeMake(22, 22));
    }];
}
#pragma mark - 关闭
- (void)backAction {
    if (self.closeBlock) {
        self.closeBlock();
    }
}
#pragma mark - 显示隐藏栏目
- (void)tapAction {
    _topView.hidden = !_topView.hidden;
    _toolView.hidden = !_toolView.hidden;
}
- (void)showToolView {
    _topView.hidden = NO;
    _toolView.hidden = NO;
}
- (void)hideToolView {
    _topView.hidden = YES;
    _toolView.hidden = YES;
}
#pragma mark - 拖拽播放
- (void)sliderTouchBeginAction {
    [_player pause];
    [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
}
- (void)sliderChangeAction {
    NSTimeInterval currentTime = _slider.value * CMTimeGetSeconds(_player.currentItem.duration);
    [self setStartTime:currentTime];
}
- (void)sliderTouchEndAction {
    NSTimeInterval changedTime = _slider.value * CMTimeGetSeconds(_player.currentItem.duration);
    if (changedTime == CMTimeGetSeconds(_player.currentItem.duration)) {
        changedTime -= 0.5;
    }
    [_player.currentItem seekToTime:CMTimeMakeWithSeconds(changedTime,NSEC_PER_SEC)];
    
    _playImageV.hidden = YES;
    [_player play];
    [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpzt"] forState:UIControlStateNormal];
    _playBtn.hidden = YES;
}
#pragma mark - 旋转
- (void)rotateAction {
    //强制转屏
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector  = NSSelectorFromString([NSString stringWithFormat:@"%@%@",@"setOrien",@"tation:"]);
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val;
        UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
        if (orientation == UIDeviceOrientationPortrait) {
            val = UIInterfaceOrientationLandscapeRight;
        }else{
            val = UIInterfaceOrientationPortrait;
        }
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}
#pragma mark - 标题
- (void)setPlayTitle:(NSString *)playTitle {
    _playTitle = playTitle;
    _titleL.text = playTitle;
}
- (void)setPlayImageUrl:(NSString *)playImageUrl {
    [PublicAction setImageView:_playImageV withImage:playImageUrl placeHolderImage:placeHold208];
}
#pragma mark - 播放
- (void)playWithUrl:(NSString *)url {
    _playImageV.hidden = NO;
    [_loadView startAnimating];
    
    _url = url;
    AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:url]];
    if (_player) {
        [_player replaceCurrentItemWithPlayerItem:item];
    }else{
        _player = [[AVPlayer alloc] initWithPlayerItem:item];
        [((AVPlayerLayer *)_playerView.layer) setPlayer:_player];
        _player.muted = YES;//默认静音
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinishNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:item];
    }
    if (@available(iOS 10.0, *)) {
        _player.automaticallyWaitsToMinimizeStalling = NO;//坑坑坑,要加不然会卡主第一帧很久才播放
    }
    
    __weak typeof(self) weakSelf = self;
    //状态
    [self.KVOController observe:item keyPath:@"status" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        
        AVPlayerItem *item = (AVPlayerItem *)object;
        if (item.status == AVPlayerItemStatusReadyToPlay) {
            strongSelf.isFail = NO;
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [strongSelf.loadView stopAnimating];
                strongSelf.playImageV.hidden = YES;
                
                if ([BaseNetWork networkType]==ReachableViaWiFi) {
                    [strongSelf continuePlay];
                }
            });
            
            CMTime duration = item.duration;
            [strongSelf setEndTime:CMTimeGetSeconds(duration)];
        }else if(item.status == AVPlayerItemStatusFailed){
            strongSelf.isFail = YES;
            [strongSelf.loadView stopAnimating];
            
            [PublicAction showHudOnlyText:@"视频加载失败" view:firstView hideDelay:hudDurTime];
            if (strongSelf.endBlock) {
                strongSelf.endBlock();
            }
        }
    }];
    //缓冲
    [self.KVOController observe:item keyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        __strong typeof(weakSelf) strongSelf = weakSelf;

        NSArray *loadedTimeRanges = [[strongSelf.player currentItem] loadedTimeRanges];
        CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度

        NSTimeInterval current = strongSelf.player.currentTime.value/strongSelf.player.currentTime.timescale;//当前播放进度

        if (strongSelf.isPlay && (result>current+2 || result==CMTimeGetSeconds(strongSelf.player.currentItem.duration))) {
           
            if (strongSelf.isBuffer) {
                strongSelf.isBuffer = NO;
                
                [strongSelf.loadView stopAnimating];
                [strongSelf.player play];
            }
               
        }
    }];
    //缓冲
    [self.KVOController observe:item keyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        
        if (strongSelf.isPlay && !strongSelf.isBuffer) {
            strongSelf.isBuffer = YES;
            
            [strongSelf.loadView startAnimating];
            [strongSelf.player pause];
        }
    }];
    
    // 每秒回调一次
    _playTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        
        NSTimeInterval totalTime = CMTimeGetSeconds(strongSelf.player.currentItem.duration);//总时长
        NSTimeInterval currentTime = time.value / time.timescale;//当前时间进度
        strongSelf.slider.value = currentTime / totalTime;
        [strongSelf setStartTime:currentTime];
    }];
}
#pragma mark - 设置时长
- (void)setEndTime:(CGFloat)second {
    NSString *time = [PublicAction stringFromDate:[NSDate dateWithTimeIntervalSince1970:second] form:@"mm:ss"];
    _endL.text = time;
}
- (void)setStartTime:(CGFloat)second {
    NSString *time = [PublicAction stringFromDate:[NSDate dateWithTimeIntervalSince1970:second] form:@"mm:ss"];
    _startL.text = time;
}
- (void)playFinishNotification:(NSNotification *)noti {
    [self stop];
}
#pragma mark - 停止
- (void)stop {
    _isPlay = NO;

    if (_player) {
        [_player.currentItem cancelPendingSeeks];
        [_player.currentItem.asset cancelLoading];
        [_player pause];
        [_player.currentItem seekToTime:kCMTimeZero];
       
        _playImageV.hidden = NO;
        [_loadView stopAnimating];
        [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
        _playBtn.hidden = NO;
    }
    if (self.endBlock) {
        self.endBlock();
    }
}
#pragma mark - 继续
- (void)continuePlay {
    if (!_isPlay) {
        [_player play];
        _isPlay = YES;
        [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpzt"] forState:UIControlStateNormal];
        _playBtn.hidden = YES;
    }
}
#pragma mark - 暂停
- (void)pausePlay {
    if (_isPlay) {
        [_player pause];
        _isPlay = NO;
        [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
        _playBtn.hidden = NO;
    }
}
#pragma mark - 操作
- (void)operaAction {
    if (_isPlay) {
        _playImageV.hidden = NO;
        [_loadView stopAnimating];
        [_player pause];
        _isPlay = NO;
        [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpbf"] forState:UIControlStateNormal];
        _playBtn.hidden = NO;
    }else{
        if (_isFail) {
            [self playWithUrl:_url];
        }else{
            _playImageV.hidden = YES;
            [_player play];
            _isPlay = YES;
            [_operaBtn setImage:[UIImage imageNamed:@"app_icon_qpzt"] forState:UIControlStateNormal];
            _playBtn.hidden = YES;
        }
    }
}

#pragma mark - 静音
- (void)onMute {
    _player.muted = YES;
}
- (void)offMute {
    _player.muted = NO;
}
@end