最近开发的avplayer视频播放器-未完待续

版权所有,禁止匿名转载;禁止商业使用。

1.h中的代码


#import <UIKit/UIKit.h>

#import "JZSVideoHttpTool.h"


// 播放器的几种状态

typedef NS_ENUM(NSUInteger, JZSVideoPlayerState) {

    JZSVideoPlayerStateFailed,        // 播放失败

    JZSVideoPlayerStateBuffering,     // 缓冲中

    JZSVideoPlayerStateReadyToPlay,  // 将要播放

    JZSVideoPlayerStatePlaying,       // 播放中

    JZSVideoPlayerStateStopped,       //暂停播放

    JZSVideoPlayerStateFinished       //播放完毕

};


//播放器的代理

@class JZSVideoPlayer;

@protocol JZSVideoPlayerDelegate <NSObject>


//播放失败的代理方法

-(void)JZSPlayerPlayerFailedPlay:(JZSVideoPlayer *)jzsvedioPlayer playerStatus:(JZSVideoPlayerState)state;

//准备播放的代理方法

-(void)JZSPlayerPlayerReadyToPlay:(JZSVideoPlayer *)jzsvedioPlayer playerStatus:(JZSVideoPlayerState)state;

//播放完毕的代理方法

-(void)JZSPlayerFinishedPlay:(JZSVideoPlayer *)jzsvedioPlayer;


//点击播放暂停按钮代理方法

-(void)JZSPlayerPlayer:(JZSVideoPlayer *)jzsvedioPlayer clickedPlayOrPauseButton:(UIButton *)playOrPauseBtn;

//点击关闭按钮代理方法

-(void)JZSPlayerPlayer:(JZSVideoPlayer *)jzsvedioPlayer clickedCloseButton:(UIButton *)closeBtn;

//点击全屏按钮代理方法

-(void)JZSPlayerPlayer:(JZSVideoPlayer *)jzsvedioPlayer clickedFullScreenButton:(UIButton *)fullScreenBtn;

//单击WMPlayer的代理方法

-(void)JZSPlayerPlayer:(JZSVideoPlayer *)jzsvedioPlayer singleTaped:(UITapGestureRecognizer *)singleTap;

//双击WMPlayer的代理方法

-(void)JZSPlayerPlayer:(JZSVideoPlayer *)jzsvedioPlayer doubleTaped:(UITapGestureRecognizer *)doubleTap;



@end


@interface JZSVideoPlayer : UIView

/**

 *  播放器player

 */

@property (nonatomic,strong) AVPlayer * player;

/**

 *playerLayer,可以修改frame

 */

@property (nonatomic,strong) AVPlayerLayer * playerLayer;

/**

 *  底部操作工具栏

 */

@property (nonatomic,retain ) UIView * bottomView;

/**

 *  顶部操作工具栏

 */

@property (nonatomic,retain ) UIView * topView;

/**

 *  菊花(加载框)

 */

@property (nonatomic,strong) UIActivityIndicatorView * loadingView;

/**

 *  控制全屏的按钮

 */

@property (nonatomic,strong ) UIButton * fullScreenBtn;

/**

 *  播放暂停按钮

 */

@property (nonatomic,strong) UIButton * playOrPauseBtn;

/**

 *  视频进度条的单击事件

 */

@property (nonatomic, strong) UITapGestureRecognizer * tap;

/**

 *  单击屏幕事件

 */

@property (nonatomic, strong) UITapGestureRecognizer * singleTap;

/**

 *  播放时双击屏幕事件

 */

@property (nonatomic, strong) UITapGestureRecognizer * doubleTap;

/**

 *  左上角关闭按钮

 */

@property (nonatomic,strong ) UIButton * closeBtn;

/**

 *  左边的时间

 */

@property (nonatomic,strong) UILabel * leftTimeLabel;

/**

 *  右边的时间

 */

@property (nonatomic,strong) UILabel * rightTimeLabel;

/**

 *  显示播放视频的title

 */

@property (nonatomic,strong) UILabel * titleLabel;


/**

 *  播放器状态

 */

@property (nonatomic, assign)JZSVideoPlayerState state;

/**

 *  预加载进度条

 */

@property (nonatomic,strong) UIProgressView *loadingProgress;

/**

 *  滑动进度条

 */

@property (nonatomic,strong) UISliderExt * progressSlider;


/**

 *  定时器

 */

@property (nonatomic, strong) NSTimer * autoDismissTimer;


/**

 *  判断是不是自动隐藏了top/bottom视图

 */

@property (nonatomic,assign)BOOL isAutoDismissTopBottomView;


/**

 *  BOOL值判断当前的状态

 */

@property (nonatomic,assign ) BOOL isFullscreen;


/**

日期格式

*/

@property (nonatomic, strong)NSDateFormatter *dateFormatter;


/**

 *  显示加载失败的UILabel

 */

@property (nonatomic,strong) UILabel * loadFailedLabel;


/**

 *  当前播放的item

 */

@property (nonatomic, strong) AVPlayerItem * currentItem;

/**

 *  监听播放起状态的监听者

 */

@property (nonatomic ,strong) id playbackTimeObserver;

/**

 *  设置播放视频的USRLString,可以是本地的路径也可以是http的网络路径

 */

@property (nonatomic,copy) NSString * URLString;


//设置代理

@property (nonatomic,weak)id<JZSVideoPlayerDelegate>delegate;



/**

 *  播放

 */

- (void)play;


/**

 * 暂停

 */

- (void)pause;


/**

 *  获取正在播放的时间点

 *

 *  @return double的一个时间点

 */

- (double)currentTime;

/**

 *  获取播放的总时间

 *

 *  @return double的一个时间点

 */

- (double)duration;

/**

 * 重置播放器

 */

- (void)resetKYVedioPlayer;

/**

 *  全屏显示播放

  @param interfaceOrientation 方向

  @param player 当前播放器

  @param fatherView 当前父视图

 **/

-(void)showFullScreenWithInterfaceOrientation:(UIInterfaceOrientation )interfaceOrientation player:(JZSVideoPlayer *)player withFatherView:(UIView *)fatherView;


2.m中的代码

//

//  JZSVideoPlayer.m

//  桔子树

//

//  Created by 张建 on 16/12/11.

//  Copyright ? 2016 桔子树艺术教育. All rights reserved.

//


#import "JZSVideoPlayer.h"


static void *PlayViewStatusObservationContext = &PlayViewStatusObservationContext;


#define AUTO_DISSMISS_TIMER 5.0


typedef NS_ENUM(NSUInteger, panDirection) {

    

    PanDirectionHorizontalMoved, // 横向移动

    PanDirectionVerticalMoved    // 纵向移动

};


@interface JZSVideoPlayer ()<UIGestureRecognizerDelegate>


//控制音量及亮度手势

@property (nonatomic,strong)UIPanGestureRecognizer * panGes;

//是否在调节音量/亮度

@property (nonatomic,assign)BOOL isVolume;

//控制声音

@property (nonatomic,strong)UISlider * volumeViewSlider;

//拖动方向的属性

@property (nonatomic,assign)panDirection direction;


@end

@implementation JZSVideoPlayer

/*

 initWithFrame的初始化方法

 */

- (id)initWithFrame:(CGRect)frame;

{

    self = [super initWithFrame:frame];

    if (self) {

        self.frame = frame;

        self.userInteractionEnabled = YES;

        self.backgroundColor = [UIColor lightGrayColor];

       

        [self initPlayer];

    }

    return self;

}


#pragma mark ---初始化视图---

- (void)initPlayer{

    

    //初始化UI

    [self initUI];

    

    //设置 autoLayout

    [self makeConstraints];

    

    //添加单击屏幕的手势

    [self addTapGesture];

    

    //添加进入前后台的监听

    [self addAppEnterActive];

}


#pragma mark --- 初始化UI---

- (void)initUI{

    

    //添加loading视图

    self.loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];

    [self addSubview:self.loadingView];

    [self.loadingView startAnimating];

    

    //添加顶部视图

    self.topView = [UIView creatViewWithFrame:CGRectZero BgColor: [UIColor colorWithWhite:0.f alpha:0.5] Hidden:NO];

    [self addSubview:self.topView];

    

    //关闭按钮

    self.closeBtn = [UIButton creatButtonWithFrame:CGRectZero Image:@"close" BgColor:[UIColor clearColor] SelectedImage:@"close" Hidden:NO];

    [self.closeBtn addTarget:self action:@selector(closeVideoBtn:) forControlEvents:UIControlEventTouchUpInside];

    self.closeBtn.showsTouchWhenHighlighted = NO;

    [self.topView addSubview:self.closeBtn];

    

    //标题

    self.titleLabel = [UILabel creatLabelWithFrame:CGRectZero Text:nil Font:16 Thick:NO TextColor:[UIColor whiteColor] Alignment:NSTextAlignmentCenter BgColor:[UIColor clearColor] Hidden:NO];;

    [self.topView addSubview:self.titleLabel];

    

    //添加底部视图

    self.bottomView = [UIView creatViewWithFrame:CGRectZero BgColor:[UIColor colorWithWhite:0.f alpha:0.5] Hidden:NO];

    [self addSubview:self.bottomView];

    

    //添加暂停和开启按钮

    self.playOrPauseBtn = [UIButton creatButtonWithFrame:CGRectZero Image:@"video_pause_nor" BgColor:[UIColor clearColor] SelectedImage:@"video_play_nor" Hidden:NO];

    self.playOrPauseBtn.showsTouchWhenHighlighted = NO;

    [self.playOrPauseBtn addTarget:self action:@selector(PlayOrPause:) forControlEvents:UIControlEventTouchUpInside];

    [self.bottomView addSubview:self.playOrPauseBtn];

    

    //左边的时间

    self.leftTimeLabel = [UILabel creatLabelWithFrame:CGRectZero Text:nil Font:11 Thick:NO TextColor:[UIColor whiteColor] Alignment:NSTextAlignmentLeft BgColor:[UIColor clearColor] Hidden:NO];

    [self.bottomView addSubview:self.leftTimeLabel];

    

    //右边的时间

    self.rightTimeLabel = [UILabel creatLabelWithFrame:CGRectZero Text:nil Font:11 Thick:NO TextColor:[UIColor whiteColor] Alignment:NSTextAlignmentRight BgColor:[UIColor clearColor] Hidden:NO];

    [self.bottomView addSubview:self.rightTimeLabel];

    

    

    //loadingProgress(加载进度条)

    self.loadingProgress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];

    self.loadingProgress.progressTintColor = kColorRGBA(0, 0, 0, .3);//设置进度条上缓冲进度的颜色

    self.loadingProgress.trackTintColor = [UIColor lightGrayColor];//进度条的颜色

    [self.bottomView addSubview:self.loadingProgress];

    [self.loadingProgress setProgress:0.0 animated:YES];//设置进度值和动画

    

    //播放进度条

    self.progressSlider = [[UISliderExt alloc] init];

    self.progressSlider.minimumValue = 0.f;

    [self.progressSlider setThumbImage:[UIImage imageNamed:@"dot"] forState:UIControlStateNormal];//滑块的图片

    self.progressSlider.maximumTrackTintColor = [UIColor clearColor];//最大值一边

    self.progressSlider.minimumTrackTintColor = [UIColor orangeColor];//最小值一边

    self.progressSlider.value = 0.0;//初始值

    self.progressSlider.backgroundColor = [UIColor clearColor];

    [self.bottomView addSubview:self.progressSlider];

    

    //全屏按钮

    self.fullScreenBtn = [UIButton creatButtonWithFrame:CGRectZero Image:@"fullscreen" BgColor:[UIColor clearColor] SelectedImage:@"fullscreen" Hidden:NO];

    [self.fullScreenBtn addTarget:self action:@selector(fullScreenAction:) forControlEvents:UIControlEventTouchUpInside];

    self.fullScreenBtn.showsTouchWhenHighlighted = NO;

    [self.bottomView addSubview:self.fullScreenBtn];

    


}

#pragma mark --- 设置 autoLayout---

-(void)makeConstraints{

    

    //loadingView视图

    self.loadingView.center = self.center;

    

    //顶部工具栏

    self.topView.frame = CGRectMake(0, 0, kScreenWidth, 40);

    

    //关闭按钮

    [self.closeBtn mas_makeConstraints:^(MASConstraintMaker *make) {

        

        make.left.mas_equalTo(_topView.mas_left).offset(0);

        make.top.mas_equalTo(_topView.mas_top).offset(0);

        make.size.mas_equalTo(CGSizeMake(40, 40));

    }];

    

    //标题

    [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {

        

        make.left.mas_equalTo(_topView.mas_left).offset(45);

        make.right.mas_equalTo(_topView.mas_right).offset(-45);

        make.top.mas_equalTo(_topView.mas_top).offset(10);

        make.bottom.mas_equalTo(_topView.mas_bottom).offset(-10);

    }];

    

    //底部工具栏

    self.bottomView.frame = CGRectMake(0, self.height - 50, kScreenWidth, 50);

    

    //播放和暂停视图

    [self.playOrPauseBtn mas_makeConstraints:^(MASConstraintMaker *make) {

        

        make.left.mas_equalTo(_bottomView.mas_left).offset(0);

        make.centerY.mas_equalTo(_bottomView.mas_centerY).offset(0);

        make.size.mas_equalTo(CGSizeMake(50, 50));

        

    }];

    

    //播放进度条

    [self.progressSlider mas_makeConstraints:^(MASConstraintMaker *make) {

        

        make.left.mas_equalTo(_bottomView.mas_left).offset(55);

        make.right.mas_equalTo(_bottomView.mas_right).offset(-40);

        make.centerY.mas_equalTo(_bottomView.mas_centerY).offset(0);

        make.height.mas_equalTo(3.0);

    }];

    

    //左边的时间

    [self.leftTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) {

        

        make.left.mas_equalTo(self.progressSlider.mas_left).offset(0);

        make.bottom.mas_equalTo(_bottomView.mas_bottom).offset(-5);

        make.size.mas_equalTo(CGSizeMake(80, 15));

    }];


    //右边的时间

    [self.rightTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) {

        

        make.right.mas_equalTo(self.progressSlider.mas_right).offset(0);

        make.bottom.mas_equalTo(_bottomView.mas_bottom).offset(-5);

        make.size.mas_equalTo(CGSizeMake(80, 15));

    }];

    //预加载进度条

    [self.loadingProgress mas_makeConstraints:^(MASConstraintMaker *make) {

        make.left.equalTo(self.progressSlider);

        make.right.equalTo(self.progressSlider);

        make.center.equalTo(self.progressSlider);

        make.height.mas_equalTo(3.0);

    }];

    

    //全屏按钮

    [self.fullScreenBtn mas_makeConstraints:^(MASConstraintMaker *make) {

        

        make.right.mas_equalTo(_bottomView.mas_right).offset(0);

        make.centerY.mas_equalTo(_bottomView.mas_centerY).offset(0);

        make.size.mas_equalTo(CGSizeMake(40, 40));

    }];

    

    //让子视图自动适应父视图的方法

    [self setAutoresizesSubviews:NO];

}



#pragma mark ---添加进入前后台的监听事件---

- (void)addAppEnterActive{

    

    //进入后台

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

    

    //进入前台

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];

}

//进入后台

- (void)appDidEnterBackground:(NSNotification *)notification{

    

    [self pause];

}

//进入前台

- (void)appWillEnterForeground:(NSNotification *)notification{

    

    [self play];

}

#pragma mark ---添加播放视频时双击屏幕的事件---

- (void)handleDoubleTap:(UITapGestureRecognizer *)doubleTap{

    

    //双击的代理

    if (_delegate && [_delegate respondsToSelector:@selector(JZSPlayerPlayer:doubleTaped:)]) {

        

        [_delegate JZSPlayerPlayer:self doubleTaped:doubleTap];

    }

    

    //查看播放状态

    //暂停状态

    if (self.player.rate != 1.0f) {

        

        [self.player play];

        self.playOrPauseBtn.selected = NO;

    }

    //播放状态

    else {

        

        [self.player pause];

        self.playOrPauseBtn.selected = YES;

    }

    

    [UIView animateWithDuration:.5 animations:^{

        

        self.topView.alpha = 1.0f;

        self.bottomView.alpha = 1.0f;

        

    } completion:^(BOOL finished) {

        

        

    }];

}


#pragma mark ---点击关闭按钮的事件---

- (void)closeVideoBtn:(UIButton *)closeBtn{

    

    //关闭按钮的代理

    if (_delegate && [_delegate respondsToSelector:@selector(JZSPlayerPlayer:clickedCloseButton:)]) {

        

        [_delegate JZSPlayerPlayer:self clickedCloseButton:closeBtn];

    }

}

#pragma mark ---添加单击播放视频屏幕的手势---

- (void)addTapGesture{

    

    //单击屏幕

    _singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];

    _singleTap.numberOfTapsRequired = 1;//tap次数

    _singleTap.numberOfTouchesRequired = 1;//手指数

    [self addGestureRecognizer:_singleTap];

    

}

// 单击播放视频屏幕的事件

- (void)handleSingleTap:(UITapGestureRecognizer *)singleTap{

    

    NSLog(@"点击了单击的事件");

    

    //单击的代理

    if (_delegate && [_delegate respondsToSelector:@selector(JZSPlayerPlayer:singleTaped:)]) {

        

        [_delegate JZSPlayerPlayer:self singleTaped:singleTap];

        

    }

    //自动隐藏top/bottom视图

    if (_isAutoDismissTopBottomView == YES) {

        

        [_autoDismissTimer invalidate];

        _autoDismissTimer = nil;

        _autoDismissTimer = [NSTimer timerWithTimeInterval:AUTO_DISSMISS_TIMER target:self selector:@selector(autoDismissTopBottomView:) userInfo:nil repeats:YES];

        [[NSRunLoop currentRunLoop] addTimer:_autoDismissTimer forMode:NSDefaultRunLoopMode];

    }

    

    //视图动画

    [UIView animateWithDuration:.5f animations:^{

        

        if (_bottomView.alpha == 0) {

            

            self.bottomView.alpha = 1.0f;

            self.topView.alpha = 1.0f;

        }else {

            

            self.bottomView.alpha = .0f;

            self.topView.alpha = .0f;

        }

        

    } completion:^(BOOL finished) {

        

        

    }];

}

#pragma mark ---获取播放资源---

- (AVPlayerItem *)getPlayItemwithURLString:(NSString *)urlString{

    

    

    //本地视频---测试

    NSString * path = [[NSBundle mainBundle] pathForResource:urlString ofType:nil];

    NSURL * url = [NSURL fileURLWithPath:path];

    AVAsset * movieAsset = [[AVURLAsset alloc] initWithURL:url options:nil];;

    AVPlayerItem * playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];

    return playerItem;


//    //视频资源管理库

//    AVPlayerItem * playerItem;

//    

//    //如果本地没有下载此视频

//    NSString * localVideoStr = [[JZSVideoHttpTool shareInstanceManager] getLocalVideo:urlString];

//    if (![localVideoStr isEqualToString:@""]) {

//        

//        NSLog(@"localVideoStr:%@",localVideoStr);

//        NSURL * sourceMovieUrl = [NSURL fileURLWithPath:[[JZSVideoHttpTool shareInstanceManager] getLocalVideo:urlString]];

//        AVAsset * movieAsset = [[AVURLAsset alloc] initWithURL:sourceMovieUrl options:nil];

//        playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];

//    }

//    //本地没有下载此视频资源

//    else {

//        

//        if ([urlString containsString:@"http"]) {

//            

//            playerItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];

//        }

//    }

//    

//    return playerItem;

}


#pragma mark ----重写URLStringsetting方法----

- (void)setURLString:(NSString *)URLString{

    

    _URLString = URLString;

    

    //重写AVPlayerItem-设置player的参数

    self.currentItem = [self getPlayItemwithURLString:URLString];


    //获取系统的声音

    [self getSystemVolume];


    //创建播放器

    if (self.player == nil) {

        

        //player

        self.player = [AVPlayer playerWithPlayerItem:self.currentItem];

        self.player.usesExternalPlaybackWhileExternalScreenIsActive=YES;

        

        //AVPlayerLayer

        self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];

        self.playerLayer.frame = self.layer.bounds;

        if ([self.playerLayer superlayer] == nil) {

            

            [self.layer addSublayer:self.playerLayer];

        }

        //视频的默认填充模式,AVLayerVideoGravityResizeAspect

        [self.playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];

//        self.playerLayer.videoGravity = AVLayerVideoGravityResize;

        

    }

    

    //设置暂停和播放按钮为暂停

    self.playOrPauseBtn.selected = NO;

    

    //播放速率不为1时-开始播放

    if (self.player.rate != 1.f) {

        

        if ([self currentTime] == [self duration]) {

            

            [self.player play];

        }

        

    }

    

    //将头部视图和底部视图显示到最前面

    [self bringSubviewToFront:self.loadFailedLabel];

    [self bringSubviewToFront:self.loadingView];

    [self bringSubviewToFront:self.topView];

    [self bringSubviewToFront:self.bottomView];


}


#pragma mark ---获取系统的音量---

- (void)getSystemVolume

{

    MPVolumeView *volumeView = [[MPVolumeView alloc] init];

    _volumeViewSlider = nil;

    for (UIView *view in [volumeView subviews]){

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

            _volumeViewSlider = (UISlider *)view;

            break;

        }

    }

   

}

#pragma mark ---重写AVPlayerItem方法,处理自己的逻辑---

- (void)setCurrentItem:(AVPlayerItem *)currentItem{

    if (_currentItem == currentItem) {

        

        return;

    }

    if (_currentItem) {

        

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

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

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

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

        [_currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];

        _currentItem = nil;

    }

    _currentItem = currentItem;

    if (_currentItem) {

        

        [_currentItem addObserver:self

                       forKeyPath:@"status"

                          options:NSKeyValueObservingOptionNew

                          context:PlayViewStatusObservationContext];

        

        [_currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:PlayViewStatusObservationContext];

        // 缓冲区空了,需要等待数据

        [_currentItem addObserver:self forKeyPath:@"playbackBufferEmpty" options: NSKeyValueObservingOptionNew context:PlayViewStatusObservationContext];

        // 缓冲区有足够数据可以播放了

        [_currentItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options: NSKeyValueObservingOptionNew context:PlayViewStatusObservationContext];


        //切换视频

        [self.player replaceCurrentItemWithPlayerItem:self.currentItem];

        

        // 添加视频播放结束通知

        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_currentItem];

    }

}

#pragma mark ---设置播放状态----

- (void)setState:(JZSVideoPlayerState)state{

    _state = state;

    //控制菊花的显示/隐藏

    if (state == JZSVideoPlayerStateBuffering) {

        

        [self.loadingView startAnimating];

    }

    else if (state == JZSVideoPlayerStatePlaying){

        

        [self.loadingView stopAnimating];

    }

    else if (state == JZSVideoPlayerStateReadyToPlay){

        

        [self.loadingView stopAnimating];

    }else {

        

        [self.loadingView stopAnimating];

    }

}


#pragma mark ---加载失败时的提示视图---

- (UILabel *)loadFailedLabel{

    

    if (_loadFailedLabel == nil) {

        

        _loadFailedLabel = [UILabel creatLabelWithFrame:CGRectZero Text:@"视频加载失败..." Font:16 Thick:NO TextColor:[UIColor whiteColor] Alignment:NSTextAlignmentCenter BgColor:[UIColor clearColor] Hidden:YES];

        [self addSubview:_loadFailedLabel];

        _loadFailedLabel.frame = CGRectMake(0, self.centerY, self.width, 30);

    }

    return _loadFailedLabel;

}


#pragma mark - KVO 监听

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


    if (context == PlayViewStatusObservationContext)

    {

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

            AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];

            switch (status)

            {

    

                case AVPlayerStatusUnknown:

                {

                    self.state = JZSVideoPlayerStateBuffering;

                    [self.loadingProgress setProgress:0.f animated:YES];

                    [self.progressSlider setValue:0.f animated:YES];

                }

                    break;

                    

                case AVPlayerStatusReadyToPlay:

                {

                    self.state = JZSVideoPlayerStateReadyToPlay;

                    

                    //隐藏播放失败时的视图

                    _loadFailedLabel.hidden = YES;

                    

                    //添加双击播放时屏幕的事件

                    _doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];

                    _doubleTap.numberOfTapsRequired = 2;//单击2

                    [self addGestureRecognizer:_doubleTap];

                    //如果双击成立-取消单击(双击的时候不走单击事件)

                    [_singleTap requireGestureRecognizerToFail:_doubleTap];

                    

                    //设置进度条的最大值

                    if (CMTimeGetSeconds(_currentItem.duration)) {

                        

                        double _x = CMTimeGetSeconds(_currentItem.duration);

                        if (!isnan(_x)) {

                            self.progressSlider.maximumValue = CMTimeGetSeconds(self.player.currentItem.duration);

                        }

                    }

                    

                    //监听播放状态

                    [self addProgressObserve];

                    

                    //5 自动隐藏底部视图

                    if (_isAutoDismissTopBottomView == YES) {

                        

                        if (self.autoDismissTimer==nil) {

                            

                            self.autoDismissTimer = [NSTimer timerWithTimeInterval:3.0 target:self selector:@selector(autoDismissTopBottomView:) userInfo:nil repeats:YES];

                            [[NSRunLoop currentRunLoop] addTimer:self.autoDismissTimer forMode:NSDefaultRunLoopMode];

                        }


                    }

                    

                    //准备去播放的代理

                    if (_delegate && [_delegate respondsToSelector:@selector(JZSPlayerPlayerReadyToPlay:playerStatus:)]) {

                        [_delegate JZSPlayerPlayerReadyToPlay:self playerStatus:JZSVideoPlayerStateReadyToPlay];

                    }

                    

                }

                    break;

                    

                case AVPlayerStatusFailed:

                {

                    self.state = AVPlayerStatusFailed;

                    

                    //让播放按钮置为暂停

                    self.playOrPauseBtn.selected = NO;

                    

                    //播放失败显示的提示文本

                    NSError *error = [self.player.currentItem error];

                    if (error) {

                        self.loadFailedLabel.hidden = NO;

                        [self bringSubviewToFront:self.loadFailedLabel];

                    }

                    NSLog(@"视频加载失败===%@",error.description);


                    //播放状态失败的代理

                    if (self.delegate&&[self.delegate respondsToSelector:@selector(JZSPlayerPlayerFailedPlay:playerStatus:)]) {

                        

                        [_delegate JZSPlayerPlayerFailedPlay:self playerStatus:JZSVideoPlayerStateFailed];

                    }

                }

                    break;

            }

            

        }

        //缓冲

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

            

            // 计算缓冲进度

            NSTimeInterval timeInterval = [self availableDuration];

            CMTime duration             = self.currentItem.duration;

            CGFloat totalDuration       = CMTimeGetSeconds(duration);

            

            //缓冲进度显示

            [self.loadingProgress setProgress:timeInterval / totalDuration animated:YES];

        }

        //缓冲为空时

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

           

            // 当缓冲是空的时候

            if (self.currentItem.playbackBufferEmpty) {

                

                self.state = JZSVideoPlayerStateBuffering;

                //缓冲为空-5秒后执行

                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

                    

                    NSLog(@"正在缓冲中-5秒后重试!");

                });


            }

            

        }

        //当缓冲好了的时候

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

            

            // 当缓冲好的时候

            if (self.currentItem.playbackLikelyToKeepUp && self.state == JZSVideoPlayerStateBuffering){

                

                self.state = JZSVideoPlayerStatePlaying;

                

            }

            

        }

    }

    

}


#pragma mark ---坚挺耳机插入情况---

- (BOOL)isHeadsetPluggedIn{

    

    AVAudioSessionRouteDescription * route = [[AVAudioSession sharedInstance] currentRoute];

    for (AVAudioSessionPortDescription * desc in  [route outputs]) {

        if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones]) {

            

            return YES;

        }

    }

    return NO;

}


#pragma mark ----监听播放状态----

- (void)addProgressObserve{

    

    JZSWeakSelf(weakSelf);

    

    //取到当前播放视频资源

    AVPlayerItem *playerItem = self.player.currentItem;

    

    //给进度条添加拖动的事件

    [self.progressSlider addTarget:self action:@selector(changeValue:) forControlEvents:UIControlEventValueChanged];

    [self.progressSlider addTarget:self action:@selector(updateSliderValue:) forControlEvents:UIControlEventTouchUpInside];

    //给进度条添加单击手势

    self.tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(actionTapGesture:)];

    self.tap.delegate = self;

    [self.progressSlider addGestureRecognizer:self.tap];

    

    //这里设置每秒执行一次

    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

        

        //监测播放和暂停按钮的状态

        if (weakSelf.player.rate != 1.f) {

            

            weakSelf.playOrPauseBtn.selected = YES;

        } else {

            

            weakSelf.playOrPauseBtn.selected = NO;

        }

        

        //监听耳机插入情况

        BOOL haveHeadset =[weakSelf isHeadsetPluggedIn];

        if (haveHeadset) {

            

            //切换为听听筒播放

            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];


        }

        else {

            

            //切换扬声器播放

            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

           

        }

        

        //进度条最小值

        double minValue = [weakSelf.progressSlider minimumValue];

        //进度条最大值

        double maxValue = [weakSelf.progressSlider maximumValue];

        //当前时间(秒)

        double current=CMTimeGetSeconds([playerItem currentTime]);

        //总时间(秒)

        double total=CMTimeGetSeconds([playerItem duration]);

        //剩余时间

        double remainTime = total - current;

        

        //左侧时间

        weakSelf.leftTimeLabel.text = [weakSelf convertTime:current];

        //右侧时间(剩余时间)

        weakSelf.rightTimeLabel.text = [weakSelf convertTime:remainTime];

        //设置播放进度条

        if (current) {

            [weakSelf.progressSlider setValue:((maxValue - minValue) * current / total + minValue) animated:YES];

        }

    }];

    

}


#pragma mark ---拖动进度条的事件---

- (void)changeValue:(UISlider *)aSlider

{

    AVPlayerItem *playerItem = self.player.currentItem;

    //从当前位置播放

    [self.player seekToTime:CMTimeMakeWithSeconds(aSlider.value, [playerItem currentTime].timescale)];

    //机智的暂停,防止造成拖动时进度条卡顿的尴尬(因为AVPlayer这个拖动后会暂停,不会自动播放)

    [self.player pause];

}

- (void)updateSliderValue:(UISlider *)slider{

    

    [self.player play];

}

- (void)actionTapGesture:(UITapGestureRecognizer *)tap{

    

    CGPoint location = [tap locationInView:self.progressSlider];

    CGFloat value = (self.progressSlider.maximumValue - self.progressSlider.minimumValue) * (location.x / self.progressSlider.frame.size.width);

    [self.progressSlider setValue:value animated:YES];

    

    [self.player seekToTime:CMTimeMakeWithSeconds(self.progressSlider.value, self.currentItem.currentTime.timescale)];

    if (self.player.rate != 1.f) {

        if ([self currentTime] == [self duration])

            [self setCurrentTime:0.f];

        self.playOrPauseBtn.selected = NO;

        [self.player play];

    }

}


#pragma mark ---隐藏顶部/底部视图---

- (void)autoDismissTopBottomView:(NSTimer *)timer{

    

    if (self.player.rate == .0f && [self currentTime] != [self duration]) {

        

        NSLog(@"暂停状态不隐藏");

    }

    //播放状态

    else if (self.player.rate == 1.f) {

        

        if (self.bottomView.alpha == 1.0f) {

            

            [UIView animateWithDuration:.5f animations:^{

                

                self.topView.alpha = .0f;

                self.bottomView.alpha = .0f;

                

            } completion:^(BOOL finished) {

                

                

            }];

            

        }

    }

}



#pragma mark ----把秒转换成时间格式---

- (NSString *)convertTime:(CGFloat)second{

    NSDate *d = [NSDate dateWithTimeIntervalSince1970:second];

    if (second/3600 >= 1) {

        [[self dateFormatter] setDateFormat:@"HH:mm:ss"];

    } else {

        [[self dateFormatter] setDateFormat:@"mm:ss"];

    }

    NSString *newTime = [[self dateFormatter] stringFromDate:d];

    return newTime;

}

- (NSDateFormatter *)dateFormatter {

    if (!_dateFormatter) {

        _dateFormatter = [[NSDateFormatter alloc] init];

    }

    return _dateFormatter;

}



#pragma mark ----点击全屏的事件---

- (void)fullScreenAction:(UIButton *)fullScreenBtn{

    

    fullScreenBtn.selected = !fullScreenBtn.selected;

    

    //添加声音和亮度视图

    [self addControlVolmeAndLight:fullScreenBtn.selected];

    

    if (_delegate && [_delegate respondsToSelector:@selector(JZSPlayerPlayer:clickedFullScreenButton:)]) {

        

        [_delegate JZSPlayerPlayer:self clickedFullScreenButton:fullScreenBtn];

    }

}

- (void)addControlVolmeAndLight:(BOOL)isLabdscape{

    

    //全屏

    if (isLabdscape) {

        

        _panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(controlVolueLightAndProgress:)];

        [self addGestureRecognizer:_panGes];

    }

    //非全屏

    else {

        

        [self removeGestureRecognizer:_panGes];

        

    }

    

}

//控制音量和亮度的手势

- (void)controlVolueLightAndProgress:(UIPanGestureRecognizer *)ges{

    

    //根据位置,确定是调节音量还是亮度

    CGPoint locationPoint = [ges locationInView:self];

    

    //获取滑动速度

    CGPoint speed = [ges velocityInView:self];

    

    //判断是垂直移动还是水平移动

    switch (ges.state) {

            //开始移动

        case UIGestureRecognizerStateBegan:

        {

            //使用绝对值来判断移动的方向

            CGFloat x = fabs(speed.x);

            CGFloat y = fabs(speed.y);

            

            //垂直移动

            if (x < y) {

                NSLog(@"垂直移动");

                self.direction = PanDirectionVerticalMoved;

                

                if (locationPoint.x > self.width / 2) {

                    

                    NSLog(@"调节音量");

                    self.isVolume = YES;

                    

                }else {

                    

                    NSLog(@"调节亮度");

                    self.isVolume = NO;

                }

                

            }else if (x > y){

                NSLog(@"水平移动");

                self.direction = PanDirectionHorizontalMoved;

            }

            

        }

            break;

            //正在移动

        case UIGestureRecognizerStateChanged:

        {

            switch (self.direction) {

                    //垂直

                case PanDirectionVerticalMoved:

                {

                    //垂直移动方法只要y方向的值

                    [self verticalMoved:speed.y];

                    

                }

                    break;

                    //水平

                case PanDirectionHorizontalMoved:

                {

                    //水平移动方法只要x轴方向的值

                    [self horizontalMoved:speed.x];

                    

                }

                    break;

                default:

                    break;

            }

            

        }

            break;

            //停止移动

        case UIGestureRecognizerStateEnded:

        {

            

            if (self.direction == PanDirectionHorizontalMoved) {

                

                [self.player play];

            }

        }

            break;

        default:

            break;

    }

    

}

//调节水平方向的进度

- (void)horizontalMoved:(CGFloat)value{

    

    //value值为手指的滑动速度,一般最快值不会超过1000,保证在0-1之间,往右滑为正,往左滑为负

    NSLog(@"valueH:%f",value);

    

    //设置进度值的改变

    CGFloat progress = self.progressSlider.value + value / 10000;

    [self.progressSlider setValue:progress animated:YES];

    

    //跳转播放

    AVPlayerItem *playerItem = self.player.currentItem;

    //从当前位置播放

    [self.player seekToTime:CMTimeMakeWithSeconds(self.progressSlider.value, [playerItem currentTime].timescale)];

    //机智的暂停,防止造成拖动时进度条卡顿的尴尬(因为AVPlayer这个拖动后会暂停,不会自动播放)

    [self.player pause];

    

}

//调节垂直方向的声音和亮度

- (void)verticalMoved:(CGFloat)value{

    

    //value为手指的滑动速度,一般最快速度值不会超过1000,保证在0-1之间,往下滑为正,往上滑为负

    NSLog(@"valueV:%f",value);

    //声音

    if (self.isVolume) {

        

        self.volumeViewSlider.value -= value / 10000;

        

        NSLog(@"value:%f",self.volumeViewSlider.value);

        

        

    }

    //亮度

    else {

        

        ([UIScreen mainScreen].brightness -= value / 10000);

    }

    

}

//获取系统的声音

- (void)systemVolume{

    

    MPVolumeView * volumeView = [[MPVolumeView alloc] init];

    volumeView = nil;

    for (UIView * view in [volumeView subviews]) {

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

            

            _volumeViewSlider = (UISlider *)view;

            break;

        }

    }

    

}


#pragma mark ---全屏显示播放器---

- (void)showFullScreenWithInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation player:(JZSVideoPlayer *)player withFatherView:(UIView *)fatherView{

    

    [player removeFromSuperview];

    

    if (player == nil) {

        

        return;

    }

    

    [UIView animateWithDuration:.5 animations:^{

        

        player.transform = CGAffineTransformIdentity;

        if (interfaceOrientation==UIInterfaceOrientationLandscapeLeft) {

            player.transform = CGAffineTransformMakeRotation(-M_PI_2);

        }else if(interfaceOrientation==UIInterfaceOrientationLandscapeRight){

            player.transform = CGAffineTransformMakeRotation(M_PI_2);

        }

        player.frame = CGRectMake(0, 0, [[UIScreen mainScreen]bounds].size.width, [[UIScreen mainScreen]bounds].size.height);

        player.playerLayer.frame =  CGRectMake(0,0, [[UIScreen mainScreen]bounds].size.height,[[UIScreen mainScreen]bounds].size.width);

        

        //加载态

        player.loadingView.frame = CGRectMake((kScreenHeight - 20) / 2.0, (kScreenWidth - 20) / 2.0, 20, 20);

        

        //播放失败的提示

        player.loadFailedLabel.frame = CGRectMake(0, (kScreenWidth - 30) / 2.0, kScreenHeight, 30);

        

        //top

        player.topView.frame = CGRectMake(0, 0, kScreenHeight, 40);

        

        

        //bottom

        player.bottomView.frame = CGRectMake(0, kScreenWidth - 50, kScreenHeight,50);

        

        

        [fatherView addSubview:player];


        

    }];

    

    

}

#pragma mark ----计算缓冲进度---

- (NSTimeInterval)availableDuration {

    

    NSArray *loadedTimeRanges = [_currentItem loadedTimeRanges];

    CMTimeRange timeRange     = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域

    float startSeconds        = CMTimeGetSeconds(timeRange.start);

    float durationSeconds     = CMTimeGetSeconds(timeRange.duration);

    NSTimeInterval result     = startSeconds + durationSeconds;// 计算缓冲总进度

    return result;

}

#pragma mark ---获取当前的播放时间---

- (double)currentTime{

    

    return CMTimeGetSeconds([[self player] currentTime]);

}


#pragma mark ---跳到time处播放---

- (void)seekToTimeToPlay:(double)time{

    

    if (self.player && self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {

        if (time > [self duration]) {

            time = [self duration];

        }

        if (time < 0) {

            

            time = 0.0f;

        }

        

        [self.player seekToTime:CMTimeMakeWithSeconds(time, self.currentItem.currentTime.timescale) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {

            

            

        }];

    }

}

- (void)setCurrentTime:(double)time{

    dispatch_async(dispatch_get_main_queue(), ^{

        

        [self.player seekToTime:CMTimeMakeWithSeconds(time, self.currentItem.currentTime.timescale)];

    });

}

#pragma mark ---获取播放的总时间---

- (double)duration{

    

    AVPlayerItem * playerItem = self.player.currentItem;

    if (playerItem.status == AVPlayerItemStatusReadyToPlay) {

        

        return CMTimeGetSeconds([[playerItem asset] duration]);

        

    }else {

        

        return 0.f;

    }

}

#pragma mark - 对外方法

/**

 *  播放

 */

- (void)play{

    [self PlayOrPause:self.playOrPauseBtn];

}


/**

 * 暂停

 */

- (void)pause{

    [self PlayOrPause:self.playOrPauseBtn];

}

#pragma mark - 播放 或者 暂停

- (void)PlayOrPause:(UIButton *)sender{

   

    //如果暂停

    if (self.player.rate != 1.f) {

        sender.selected = NO;

        [self.player play];

    }

    //如果播放

    else {

        sender.selected = YES;

        [self.player pause];

    }

    

    //点击播放和暂停按钮的回调

    if ([self.delegate respondsToSelector:@selector(JZSPlayerPlayer:clickedPlayOrPauseButton:)]) {

        [self.delegate JZSPlayerPlayer:self clickedPlayOrPauseButton:sender];

    }

}


#pragma mark -----接收播放完成的通知----

- (void)moviePlayDidEnd:(NSNotification *)note {

   

    self.state = JZSVideoPlayerStateFinished;

    

    //播放结束的代理

    if (self.delegate&&[self.delegate respondsToSelector:@selector(JZSPlayerFinishedPlay:)]) {

        [self.delegate JZSPlayerFinishedPlay:self];

    }

    JZSWeakSelf(weakSelf);

    [self.player seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {

       

        [weakSelf.progressSlider setValue:0.0 animated:YES];

        

        [_autoDismissTimer invalidate];

        

        weakSelf.playOrPauseBtn.selected = NO;

        

    }];


}


#pragma mark - 重置播放器 销毁

/**

 * 重置播放器

 */

- (void)resetKYVedioPlayer{

    

    self.currentItem = nil;


    // 移除通知

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    // 关闭定时器

    if ([self.autoDismissTimer isValid]) {

        [self.autoDismissTimer invalidate];

        self.autoDismissTimer = nil;

    }

    self.playOrPauseBtn = nil;

    // 暂停

    [self.player pause];

    // 移除原来的layer

    [self.playerLayer removeFromSuperlayer];

    // 替换PlayerItemnil

    [self.player replaceCurrentItemWithPlayerItem:nil];

    // player置为nil

    self.playerLayer = nil;

    self.player = nil;

    

}


-(void)dealloc{

    

    NSLog(@"KYVedioPlayer dealloc");

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    [self.player.currentItem cancelPendingSeeks];

    [self.player.currentItem.asset cancelLoading];

    [self.player pause];

    

    [self.player removeTimeObserver:self.playbackTimeObserver];

    

    //移除观察者

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

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

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

    [_currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];

    

    

    [self.playerLayer removeFromSuperlayer];

    [self.player replaceCurrentItemWithPlayerItem:nil];

    self.player = nil;

    self.currentItem = nil;

    self.playOrPauseBtn = nil;

    self.playerLayer = nil;

    

    self.autoDismissTimer = nil;

    

}


@end




0 0