引言 本文主要針對ZFPlayer的功能實現來剖析,以及總結一下你們遇到的問題和解決方案 首先ZFPlayer如今擁有的功能: 支持橫、豎屏切換,在全屏播放模式下還能夠鎖定屏幕方向 支持本地視頻、網絡視頻播放 支持在TableviewCell播放視頻 左側1/2位置上下滑動調節屏幕亮度(模擬器調不了亮度,請在真機調試) 右側1/2位置上下滑動調節音量(模擬器調不了音量,請在真機調試) 左右滑動調節播放進度 全屏狀態下拖動slider控制進度,顯示視頻的預覽圖 斷點下載功能 切換視頻分辨率、 ZFPlayer是對AVPlayer的封裝,有人會問它支持什麼格式的視頻播放,問這個問題的能夠自行搜索AVPlayer支持的格式。 跟AVPlayer聯繫密切的名詞: Asset:AVAsset是抽象類,不能直接使用,其子類AVURLAsset能夠根據URL生成包含媒體信息的Asset對象。 AVPlayerItem:和媒體資源存在對應關係,管理媒體資源的信息和狀態。 AVPlayerLayer: CALayer的subclass,它主要用來在iOS中播放視頻內容 具體功能實現 一、經過一個網絡連接播放視頻 AVURLAsset *urlAsset = [AVURLAsset assetWithURL:videoURL]; // 初始化playerItem AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:urlAsset]; // 也可使用來初始化playerItem // AVPlayerItem * playerItem = [AVPlayerItem playerItemWithURL:videoURL]; // 初始化Player AVPlayer *player = [AVPlayer playerWithPlayerItem:self.playerItem]; // 初始化playerLayer AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; // 添加playerLayer到self.layer [self.layer insertSublayer:self.playerLayer atIndex:0]; 二、播放器的經常使用操做 播放: [player play]; 須要注意的是初始化完player以後不必定會立刻開始播放,須要等待player的狀態變爲ReadyToPlay纔會進行播放。 暫停: [player pause]; 三、播放多個items 這裏咱們有兩種方式能夠實現,一種是由你自行控制下一首歌曲的item,將其替換到當前播放的item [player replaceCurrentItemWithPlayerItem:playerItem]; 在iOS9後,AVPlayer的replaceCurrentItemWithPlayerItem方法在切換視頻時底層會調用信號量等待而後致使當前線程卡頓,若是在UITableViewCell中切換視頻播放使用這個方法,會致使當前線程凍結幾秒鐘。遇到這個坑還真很差在系統層面對它作什麼,後來找到的解決方法是在每次須要切換視頻時,需從新建立AVPlayer和AVPlayerItem。 另外一種可使用AVQueuePlayer播放多個items,AVQueuePlayer是AVPlayer的子類,能夠用一個數組來初始化一個AVQueuePlayer對象。代碼以下: NSArray *items = <#An array of player items#>; AVQueuePlayer *queuePlayer = [[AVQueuePlayer alloc] initWithItems:items]; 和AVPlayer同樣,直接調用play方法來播放,queue player順序播放隊列中的item,若是想要跳過一個item,播放下一個item,能夠調用方法advanceToNextItem。 能夠對隊列進行插入和刪除操做,調用方法insertItem:afterItem:, removeItem:, 和 removeAllItems。正常狀況下當插入一個item以前,應該檢查是否能夠插入,經過使用canInsertItem:afterItem:方法,第二個參數傳nil,代碼以下: AVPlayerItem *anItem = <#Get a player item#>; if ([queuePlayer canInsertItem:anItem afterItem:nil]) { [queuePlayer insertItem:anItem afterItem:nil]; } 四、seekToTime指定從某一秒開始播放 可使用seekToTime:定位播放頭到指定的時間,以下代碼: CMTime fiveSecondsIn = CMTimeMake(5, 1); [player seekToTime:fiveSecondsIn]; seekTime:不能精肯定位,若是須要精肯定位,可使用seekToTime:toleranceBefore:toleranceAfter:,代碼以下: CMTime fiveSecondsIn = CMTimeMake(5, 1); [player seekToTime:fiveSecondsIn toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; 當tolerance=0的時候,framework須要進行大量解碼工做,比較耗性能,因此,只有當你必須使用的時候才用這個方法,好比開發一個複雜的多媒體編輯應用,這須要精確的控制。 關於重播什麼的就不用我多說了吧,點擊重播seekToTime:kCMTimeZero。還有關於下次播放的時候從上次離開的那個時間開始播放,你們都有思路啦吧,當離開當前視頻時候記錄播放到哪一秒了,下次點開直接seekToTime到那一秒開始播放就行了嘛。 五、監聽播放進度 使用addPeriodicTimeObserverForInterval:queue:usingBlock:來監聽播放器的進度 (1)方法傳入一個CMTime結構體,每到必定時間都會回調一次,包括開始和結束播放 (2)若是block裏面的操做耗時太長,下次不必定會收到回調,因此儘可能減小block的操做耗時 (3)方法會返回一個觀察者對象,當播放完畢時須要移除這個觀察者 添加觀察者: id timeObserve = [player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { float current = CMTimeGetSeconds(time); float total = CMTimeGetSeconds(songItem.duration); if (current) { weakSelf.progress = current / total; weakSelf.playTime = [NSString stringWithFormat:@"%.f",current]; weakSelf.playDuration = [NSString stringWithFormat:@"%.2f",total]; } }]; 移除觀察者: if (timeObserve) { [player removeTimeObserver:_timeObserve]; timeObserve = nil; } 六、監聽改播放器狀態 [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; 播放器的三種狀態,當playerItem的狀態變爲AVPlayerItemStatusReadyToPlay纔會進行播放。 typedef NS_ENUM(NSInteger, AVPlayerItemStatus) { AVPlayerItemStatusUnknown, AVPlayerItemStatusReadyToPlay, AVPlayerItemStatusFailed }; 播放完了須要移除觀察者 [playerItem removeObserver:self forKeyPath:@"status"]; 七、監聽緩衝進度 [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; 播放完了須要移除觀察者 [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; 八、監聽網絡緩衝狀態 // 緩衝區空了,須要等待數據 [playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; // 緩衝區有足夠數據能夠播放了 [playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; 播放完了須要移除觀察者 [playerItem removeObserver:self forKeyPath:@"playbackBufferEmpty"]; [playerItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; 九、監聽AVPlayer播放完成通知 監聽通知AVPlayerItemDidPlayToEndTimeNotification,來處理一些播放完的事情 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 十、 系統音量相關 /** * 獲取系統音量 */ - (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 */ } // 監聽耳機插入和拔掉通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil]; } /** * 耳機插入、拔出事件 */ - (void)audioRouteChangeListenerCallback:(NSNotification*)notification { NSDictionary *interuptionDict = notification.userInfo; NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; switch (routeChangeReason) { case AVAudioSessionRouteChangeReasonNewDeviceAvailable: // 耳機插入 break; case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: { // 耳機拔掉 // 拔掉耳機繼續播放 [self play]; } break; case AVAudioSessionRouteChangeReasonCategoryChange: // called at start - also when other audio wants to play NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange"); break; } } 設置系統音量 // 0 ... 1.0的數值, 1.0是最大的聲音. self.volumeViewSlider.value = ... 十一、屏幕亮度相關 // 0 ... 1.0的數值, 1.0是最大的亮度. [UIScreen mainScreen].brightness -= ... 十二、屏幕旋轉相關 蘋果手機除iPhone 4s(320*480)屏幕寬高比不是16:9外,其餘都爲16:9,因此橫豎屏能夠這樣實現,這裏必須使用autolayout,這裏提供兩種方法實現: 使用Xib或者Storyboard的話,必須把播放器view的寬高比設置成16:9,4s的話能夠單獨適配加約束(使用sizeClasses) 使用masonry,具體代碼以下: [self.playerView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view).offset(20); make.left.right.equalTo(self.view); // 注意此處,寬高比16:9優先級比1000低就行,在由於iPhone 4S寬高比不是16:9 make.height.equalTo(self.playerView.mas_width).multipliedBy(9.0f/16.0f).with.priority(750); }]; 關於屏幕旋轉能夠這樣強制讓屏幕轉屏,有人會問了,在我demo中爲啥能轉屏,而集成到本身項目中不能轉屏,我能夠明確的告訴你,是大家項目的橫屏給禁止掉了,你能夠看一下這裏是否打鉤啦: 設備方向 有人又會問了,咱們想實現這麼個需求,只有在播放器頁面支持橫屏,其餘頁面不支持橫屏。好了,那下邊我來告訴怎麼實現,首先上圖中的橫屏必須勾選,其次在你項目window的rootViewController中來實現兩個方法: // 哪些頁面支持自動轉屏 - (BOOL)shouldAutorotate; // viewcontroller支持哪些轉屏方向 - (UIInterfaceOrientationMask)supportedInterfaceOrientations; 這兩個方法看我註釋你就知道什麼意思啦,這兩個方法會不停的調用,不信你能夠打斷點試試,具體實現你去demo看看吧,在MainViewcontroller中。 下邊來講說強制屏幕旋轉,即便用戶的手機鎖定啦屏幕方法,調用這個方法照樣能夠旋轉: /** * 強制屏幕轉屏 * * @param orientation 屏幕方向 */ - (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; // 從2開始是由於0 1 兩個參數已經被selector和target佔用 [invocation setArgument:&val atIndex:2]; [invocation invoke]; } /* // 非arc下 if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) { [[UIDevice currentDevice] performSelector:@selector(setOrientation:) withObject:@(orientation)]; } // 直接調用這個方法通不過apple上架審覈 [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"]; */ } 監聽設備旋轉通知,來處理一些UI顯示問題 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDeviceOrientationChange) name:UIDeviceOrientationDidChangeNotification object:nil ]; 文/renzifeng(簡書做者) 原文連接:http://www.jianshu.com/p/5566077bb25f 著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。