博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS AVPlayer的那些坑
阅读量:6911 次
发布时间:2019-06-27

本文共 5695 字,大约阅读时间需要 18 分钟。

这次主要是总结和记录下视频播放遇到的坑,视频播放采用的是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一样。这个功能在我的另一篇文章中解决了这个问题。

问题四:音频通道的抢占引起的无法播放视频问题

这个问题下周我会另开一篇博客专门讲述,今儿就此略过。

问题五:其它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 play                        break;    }}    复制代码

问题七:打电话等中断事件

//中断的通知    [[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能满足一般的需求,虽然坑不少。最后,再来推销一下我自己封装的,简单方便,支持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我。

转载地址:http://umfcl.baihongyu.com/

你可能感兴趣的文章
keepalived双BACKUP加nopreempt失效、手动监控服务脚步。
查看>>
form表单回车提交问题,JS监听回车事件
查看>>
ubuntu12.04 修改登陆用户 为root
查看>>
silverlight开发实例(Prism+MVVM+RIA)(二)--创建shell及用户登录
查看>>
jsp中将后台传递过来的json格式的list数据绑定到下拉菜单select
查看>>
Project Euler 85 :Counting rectangles 数长方形
查看>>
MYSQL查询某字段中以逗号分隔的字符串的方法
查看>>
Excel设置下拉菜单并隐藏下拉菜单来源单元格内容
查看>>
Java8初体验(二)Stream语法详解
查看>>
微服务架构——不是免费的午餐
查看>>
基于HTML5的Web SCADA工控移动应用
查看>>
VS 2015相当不错的功能:C#交互窗口
查看>>
hive复杂类型与java类型的对应
查看>>
[Ubuntu] ubuntu10.04系统维护之Wine的安装
查看>>
iOS获取UIView上某点的颜色值
查看>>
cocos2d-x 3.0 android mk文件 之 自己主动遍历*.cpp文件
查看>>
python数字图像处理(7):图像的形变与缩放
查看>>
设计模式-观察者模式(上)<转>
查看>>
RabbitMQ 集群与高可用配置
查看>>
Java学习——何为JNDI
查看>>