第一次看到簡(jiǎn)書(shū)蚕捉,感覺(jué)就喜歡上了它薪前,想我這么懶的人,看到簡(jiǎn)書(shū)后关斜,第一次有了寫點(diǎn)什么東西的欲望示括。工作也有一段時(shí)間了,有時(shí)候很想把工作上學(xué)到的東西或者一些感悟?qū)懴聛?lái)痢畜,但總是由于種種原因沒(méi)有動(dòng)手垛膝。以后工作的時(shí)間鳍侣,每天抽半個(gè)小時(shí)時(shí)間,寫技術(shù)博客吼拥,自認(rèn)為能力還遠(yuǎn)遠(yuǎn)不夠倚聚,記錄下每日點(diǎn)滴感悟,便是足夠了凿可,希望能夠堅(jiān)持下去惑折。
做視頻播放,邊下邊播枯跑,都做了一個(gè)多月了惨驶,前前后后3套方案,最后終于算是完美的解決了敛助。今天關(guān)于avplayer又學(xué)到了一點(diǎn)新的知識(shí)粗卜。
1.一般情況下需要實(shí)現(xiàn)的4個(gè)監(jiān)聽(tīng)屬性
//預(yù)播放狀態(tài),有三種情況AVPlayerItemStatusUnknown,AVPlayerItemStatusReadyToPlay,AVPlayerItemStatusFailed
[self.videoPlayItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
//緩沖進(jìn)度纳击,可有可無(wú)续扔,可以增加用戶體驗(yàn)
[self.videoPlayItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
//seekToTime后,緩沖數(shù)據(jù)為空焕数,而且有效時(shí)間內(nèi)數(shù)據(jù)無(wú)法補(bǔ)充纱昧,播放失敗
[self.videoPlayItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
//seekToTime后,可以正常播放,相當(dāng)于readyToPlay百匆,一般拖動(dòng)滑竿菊花轉(zhuǎn)砌些,到了這個(gè)這個(gè)狀態(tài)菊花隱藏
[self.videoPlayItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
2. 一般情況下需要監(jiān)聽(tīng)的4個(gè)通知中心
[[NSNotificationCenter defaultCenter]addObserver:self
selector:@selector(moviePlayDidEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:_videoPlayItem];? // 添加視頻播放結(jié)束通知
[[NSNotificationCenter defaultCenter]addObserver:self
selector:@selector(moviePlayInterrupt:)
name:AVPlayerItemPlaybackStalledNotification
object:_videoPlayItem];? //添加視頻異常中斷通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(enterBcakground:)
name:UIApplicationWillResignActiveNotification
object:nil]; //進(jìn)入后臺(tái)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(enterPlayGround:)
name:UIApplicationDidBecomeActiveNotification
object:nil]; // 返回前臺(tái)
3. rate屬性
avplayer自身有一個(gè)rate屬性
rate ==1.0,表示正在播放加匈;rate == 0.0存璃,暫停;rate == -1.0雕拼,播放失敗
為了嚴(yán)謹(jǐn)纵东,可以這樣判斷播放器狀態(tài)
if (avplayer.rate > 0 && avplayer.error == nil) {
//playing
} else {
//failed
}
4. slider
為了更好地用戶體驗(yàn),一般在滑竿滑動(dòng)過(guò)程中啥寇,停止播放時(shí)間的走動(dòng)偎球,當(dāng)前時(shí)間的顯示隨滑竿移動(dòng)而改變,當(dāng)手指離開(kāi)滑竿后辑甜,播放器從當(dāng)前點(diǎn)開(kāi)始播放衰絮,在從當(dāng)前點(diǎn)開(kāi)始播放的時(shí)候,有可能沒(méi)有緩沖數(shù)據(jù)磷醋,需要臨時(shí)加載猫牡,這時(shí)候就需要掏用到上面的方法來(lái)判斷當(dāng)前狀態(tài),加載數(shù)據(jù)還沒(méi)有播放的時(shí)候邓线,時(shí)間走動(dòng)需要停止淌友,當(dāng)開(kāi)始播放了煌恢,才開(kāi)始時(shí)間走動(dòng)。
[_playSlider addTarget:self action:@selector(playSliderChange:) forControlEvents:UIControlEventValueChanged]; //拖動(dòng)滑竿更新時(shí)間
[_playSlider addTarget:self action:@selector(playSliderChangeEnd:) forControlEvents:UIControlEventTouchUpInside];? //松手,滑塊拖動(dòng)停止
//在松手的時(shí)候震庭,也有可能會(huì)走下面其中某一個(gè)時(shí)間瑰抵,為了嚴(yán)謹(jǐn),建議加上這些事件器联,當(dāng)然二汛,松手的時(shí)候,只可能走這3個(gè)事件中的某一個(gè)
[_playSlider addTarget:self action:@selector(playSliderChangeOutEnd:) forControlEvents:UIControlEventTouchUpOutside];
[_playSlider addTarget:self action:@selector(playSliderChangeCancel:) forControlEvents:UIControlEventTouchCancel];
5.視頻播放時(shí)間顯示小技巧
這樣寫主籍,不管時(shí)間有多長(zhǎng)习贫,都可以按照 ?小時(shí):分鐘:秒數(shù) 的形式來(lái)顯示,簡(jiǎn)單吧千元!
NSString *str = nil;
if (videocurrent < 3600) {
str =? [NSString stringWithFormat:@"%02li:%02li",lround(floor(videocurrent/60.f)),lround(floor(videocurrent/1.f))%60];
} else {
str =? [NSString stringWithFormat:@"%02li:%02li:%02li",lround(floor(videocurrent/3600.f)),lround(floor(videocurrent%3600)/60.f),lround(floor(videocurrent/1.f))%60];
}
6.附上比較重要的一段代碼苫昌,監(jiān)聽(tīng)事件
//kvo監(jiān)聽(tīng)播放狀態(tài)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
AVPlayerItem *playerItem = (AVPlayerItem *)object;
if ([keyPath isEqualToString:@"status"]) {
if ([playerItem status] == AVPlayerStatusReadyToPlay) {
CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 轉(zhuǎn)換成秒
[self updateTotolTime:totalSecond];
[self setPlaySliderValue:totalSecond];
_videoLength = totalSecond;
[self monitoringPlayback:_videoPlayItem];// 監(jiān)聽(tīng)播放狀態(tài)
NSLog(@"readytoPlayreadytoPlayreadytoPlayreadytoPlay");
if ([self.delegate respondsToSelector:@selector(TBAVplayerReadyToPlay:)]) {
[self.delegate TBAVplayerReadyToPlay:self];
}
[_showView.layer addSublayer:self.videoPlayerLayer];
} else if ([playerItem status] == AVPlayerStatusFailed || [playerItem status] == AVPlayerStatusUnknown) {
if ([self.delegate respondsToSelector:@selector(TBAVPlayerPlayReadyFailed:)]) {
[self.delegate TBAVPlayerPlayReadyFailed:self];
}
}
} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
if (_isFinishLoading || _localVideo) {
[self.videoProgressView setProgress:1 animated:NO];? ? //如果視頻下載完成,把緩沖進(jìn)度置為滿格
} else {
NSTimeInterval timeInterval = [self availableDuration];// 計(jì)算緩沖進(jìn)度
CMTime duration = self.videoPlayItem.duration;
CGFloat totalDuration = CMTimeGetSeconds(duration);
[self.videoProgressView setProgress:timeInterval / totalDuration animated:YES];
}
} else if (object == playerItem && [keyPath isEqualToString:@"playbackBufferEmpty"]) {
if (playerItem.playbackBufferEmpty) {
//Your code here
[_videoPlayerLayer.player pause];
[_hudHelper showHudOnView:_showView caption:nil image:nil acitivity:YES autoHideTime:0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_videoPlayerLayer.player play];
//rate 是avplayer 是一個(gè)屬性幸海,rate 1.0表示正在播放祟身,0.0暫停, -1播放器失效
if (self.rate <= 0) {
[[iToast makeText:@"播放異常"] show];
}
});
}
} else if (object == playerItem && [keyPath isEqualToString:@"playbackLikelyToKeepUp"])
{
if (playerItem.playbackLikelyToKeepUp)
{
_playState = YES;
//Your code here拖動(dòng)之后開(kāi)始播放
[_hudHelper hideHud];
}
}
}