iOS AVPlayer的那些坑
這次主要是總結和記錄下視頻播放遇到的坑,視頻播放采用的是AVPlayer這個控件,語法大致如下:
NSURL * url = [NSURL fileURLWithPath:@"視頻地址"];AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];self.player = [AVPlayer playerWithPlayerItem:playerItem];[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];self.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;self.playerLayer.frame = self.view.bounds;[self.view.layer addSublayer:self.playerLayer]; 復制代碼一般說來,這里要監聽AVPlayerItem的status屬性:
**AVPlayerItem的三種狀態*AVPlayerItemStatusUnknown,*AVPlayerItemStatusReadyToPlay,*AVPlayerItemStatusFailed*/ 復制代碼如果是AVPlayerStatusFailed說明視頻加載失敗,這時可以通過self.player.error.description屬性來找出具體的原因。 問題一:當status變為AVPlayerStatusReadyToPlay后,我們調用play方法真的就能保證視頻正常播放嗎?
眾所周知,AVPlayer支持的視頻、音頻格式非常廣泛,拋開那些無法正常編解碼的情況,在某些情況下其可能就是無法正常播放。AVPlayer在進行播放時,會預先解碼一些內容,而此時如果我們的App使用CPU過多,I/O讀寫過多時,有可能導致視頻播放聲/畫不同步,這點尤其在iPhone4上面表現更為明顯,用戶反饋iOS9.3.2的系統上也很明顯。而如果是發生在AVPlayer初始化解碼視頻的時候,有可能導致視頻直接無法播放,這時,我們再調用play或者seekToTime:方法都無法正常播放。建議不要在CPU或者I/O很頻繁的情況下使用AVPlayer,例如剛登錄App加載各種數據的情況下,可以等App預熱以后再使用。
問題二:當rate屬性的值大于0后,真的就在播放視頻了嗎?
當然不是。當發生上面所講的情況時,我打印了當前的rate情況,是大于0的,但是頁面上顯示的情況卻還是什么也沒有。有時候我們如果想要在視頻一播放的時候去做一些事情,例如設置一下播放器的背景色,如果我們僅僅是監聽這個rate可能無法100%保證有效,而如果我們真的要監聽這種情況的話,有一個取巧的方法:
id _timerObserver = [self.player addBoundaryTimeObserverForTimes:@[[NSValue valueWithCMTime:CMTimeMake(1, 30)]] queue:dispatch_get_main_queue()usingBlock:^{//do something}]; 復制代碼另外如果不需要監聽播放進度的時候可以調下面的方法:
[self.player removeTimeObserver:_timerObserver]; 復制代碼問題三:AVPlayer前后臺播放
當我們切換到后臺后,這時AVPlayer通常會自動暫停,當然如果設置了后臺播放音頻的話,是可以在后臺繼續播放聲音的,正如蘋果自己的WWDC這個App一樣。這個功能在我的另一篇文章iOS AVPlayer之后臺連續播放視頻中解決了這個問題。
問題四:音頻通道的搶占引起的無法播放視頻問題
這個問題下周我會另開一篇博客專門講述,今兒就此略過。
問題五:其它App播放聲音打斷
如果用戶當時在后臺聽音樂,如QQ音樂,或者喜馬拉雅這些App,這個時候播放視頻后,其會被我們打斷,當我們不再播放視頻的時候,自然需要繼續這些后臺聲音的播放。
首先,我們需要先向設備注冊激活聲音打斷AudioSessionSetActive(YES);,當然我們也可以通過 [AVAudioSession sharedInstance].otherAudioPlaying;這個方法來判斷還有沒有其它業務的聲音在播放。 當我們播放完視頻后,需要恢復其它業務或App的聲音,這時我們可以在退到后臺的事件中調用如下方法:
- (void)applicationDidEnterBackground:(UIApplication *)application {NSError *error =nil;AVAudioSession *session = [AVAudioSession sharedInstance];// [session setCategory:AVAudioSessionCategoryPlayback error:nil];BOOL isSuccess = [session setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];if (!isSuccess) {NSLog(@"__%@",error);}else{NSLog(@"成功了");}} 復制代碼問題六:在用戶插入和拔出耳機時,導致視頻暫停,解決方法如下
//耳機插入和拔掉通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];//耳機插入、拔出事件 - (void)audioRouteChangeListenerCallback:(NSNotification*)notification {NSDictionary *interuptionDict = notification.userInfo;NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];switch (routeChangeReason) {case AVAudioSessionRouteChangeReasonNewDeviceAvailable:break;case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:{//判斷為耳機接口AVAudioSessionRouteDescription *previousRoute =interuptionDict[AVAudioSessionRouteChangePreviousRouteKey];AVAudioSessionPortDescription *previousOutput =previousRoute.outputs[0];NSString *portType =previousOutput.portType;if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {// 拔掉耳機繼續播放if (self.playing) {[self.player play];}}}break;case AVAudioSessionRouteChangeReasonCategoryChange:// called at start - also when other audio wants to playbreak;} }復制代碼問題七:打電話等中斷事件
//中斷的通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];//中斷事件 - (void)handleInterruption:(NSNotification *)notification{NSDictionary *info = notification.userInfo;//一個中斷狀態類型AVAudioSessionInterruptionType type =[info[AVAudioSessionInterruptionTypeKey] integerValue];//判斷開始中斷還是中斷已經結束if (type == AVAudioSessionInterruptionTypeBegan) {//停止播放[self.player pause];}else {//如果中斷結束會附帶一個KEY值,表明是否應該恢復音頻AVAudioSessionInterruptionOptions options =[info[AVAudioSessionInterruptionOptionKey] integerValue];if (options == AVAudioSessionInterruptionOptionShouldResume) {//恢復播放[self.player play];}}}復制代碼小提示:如果不起作用,請檢查退到后臺事件中有什么其它的操作沒。因為電話來時,會調用退到后臺的事件。
問題七:內存泄露問題 當我們釋放一個正在播放的視頻時,需要先調用pause方法,如果由于某些原因,例如切前后臺時,導致又調用了play方法,那么有可能會hold不住內存空間而導致內存泄漏。其實更靠譜的方法是,還要移除player加載的資源。
總的來說,AVPlayer能滿足一般的需求,雖然坑不少。最后,再來推銷一下我自己封裝的LYAVPlayer,簡單方便,支持cocoa pods,只需幾行代碼即可完成播放:
LYAVPlayerView *playerView =[LYAVPlayerView alloc]init]; playerView.frame =CGRectMake(0, 64, ScreenWidth,200);playerView.delegate =self;//設置代理[self.view addSubview:playerView];[playerView setURL:[NSURL URLWithString:VideoURL]];//設置播放的URL[playerView play];//開始播放 復制代碼工程中pod 'LYAVPlayer','~> 1.0.1'即可使用。有什么問題請Issues我。
總結
以上是生活随笔為你收集整理的iOS AVPlayer的那些坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言再学习 -- ASCII码表(转)
- 下一篇: k2p华硕系统怎么设置_双频路由器怎么设