VideoHelperViewController.m 13.8 KB
//
//  VideoHelperViewController.m
//  Lighting
//
//  Created by 曹云霄 on 2016/11/25.
//  Copyright © 2016年 上海勾芒科技有限公司. All rights reserved.
//

#import "VideoHelperViewController.h"

@interface VideoHelperViewController ()<UIDocumentInteractionControllerDelegate>
{
    UISlider* volumeViewSlider;//保存需要改变的量
    float systemVolume;//系统音量值
    CGPoint startPoint;//起始位置
}


/**
 导航栏、工具类是否隐藏
 */
@property (nonatomic,assign) BOOL toolNaviViewIsHide;

@end

@implementation VideoHelperViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self uiConfigAction];
}

#pragma mark - UI
- (void)uiConfigAction
{
    self.videoNavigationView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
    self.videoToolView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
    [self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideOrShowNavigationBarAndToolBar)]];
    
    //获取系统音量
    MPVolumeView *volumeView = [[MPVolumeView alloc] init];
    volumeViewSlider = nil;
    for (UIView *view in [volumeView subviews]){
        if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
            volumeViewSlider = (UISlider *)view;
            break;
        }
    }
    systemVolume = volumeViewSlider.value;
}


#pragma mark - 播放地址
- (void)setLearningItem:(CustomStudyEntity *)learningItem
{
    _learningItem = learningItem;
    [self resetPlayer];
    if (_learningItem) {
        [self setUpAVPlayer];
        [self addAVPlayerKVO];
        [self addProgressObserver];
    }
}


#pragma mark - SetUp AVPlayer
- (void)setUpAVPlayer
{
    VIResourceLoaderManager *resourceLoaderManager = [VIResourceLoaderManager new];
    self.resourceLoaderManager = resourceLoaderManager;
    self.playerItem = [resourceLoaderManager playerItemWithURL:[NSURL URLWithString:self.learningItem.attachment.fileUrl]];
    VICacheConfiguration *configuration = [VICacheManager cacheConfigurationForURL:[NSURL URLWithString:self.learningItem.attachment.fileUrl]];
    if (configuration.progress >= 1.0) {
        NSLog(@"缓存完成");
    }
    [XBLoadingView showHUDViewWithDefaultWithView:self.view];
    self.customPlayer = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.customPlayer];
    self.playerLayer.frame = CGRectMake(0, 0, ScreenWidth*2/3, ScreenHeight/2);
    [self.view.layer insertSublayer:self.playerLayer atIndex:0];
    self.videoTitleLabel.text = self.learningItem.title;
}

#pragma mark - AVPlayer KVO
- (void)addAVPlayerKVO
{
    //播放状态属性
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //监控网络加载情况属性
    [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    //给AVPlayerItem添加播放完成通知
    [Notification addObserver:self selector:@selector(playFinish) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}

#pragma mark -KVO回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    WS(weakSelf);
    if ([keyPath isEqualToString:@"status"]) {//播放状态
        NSInteger status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
        switch (status) {
            case AVPlayerStatusFailed:
                self.bufferProgressView.progress = ZERO;
                self.playButton.selected = YES;
                [XBLoadingView hideHUDViewWithDefaultWithView:self.view];
                [XBLoadingView showHUDViewWithText:@"播放失败"];
                break;
            case AVPlayerStatusReadyToPlay://正在播放
            {
                self.playButton.selected = NO;
                [XBLoadingView hideHUDViewWithDefaultWithView:self.view];
                self.playItemTotalTimeLabel.text = [NSString stringWithFormat:@"/ %@",[self convertTime:CMTimeGetSeconds(self.playerItem.duration)]];
                NSInteger second = [self.learningItem.attachment.playTime integerValue];
                NSString *timeString = [self timeFormatted:second];
                if (![[self class] isBlankString:timeString] && second < (NSInteger)CMTimeGetSeconds(self.playerItem.duration)) {
                    [self stopPlay];
                    ShowDefaultAlertView(self, nil, [NSString stringWithFormat:@"上次播放时间:%@,是否继续播放",timeString], UIAlertControllerStyleAlert, ^{
                        [weakSelf.customPlayer seekToTime:CMTimeMake([weakSelf.learningItem.attachment.playTime integerValue], ONE) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
                        [weakSelf startPlay];
                    }, ^{
                        [weakSelf startPlay];
                    });
                }
                break;
            }
            default:
                break;
        }
    }else if ([keyPath isEqualToString:@"loadedTimeRanges"]){//缓冲
        NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度
        if (timeInterval > self.getCurrentPlayingTime+5 && !self.playButton.selected){ // 缓存 大于 播放 当前时长+5
            [self.customPlayer play];
        }
    }
}


#pragma mark - 返回当前视频播放时长
- (double)getCurrentPlayingTime{
    return self.customPlayer.currentTime.value/self.customPlayer.currentTime.timescale;
}

#pragma mark - 返回当前视频缓存时长
- (NSTimeInterval)availableDuration{
    NSArray *loadedTimeRanges = [[self.customPlayer 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)addProgressObserver {
    
    WS(weakSelf);
    AVPlayerItem *playerItem = self.customPlayer.currentItem;
    self.avplayerServer = [self.customPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time){
        float current = CMTimeGetSeconds(time);
        float total = CMTimeGetSeconds(playerItem.duration);
        //更新进度条
        float progress = current/total;
        weakSelf.bufferProgressView.progress = progress;
        //更新播放时间
        CMTime ctime = weakSelf.customPlayer.currentTime;
        weakSelf.playingTimeLabel.text = [weakSelf convertTime:ctime.value/ctime.timescale];
        //更新播放百分比
        if (progress) {
            if ([weakSelf.progressDelegate respondsToSelector:@selector(videoPlayProportion:withIndexPath:)]) {
                [weakSelf.progressDelegate videoPlayProportion:progress*100 withIndexPath:weakSelf.indexPath];
            }
        }
    }];
}

#pragma mark - 隐藏(显示)状态栏、工具栏
- (void)hideOrShowNavigationBarAndToolBar
{
    [UIView animateWithDuration:0.4 animations:^{
        self.videoNavigationView.alpha = self.toolNaviViewIsHide?1:0;
        self.videoToolView.alpha = self.toolNaviViewIsHide?1:0;
        
    }completion:^(BOOL finished) {
        self.toolNaviViewIsHide = !self.toolNaviViewIsHide;
        if (!self.toolNaviViewIsHide) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self hideOrShowNavigationBarAndToolBar];
            });
        }
    }];
}

#pragma mark -计算时间
- (NSString *)convertTime:(NSInteger)interval
{
    NSUInteger secondPerDay = 24 * 60 * 60;
    NSUInteger secondPerHour = 60 * 60;
    NSUInteger secondPerMinute = 60;
    // 剩余小时不应该大于24小时,所以应该先除去满足一天的秒数,再计算还剩下多少小时
    NSInteger hour = interval % secondPerDay / secondPerHour;
    // 剩余分钟数与上面同理
    NSInteger minute = interval % secondPerHour / secondPerMinute;
    // 剩余秒数直接等于秒数对每分钟秒数所取的余数
    NSInteger second = interval % secondPerMinute;
    NSMutableString *string = [NSMutableString string];
    if (hour) {
        [string appendString:[NSString stringWithFormat:@"%02zd:",hour]];
    }
    [string appendString:[NSString stringWithFormat:@"%02zd:%02zd",minute,second]];
    return string;
}

#pragma mark - 播放完成
- (void)playFinish
{
    [self.customPlayer seekToTime:kCMTimeZero];
    self.playButton.selected = YES;
    if ([self.delegate respondsToSelector:@selector(videoPlayFinish:withIndexPath:)]) {
        [self.delegate videoPlayFinish:self.learningItem withIndexPath:self.indexPath];
    } 
}

#pragma mark - 播放、暂停
- (IBAction)playOrPauseButtonClickAction:(UIButton *)sender {
   
    if (sender.selected && !self.learningItem) {
        if ([self.delegate respondsToSelector:@selector(isFirstPLayOrPPTPlay:)]) {
            if (!self.indexPath) {
                self.indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
            }
            [self.delegate isFirstPLayOrPPTPlay:self.indexPath];
        }
        return;
    }
    sender.selected = !sender.selected;
    if (sender.selected) {
        [self stopPlay];
    }else {
        [self startPlay];
    }
}

#pragma mark - 播放
- (void)startPlay
{
    self.playButton.selected = NO;
    [self.customPlayer play];
}

#pragma mark - 暂停
- (void)stopPlay
{
    self.playButton.selected = YES;
    [self.customPlayer pause];
}

#pragma mark - 退出播放
- (IBAction)exitPlayControllerButtonClick:(UIButton *)sender {

    if (self.playerLayer.frame.size.height == ScreenHeight) {
        if (self.zoomButtonClickBlock) {
            self.zoomButton.selected = NO;
            self.zoomButtonClickBlock(NO);
        }
    }else {
        [self.navigationController popViewControllerAnimated:YES];
    }
}

#pragma mark - 后退5秒
- (IBAction)backFiveSecondButtnClick:(UIButton *)sender {
    
    [self stopPlay];
    [self.customPlayer seekToTime:CMTimeMake([self getCurrentPlayingTime]-5, 1) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}

#pragma mark - 放大缩小按钮
- (IBAction)zoomButtonClick:(UIButton *)sender {
    
    if (!self.learningItem) {
        [XBLoadingView showHUDViewWithText:@"请先选择学习项"];return;
    }
    sender.selected = !sender.selected;
    if (self.zoomButtonClickBlock) {
        self.zoomButtonClickBlock(sender.selected);
    }
}

#pragma mark - 页面消失后释放播放器
- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [self.customPlayer pause];
    [self.customPlayer.currentItem cancelPendingSeeks];
    [self.customPlayer.currentItem.asset cancelLoading];
}

#pragma mark - 重置播放器
- (void)resetPlayer
{
    [self stopPlay];
    [self.customPlayer seekToTime:kCMTimeZero];
    [self customDealloc];
    self.bufferProgressView.progress = ZERO;
    self.playingTimeLabel.text = @"00:00";
    self.playItemTotalTimeLabel.text = @"/ 00:00";
    self.videoTitleLabel.text = nil;
    [self.playerLayer removeFromSuperlayer];
}

#pragma mark - 释放KVO
- (void)customDealloc
{
    //避免多次释放崩溃
    @try {
        [self.playerItem removeObserver:self forKeyPath:@"status"];
        [self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
        [self.customPlayer removeTimeObserver:self.avplayerServer];
        [Notification removeObserver:self];
    } @catch (NSException *exception) {
        NSLog(@"多次释放");
    }
}


#pragma mark -开始滑动时
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    if(event.allTouches.count == 1){
        //保存当前触摸的位置
        CGPoint point = [[touches anyObject] locationInView:self.view];
        startPoint = point;
    }
}

#pragma mark -手势滑动的距离
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    
    if(event.allTouches.count == 1){
        //计算位移
        CGPoint point = [[touches anyObject] locationInView:self.view];
        float dy = point.y - startPoint.y;
        int index = (int)dy;
        //20是因为排除横向滑动时的偏差
        if(index > 20){
            if(index%5==0){//每10个像素声音减一格
                if(systemVolume > 0.1){
                    systemVolume = systemVolume-0.05;
                    [volumeViewSlider setValue:systemVolume animated:YES];
                    [volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
                }
            }
        }else if (index < -20){
            if(index%5==0){//每10个像素声音增加一格
                if(systemVolume>=0 && systemVolume<1){
                    systemVolume = systemVolume+0.05;
                    [volumeViewSlider setValue:systemVolume animated:YES];
                    [volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
                }
            }
        }
        //音量调节
        [self volumeSet:volumeViewSlider];
    }
}

#pragma mark -调节音量
- (void)volumeSet:(UISlider *)slider
{
    NSArray *audioTracks = [self.playerItem.asset tracksWithMediaType:AVMediaTypeAudio];
    NSMutableArray *allAudioParams = [NSMutableArray array];
    for (AVAssetTrack *track in audioTracks) {
        
        AVMutableAudioMixInputParameters *audioInputParams =
        [AVMutableAudioMixInputParameters audioMixInputParameters];
        [audioInputParams setVolume:slider.value atTime:kCMTimeZero];
        [audioInputParams setTrackID:[track trackID]];
        [allAudioParams addObject:audioInputParams];
    }
    AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
    [audioMix setInputParameters:allAudioParams];
    [self.playerItem setAudioMix:audioMix];
}

- (void)dealloc
{
    [self customDealloc];
}

@end