1、引言
本文主要针对ZFPlayer的功能实现来剖析,以及总结一下大家遇到的问题和解决方案 首先ZFPlayer现在拥有的功能:
ZFPlayer是对AVPlayer的封装,有人会问它支持什么格式的视频播放,问这个问题的可以自行搜索AVPlayer支持的格式。
2、具体功能实现
2.1 通过一个网络链接播放视频
AVURLAsset *urlAsset = [AVURLAsset assetWithURL:videoURL];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:urlAsset];
// AVPlayerItem * playerItem = [AVPlayerItem playerItemWithURL:videoURL];
// 初始化Player
AVPlayer *player = [AVPlayer playerWithPlayerItem:self.playerItem];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
[self.layer insertSublayer:self.playerLayer atIndex:0];
2.2 播放器的常用操作
-
播放:
需要注意的是初始化完player之后不一定会马上开始播放,需要等待player的状态变为ReadyToPlay才会进行播放。
-
暂停:
2.3 播放多个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];
}
2.4 seekToTime指定从某一秒开始播放
可以使用seekToTime:定位播放头到指定的时间,如下代码:
CMTime fiveSecondsIn = CMTimeMake(5, 1);
[player seekToTime:fiveSecondsIn];
seekTime:不能精确定位,如果需要精确定位,可以使用seekToTie:toleranceBefore:toleranceAfter:,代码如下:
CMTime fiveSecondsIn = CMTimeMake(5, 1);
[player seekToTime:fiveSecondsIn toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
当tolerance=0的时候,framework需要进行大量解码工作,比较耗性能,所以,只有当你必须使用的时候才用这个方法,比如开发一个复杂的多媒体编辑应用,这需要精确的控制。
关于重播什么的就不用我多说了吧,点击重播seekToTime:kCMTimeZero。还有关于下次播放的时候从上次离开的那个时间开始播放,大家都有思路啦吧,当离开当前视频时候记录播放到哪一秒了,下次点开直接seekToTime到那一秒开始播放就好了嘛。
2.5 监听播放进度
使用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);
weakSelf.progress = current / total;
weakSelf.playTime = [NSString stringWithFormat:@”%.f“,current];
weakSelf.playDuration = [NSString stringWithFormat:@”%.2f”,total];
}
}];
移除观察者:
[player removeTimeObserver:_timeObserve];
}
2.6 监听改播放器状态
[playerItem addObserver:self forKeyPath:@”status” options:NSKeyValueObservingOptionNew context:nil];
播放器的三种状态,当playerItem的状态变为AVPlayerItemStatusReadyToPlay才会进行播放。
typedef NS_ENUM(NSInteger, AVPlayerItemStatus) {
AVPlayerItemStatusReadyToPlay,
};
播放完了需要移除观察者
[playerItem removeObserver:self forKeyPath:@”status“];
2.7 监听缓冲进度
[playerItem addObserver:self forKeyPath:@”loadedTimeRanges” options:NSKeyValueObservingOptionNew context:nil];
播放完了需要移除观察者
[playerItem removeObserver:self forKeyPath:@”loadedTimeRanges”];
2.8 监听网络缓冲状态
[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”];
2.9 监听AVPlayer播放完成通知
监听通知AVPlayerItemDidPlayToEndTimeNotification,来处理一些播放完的事情
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
2.10 系统音量相关
/**
* 获取系统音量
*/
{
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
for (UIView *view in [volumeView subviews]){
if ([view.class.description isEqualToString:@”MPVolumeSlider”]){
_volumeViewSlider = (UISlider *)view;
}
}
// 使用这个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];
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
// 耳机插入
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
{
// 耳机拔掉
// 拔掉耳机继续播放
[self play];
}
case AVAudioSessionRouteChangeReasonCategoryChange:
// called at start – also when other audio wants to play
NSLog(@”AVAudioSessionRouteChangeReasonCategoryChange”);
}
}
设置系统音量
self.volumeViewSlider.value = …
2.11 屏幕亮度相关
[UIScreen mainScreen].brightness = …
2.12 屏幕旋转相关
苹果手机除iPhone 4s(320*480)屏幕宽高比不是16:9外,其他都为16:9,所以横竖屏可以这样实现,这里必须使用autolayout,这里提供两种方法实现:
[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中为啥能转屏,而集成到自己项目中不能转屏,我可以明确的告诉你,是你们项目的横屏给禁止掉了,你可以看一下这里是否打钩啦:
有人又会问了,我们想实现这么个需求,只有在播放器页面支持横屏,其他页面不支持横屏。好了,那下边我来告诉怎么实现,首先上图中的横屏必须勾选,其次在你需要转屏的ViewController中来实现三个方法:
{
// 调用ZFPlayerSingleton单例记录播放状态是否锁定屏幕方向
return !ZFPlayerShared.isLockScreen;
}
– (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
// 页面展示的时候默认屏幕方向(当前ViewController必须是通过模态ViewController(模态带导航的无效)方式展现出来的,才会调用这个方法)
– (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
ZFPlayer内部已经实现屏幕旋转的分类(UITabBarController+ZFPlayerRotation.h UINavigationController+ZFPlayerRotation UIViewController+ZFPlayerRotation),不管你项目的rootViewController的是UINavigationController还是UITabBarController,则只需要在支持除竖屏以外的控制器实现上边三个方法就行。
下边来说说强制屏幕旋转,即使用户的手机锁定了屏幕方法,调用这个方法照样可以旋转:
/**
* 强制屏幕转屏
*
* @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];
}
/*
// 非arc下
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
[[UIDevice currentDevice] performSelector:@selector(setOrientation:)
withObject:@(orientation)];
}
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@”orientation“];
*/
}
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onDeviceOrientationChange)
name:UIDeviceOrientationDidChangeNotification
object:nil
];
原文链接:https://www.jianshu.com/p/5566077bb
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
原文地址:https://blog.csdn.net/yinshipin007/article/details/129758696
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_47746.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!