一、運行效果
二、實現過程
①、創建播放器avPlayer
//創建播放器
url = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
AVPlayer *player = [AVPlayer playerWithURL:[NSURL URLWithString:url]];
self.avPlayer = player;
②、創建顯示屏_videoLayer
- (void)createDisplay{
// 顯示圖像的
_videoLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
//錨點的坐標
_videoLayer.position = CGPointMake(KPLAYVIEWWIDTH/2, KPLAYVIEWHEIGHT/2);
_videoLayer.bounds = CGRectMake(0, 0, KPLAYVIEWWIDTH, KPLAYVIEWHEIGHT);
// 錨點,值只能是0,1之間
_videoLayer.anchorPoint = CGPointMake(0.5, 0.5);
// AVLayerVideoGravityResizeAspect 按比例壓縮,視頻不會超出Layer的范圍(默認)
// AVLayerVideoGravityResizeAspectFill 按比例填充Layer,不會有黑邊
// AVLayerVideoGravityResize 填充整個Layer,視頻會變形
// 視頻內容拉伸的選項
_videoLayer.videoGravity = AVLayerVideoGravityResizeAspect;
// //播放時,視頻實際占的區域
// NSLog(@"%@", NSStringFromCGRect(videoLayer.videoRect));
//Layer只能添加到Layer上面
[self.displayView.layer addSublayer:_videoLayer];
}
③、添加觀察者和通知,用於監測更新視頻播放的狀態、進度、屏幕方向、狀態欄屬性、音量等等。
主要代碼如下:
//監測屏幕旋轉
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
//添加AVPlayerItem播放結束通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.avPlayer.currentItem];
//添加AVPlayerItem開始緩沖通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bufferStart:) name:AVPlayerItemPlaybackStalledNotification object:self.avPlayer.currentItem];
//KOV監控 播放器進度更新
- (void)addObserverForAVPlayer
{
AVPlayerItem *playerItem = self.avPlayer.currentItem;
// 給AVPlayer添加觀察者 必須實現 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
//監控播放速率
[self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
//監控狀態屬性(AVPlayer也有一個status屬性,通過監控它的status也可以獲得播放狀態)
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
//監控網絡加載緩沖情況屬性
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
//監控是否可播放
[playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
//播放進度觀察者 //設置每0.1秒執行一次
__weak GOVVideoView *weakSelf = self;
_playerTimeObserver = [self.avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 10.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
if (weakSelf.dragSlider) {
return ;
}
CGFloat current = CMTimeGetSeconds(time);
weakSelf.current = current;
CMTime totalTime = weakSelf.avPlayer.currentItem.duration;
CGFloat total = CMTimeGetSeconds(totalTime);
weakSelf.total = total;
weakSelf.slider.value = current/total;
weakSelf.currentTimeLabel.text = [weakSelf timeFormatted:current];
weakSelf.totalTimeLabel.text = [NSString stringWithFormat:@"/%@",[weakSelf timeFormatted:total]] ;
}];
}
//通過KVO監控回調
//keyPath 監控屬性 object 監視器 change 狀態改變 context 上下文
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
//監控網絡加載情況屬性
NSArray *array = self.avPlayer.currentItem.loadedTimeRanges;
//本次緩沖時間范圍
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
CGFloat startSeconds = CMTimeGetSeconds(timeRange.start);
CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration);
//現有緩沖總長度
CGFloat totalBuffer = startSeconds + durationSeconds;
//視頻總時長
CMTime totalTime = self.avPlayer.currentItem.duration;
CGFloat total = CMTimeGetSeconds(totalTime);
if (totalBuffer/total <= 1.0 ) {
[self.progressView setProgress:totalBuffer/total animated:YES];
}
}else if([keyPath isEqualToString:@"playbackLikelyToKeepUp"]){
if (self.avPlayer.currentItem.playbackLikelyToKeepUp == YES) {
if (_activityView != nil) {
[self.activityView startAnimating];
[self.activityView removeFromSuperview];
_activityView = nil;
}
}
}else if ([keyPath isEqualToString:@"status"]){
//監控狀態屬性
AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
switch ((status)) {
case AVPlayerStatusReadyToPlay:
break;
case AVPlayerStatusUnknown:
break;
case AVPlayerStatusFailed:
break;
}
}else if ([keyPath isEqualToString:@"rate"]){
if (self.avPlayer.rate == 1) {
}
}
}
隱藏/顯示狀態欄的方法:
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];這個方法在iOS9之后棄用了,並且需要
將View controller-based status bar appearance設置為NO;而下面的重寫方法需要將View controller-based status bar appearance設置為YES,這個方法在iOS7之后就有了;
//刷新狀態欄狀態
[self setNeedsStatusBarAppearanceUpdate];
#pragma mark -- 隱藏/顯示狀態欄的方法
/*[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];這個方法在iOS9之后棄用了,並且需要
將View controller-based status bar appearance設置為NO;而下面的重寫方法需要將View controller-based status bar appearance設置為YES,這個方法在iOS7之后就有了;
//刷新狀態欄狀態
[self setNeedsStatusBarAppearanceUpdate];
*/
//設置樣式
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
//設置是否隱藏
- (BOOL)prefersStatusBarHidden {
return self.isHiddenStatusBar;
}
//設置隱藏動畫
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationNone;
}
④、創建用於處理播放結束、關閉播放器、全屏/退出全屏、隱藏/展示footBar和topBar的Block回調方法和代理方法,方便用戶做些自定義的操作。
//Block回調方法
typedef void(^FullScreenBlock)(BOOL isFull);
typedef void(^ClosePLayerBlock)();
typedef void(^ShowBarBlock)(BOOL isShow);
typedef void(^PlayFinishedBlock)();
@class GOVVideoPlayer;
//代理方法
@protocol GOVVideoPlayerDelegate <NSObject>
//播放結束
- (void)videoPlayerPlayFinished:(GOVVideoPlayer *)videoPlayer;
//關閉播放器
- (void)videoPlayerClosePlayer:(GOVVideoPlayer *)videoPlayer;
//全屏按鈕
- (void)videoPlayerFullScreen:(GOVVideoPlayer *)videoPlayer withIsFull:(BOOL)isFull;
//隱藏/展示footBar和topBar
- (void)videoPlayerShowBar:(GOVVideoPlayer *)videoPlayer withIsShow:(BOOL)isShow;
三、項目結構圖
四、補充
GOVVideoPlayer是在繼承於UIView的基礎上封裝的視頻View;
GOVVideoController是在繼承於UIViewController的基礎上封裝的視頻視圖控制器。
兩者最大的不同是在全屏和取消全屏的處理上面:前者是一個視圖View,可以直接加在父視圖上面,全屏時是加在 [UIApplication sharedApplication].keyWindow上的,而后者,小屏時是取GOVVideoController的View加在父視圖上,全屏和取消全屏時是采用present和dismiss模態化轉場的方法 。
iOS AVPlayer視頻播放器
注:本文著作權歸作者,由demo大師代發,拒絕轉載,轉載需要作者授權