AVPlayer常用

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

NSURL*url=[NSURLfileURLWithPath:@"视频地址"];AVPlayerItem*playerItem=[AVPlayerItemplayerItemWithURL:url];self.player=[AVPlayerplayerWithPlayerItem:playerItem];[self.playeraddObserver:selfforKeyPath:@"status"options:NSKeyValueObservingOptionNewcontext:nil];self.player.actionAtItemEnd=AVPlayerActionAtItemEndNone;self.playerLayer=[AVPlayerLayerplayerLayerWithPlayer:self.player];self.playerLayer.videoGravity=AVLayerVideoGravityResizeAspect;self.playerLayer.frame=self.view.bounds;[self.view.layeraddSublayer:self.playerLayer];

这里要监听一下AVPlayer的status属性,当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.playeraddBoundaryTimeObserverForTimes:@[[NSValuevalueWithCMTime:CMTimeMake(1,30)]]queue:dispatch_get_main_queue()usingBlock:^{//do something}];

另外如果不需要监听播放进度的时候可以调

[self.playerremoveTimeObserver:_timerObserver];

AVPlayer前后台播放的那些问题

当我们切换到后台后,这时AVPlayer通常会自动暂停,当然如果设置了后台播放音频的话,是可以在后台继续播放声音的,正如苹果自己的WWDC这个App一样。

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

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

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

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

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

@try{[self.playerseekToTime:self.timetoleranceBefore:kCMTimeZerotoleranceAfter:kCMTimeZerocompletionHandler:^(BOOLfinished){if(finished){[self.playerplay];}}];}@catch(NSException*exception){[self.playerplay];}

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

[self.playerseekToTime:self.timetoleranceBefore:kCMTimeZerotoleranceAfter:kCMTimeZerocompletionHandler:

当然这个方法也会有一些问题,例如在刚启动播放的时候,以及在播放到最后一帧的时候,首先是其有可能会出现异常并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或者其它合适的模式,切换模式的代码如下:

UInt32category=kAudioSessionCategory_MediaPlayback;UInt32size=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的声音,这时我们可以调用如下方法:

OSStatusret=AudioSessionSetActiveWithFlags(NO,kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation);

其它的坑

1、在用户插入和拔出耳机时,有可能也会导致视频暂停。

其实插、拔耳机是属性改变声音输出设备的一种方式,其次还有修改为听筒、扬声器,或者其它蓝牙设备输出。相关代码如下:

AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,audioRouteChangeListenerCallback,(__bridgevoid*)self);voidaudioRouteChangeListenerCallback(void*inUserData,AudioSessionPropertyIDinPropertyID,UInt32inPropertyValueS,constvoid*inPropertyValue){UInt32propertySize=sizeof(CFStringRef);AudioSessionInitialize(NULL,NULL,NULL,NULL);CFStringRefstate=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=[[AVAssetImageGeneratoralloc]initWithAsset:self.source];if(imageGen){imageGen.appliesPreferredTrackTransform=YES;CMTimeactualTime;CGImageRefcgImage=[imageGencopyCGImageAtTime:CMTimeMakeWithSeconds(0,30)actualTime:&actualTimeerror:NULL];if(cgImage){UIImage*image=[UIImageimageWithCGImage:cgImage];CGImageRelease(cgImage);returnimage;}}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 143,809评论 1 304
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,651评论 1 257
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 95,178评论 0 213
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,241评论 0 181
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 49,047评论 1 259
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,899评论 1 178
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,503评论 2 274
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,249评论 0 168
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,125评论 6 235
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,605评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,368评论 2 215
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,723评论 1 232
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,285评论 0 32
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,190评论 2 214
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,634评论 3 209
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,651评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,052评论 0 167
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,638评论 2 232
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,760评论 2 237

推荐阅读更多精彩内容