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 ----重写URLString的setting方法----
- (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];
// 替换PlayerItem为nil
[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