AVPlayer可以自定義視屏播放器的樣式
其中主要是理解CMTime的用法,CMTime是一個結構體,它有2個屬性很重要,一個是value:表示總共有多少幀,一個是timeScale這個表示每秒鐘多少幀
value/timeScale得到秒(我們想要的時間)
ps:支持手勢雙擊暫停和開始播放,支持左右滑動
快進快退
note : 當橫屏的時候屏幕的寬高要調換
<h2>
//
// CustomAVPlayer.m
// PlayOnNet
//
// Created by ios on 16/9/3.
// Copyright ? 2016年 fenglei. All rights reserved.
//
#import "CustomAVPlayer.h"
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#define KScreenHeight [[UIScreen mainScreen]bounds].size.height
#define KScreenWidth [[UIScreen mainScreen]bounds].size.width
@interface CustomAVPlayer ()
@property (nonatomic, strong)AVPlayer *player;//播放對象
@property (nonatomic, strong)AVPlayerItem *playerItem;//播放資源對象
@property (nonatomic, strong)UIView *backView;//背景視圖
@property (nonatomic, strong)UIButton *backButton;//返回按鈕
@property (nonatomic, strong)UILabel *currentTimeLabel;//當前播放時間label;
@property (nonatomic, strong)UILabel *totalTimeLabel;//總時間
@property (nonatomic, strong)UISlider *currentSlider;//當前播放進度
@property (nonatomic, strong)UIProgressView *availableProgress;//已緩存的進度
@property (nonatomic, strong)UIButton *playButton;//播放和暫停
@property (nonatomic, strong)NSTimer *timer;//定時器
@end
@implementation CustomAVPlayer
- (void)viewDidLoad {
[super viewDidLoad];
[self createSubviews];
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(moveSlider:) userInfo:nil repeats:YES];
}
#pragma mark - 創建子視圖
- (void)createSubviews{
//初始化媒體播放對象
self.playerItem = [[AVPlayerItem alloc]initWithURL:[NSURL URLWithString:@"http://vf1.mtime.cn/Video/2012/04/23/mp4/120423212602431929.mp4"]];
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
//創建AVPlayerLayer
AVPlayerLayer *AVLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
AVLayer.frame = CGRectMake(0, 0, self.view.layer.bounds.size.height, self.view.layer.bounds.size.width);
AVLayer.videoGravity = AVLayerVideoGravityResize;
[self.view.layer addSublayer:AVLayer];
[_player play];
//創建背景視圖
[self createbackView];
//播放完成時候的通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(finishPlay:) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
//kVO監聽已經緩存的進度(是否達到播放要求)
[self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
}
//創建背景視圖
- (void)createbackView {
self.backView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, KScreenHeight, KScreenWidth)];
self.backView.backgroundColor = [UIColor clearColor];
[self.view addSubview:self.backView];
//返回按鈕
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.backButton.frame = CGRectMake(10, 20, 40, 40);
[_backButton setImage:[UIImage imageNamed:@"gobackBtn"] forState:UIControlStateNormal];
[_backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
[self.backView addSubview:_backButton];
//創建子視圖
#pragma mark -創建子控件
[self createProgressView];
[self createSlider];
[self createLabel];
[self createPlayButton];
//添加手勢
[self createGesture];
//當用戶不在操作屏幕的時候自動隱藏背景視圖
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.5 animations:^{
_backView.alpha = 0;
}];
});
}
//當前時間進度
- (void)createSlider {
self.currentSlider = [[UISlider alloc]initWithFrame:CGRectMake(100, KScreenWidth - 60, KScreenHeight - 300, 31)];
_currentSlider.maximumValue = 1;
_currentSlider.minimumValue = 0;
[_currentSlider setThumbImage:[UIImage imageNamed:@"iconfont-yuan"] forState:UIControlStateNormal];
[_currentSlider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
[_currentSlider setMaximumTrackTintColor:[UIColor clearColor]];
[_currentSlider setMinimumTrackTintColor:[UIColor orangeColor]];
[_backView addSubview:_currentSlider];
}
//時間的label
- (void)createLabel {
//當前的時間
self.currentTimeLabel = [[UILabel alloc]initWithFrame:CGRectMake(CGRectGetMaxX(_currentSlider.frame) + 10, CGRectGetMinY(_currentSlider.frame)+5, 60, 20)];
_currentTimeLabel.text = @"00:00/";
_currentTimeLabel.backgroundColor = [UIColor redColor];
_currentTimeLabel.textColor = [UIColor whiteColor];
_currentTimeLabel.font = [UIFont boldSystemFontOfSize:17];
[self.backView addSubview:_currentTimeLabel];
//總時間
self.totalTimeLabel = [[UILabel alloc]initWithFrame:CGRectMake(CGRectGetMaxX(_currentTimeLabel.frame)+10, _currentTimeLabel.frame.origin.y, 60, 20)];
_totalTimeLabel.text = @"00:00";
_totalTimeLabel.textColor = [UIColor whiteColor];
[_backView addSubview:_totalTimeLabel];
}
//創建一個已緩存的進度條
- (void)createProgressView {
self.availableProgress = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleDefault];
_availableProgress.frame = CGRectMake(102, KScreenWidth - 60 +15 , KScreenHeight - 300+2, 2);
//進度顏色
_availableProgress.progressTintColor = [UIColor colorWithRed:67/255.0 green:67/255.0 blue:67/255.0 alpha:1];
//進度條當前的顏色
_availableProgress.trackTintColor = [UIColor colorWithRed:154/255.0 green:154/255.0 blue:154/255.0 alpha:1];
[_backView addSubview:_availableProgress];
}
//播放和暫停的按鈕
- (void)createPlayButton {
self.playButton = [UIButton buttonWithType:UIButtonTypeCustom];
_playButton.frame = CGRectMake(15, CGRectGetMinY(_currentSlider.frame), 30, 30);
[_playButton setImage:[UIImage imageNamed:@"pauseBtn@2x"] forState:UIControlStateNormal];
[_backView addSubview:_playButton];
[_playButton addTarget:self action:@selector(playAction:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - 定時器
//滑動滑塊和計算當前時間
- (void)moveSlider:(NSTimer *)timer{
//幀數不為0(能播放的狀態)
if (_playerItem.duration.timescale != 0) {
_currentSlider.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;//當前分鐘
NSInteger durMin = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale / 60;//總秒
NSInteger durSec = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale % 60;//總分鐘
_totalTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld",durMin, durSec];
_currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld/",proMin, proSec];
}
}
#pragma mark - 響應事件
//返回按鈕
- (void)backAction {
[self.player pause];
[self dismissViewControllerAnimated:YES completion:nil];
}
//播放按鈕的響應事件
- (void)playAction :(UIButton *)button {
if (_player.rate == 0) {
[_player play];
[_playButton setImage:[UIImage imageNamed:@"pauseBtn@2x"] forState:UIControlStateNormal];
}else if(_player.rate == 1) {
[_player pause];
[_playButton setImage:[UIImage imageNamed:@"playBtn@2x"] forState:UIControlStateNormal];
}
button.selected = !button.selected;
}
//滑動條的響應事件
- (void)sliderAction:(UISlider *)slider {
//視頻的總時間
CGFloat total = (CGFloat)_playerItem.duration.value / _playerItem.duration.timescale;
//當前的進度對應的播放時間
NSInteger time = floorf(total * slider.value);//求不大于傳入值的最大整數
CMTime currentTime = CMTimeMake(time, 1);
//暫停
[_player pause];
//當前視屏滑到指定位置播放
[_player seekToTime:currentTime completionHandler:^(BOOL finished) {
[_player play];
}];
}
//手勢(雙擊)響應事件
-(void)tapAction {
//暫停的狀態
if (_player.rate == 0) {
[_player play];
[_playButton setImage:[UIImage imageNamed:@"pauseBtn@2x"] forState:UIControlStateNormal];
}else if (_player.rate == 1){//播放的狀態
[_player pause];
[_playButton setImage:[UIImage imageNamed:@"playBtn@2x"] forState:UIControlStateNormal];
}
}
#pragma mark - 通知
//結束播放時候的通知
- (void)finishPlay:(id)sender {
}
#pragma mark - KVO監聽
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
//總緩存
NSTimeInterval totalCashe = [self availabelDuration];
//視屏總時長
CMTime duration = _playerItem.duration;
CGFloat totalDuration = CMTimeGetSeconds(duration);
//求緩存進度條
_availableProgress.progress = totalCashe / totalDuration;
}
}
//獲取緩存區域
- (NSTimeInterval)availabelDuration {
NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges];
//獲取緩存區域
CMTimeRange range = [loadedTimeRanges.firstObject CMTimeRangeValue];
float startSeconds = CMTimeGetSeconds(range.start);
float durationSeconds = CMTimeGetSeconds(range.duration);
NSTimeInterval total = startSeconds + durationSeconds;//計算緩存總進度
return total;
}
#pragma mark - 手勢
- (void)createGesture {
//雙擊
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction)];
tap.numberOfTapsRequired = 2;
[_backView addGestureRecognizer:tap];
}
#pragma mark - 屏幕旋轉
- (BOOL)shouldAutorotate {
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;//只支持橫屏
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;//默認橫屏向右
}
- (BOOL)prefersStatusBarHidden {
return NO;//不隱藏狀態欄
}
#pragma mark - 點擊屏幕的響應事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
if (_backView.alpha != 1) {
[UIView animateWithDuration:0.5 animations:^{
_backView.alpha = 1;
}];
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//獲取滑動后的x軸偏移量
//1>獲取最先點擊的點
CGPoint prePoint = [[touches anyObject] previousLocationInView:_backView];
//2>最后手指離開屏幕的那個點
CGPoint lastPoint = [[touches anyObject]locationInView:_backView];
//x軸偏移量
CGFloat offsetX = lastPoint.x - prePoint.x;
if (offsetX >= 20 || offsetX <= -20) {
//滑動的百分比
CGFloat percent = offsetX / KScreenHeight;
//視頻的總時間
CGFloat totalTime = (_playerItem.duration.value / _playerItem.duration.timescale);
CGFloat currentTime = CMTimeGetSeconds([_playerItem currentTime]);
currentTime = totalTime * percent + currentTime;
if (currentTime >= totalTime) {
[_player pause];
return;
}
CMTime time = CMTimeMake(currentTime, 1);
[_player pause];
[_player seekToTime:time completionHandler:^(BOOL finished) {
[_player play];
}];
}
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGFloat currentTime = CMTimeGetSeconds([_playerItem currentTime]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((currentTime+7) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.5 animations:^{
_backView.alpha = 0;
}];
});
// [_backView performSelector:@selector(setAlpha:) withObject:[NSNumber numberWithInt:0] afterDelay:currentTime + 7];
}
#pragma mark - 銷毀通知和KVO
- (void)dealloc {
[_timer invalidate];
_timer = nil;
[[NSNotificationCenter defaultCenter]removeObserver:self];
[_playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}
@end
其中很重要的一個是loadedTimeRanges屬性,它是一個數組。
//獲取緩存區域
CMTimeRange range = [loadedTimeRanges.firstObject CMTimeRangeValue];
//獲取緩存的開始
float startSeconds = CMTimeGetSeconds(range.start);
float durationSeconds = CMTimeGetSeconds(range.duration);
NSTimeInterval total = startSeconds + durationSeconds;//計算緩存總進度
有些地方寫的不好,各位看官隨意,多多體諒