AVPlayer自定義視訊播放器
阿新 • • 發佈:2019-02-05
我的avplayer播放器,能橫屏,豎屏,適應螢幕,上程式碼
1.我把我的那個最主要的類的.m的主要的程式碼附上
@implementation LDZMoviePlayerController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor blackColor]; self.topProgressSlider.value = 0.0; [self addGestureRecognizer]; // 新增觀察者 [self addNotificationCenters]; [self addAVPlayer]; } - (void)addAVPlayer{ playItem = [AVPlayerItem playerItemWithURL: self.movieURL]; self.playerHelper = [[LDZAVPlayerHelper alloc] init]; [_playerHelper initAVPlayerWithAVPlayerItem:playItem]; // 建立顯示層 self.playerLayer = [AVPlayerLayer playerLayerWithPlayer: _playerHelper.getAVPlayer]; _playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; // 豎屏的時候frame [self setVerticalFrame]; // 這是視訊的填充模式, 預設為AVLayerVideoGravityResizeAspect _playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; // 插入到view層上面, 沒有用addSubLayer [self.view.layer insertSublayer:_playerLayer atIndex:0]; // 新增進度觀察 [self addProgressObserver]; [self addObserverToPlayerItem: playItem]; } // 播放頁面新增輕拍手勢 - (void)addGestureRecognizer { UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissAllSubViews:)]; tap.delegate = self; [self.view addGestureRecognizer:tap]; } #pragma mark - 觀察者 觀察播放完畢 觀察螢幕旋轉 - (void)addNotificationCenters { // 註冊觀察者用來觀察,是否播放完畢 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; // 註冊觀察者來觀察螢幕的旋轉 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; } #pragma mark - 橫屏 豎屏的時候frame的設定 - (void)statusBarOrientationChange:(NSNotification *)notification { UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; if (orientation == UIInterfaceOrientationLandscapeRight) { [self setPlayerLayerFrame]; self.isFirstRotatorTap = YES; [self setTopRightBottomFrame]; } if (orientation == UIInterfaceOrientationLandscapeLeft) { [self setPlayerLayerFrame]; self.isFirstRotatorTap = YES; [self setTopRightBottomFrame]; } if (orientation == UIInterfaceOrientationPortrait) { // 豎屏的時候 [self setVerticalFrame]; self.isFirstRotatorTap = YES; [self setTopRightBottomFrame]; } } // 橫屏的時候frame - (void)setPlayerLayerFrame { CGRect frame = self.view.bounds; frame.origin.x = 20; frame.origin.y = (SCREEN_HEIGHT - SCREEN_HEIGHT * (SCREEN_WIDTH - 40) / SCREEN_WIDTH) / 2; frame.size.width = SCREEN_WIDTH - 40; frame.size.height = SCREEN_HEIGHT * (SCREEN_WIDTH - 40) / SCREEN_WIDTH; _playerLayer.frame = frame; } // 豎屏的時候frame - (void)setVerticalFrame { CGRect frame = self.view.bounds; frame.origin.x = ZERO; frame.origin.y = (SCREEN_HEIGHT - SCREEN_WIDTH * (SCREEN_WIDTH / SCREEN_HEIGHT)) / 2; frame.size.width = SCREEN_WIDTH; frame.size.height = SCREEN_WIDTH * (SCREEN_WIDTH / SCREEN_HEIGHT); _playerLayer.frame = frame; } // 動畫(出現或隱藏top - right - bottom) - (void)dismissAllSubViews:(UITapGestureRecognizer *)tap { [self setTopRightBottomFrame]; } // 設定TopRightBottomFrame - (void)setTopRightBottomFrame { __weak typeof (self) myself = self; if (!self.isFirstRotatorTap) { [UIView animateWithDuration:.2f animations:^{ myself.topView.frame = CGRectMake(myself.topView.frame.origin.x, -TOPVIEW_HEIGHT, myself.topView.frame.size.width, myself.topView.frame.size.height); myself.rightView.frame = CGRectMake(SCREEN_WIDTH, myself.rightView.frame.origin.y, myself.rightView.frame.size.width, myself.rightView.frame.size.height); myself.verticalBottomView.frame = CGRectMake(myself.verticalBottomView.frame.origin.x, SCREEN_HEIGHT, myself.verticalBottomView.frame.size.width, myself.verticalBottomView.frame.size.height); myself.ratotarBottomView.frame = CGRectMake(myself.ratotarBottomView.frame.origin.x, SCREEN_HEIGHT, myself.ratotarBottomView.frame.size.width, myself.ratotarBottomView.frame.size.height); }]; self.isFirstRotatorTap = YES; } else { [UIView animateWithDuration:.2f animations:^{ myself.topView.frame = CGRectMake(myself.topView.frame.origin.x, ZERO, myself.topView.frame.size.width, myself.topView.frame.size.height); myself.rightView.frame = CGRectMake(SCREEN_WIDTH - RIGHT_WIDTH, myself.rightView.frame.origin.y, myself.rightView.frame.size.width, myself.rightView.frame.size.height); myself.verticalBottomView.frame = CGRectMake(myself.verticalBottomView.frame.origin.x, SCREEN_HEIGHT - VERTICAL_BOTTOM_HEIGHT, myself.verticalBottomView.frame.size.width, myself.verticalBottomView.frame.size.height); myself.ratotarBottomView.frame = CGRectMake(myself.ratotarBottomView.frame.origin.x, SCREEN_HEIGHT - ROTATOR_BOTTOM_HEIGHT, myself.ratotarBottomView.frame.size.width, myself.ratotarBottomView.frame.size.height); }]; self.isFirstRotatorTap = NO; } } // 顯示top,right,bottom的View - (void)setTopRightBottomViewHiddenToShow { _topView.hidden = NO; _rightView.hidden = NO; _ratotarBottomView.hidden = NO; _verticalBottomView.hidden = NO; _isFirstRotatorTap = NO; } // 隱藏top,right,bottom的View - (void)setTopRightBottomViewShowToHidden { _topView.hidden = YES; _rightView.hidden = YES; _ratotarBottomView.hidden = YES; _verticalBottomView.hidden = YES; _isFirstRotatorTap = YES; } #pragma mark - 暫停 - (void)setMovieParse { [_playerHelper.getAVPlayer pause]; isPlay = NO; // 因為用的是xib,不設定的話圖片會重合 _verticalPlayButton.imageView.image = nil; _rotatorPlayButton.imageView.image = nil; [_verticalPlayButton setImage:[UIImage imageNamed:@"播放器_播放"] forState: UIControlStateNormal]; [_rotatorPlayButton setImage:[UIImage imageNamed:@"播放器_播放"] forState:UIControlStateNormal]; } #pragma mark - 播放 - (void)setMoviePlay { [_playerHelper.getAVPlayer play]; isPlay = YES; // 因為用的是xib,不設定的話圖片會重合 _verticalPlayButton.imageView.image = nil; _rotatorPlayButton.imageView.image = nil; [_verticalPlayButton setImage:[UIImage imageNamed:@"播放器_暫停"] forState: UIControlStateNormal]; [_rotatorPlayButton setImage:[UIImage imageNamed:@"播放器_暫停"] forState:UIControlStateNormal]; } #pragma mark - 新增進度觀察 - addProgressObserver - (void)addProgressObserver { // 設定每秒執行一次 [_playerHelper.getAVPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue: NULL usingBlock:^(CMTime time) { NSLog(@"進度觀察 + %f", _topProgressSlider.value); // 獲取當前時間 CMTime currentTime = _playerHelper.getAVPlayer.currentItem.currentTime; // 轉化成秒數 CGFloat currentPlayTime = (CGFloat)currentTime.value / currentTime.timescale; // 總時間 CMTime totalTime = playItem.duration; // 轉化成秒 _totalMovieDuration = (CGFloat)totalTime.value / totalTime.timescale; // 相減後 _topProgressSlider.value = CMTimeGetSeconds(currentTime) / _totalMovieDuration; progressSlider = CMTimeGetSeconds(currentTime) / _totalMovieDuration; NSLog(@"%f", _topProgressSlider.value); NSDate *pastDate = [NSDate dateWithTimeIntervalSince1970: currentPlayTime]; _topPastTimeLabel.text = [self getTimeByDate:pastDate byProgress: currentPlayTime]; CGFloat remainderTime = _totalMovieDuration - currentPlayTime; NSDate *remainderDate = [NSDate dateWithTimeIntervalSince1970: remainderTime]; _topRemainderLabel.text = [self getTimeByDate:remainderDate byProgress: remainderTime]; if (_isFirstRotatorTap) { [self setTopRightBottomViewShowToHidden]; } else { [self setTopRightBottomViewHiddenToShow]; } }]; // 設定topProgressSlider圖片 UIImage *thumbImage = [UIImage imageNamed:@"slider-metal-handle.png"]; [self.topProgressSlider setThumbImage:thumbImage forState:UIControlStateHighlighted]; [self.topProgressSlider setThumbImage:thumbImage forState:UIControlStateNormal]; } - (NSString *)getTimeByDate:(NSDate *)date byProgress:(float)current { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; if (current / 3600 >= 1) { [formatter setDateFormat:@"HH:mm:ss"]; } else { [formatter setDateFormat:@"mm:ss"]; } return [formatter stringFromDate:date]; } - (void)addObserverToPlayerItem:(AVPlayerItem *)playerItem { [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; } - (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem { [playerItem removeObserver:self forKeyPath:@"status"]; [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; } // 觀察者的方法, 會在載入好後觸發, 可以在這個方法中, 儲存總檔案的大小, 用於後面的進度的實現 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ AVPlayerItem *playerItem = (AVPlayerItem *)object; if ([keyPath isEqualToString:@"status"]) { AVPlayerStatus status = [[change objectForKey:@"new"] intValue]; if (status == AVPlayerStatusReadyToPlay) { NSLog(@"正在播放...,視訊總長度: %.2f",CMTimeGetSeconds(playerItem.duration)); CMTime totalTime = playerItem.duration; self.totalMovieDuration = (CGFloat)totalTime.value / totalTime.timescale; } } if ([keyPath isEqualToString:@"loadedTimeRanges"]) { NSArray *array = playerItem.loadedTimeRanges; // 本次緩衝時間範圍 CMTimeRange timeRange = [array.firstObject CMTimeRangeValue]; float startSeconds = CMTimeGetSeconds(timeRange.start); float durationSeconds = CMTimeGetSeconds(timeRange.duration); // 緩衝總長度 NSTimeInterval totalBuffer = startSeconds + durationSeconds; // NSLog(@"共緩衝%.2f", totalBuffer); NSLog(@"進度 + %f", progressSlider); self.topProgressSlider.value = progressSlider; } } #pragma mark - UIGestureRecognizerDelegate Method 方法 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { // 不讓子檢視響應點選事件 if( CGRectContainsPoint(self.topView.frame, [gestureRecognizer locationInView:self.view]) || CGRectContainsPoint(self.rightView.frame, [gestureRecognizer locationInView:self.view]) || CGRectContainsPoint(self.ratotarBottomView.frame, [gestureRecognizer locationInView:self.view]) || CGRectContainsPoint(self.verticalBottomView.frame, [gestureRecognizer locationInView:self.view])) { return NO; } else{ return YES; }; } #pragma mark - 播放進度 - (IBAction)topSliderValueChangedAction:(id)sender { UISlider *test = (UISlider *)sender; NSLog(@"進度條進度 + %f", test.value); UISlider *senderSlider = sender; double currentTime = floor(_totalMovieDuration * senderSlider.value); //轉換成CMTime才能給player來控制播放進度 CMTime dragedCMTime = CMTimeMake(currentTime, 1); [_playerHelper.getAVPlayer seekToTime:dragedCMTime completionHandler:^(BOOL finished) { if (_isPlayOrParse) { [_playerHelper.getAVPlayer play]; } }]; } - (IBAction)topSliderTouchDownAction:(id)sender { } - (IBAction)topSliderTouchUpInsideAction:(id)sender { } #pragma mark - 播放上一個 - (IBAction)rotatorUpAction:(id)sender { NSLog(@"上一個~~~"); } #pragma mark - 播放... - (IBAction)rotatorPlayAction:(UIButton *)sender { if (isPlay) { [self setMovieParse]; } else { [self setMoviePlay]; } } #pragma mark - 播放下一個... - (IBAction)rotatorNextAction:(UIButton *)sender { NSLog(@"下一個~~~"); } #pragma mark - 返回按鈕... - (IBAction)finishAction:(UIButton *)sender { NSLog(@"完成~~~"); } #pragma mark 播放結束後的代理回撥 - (void)moviePlayDidEnd:(NSNotification *)notify { // LettopRightBottomViewShow [self setTopRightBottomViewHiddenToShow]; [self setMovieParse]; // 讓這個視訊迴圈播放... } #pragma mark - 音量slider - (IBAction)bottomSoundSliderAction:(UISlider *)sender { // 0 - 1 [_playerHelper setAVPlayerVolume:sender.value]; self.rotatorBottomSlider.value = sender.value; self.verticalBottomSlider.value = sender.value; if (sender.value == 0) { self.rotatorSoundImageView.image = [UIImage imageNamed:@"播放器_靜音"]; self.verticalSoundImageView.image = [UIImage imageNamed:@"播放器_靜音"]; } else { self.rotatorSoundImageView.image = [UIImage imageNamed:@"播放器_音量"]; self.verticalSoundImageView.image = [UIImage imageNamed:@"播放器_音量"]; } } #pragma mark - 分享 - 收藏 - 快取 - (IBAction)rightShareButton:(UIButton *)sender { NSLog(@"分享~~~"); } - (IBAction)rightCollectButton:(UIButton *)sender { NSLog(@"收藏~~~"); } - (IBAction)rightCacheButton:(UIButton *)sender { NSLog(@"快取~~~"); } - (void)dealloc { // 移除觀察者,使用觀察者模式的時候,記得在不使用的時候,進行移除 [self removeObserverFromPlayerItem: _playerHelper.getAVPlayer.currentItem]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; // 返回前一個頁面的時候釋放記憶體 [self.playerHelper.getAVPlayer replaceCurrentItemWithPlayerItem:nil]; }
2.到此說完啦,為了寫這個玩意,我寫了一週啊.
3.實現了橫屏,豎屏,快進,快退,暫停等功能
原始碼地址:http://download.csdn.net/detail/fengchenlangzi_/9354939