引言 本文主要针对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 著做权归做者全部,转载请联系做者得到受权,并标注“简书做者”。