// // VideoHelperViewController.m // Lighting // // Created by 曹云霄 on 2016/11/25. // Copyright © 2016年 上海勾芒科技有限公司. All rights reserved. // #import "VideoHelperViewController.h" @interface VideoHelperViewController () { 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], UIAlertControllerStyleActionSheet, ^{ [weakSelf startPlay]; }, ^{ [weakSelf.customPlayer seekToTime:CMTimeMake([weakSelf.learningItem.attachment.playTime integerValue], ONE) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; [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