http://ivanyuan.farbox.com/post/shi-pin-bo-fang-de-na-xie-keng

最近一直在做视频动态挂件以及一个视频播放的功能,在开始做之前,先学习了苹果的官方文档RosyWriter,熟悉了短视频拍摄、滤镜处理的一些小技巧,同学也学习了下GPUImage,最后在踩了很多坑以后才实现了视频挂件的处理。

这次主要是总结和记录下视频播放遇到的坑,视频播放采用的是AVPlayer这个控件,语法大致如下:

    NSURL * url = [NSURL fileURLWithPath:@"视频地址"];AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];self.player = [AVPlayer playerWithPlayerItem:playerItem];[self.player 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];

这里要监听一下AVPlayerstatus属性,当status的状态变为AVPlayerStatusReadyToPlay时,说明视频就可以播放了,此时我们调用[self.player play];就好了。

如果是AVPlayerStatusFailed说明视频加载失败,这时可以通过self.player.error.description属性来找出具体的原因。


status变为AVPlayerStatusReadyToPlay后,我们调用play方法真的就能保证视频正常播放吗?

众所周知,AVPlayer支持的视频、音频格式非常广泛,抛开那些无法正常编解码的情况,在某些情况下其可能就是无法正常播放。

AVPlayer在进行播放时,会预先解码一些内容,而此时如果我们的App使用CPU过多,I/O读写过多时,有可能导致视频播放声/画不同步,这点尤其在iPhone4上面表现更为明显。
而如果是发生在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一样。

如果我们想要在程序切回来前台继续播放的话,我们需要监听两个通知

    [[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(appBecomeActive:)name:UIApplicationDidBecomeActiveNotificationobject:nil];[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(appWillResignActive:)name:UIApplicationWillResignActiveNotificationobject:nil];

先在appWillResignActive:方法中记录当前播放的时间CMTime

- (void)appWillResignActive:(NSNotification *)notification
{if (self.player) {[self.player pause];self.time = self.player.currentTime;}
}

等到切回前台的时候再继续播放

@try {[self.player seekToTime:self.time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {if (finished) {[self.player play];}}];} @catch (NSException *exception) {[self.player play];}

这里如果我们只是调用[self.player play];,则在继续播放的时候可能会后退一定的时间,而如果我们想要精准地继续播放则需要下面这个方法,toleranceBefore:toleranceAfter:均设置成kCMTimeZero.

[self.player seekToTime:self.time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:

当然这个方法也会有一些问题,例如在刚启动播放的时候,以及在播放到最后一帧的时候,首先是其有可能会出现异常并crash.
所以我们用了@try@catch来捕获这个异常,当出现异常的时候直接调用play让播放器自己决定播放的进度。

另外一个问题是其在最后一帧的时候有可能会白屏,因为最后一帧的内容有可能是空的,或者其它一些特殊的中间情况,所以在视频快要播放结束的时候建议,直接使用play方法。


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

iOS系统有如下几种声音播放模式

    enum {kAudioSessionCategory_AmbientSound               = 'ambi',kAudioSessionCategory_SoloAmbientSound           = 'solo',kAudioSessionCategory_MediaPlayback              = 'medi',kAudioSessionCategory_RecordAudio                = 'reca',kAudioSessionCategory_PlayAndRecord              = 'plar',kAudioSessionCategory_AudioProcessing            = 'proc'};

App运行的时候通常只能使用一种声音播放模式,而如果我们在录制视频或者录制声音的时候,把模式设置成了kAudioSessionCategory_RecordAudio,这个时候如果我们使用AVPlayer播放视频,可能就无法播放视频。

这个时候我们需要把模式切换成kAudioSessionCategory_MediaPlayback或者其它合适的模式,切换模式的代码如下:

        UInt32 category = kAudioSessionCategory_MediaPlayback;UInt32 size = sizeof(category);AudioSessionGetProperty(kAudioSessionProperty_AudioCategory, &size, &category);if (category != kAudioSessionCategory_MediaPlayback) {category = kAudioSessionCategory_MediaPlayback;AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, size, &category);QLog_Event(MODULE_IMPB_RICHMEDIA,"change route category to media play back.");}

关于上面这几个模式的作用这儿有比较详细的解释,如果我们需要在用户静音时,不播放声音,可以选择kAudioSessionCategory_SoloAmbientSound.


其它App播放声音打断问题

如果用户当时在后台听音乐,如QQ音乐,或者喜马拉雅这些App,这个时候播放视频后,其会被我们打断,当我们不再播放视频的时候,自然需要继续这些后台声音的播放。

首先,我们需要先向设备注册激活声音打断AudioSessionSetActive(YES);,当然我们也可以通过
[AVAudioSession sharedInstance].otherAudioPlaying;这个方法来判断还有没有其它业务的声音在播放。

当我们播放完视频后,需要恢复其它业务或App的声音,这时我们可以调用如下方法:

OSStatus ret = AudioSessionSetActiveWithFlags(NO, kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation);

其它的坑

1、在用户插入和拔出耳机时,有可能也会导致视频暂停。
其实插、拔耳机是属性改变声音输出设备的一种方式,其次还有修改为听筒、扬声器,或者其它蓝牙设备输出。相关代码如下:

        AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, audioRouteChangeListenerCallback, (__bridge void*)self);void audioRouteChangeListenerCallback (void                      *inUserData,AudioSessionPropertyID    inPropertyID,UInt32                    inPropertyValueS,const void                *inPropertyValue) {UInt32 propertySize = sizeof(CFStringRef);AudioSessionInitialize(NULL, NULL, NULL, NULL);CFStringRef state = nil;//获取音频路线AudioSessionGetProperty(kAudioSessionProperty_AudioRoute,&propertySize,&state);//kAudioSessionProperty_AudioRoute:音频路线NSLog(@"%@",(NSString *)state);//Headphone 耳机  Speaker 喇叭.
}

如果是输出设备发生变化,我们如果要继续播放视频的话,我们只需监听到设备变化时调用play就好了.


2、性能问题
其实在UITableView中使用AVPlayer播放多个视频时,是很容易出现性能问题的,当然这个时候我们也通常是静音的,不然多个视频一起播声音,没有人会承受得了。

当然你可以选择muted以及把volume设置为0来达到目的。在TableViewCell重用时,我们也可以使用pause方法来暂停之前的视频,并使用- (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item方法来加载一个新的视频。

使用这样的一个套路,可能仍然无法解决切换视频时带来的卡顿,尤其在视频内容比较多的时候。关于这个问题,微信内部自己写了一个简易版的AVAssetReader+AVAssetReaderTrackOutput组件,在静音模式下播放列表里面的视频,同时也不用考虑播放模式了,微信博客链接为iOS小视频优化心得。


3、内存泄漏问题
当我们释放一个正在播放的视频时,需要先调用pause方法,如果由于某些原因,例如切前后台时,导致又调用了play方法,那么有可能会hold住内存空间而导致内存泄漏。


4、获取视频缩略图
获取首帧视频截图的方法如下:

        AVAssetImageGenerator  *imageGen = [[AVAssetImageGenerator alloc] initWithAsset:self.source];if (imageGen) {imageGen.appliesPreferredTrackTransform = YES;CMTime actualTime;CGImageRef cgImage = [imageGen copyCGImageAtTime:CMTimeMakeWithSeconds(0, 30) actualTime:&actualTime error:NULL];if (cgImage) {UIImage *image = [UIImage imageWithCGImage:cgImage];CGImageRelease(cgImage);return image;}}

总结:

视频播放只是整个富媒体的一小部分,在拍摄短视频时,各种参数的应用,对于视频的后期美颜、滤镜、着色、人脸识别,视频压缩等技术这些才是真正的难点。

细数AVPlayer的那些坑相关推荐

  1. 细数ST-LINK的种种坑:ST-LINK Connection error,USB communication error,NO target connected等

    一.正确配置ST-LINK 此部分可以帮助解决ST-LINK Connection error的问题 ST-LINK有两种接线方式,一种是JTAG,一种是SWD(SW),网上购买的ST-LINK(下图 ...

  2. 细数STM32F103的那些坑

    1.串口时钟 GPIO外设时钟都挂载在APB1总线上 串口1的时钟挂在APB2上,而串口2.串口3则是挂在APB1上 所以,在初始化串口1时,我们可以使用以下代码: RCC_APB2PeriphClo ...

  3. 细数sass安装中遇到的坑

    前言: 前两天打算清理电脑的时候,遇到了一点特殊的问题,打算重装一些东西,其中就有我一直用的顺手的SASS预编译工具. 但是在重装的时候,我发现我居然不会用了??? 靠,要不是我用了半年的Sass,我 ...

  4. 细数Qt开发的各种坑(欢迎围观)

    1:Qt的版本多到你数都数不清,多到你开始怀疑人生.从4.6开始到5.8,从MSVC编译器到MINGW编译器,从32位到64位,从Windows到Linux到MAC.MSVC版本还必须安装对应的VS2 ...

  5. 百密一疏,防不胜防,细数那些大型数据库建设过程中绕不开的坑

    构建大型数据库时,无论最开始的设计多么精妙,到后续操作的时候或多或少都会遇到一些问题,本文将来细数大型数据库中不可避免会遇到的问题. 原文标题:Feature Casualties of Large ...

  6. 细数近年来机器学习研究的几大怪现状

    人工智能领域的发展离不开学者们的贡献,然而随着研究的进步,越来越多的论文出现了「标题党」.「占坑」.「注水」等现象,暴增的顶会论文接收数量似乎并没有带来更多技术突破.最近,来自卡耐基梅隆大学的助理教授 ...

  7. 年终盘点丨细数2017云栖社区20大热点话题(附100+话题清单)

    2017,你在聚能聊里分享了多少内容?贡献了多少话题?又收获了多少呢?社区聚能聊不仅可以请教技术难题,探讨热点话题,也可以八卦日常生活,分享码农们的点点滴滴. 程序员的世界不止是眼前的代码,一样有诗和 ...

  8. 《八股文》细数Java线程、并发、锁,温故而知新

    <八股文>细数Java线程.并发.锁,温故而知新 基础 1. 并行.并发有什么区别? 2. 说说什么是进程和线程? 3. 说说线程有几种创建方式? 4. 为什么调用start()方法时会执 ...

  9. 怼完Sophia怼深度学习!细数完大神Yann LeCun 这些年怼过的N件事,原来顶级高手是这样怼人的...

    图片来源:PCmag.com 十多个小时前,深度学习大神Yann LeCun语出惊人,他宣布放弃"深度学习"这个词.因为媒体对这个词的炒作言过其实,混淆了大家真正的工作,而&quo ...

最新文章

  1. 使用Powershell批量为Azure资源添加Tags
  2. 安卓手机怎么查看iccid_安卓便签敬业签怎么查看日历月视图中一天所有的新增内容?...
  3. 【转】使用dos2unix批量转换文件
  4. 地理文本处理技术在高德的演进(下)
  5. Netty 服务 如何 接收新的连接
  6. 2017.9.29 数三角形 思考记录
  7. 量化干货:量化交易系统设计的六大细节
  8. compose yaml规则
  9. 东芝2000ac废粉盒怎么二次利用_阜新降级组件回收厂家,废太阳能板回收_振昌_光伏...
  10. 粉笔画粉笔字体样式_20多种很棒的粉笔字体可供下载
  11. 二维otsu算法python_OpenCV-Python系列之OTSU算法
  12. linux中如何从txt转为nc文件,【转】linux下nc的使用
  13. 关于邮箱显示已经回复,但是已发送邮件里面没有
  14. 率土之滨服务器维修,率土之滨征服赛季合服与转服功能详解
  15. 个人认为最low的10款鞋子
  16. mongodb数据库扩展名_MongoDB文件型数据库
  17. Java实现 LeetCode 417 太平洋大西洋水流问题
  18. 老九学堂 学习C++ 第六天
  19. STM32入门指南:了解STM32
  20. HDU1869:六度分离

热门文章

  1. android 模仿 弹性菜单
  2. string 转换int
  3. 自定义WP日志标题长度
  4. 【自然框架】 权限 的视频演示(二): 权限到字段、权限到记录
  5. 机器学习--线性回归2(共线性问题、岭回归、lasso算法)
  6. 考软件测试初学者眼影,Summer 大讲堂第一期:如何制作出版级的高分辨率图表?...
  7. vue项目中vue-router的使用
  8. houghlinesp找到多条直线_拿什么拯救焦虑的你,一个有勇气的人终将找到他的路...
  9. dataframe 排序_如何对Pandas DataFrame进行自定义排序
  10. tcpprep man 手册翻译