//
// PlayerMenu.h
// video
//
// Created by admin on 17/5/5.
// Copyright © 2017年 wsl. All rights reserved.
//
#import <UIKit/UIKit.h>
@class PlayerSlider;
@protocol PlayerMenuDelegate <NSObject>
/**返回按钮代理*/
- (void)backButtonAction:(UIButton *)sender;
/**播放按钮代理*/
- (void)playButtonAction:(UIButton *)sender;
/**全屏按钮代理*/
- (void)fullButtonAction:(UIButton *)sender;
/**开始滑动*/
- (void)progressSliderTouchBegan:(PlayerSlider *)sender;
/**滑动中*/
- (void)progressSliderValueChanged:(PlayerSlider *)sender;
/**滑动结束*/
- (void)progressSliderTouchEnded:(PlayerSlider *)sender;
/**失败按钮代理*/
- (void)failButtonAction:(UIButton *)sender;
/**重播按钮代理*/
- (void)repeatButtonAction:(UIButton*)sender;
@end
@interface PlayerMenuView : UIButton
@property (nonatomic, strong) UIView *topToolBar; //顶部工具条
@property (nonatomic, strong) UIView *bottomToolBar; //底部工具条
@property (nonatomic, strong) UIActivityIndicatorView *activity;//菊花转
@property (nonatomic, strong) UIButton *backButton; //返回按钮
@property (nonatomic, strong) UIButton *playButton; //播放按钮
@property (nonatomic, strong) UIButton *fullButton; //全屏按钮
@property (nonatomic, strong) UILabel *currentTimeLabel; //当前时间
@property (nonatomic, strong) UILabel *totalTimeLabel; //总时间
@property (nonatomic, strong) UIProgressView *progress; //缓冲进度
@property (nonatomic, strong) PlayerSlider *slider; //进度条
@property (nonatomic, strong) UIButton *failButton; //失败按钮
@property (nonatomic, strong) UIButton *repeatButton; //重复按钮
@property (nonatomic, weak) id<PlayerMenuDelegate> delegate;
@end
@interface PlayerSlider : UISlider
@end
.
//
// PlayerMenu.m
// video
//
// Created by admin on 17/5/5.
// Copyright © 2017年 wsl. All rights reserved.
//
#import "PlayerMenuView.h"
#import "Masonry.h"
#define ProgressColor [UIColor colorWithRed:0.54118 green:0.51373 blue:0.50980 alpha:1.00000] //进度条颜色
#define ProgressTintColor [UIColor colorWithRed:248/255.0 green:248/255.0 blue:248/255.0 alpha:1.00000]//缓冲颜色
#define PlayFinishColor [UIColor colorWithRed:62/255.0 green:188/255.0 blue:181/255.0 alpha:1.00000]//播放完成颜色
#define Padding 10 //间距
#define ToolBarHeight 40 //顶部底部工具条高度
@interface PlayerMenuView()
@property (nonatomic, strong) UIButton *bgBtn;
@end
@implementation PlayerMenuView
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
[self setupUI];
}
return self;
}
-(void)setupUI{
[self addSubview:self.bottomToolBar];
[self addSubview:self.repeatButton];
[self addSubview:self.topToolBar];
[self addSubview:self.activity];
[self.topToolBar addSubview:self.backButton];
[self.topToolBar addSubview:self.bgBtn];
[self.bottomToolBar addSubview:self.playButton];
[self.bottomToolBar addSubview:self.fullButton];
[self.bottomToolBar addSubview:self.currentTimeLabel];
[self.bottomToolBar addSubview:self.totalTimeLabel];
[self.bottomToolBar addSubview:self.progress];
[self.bottomToolBar addSubview:self.slider];
[self addSubview:self.failButton];
[self makeConstraints];
self.topToolBar.backgroundColor = [UIColor colorWithRed:0.00000f green:0.00000f blue:0.00000f alpha:0.01000f];
self.bottomToolBar.backgroundColor = [UIColor colorWithRed:0.00000f green:0.00000f blue:0.00000f alpha:0.50000f];
}
#pragma mark - 约束
- (void)makeConstraints{
//底部工具条
[self.bottomToolBar mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.equalTo(self);
make.height.mas_equalTo(ToolBarHeight);
}];
[self.repeatButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.right.bottom.equalTo(self);
}];
//顶部工具条
[self.topToolBar mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.equalTo(self);
make.height.mas_equalTo(ToolBarHeight);
}];
//转子
[self.activity mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
}];
//返回按钮
[self.backButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.mas_equalTo(Padding);
make.bottom.mas_equalTo(-Padding);
make.width.equalTo(self.backButton.mas_height);
}];
[self.bgBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.bottom.equalTo(self.topToolBar);
make.width.mas_equalTo(80);
}];
//播放按钮
[self.playButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.mas_equalTo(Padding);
make.bottom.mas_equalTo(-Padding);
make.width.equalTo(self.backButton.mas_height);
}];
//全屏按钮
[self.fullButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.bottom.mas_equalTo(-Padding);
make.top.mas_equalTo(Padding);
make.width.equalTo(self.backButton.mas_height);
}];
//当前播放时间
[self.currentTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.playButton.mas_right).offset(Padding);
make.centerY.equalTo(self.bottomToolBar);
}];
//总时间
[self.totalTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.fullButton.mas_left).offset(-Padding);
make.centerY.equalTo(self.bottomToolBar);
}];
//缓冲条
[self.progress mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.currentTimeLabel.mas_right).offset(Padding);
make.right.equalTo(self.totalTimeLabel.mas_left).offset(-Padding);
make.height.mas_equalTo(2);
make.centerY.equalTo(self.bottomToolBar);
}];
//滑杆
[self.slider mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.progress);
}];
//失败按钮
[self.failButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
}];
}
#pragma mark - 按钮点击事件
//返回按钮
- (void)backButtonAction:(UIButton *)sender{
if ([self.delegate respondsToSelector:@selector(backButtonAction:)]) {
[self.delegate backButtonAction:sender];
}
}
//播放按钮
- (void)playButtonAction:(UIButton *)sender{
sender.selected = !sender.selected;
if ([self.delegate respondsToSelector:@selector(playButtonAction:)]) {
[self.delegate playButtonAction:sender];
}
}
//全屏按钮
- (void)fullButtonAction:(UIButton *)sender{
sender.selected = !sender.selected;
self.topToolBar.hidden = sender.selected;
if ([self.delegate respondsToSelector:@selector(fullButtonAction:)]) {
[self.delegate fullButtonAction:sender];
}
}
//失败按钮
- (void)failButtonAction:(UIButton *)sender{
self.failButton.hidden = YES;
[self.activity startAnimating];
if ([self.delegate respondsToSelector:@selector(failButtonAction:)]) {
[self.delegate failButtonAction:sender];
}
}
//重播按钮
- (void)repeatButtonAction:(UIButton*)sender{
self.repeatButton.hidden = YES;
[self.activity startAnimating];
if ([self.delegate respondsToSelector:@selector(repeatButtonAction:)]) {
[self.delegate repeatButtonAction:sender];
}
}
#pragma mark - 滑杆
//开始滑动
- (void)progressSliderTouchBegan:(PlayerSlider *)sender{
if ([self.delegate respondsToSelector:@selector(progressSliderTouchBegan:)]) {
[self.delegate progressSliderTouchBegan:sender];
}
}
//滑动中
- (void)progressSliderValueChanged:(PlayerSlider *)sender{
if ([self.delegate respondsToSelector:@selector(progressSliderValueChanged:)]) {
[self.delegate progressSliderValueChanged:sender];
}
}
//滑动结束
- (void)progressSliderTouchEnded:(PlayerSlider *)sender{
if ([self.delegate respondsToSelector:@selector(progressSliderTouchEnded:)]) {
[self.delegate progressSliderTouchEnded:sender];
}
}
#pragma mark - 懒加载
//顶部工具条
- (UIView *) topToolBar{
if (nil == _topToolBar){
_topToolBar = [[UIView alloc] init];
_topToolBar.userInteractionEnabled = YES;
}
return _topToolBar;
}
//底部工具条
- (UIView *) bottomToolBar{
if (nil == _bottomToolBar){
_bottomToolBar = [[UIView alloc] init];
_bottomToolBar.userInteractionEnabled = YES;
}
return _bottomToolBar;
}
//转子
- (UIActivityIndicatorView *)activity{
if (nil == _activity){
_activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[_activity startAnimating];
}
return _activity;
}
//返回按钮
- (UIButton *) backButton{
if (nil == _backButton){
_backButton = [[UIButton alloc] init];
[_backButton setImage:[UIImage imageNamed:@"VideoBackBtn"] forState:UIControlStateNormal];
[_backButton setImage:[UIImage imageNamed:@"VideoBackBtn"] forState:UIControlStateHighlighted];
[_backButton addTarget:self action:@selector(backButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _backButton;
}
//返回按钮遮罩
-(UIButton *)bgBtn{
if (nil == _bgBtn) {
_bgBtn = [[UIButton alloc]init];
[_bgBtn addTarget:self action:@selector(backButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _bgBtn;
}
//播放按钮
- (UIButton *) playButton{
if (nil == _playButton){
_playButton = [[UIButton alloc] init];
[_playButton setImage:[UIImage imageNamed:@"VideoPlayBtn"] forState:UIControlStateNormal];
[_playButton setImage:[UIImage imageNamed:@"VideoPauseBtn"] forState:UIControlStateSelected];
[_playButton addTarget:self action:@selector(playButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _playButton;
}
//全屏按钮
- (UIButton *) fullButton{
if (nil == _fullButton){
_fullButton = [[UIButton alloc] init];
[_fullButton setImage:[UIImage imageNamed:@"VideoMaxBtn"] forState:UIControlStateNormal];
[_fullButton setImage:[UIImage imageNamed:@"VideoMinBtn"] forState:UIControlStateSelected];
[_fullButton addTarget:self action:@selector(fullButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _fullButton;
}
//当前播放时间
- (UILabel *) currentTimeLabel{
if (nil == _currentTimeLabel){
_currentTimeLabel = [[UILabel alloc] init];
_currentTimeLabel.textColor = [UIColor whiteColor];
_currentTimeLabel.font = [UIFont systemFontOfSize:kRealValue(12)];
_currentTimeLabel.text = @"00:00";
_currentTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _currentTimeLabel;
}
//总时间
- (UILabel *) totalTimeLabel{
if (nil == _totalTimeLabel){
_totalTimeLabel = [[UILabel alloc] init];
_totalTimeLabel.textColor = [UIColor whiteColor];
_totalTimeLabel.font = [UIFont systemFontOfSize:kRealValue(12)];
_totalTimeLabel.text = @"00:00";
_totalTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _totalTimeLabel;
}
//缓冲条
- (UIProgressView *) progress{
if (nil == _progress){
_progress = [[UIProgressView alloc] init];
_progress.trackTintColor = ProgressColor;
_progress.progressTintColor = ProgressTintColor;
}
return _progress;
}
//滑动条
- (PlayerSlider *) slider{
if (nil == _slider){
_slider = [[PlayerSlider alloc] init];
// slider开始滑动事件
[_slider addTarget:self action:@selector(progressSliderTouchBegan:) forControlEvents:UIControlEventTouchDown];
// slider滑动中事件
[_slider addTarget:self action:@selector(progressSliderValueChanged:) forControlEvents:UIControlEventValueChanged];
// slider结束滑动事件
[_slider addTarget:self action:@selector(progressSliderTouchEnded:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchUpOutside];
//左边颜色
_slider.minimumTrackTintColor = PlayFinishColor;
//右边颜色
_slider.maximumTrackTintColor = [UIColor clearColor];
}
return _slider;
}
//加载失败按钮
- (UIButton *) failButton{
if (nil == _failButton) {
_failButton = [[UIButton alloc] init];
_failButton.hidden = YES;
[_failButton setTitle:@"加载失败,点击重试" forState:UIControlStateNormal];
[_failButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_failButton.titleLabel.font = [UIFont systemFontOfSize:kRealValue(14.0f)];
_failButton.backgroundColor = [UIColor colorWithRed:0.00000f green:0.00000f blue:0.00000f alpha:0.50000f];
[_failButton addTarget:self action:@selector(failButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _failButton;
}
-(UIButton *)repeatButton{
if (nil == _repeatButton) {
_repeatButton = [UIButton buttonWithType:UIButtonTypeCustom];
_repeatButton.hidden = YES;
[_repeatButton setTitle:@"重播" forState:UIControlStateNormal];
[_repeatButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_repeatButton.titleLabel.font = [UIFont systemFontOfSize:kRealValue(14.0f)];
_repeatButton.backgroundColor = [UIColor blackColor];
[_repeatButton addTarget:self action:@selector(repeatButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _repeatButton;
}
@end
@implementation PlayerSlider
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
- (void)setup {
UIImage *thumbImage = [UIImage imageNamed:@"VideoRound"];
[self setThumbImage:thumbImage forState:UIControlStateHighlighted];
[self setThumbImage:thumbImage forState:UIControlStateNormal];
}
// 控制slider的宽和高,这个方法才是真正的改变slider滑道的高的
- (CGRect)trackRectForBounds:(CGRect)bounds{
[super trackRectForBounds:bounds];
return CGRectMake(bounds.origin.x, bounds.origin.y, CGRectGetWidth(bounds), 2);
}
//修改滑块位置
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
rect.origin.x = rect.origin.x - 6 ;
rect.size.width = rect.size.width + 12;
return CGRectInset ([super thumbRectForBounds:bounds trackRect:rect value:value], 10 , 10);
}
@end
.
//
// PlayerView.h
// video
//
// Created by admin on 17/5/5.
// Copyright © 2017年 wsl. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger,PlayerViewFillMode){
Resize = 0, //拉伸占满整个播放器,不按原比例拉伸
ResizeAspect, //按原视频比例显示,是竖屏的就显示出竖屏的,两边留黑
ResizeAspectFill, //按照原比例拉伸占满整个播放器,但视频内容超出部分会被剪切
};
@protocol PlayerViewDelegate <NSObject>
@optional
//返回按钮
- (void)clickMenuBackButton;
//播放结束
- (void)endPlay;
//旋转屏幕方向
- (void)autoFullScreen:(UIInterfaceOrientation)interfaceOrientation;
@end
@interface PlayerView : UIView
@property (nonatomic,strong) NSURL *url; //视频url
@property (nonatomic,assign) BOOL autoFullScreen; //旋转自动全屏,默认Yes
@property (nonatomic,assign) BOOL repeatPlay; //重复播放,默认No
@property (nonatomic,assign) BOOL isLandscape; //是否支持横屏,默认No
@property (nonatomic,assign) PlayerViewFillMode fillMode; //拉伸方式,默认全屏填充
@property (weak, nonatomic) id<PlayerViewDelegate> delegate;
@property (assign,nonatomic) BOOL isShowTopTool;
/**播放*/
- (void)playVideo;
/**暂停*/
- (void)pausePlay;
/**销毁播放器*/
- (void)destroyPlayer;
@end
.
//
// PlayerView.m
// video
//
// Created by admin on 17/5/5.
// Copyright © 2017年 wsl. All rights reserved.
//
#import "PlayerView.h"
#import <AVFoundation/AVFoundation.h>
#import "PlayerMenuView.h"
// 播放器的几种状态
typedef NS_ENUM(NSInteger, CLPlayerState) {
CLPlayerStateFailed, // 播放失败
CLPlayerStateBuffering, // 缓冲中
CLPlayerStatePlaying, // 播放中
CLPlayerStateStopped, // 停止播放
CLPlayerStatePause // 暂停播放
};
//方向枚举
typedef NS_ENUM(NSInteger,Direction){
Letf = 0,
Right,
};
#define DisappearTime 10 //工具条消失时间
#define KScreen_Width [UIScreen mainScreen].bounds.size.width
#define KScreen_Height [UIScreen mainScreen].bounds.size.height
#define KScreen_Bounds [UIScreen mainScreen].bounds
@interface PlayerView()<PlayerMenuDelegate>
@property (nonatomic, strong) AVPlayer *player; //播放器
@property (nonatomic, strong) AVPlayerLayer *playerLayer;//playerLayer
@property (nonatomic, strong) AVPlayerItem *playerItem; //播放器item
@property (nonatomic, assign) CLPlayerState state; //播放器的几种状态
@property (nonatomic, assign) CGRect customFarme; //控件原始Farme
@property (nonatomic, strong) UIView *fatherView; //父控件
@property (nonatomic, copy) NSString *videoFillMode; //视频拉伸模式
@property (nonatomic, strong) NSTimer *timer; //定时器
@property (nonatomic, strong) NSTimer *sliderTimer; //刷新进度的定时器
@property (nonatomic, strong) PlayerMenuView *playerMenuView;//工具条
@property (nonatomic, assign) BOOL isFullScreen; //全屏标记
@property (nonatomic, assign) BOOL landscape; //横屏标记
@property (nonatomic, assign) BOOL isDisappear; //工具条隐藏标记
@property (nonatomic, assign) BOOL isUserPlay; //用户点击播放标记
@end
@implementation PlayerView
-(PlayerMenuView *)playerMenuView{
if (nil == _playerMenuView) {
_playerMenuView = [[PlayerMenuView alloc]init];
_playerMenuView.delegate = self;
//点击事件
[_playerMenuView addTarget:self action:@selector(disappearAction:) forControlEvents:UIControlEventTouchUpInside];
//计时器,循环执行
_sliderTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timeStack) userInfo:nil repeats:YES];
//定时器,工具条消失
_timer = [NSTimer scheduledTimerWithTimeInterval:DisappearTime target:self selector:@selector(disappear) userInfo:nil repeats:NO];
}
return _playerMenuView;
}
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor blackColor];
//
_isFullScreen = NO;
_autoFullScreen = YES;
_repeatPlay = NO;
_isLandscape = NO;
_landscape = NO;
_isDisappear = NO;
_isUserPlay = NO;
//
//开启
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
//注册屏幕旋转通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientChange:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
//APP运行状态通知,将要被挂起
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
// app进入前台
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterPlayground:) name:UIApplicationDidBecomeActiveNotification object:nil];
//最上面的View, 菜单
[self addSubview:self.playerMenuView];
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
self.playerLayer.frame = self.bounds;
self.playerMenuView.frame = self.bounds;
}
#pragma mark - 屏幕旋转通知
- (void)orientChange:(NSNotification *)notification{
}
#pragma mark - APP活动通知
- (void)appDidEnterBackground:(NSNotification *)note{
//将要挂起,停止播放
[self pausePlay];
}
- (void)appDidEnterPlayground:(NSNotification *)note{
//继续播放
if (_isUserPlay) {
[self playVideo];
}
}
-(void)setIsShowTopTool:(BOOL)isShowTopTool{
_isShowTopTool = isShowTopTool;
self.playerMenuView.topToolBar.hidden = isShowTopTool;
}
#pragma mark - 定时器
- (void)timeStack{
//判断总时长
if (_playerItem.duration.timescale != 0){
//总共时长
self.playerMenuView.slider.maximumValue = 1;
//当前进度
self.playerMenuView.slider.value = CMTimeGetSeconds([_playerItem currentTime]) / (_playerItem.duration.value / _playerItem.duration.timescale);
//当前时长进度progress
NSInteger proMin = (NSInteger)CMTimeGetSeconds([_player currentTime]) / 60;//当前秒
NSInteger proSec = (NSInteger)CMTimeGetSeconds([_player currentTime]) % 60;//当前分钟
self.playerMenuView.currentTimeLabel.text = [NSString stringWithFormat:@"%02zd:%02zd", proMin, proSec];
//duration 总时长
NSInteger durMin = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale / 60;//总分钟
NSInteger durSec = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale % 60;//总秒
self.playerMenuView.totalTimeLabel.text = [NSString stringWithFormat:@"%02zd:%02zd", durMin, durSec];
}
}
//定时消失
- (void)disappear{
[UIView animateWithDuration:0.5 animations:^{
//self.playerMenuView.topToolBar.alpha = 0;
self.playerMenuView.bottomToolBar.alpha = 0;
}];
}
#pragma mark - 取消定时器
//销毁所有定时器
- (void)destroyAllTimer{
[_sliderTimer invalidate];
[_timer invalidate];
_sliderTimer = nil;
_timer = nil;
}
//销毁定时消失定时器
- (void)destroyTimer{
[_timer invalidate];
_timer = nil;
}
#pragma mark - set
//MARK: - 视频拉伸方式
-(void)setFillMode:(PlayerViewFillMode)fillMode{
switch (fillMode){
case ResizeAspectFill:
//原比例拉伸视频,直到两边屏幕都占满,但视频内容有部分会被剪切
_videoFillMode = AVLayerVideoGravityResizeAspectFill;
break;
case ResizeAspect:
//按原视频比例显示,是竖屏的就显示出竖屏的,两边留黑
_videoFillMode = AVLayerVideoGravityResizeAspect;
break;
case Resize:
//拉伸视频内容达到边框占满,但不按原比例拉伸
_videoFillMode = AVLayerVideoGravityResize;
break;
}
}
//MARK: - 是否自动支持全屏
- (void)setAutoFullScreen:(BOOL)autoFullScreen{
_autoFullScreen = autoFullScreen;
}
//MARK: - 是否支持横屏
-(void)setIsLandscape:(BOOL)isLandscape{
_isLandscape = isLandscape;
_landscape = isLandscape;
}
//MARK: - 重复播放
- (void)setRepeatPlay:(BOOL)repeatPlay{
_repeatPlay = repeatPlay;
}
//MARK: - 传入播放地址
- (void)setUrl:(NSURL *)url{
if (_url == url) {
return;
}
_url = url;
//
self.playerItem = [AVPlayerItem playerItemWithAsset:[AVAsset assetWithURL:_url]];
//判断当前的
if (self.player.currentItem) {
[self.player replaceCurrentItemWithPlayerItem:self.playerItem];
}else{
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
}
//
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
//全屏拉伸
_playerLayer.videoGravity = AVLayerVideoGravityResize;
//设置静音模式播放声音
AVAudioSession * session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
[session setActive:YES error:nil];
if (_videoFillMode){
_playerLayer.videoGravity = _videoFillMode;
}
//放到最下面,防止遮挡
[self.layer insertSublayer:_playerLayer atIndex:0];
[self setNeedsLayout];
[self layoutIfNeeded];
//播放
[self playVideo];
}
-(void)setPlayerItem:(AVPlayerItem *)playerItem{
_playerItem = playerItem;
//当播放结束, 去做一些其它的操作
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
//预播放状态,有三种情况
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
//已经缓冲的值
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
//seekToTime后,缓冲数据为空,而且有效时间内数据无法补充,播放失败
[playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
//seekToTime后,可以正常播放,相当于readyToPlay,一般拖动滑竿菊花转,到了这个这个状态菊花隐藏
[playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
}
//
- (void)setState:(CLPlayerState)state{
_state = state;
if (state == CLPlayerStateBuffering) {//缓冲中
[self.playerMenuView.activity startAnimating];
}else if (state == CLPlayerStateFailed){//加载失败
[self.playerMenuView.activity stopAnimating];
self.playerMenuView.failButton.hidden = NO;
}else{//暂停, 结束, 播放中
[self.playerMenuView.activity stopAnimating];
if (_isUserPlay) {
[self playVideo];
}
}
}
//MARK: - 隐藏或者显示状态栏方法
- (void)setStatusBarHidden:(BOOL)hidden{
//取出当前控制器的导航条
UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
//设置是否隐藏
statusBar.hidden = hidden;
}
//MARK: - 监听
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@"status"]) {
if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
self.state = CLPlayerStatePlaying;
} else if (self.player.currentItem.status == AVPlayerItemStatusFailed) {
self.state = CLPlayerStateFailed;
}
} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
// 计算缓冲进度
NSTimeInterval timeInterval = [self availableDuration];
CMTime duration = self.playerItem.duration;
CGFloat totalDuration = CMTimeGetSeconds(duration);
[self.playerMenuView.progress setProgress:timeInterval / totalDuration animated:NO];
} else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {
// 当缓冲是空的时候
if (self.playerItem.playbackBufferEmpty) {
self.state = CLPlayerStateBuffering;
[self bufferingSomeSecond];
}
} else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
// 当缓冲好的时候
if (self.playerItem.playbackLikelyToKeepUp && self.state == CLPlayerStateBuffering){
self.state = CLPlayerStatePlaying;
}
}
}
#pragma mark - 缓冲较差时候
//卡顿时会走这里
- (void)bufferingSomeSecond{
self.state = CLPlayerStateBuffering;
__block BOOL isBuffering = NO;
if (isBuffering) {
return;
}
isBuffering = YES;
// 需要先暂停一小会之后再播放,否则网络状况不好的时候时间在走,声音播放不出来
[self pausePlay];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self playVideo];
// 如果执行了play还是没有播放则说明还没有缓存好,则再次缓存一段时间
isBuffering = NO;
if (!self.playerItem.isPlaybackLikelyToKeepUp) {
[self bufferingSomeSecond];
}
});
}
//计算缓冲进度
- (NSTimeInterval)availableDuration{
NSArray *loadedTimeRanges = [[_player 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 - 播放完成
- (void)moviePlayDidEnd:(id)sender{
if (_repeatPlay){
//重播
[self resetPlay];
} else {
//暂停
[self pausePlay];
}
//显示重播按钮
self.playerMenuView.repeatButton.hidden = NO;
//
if ([self.delegate respondsToSelector:@selector(endPlay)]) {
[self.delegate endPlay];
}
}
#pragma mark - PlayerMenuDelegate
//返回按钮代理
- (void)backButtonAction:(UIButton *)sender{
if ([self.delegate respondsToSelector:@selector(clickMenuBackButton)]) {
[self.delegate clickMenuBackButton];
}
}
//播放按钮
-(void)playButtonAction:(UIButton *)sender{
if (sender.selected){
[self playVideo];
} else {
[self pausePlay];
}
}
//全屏按钮代理
- (void)fullButtonAction:(UIButton *)sender{
_isLandscape = NO;
if (_isFullScreen){
[self originalscreen];
} else {
[self fullScreenWithDirection:Letf];
}
_isLandscape = _landscape;
}
//开始滑动
- (void)progressSliderTouchBegan:(PlayerSlider *)sender{
//暂停
[self pausePlay];
[self destroyTimer];
}
//滑动中
- (void)progressSliderValueChanged:(PlayerSlider *)sender{
//计算出拖动的当前秒数
CGFloat total = (CGFloat)_playerItem.duration.value / _playerItem.duration.timescale;
NSInteger dragedSeconds = floorf(total * sender.value);
//转换成CMTime才能给player来控制播放进度
CMTime dragedCMTime = CMTimeMake(dragedSeconds, 1);
[_player seekToTime:dragedCMTime];
}
//滑动结束
- (void)progressSliderTouchEnded:(PlayerSlider *)sender{
//继续播放
[self playVideo];
_timer = [NSTimer scheduledTimerWithTimeInterval:DisappearTime target:self selector:@selector(disappear) userInfo:nil repeats:NO];
}
//失败按钮代理
- (void)failButtonAction:(UIButton *)sender{
[self setUrl:_url];
[self setNeedsLayout];
[self layoutIfNeeded];
}
//重播按钮
- (void)repeatButtonAction:(UIButton*)sender{
[self resetPlay];
}
#pragma mark - 控制
//播放
- (void)playVideo{
_isUserPlay = YES;
self.playerMenuView.playButton.selected = YES;
[_player play];
}
//暂停播放
- (void)pausePlay{
self.playerMenuView.playButton.selected = NO;
[_player pause];
}
//重新开始播放
- (void)resetPlay{
[_player seekToTime:CMTimeMake(0, 1)];
[self playVideo];
}
//销毁播放器
- (void)destroyPlayer{
//销毁定时器
[self destroyAllTimer];
[self removeObserver];
//暂停
[_player pause];
//清除
[self.player setRate:0];
[self.player replaceCurrentItemWithPlayerItem:nil];
self.playerItem = nil;
self.player = nil;
}
-(void)removeObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
[_playerItem removeObserver:self forKeyPath:@"status"];
[_playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
[_playerItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
[_playerItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
}
//点击屏幕
- (void)disappearAction:(UIButton *)button{
//取消定时消失
[self destroyTimer];
//
if (_isDisappear){
//添加定时消失
_timer = [NSTimer scheduledTimerWithTimeInterval:DisappearTime target:self selector:@selector(disappear) userInfo:nil repeats:NO];
[UIView animateWithDuration:0.5 animations:^{
// self.playerMenuView.topToolBar.alpha = 1.0;
self.playerMenuView.bottomToolBar.alpha = 1.0;
}];
} else {
[UIView animateWithDuration:0.5 animations:^{
// self.playerMenuView.topToolBar.alpha = 0;
self.playerMenuView.bottomToolBar.alpha = 0;
}];
}
_isDisappear = !_isDisappear;
}
#pragma mark - 全屏小屏
//原始大小
- (void)originalscreen{
// [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"];
if ([self.delegate respondsToSelector:@selector(autoFullScreen:)]) {
[self.delegate autoFullScreen:UIInterfaceOrientationPortrait];
}
_isFullScreen = NO;
[self setStatusBarHidden:NO];
[UIView animateWithDuration:0.25 animations:^{
//还原
self.transform = CGAffineTransformMakeRotation(0);
}];
self.frame = _customFarme;
//还原到原有父类上
[_fatherView addSubview:self];
self.playerMenuView.fullButton.selected = NO;
self.playerMenuView.topToolBar.hidden = self.isShowTopTool;
}
//全屏
- (void)fullScreenWithDirection:(Direction)direction{
if ([self.delegate respondsToSelector:@selector(autoFullScreen:)]) {
[self.delegate autoFullScreen:UIInterfaceOrientationLandscapeRight];
}
//记录播放器父类
_fatherView = self.superview;
//记录原始大小
_customFarme = self.frame;
_isFullScreen = YES;
[self setStatusBarHidden:YES];
//添加到Window上
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
[keyWindow addSubview:self];
if (_isLandscape){
self.frame = CGRectMake(0, 0, KScreen_Width, KScreen_Height);
} else {
if (direction == Letf){
[UIView animateWithDuration:0.25 animations:^{
self.transform = CGAffineTransformMakeRotation(M_PI / 2);
}];
} else {
[UIView animateWithDuration:0.25 animations:^{
self.transform = CGAffineTransformMakeRotation( - M_PI / 2);
}];
}
self.frame = CGRectMake(0, 0, KScreen_Height, KScreen_Width);
}
self.playerMenuView.fullButton.selected = YES;
[self setNeedsLayout];
[self layoutIfNeeded];
}
@end