AVPlayer ---- 视频播放器常规功能



    大部分代码来源于一个Demo中的一个功能点, Demo已经给不知道地址了, 囧。  


    主要代码如下:      播放器组件类 


   

//
//  MZPlayView.m
//  视频播放器常规功能
//
//  Created by yubo on 16/7/27.
//

#import "MZPlayView.h"
#import 
#import 
#import 


/**
 *  Description
    
    关于视频这一块的业务操作全部封装在这个里面。 对外就提供一个NSUrl属性。
 
 */


typedef NS_ENUM(NSInteger,PanDirection){
    
    PanDirectionHorizontalMoved, //横向移动
    PanDirectionVerticalMoved    //纵向移动
    
};


@interface MZPlayView()


@property (nonatomic,strong) AVPlayer *player;
@property (nonatomic,strong) AVPlayerItem *playerItem;
@property (nonatomic,strong) AVPlayerLayer *playerLayer;

@property (weak, nonatomic) IBOutlet UIButton *backBtn;
@property (weak, nonatomic) IBOutlet UILabel *forwardLabel;
@property (weak, nonatomic) IBOutlet UILabel *currentTimeLabel;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (weak, nonatomic) IBOutlet UILabel *totalTimeLabel;
@property (weak, nonatomic) IBOutlet UIButton *fullScreanBtn;
@property (weak, nonatomic) IBOutlet UISlider *sliderView;
@property (weak, nonatomic) IBOutlet UIButton *startBtn;
@property (weak, nonatomic) IBOutlet UIButton *lockBtn;

@property (nonatomic,assign) bool  playViewFlag; // 隐藏和显示标识
@property (nonatomic,strong) NSTimer *timer;
@property (nonatomic,assign) bool  lockFlag; // 锁标识


@property (nonatomic,assign) PanDirection panDirection;
@property (nonatomic, assign) BOOL isVolume; // 是否调节音量
@property (nonatomic, strong) UISlider *volumeViewSlider;
@property (nonatomic, assign) CGFloat sumTime; // 用来保存的总时长


@end





@implementation MZPlayView


- (void)awakeFromNib{
    [super awakeFromNib];
   
    [_sliderView setThumbImage:[UIImage imageNamed:@"slider"] forState:UIControlStateNormal];
    
    [self insertSubview:_progressView belowSubview:_sliderView];
    // 滑块左边的颜色
    _sliderView.minimumTrackTintColor = [UIColor orangeColor];
    // 滑块右边的颜色
    _sliderView.maximumTrackTintColor = [UIColor clearColor];
}

- (void)setUrl:(NSURL *)url{
    _playerItem = [AVPlayerItem playerItemWithURL:url];
    _player = [AVPlayer playerWithPlayerItem:_playerItem];
    _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
    [self.layer insertSublayer:_playerLayer atIndex:0];
    [_player play];
    _forwardLabel.hidden = YES;
    
    // 添加播完通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
    // 添加手势
    [self createGesture];
    
    //获取系统音量
    [self configureVolume];
    
    // 添加事件
    [self sliderAddEvent];
    // 通过定时器获取 时间,
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(playerTimerAction) userInfo:nil repeats:YES];
    // 计算缓冲
    [_playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    
    [_playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    
}


#pragma mark  滑动事件添加 

- (void)sliderAddEvent{
   
    // slider开始滑动事件
    [_sliderView addTarget:self action:@selector(progressSliderTouchBegan:) forControlEvents:UIControlEventTouchDown];
    
    // slider滑动中事件
    [_sliderView addTarget:self action:@selector(progressSliderValueChanged:) forControlEvents:UIControlEventValueChanged];
    
    // slider结束滑动事件
    [_sliderView addTarget:self action:@selector(progressSliderTouchEnded:) forControlEvents:UIControlEventTouchUpInside];

}

// 开始滑动

- (void)progressSliderTouchBegan:(UISlider *)slider{
 
    // 取消延迟
//    [NSObject cancelPreviousPerformRequestsWithTarget:self];
}


// 开始变化

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

    // 拖动改变视频播放进度
    if (_player.status == AVPlayerItemStatusReadyToPlay) {
        // 总秒数
        CGFloat total = (CGFloat)_playerItem.duration.value / _playerItem.duration.timescale;
        // 已经拖动到了现在的秒数了。
        NSInteger dragedSeconds = floorf(total * slider.value);
        //转换成CMTime才能给player来控制播放进度---这个类用来控制时间的。
        CMTime dragedCMTime = CMTimeMake(dragedSeconds, 1);
        [_player pause];
        [_player seekToTime:dragedCMTime completionHandler:^(BOOL finished) {
            
            [_player play];
            
        }];
    }


}

#pragma mark slider事件结束

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



    
}



#pragma mark 重新布局 frame

- (void)layoutSubviews{
    _playerLayer.frame = self.bounds;
};


#pragma mark 添加手势, 点击隐藏视频所有按钮

- (void)createGesture{
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(playViewHanlder:)];
    tap.numberOfTapsRequired = 1;
    [self addGestureRecognizer:tap];
}


#pragma mark 手势回调处理

- (void)playViewHanlder:(UITapGestureRecognizer *)recognizer{
    if (!_lockFlag) {
        if (recognizer.state == UIGestureRecognizerStateRecognized) {
            if (!_playViewFlag) {
                [self hiddlePlayView];
            }else{
                [self showPlayView];
            }
        }
    }
}


#pragma mark 隐藏组件

- (void)hiddlePlayView{
    [UIView animateWithDuration:0.5 animations:^{
        _backBtn.alpha = 0;
        _currentTimeLabel.alpha = 0;
        _progressView.alpha = 0;
        _totalTimeLabel.alpha = 0;
        _fullScreanBtn.alpha = 0;
        _sliderView.alpha = 0;
        _startBtn.alpha = 0;
        
    } completion:^(BOOL finished) {
        _playViewFlag = !_playViewFlag;
    }];
    
}


#pragma mark 显示组件

- (void)showPlayView{
    [UIView animateWithDuration:0.5 animations:^{
        _backBtn.alpha = 1;
        _currentTimeLabel.alpha = 1;
        _progressView.alpha = 1;
        _totalTimeLabel.alpha = 1;
        _fullScreanBtn.alpha = 1;
        _sliderView.alpha = 1;
        _startBtn.alpha = 1;
        
    } completion:^(BOOL finished) {
         _playViewFlag = !_playViewFlag;
    }];
    
}


#pragma mark  播放完后---通知处理

- (void)moviePlayDidEnd:(NSNotification *)notification{
    
    
}


#pragma mark 播放或者暂停

- (IBAction)startAction:(id)sender {
    UIButton *btn = (UIButton *)sender;
    if (btn.selected) {
        [_player play];
    }else{
        [_player pause];
    }
    btn.selected = !btn.selected;
}


#pragma mark 全屏按钮事件

- (IBAction)fullScreanAction:(id)sender {
    
    UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
    UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;
    switch (interfaceOrientation) {
        case UIInterfaceOrientationPortraitUpsideDown: // 电池栏在下
            
            [self interfaceOrientation:UIInterfaceOrientationLandscapeRight];
            
            break;
        case UIInterfaceOrientationPortrait: // 电池栏在上
            
            [self interfaceOrientation:UIInterfaceOrientationLandscapeRight];
            
            break;
            
        case UIInterfaceOrientationLandscapeLeft: // 电池栏在右边
            
            [self interfaceOrientation:UIInterfaceOrientationPortrait];
            
            break;
            
        case UIInterfaceOrientationLandscapeRight: // 电池栏左边
            
            [self interfaceOrientation:UIInterfaceOrientationPortrait];
            
            break;
            
        default:
            break;
    }
}


#pragma mark  强制转屏相关

- (void)interfaceOrientation:(UIInterfaceOrientation)orientation{
    // arc下
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val = orientation;
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}


#pragma mark 通过定时器获取视频的时间问题

- (void)playerTimerAction{
    if (_playerItem.duration.timescale != 0) {
        
        _sliderView.maximumValue = 1;//音乐总共时长
        _sliderView.value = CMTimeGetSeconds([_playerItem currentTime]) / (_playerItem.duration.value / _playerItem.duration.timescale);//当前进度
        
        //duration 总时长
        NSInteger durMin = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale;//总秒
        NSInteger durSec = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale / 60;//总分钟
        _totalTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld",durSec,durMin];
        
        
        //当前时长进度progress
        NSInteger proMin = (NSInteger)CMTimeGetSeconds([_playerItem currentTime]);//当前秒
        NSInteger proSec = (NSInteger)CMTimeGetSeconds([_playerItem currentTime]) / 60;//当前分
        _currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld", proSec, proMin];
    }


}




#pragma mark 计算出总时间 

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

    if (object == _playerItem) {
        
        
        if ([keyPath isEqualToString:@"status"]) { // 这里代表添加完成
            if (_playerItem.status == AVPlayerItemStatusReadyToPlay) {
                 // 添加滑动手势
                UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panDirection:)];
                [self addGestureRecognizer:pan];
            }
        }else if([keyPath isEqualToString:@"loadedTimeRanges"]){
            NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度
            CMTime  duration = _playerItem.duration;
            CGFloat totalDuration = CMTimeGetSeconds(duration);
            [_progressView setProgress:timeInterval / totalDuration animated:NO];
        }
    }
}

#pragma mark  计算缓存

- (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 锁屏

- (IBAction)lockAction:(id)sender {
    
    UIButton *btn = (UIButton *)sender;
    btn.selected = !btn.selected;
    _lockFlag = btn.selected;
    
    if (btn.selected) {
        [btn setImage:[UIImage imageNamed:@"lock-nor"] forState:UIControlStateNormal];
    }else{
        [btn setImage:[UIImage imageNamed:@"unlock-nor"] forState:UIControlStateNormal];
    }
}



#pragma mark   平移手势

- (void)panDirection:(UIPanGestureRecognizer *)pan{

     // 根据在Uiview的位置,调节音量或者亮度
     CGPoint locationPoint = [pan locationInView:self];
     NSLog(@"当前点的位置--->%@",NSStringFromCGPoint(locationPoint));
    
     // 拖动的速度---->返回值就是你拖动时X和Y轴上的速度,速度是矢量,有方向
     CGPoint veloctyPoint = [pan velocityInView:self];
     NSLog(@"拖动的速度--->%@",NSStringFromCGPoint(veloctyPoint));
    
     // 判断是垂直移动还是水平移动
    switch (pan.state) {
        case UIGestureRecognizerStateBegan:{ // 开始移动
            
            CGFloat x = fabs(veloctyPoint.x);
            CGFloat y = fabs(veloctyPoint.y);
            if (x > y) { // 水平移动
                   _panDirection = PanDirectionHorizontalMoved;
                   CMTime time = _player.currentTime;
                   _sumTime = time.value/time.timescale;
                   NSLog(@"===%f",_sumTime);
               }else if(x < y){ // 垂直移动
                   _panDirection = PanDirectionVerticalMoved;
                   if (locationPoint.x > self.bounds.size.width/2) { // 右边是控制音量
                       _isVolume = true;
                       
                   }else{ // 左边是控制亮度
                       _isVolume = false;
                   }
               }
            }
            break;
            
        case UIGestureRecognizerStateChanged:{ // 正在移动
            
                 if (self.panDirection == PanDirectionHorizontalMoved) { // 水平
                   
                     [self horizontalMoved:veloctyPoint.x]; // 水平移动的方法只要x方向的值
                     
                 }else{ // 垂直
                   
                      [self verticalMoved:veloctyPoint.y]; // 垂直移动方法只要y方向的值
                     
                 }
            }
            break;
            
        case UIGestureRecognizerStateEnded:{
        
              NSLog(@"移动停止l......");
            
              dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                // 隐藏视图
                _forwardLabel.hidden = YES;
              });
            
              if (self.panDirection == PanDirectionHorizontalMoved) {
                  //转换成CMTime才能给player来控制播放进度
                  CMTime dragedCMTime = CMTimeMake(self.sumTime, 1);
                  [_player pause];
                  [_player seekToTime:dragedCMTime completionHandler:^(BOOL finish){
                      
                      [_player play];
                      
                  }];
                  // 把sumTime滞空,不然会越加越多
                  self.sumTime = 0;
              
              }else{
              
              
              }
        
           }
            break;
            
        default:
            break;
    }
}


#pragma mark   垂直移动处理。

- (void)verticalMoved:(CGFloat)value{

    if (_isVolume) { // 控制音量
        // 更改系统的音量
        self.volumeViewSlider.value -= value / 10000; // 越小幅度越小
        
        // 这个就对手机有效果, 最大的值为 1  。
        NSLog(@"声音----->%lf", self.volumeViewSlider.value);

    }else{ // 调节亮度
     
        [UIScreen mainScreen].brightness -= value / 10000;
        NSString *brightness = [NSString stringWithFormat:@"亮度%.0f%%",[UIScreen mainScreen].brightness/1.0*100];
        _forwardLabel.hidden = NO;
        _forwardLabel.text = brightness;
    }
}


//获取系统音量
- (void)configureVolume
{
    // 系统声音控制
    MPVolumeView *volumeView = [[MPVolumeView alloc] init];
    _volumeViewSlider = nil;
    for (UIView *view in [volumeView subviews]){
        if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
            _volumeViewSlider = (UISlider *)view;
            break;
        }
    }
    
    // 使用这个category的应用不会随着手机静音键打开而静音,可在手机静音下播放声音
    NSError *setCategoryError = nil;
    BOOL success = [[AVAudioSession sharedInstance]
                    setCategory: AVAudioSessionCategoryPlayback
                    error: &setCategoryError];
    
    if (!success) { /* handle the error in setCategoryError */ }
}



#pragma mark - pan水平移动的方法

- (void)horizontalMoved:(CGFloat)value{
    // 快进快退的方法
    NSString *style = @"";
    if (value < 0) {
        style = @"<<";
    }
    else if (value > 0){
        style = @">>";
    }
    
    // 每次滑动需要叠加时间
    self.sumTime += value / 200;
    
    // 需要限定sumTime的范围
    CMTime totalTime = self.playerItem.duration;
    CGFloat totalMovieDuration = (CGFloat)totalTime.value/totalTime.timescale;
    if (self.sumTime > totalMovieDuration) {
        self.sumTime = totalMovieDuration;
    }else if (self.sumTime < 0){
        self.sumTime = 0;
    }
    
    // 当前快进的时间
    NSString *nowTime = [self durationStringWithTime:(int)self.sumTime];
    // 总时间
    NSString *durationTime = [self durationStringWithTime:(int)totalMovieDuration];
    // 给label赋值
    _forwardLabel.text = [NSString stringWithFormat:@"%@ %@ / %@",style, nowTime, durationTime];
    _forwardLabel.hidden = NO;
}


#pragma mark - 根据时长求出字符串

- (NSString *)durationStringWithTime:(int)time
{
    // 获取分钟
    NSString *min = [NSString stringWithFormat:@"%02d",time / 60];
    // 获取秒数
    NSString *sec = [NSString stringWithFormat:@"%02d",time % 60];
    return [NSString stringWithFormat:@"%@:%@", min, sec];
}


@end

视图只设置了   6+ 的适配, 其他的没做处理。   

AVPlayer ---- 视频播放器常规功能_第1张图片



调用  

self.view.backgroundColor = [UIColor blackColor];

    NSURL *url = [NSURL URLWithString:@"http://baobab.wdjcdn.com/14562919706254.mp4"];
    
//    NSURL *url = [[NSBundle mainBundle] URLForResource:@"minion_01" withExtension:@"mp4"];
    
    [_playView setUrl:url];



效果:


AVPlayer ---- 视频播放器常规功能_第2张图片



   实现了,  暂停, 拖放,声音,亮度, 锁屏。 这一些基本简单操作。 






你可能感兴趣的:(ios)