序言
巴青ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!微信現(xiàn)在這么普及,功能也做的越來越強大,不知大家對于微信朋友圈發(fā)視頻截取的功能或者蘋果拍視頻對視頻編輯的功能有沒有了解(作者這里也猜測,微信的這個功能也是仿蘋果的)。感覺這個功能確實很方便實用,近來作者也在研究音視頻功能,所以就實現(xiàn)了一下這個功能。
功能其實看著挺簡單,實現(xiàn)過程也踩了不少坑。一方面記錄一下;另一方面也算是對實現(xiàn)過程的再一次梳理,這樣大家看代碼也會比較明白。
效果
我們先看看我實現(xiàn)的效果
實現(xiàn)
實現(xiàn)過程分析
整個功能可以分為三部分:
這部分我們單獨封裝一個視頻播放器即可
這部分實現(xiàn)過程比較復(fù)雜,一共分成了4部分?;疑谏w、左右把手滑塊、滑塊中間上下兩條線、圖片管理視圖
控制器視圖邏輯組裝和功能實現(xiàn)
這里使用AVPlayer、playerLayer、AVPlayerItem這三個類實現(xiàn)了視頻播放功能;由于整個事件都是基于KVO監(jiān)聽的,所以增加了Block代碼提供了對外監(jiān)聽使用。
#import "FOFMoviePlayer.h" @interface FOFMoviePlayer() { AVPlayerLooper *_playerLooper; AVPlayerItem *_playItem; BOOL _loop; } @property(nonatomic,strong)NSURL *url; @property(nonatomic,strong)AVPlayer *player; @property(nonatomic,strong)AVPlayerLayer *playerLayer; @property(nonatomic,strong)AVPlayerItem *playItem; @property (nonatomic,assign) CMTime duration; @end @implementation FOFMoviePlayer -(instancetype)initWithFrame:(CGRect)frame url:(NSURL *)url superLayer:(CALayer *)superLayer{ self = [super init]; if (self) { [self initplayers:superLayer]; _playerLayer.frame = frame; self.url = url; } return self; } -(instancetype)initWithFrame:(CGRect)frame url:(NSURL *)url superLayer:(CALayer *)superLayer loop:(BOOL)loop{ self = [self initWithFrame:frame url:url superLayer:superLayer]; if (self) { _loop = loop; } return self; } - (void)initplayers:(CALayer *)superLayer{ self.player = [[AVPlayer alloc] init]; self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; self.playerLayer.videoGravity = AVLayerVideoGravityResize; [superLayer addSublayer:self.playerLayer]; } - (void)initLoopPlayers:(CALayer *)superLayer{ self.player = [[AVQueuePlayer alloc] init]; self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; self.playerLayer.videoGravity = AVLayerVideoGravityResize; [superLayer addSublayer:self.playerLayer]; } -(void)fof_play{ [self.player play]; } -(void)fof_pause{ [self.player pause]; } #pragma mark - Observe -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context{ if ([keyPath isEqualToString:@"status"]) { AVPlayerItem *item = (AVPlayerItem *)object; AVPlayerItemStatus status = [[change objectForKey:@"new"] intValue]; // 獲取更改后的狀態(tài) if (status == AVPlayerItemStatusReadyToPlay) { _duration = item.duration;//只有在此狀態(tài)下才能獲取,不能在AVPlayerItem初始化后馬上獲取 NSLog(@"準(zhǔn)備播放"); if (self.blockStatusReadyPlay) { self.blockStatusReadyPlay(item); } } else if (status == AVPlayerItemStatusFailed) { if (self.blockStatusFailed) { self.blockStatusFailed(); } AVPlayerItem *item = (AVPlayerItem *)object; NSLog(@"%@",item.error); NSLog(@"AVPlayerStatusFailed"); } else { self.blockStatusUnknown(); NSLog(@"%@",item.error); NSLog(@"AVPlayerStatusUnknown"); } }else if ([keyPath isEqualToString:@"tracking"]){ NSInteger status = [change[@"new"] integerValue]; if (self.blockTracking) { self.blockTracking(status); } if (status) {//正在拖動 [self.player pause]; }else{//停止拖動 } }else if ([keyPath isEqualToString:@"loadedTimeRanges"]){ NSArray *array = _playItem.loadedTimeRanges; CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次緩沖時間范圍 CGFloat startSeconds = CMTimeGetSeconds(timeRange.start); CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩沖總長度 double progress = totalBuffer/CMTimeGetSeconds(_duration); if (self.blockLoadedTimeRanges) { self.blockLoadedTimeRanges(progress); } NSLog(@"當(dāng)前緩沖時間:%f",totalBuffer); }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]){ NSLog(@"緩存不夠,不能播放!"); }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]){ if (self.blockPlaybackLikelyToKeepUp) { self.blockPlaybackLikelyToKeepUp([change[@"new"] boolValue]); } } } -(void)setUrl:(NSURL *)url{ _url = url; [self.player replaceCurrentItemWithPlayerItem:self.playItem]; } -(AVPlayerItem *)playItem{ _playItem = [[AVPlayerItem alloc] initWithURL:_url]; //監(jiān)聽播放器的狀態(tài),準(zhǔn)備好播放、失敗、未知錯誤 [_playItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; // 監(jiān)聽緩存的時間 [_playItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; // 監(jiān)聽獲取當(dāng)緩存不夠,視頻加載不出來的情況: [_playItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; // 用于監(jiān)聽緩存足夠播放的狀態(tài) [_playItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(private_playerMovieFinish) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; return _playItem; } - (void)private_playerMovieFinish{ NSLog(@"播放結(jié)束"); if (self.blockPlayToEndTime) { self.blockPlayToEndTime(); } if (_loop) {//默認(rèn)提供一個循環(huán)播放的功能 [self.player pause]; CMTime time = CMTimeMake(1, 1); __weak typeof(self)this = self; [self.player seekToTime:time completionHandler:^(BOOL finished) { [this.player play]; }]; } } -(void)dealloc{ NSLog(@"-----銷毀-----"); } @end
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站www.cdcxhl.com,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。