iOS---AVPlayer

AVPlayer:

AVPlayer存在于AVFoundation中,其实它是一个视频播放器,但是用它来播放音乐是没问题的,本地音频和流媒体播放,但处理音频不够灵活;但是它对视屏有很高自由度的控制,而且能够自定义视屏播放界面。.iOS9后,AVFoundation框架还做了几点修改,如果需要切换视频播放的时间,或需要控制视频从头播放调用seekToDate方法,需要保持视频的播放rate大于0才能修改,还有canUseNetworkResourcesForLiveStreamingWhilePaused这个属性,在iOS9前默认为YES,之后默认为NO。在iOS9后,AVPlayer的replaceCurrentItemWithPlayerItem方法在切换视频时底层会调用信号量等待然后导致当前线程卡顿,如果在UITableViewCell中切换视频播放使用这个方法,会导致当前线程冻结几秒钟。解决方法是在每次需要切换视频时,需重新创建AVPlayer和AVPlayerItem。

利用AVPlayer播放本地音乐代码如下:

- (void)initMusic3{

NSString* musicPath = [[NSBundle mainBundle]pathForResource:@"chirp" ofType:@"mp3"];

//构建URL

NSURL *url = [NSURL fileURLWithPath:musicPath];

AVPlayerItem* songItem = [[AVPlayerItem alloc]initWithURL:url];

_avplayer = [[AVPlayer alloc]initWithPlayerItem:songItem];

[_avplayer play];

}

- (void)dealloc{

_avplayer = nil;

}

使用AVPlayer的方法开启下载服务


1.AVURLAsset *urlAsset = [[AVURLAsset alloc]initWithURL:url options:nil];

2.AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset];

3.[self.avPlayer replaceCurrentItemWithPlayerItem:item];

4.[self addObserverToPlayerItem:item];

但由于AVPlayer是没有提供方法给我们直接获取它下载下来的数据,所以我们只能在视频下载完之后自己去寻找缓存视频数据的办法,AVFoundation框架中有一种从多媒体信息类AVAsset中提取视频数据的类AVMutableComposition和AVAssetExportSession。

其中AVMutableComposition的作用是能够从现有的asset实例中创建出一个新的AVComposition(它也是AVAsset的字类),使用者能够从别的asset中提取他们的音频轨道或视频轨道,并且把它们添加到新建的Composition中。

AVAssetExportSession的作用是把现有的自己创建的asset输出到本地文件中。

为什么需要把原先的AVAsset(AVURLAsset)实现的数据提取出来后拼接成另一个AVAsset(AVComposition)的数据后输出呢,由于通过网络url下载下来的视频没有保存视频的原始数据(或者苹果没有暴露接口给我们获取),下载后播放的avasset不能使用AVAssetExportSession输出到本地文件,要曲线地把下载下来的视频通过重构成另外一个AVAsset实例才能输出。代码例子如下:

NSString *documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

NSString *myPathDocument = [documentDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",[_source.videoUrl MD5]]];

NSURL *fileUrl = [NSURL fileURLWithPath:myPathDocument];

if (asset != nil) {

AVMutableComposition *mixComposition = [[AVMutableComposition alloc]init];

AVMutableCompositionTrack *firstTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

[firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0] atTime:kCMTimeZero error:nil];

AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0] atTime:kCMTimeZero error:nil];

AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];

exporter.outputURL = fileUrl;

if (exporter.supportedFileTypes) {

exporter.outputFileType = [exporter.supportedFileTypes objectAtIndex:0] ;

exporter.shouldOptimizeForNetworkUse = YES;

[exporter exportAsynchronouslyWithCompletionHandler:^{

}];

}

}


利用AVPlayer-在线播放

- (void)initMusic3{

NSURL* url = [NSURL URLWithString:@"http://qy.bbzx.wuxi.cn/MyImages/2010-3/b478f0bb-b906-435c-9150-d691fde227f1.mp3"];

AVPlayerItem* songItem = [[AVPlayerItem alloc]initWithURL:url];

_avplayer = [[AVPlayer alloc]initWithPlayerItem:songItem];

[_avplayer play];

}

- (void)dealloc{

_avplayer = nil;

}

利用AVPlayer-在线播放--并且获取播放器的播放进度

- (void)initMusic3{

NSURL* url = [NSURL URLWithString:@"http://qy.bbzx.wuxi.cn/MyImages/2010-3/b478f0bb-b906-435c-9150-d691fde227f1.mp3"];

AVPlayerItem* songItem = [[AVPlayerItem alloc]initWithURL:url];//AVPlayerItem:和媒体资源存在对应关系,管理媒体资源的信息和状态

_avplayer = [[AVQueuePlayer alloc]init];

[_avplayer replaceCurrentItemWithPlayerItem:songItem];//此方法也可以实现歌曲切换-上一首或者下一首--自行控制下一首的item,将其替换成当前播放的item

[songItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];//播放完成之后需要移除观察者

//添加通知----也要记得移除通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemAction:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];

//添加观察者-----使用addPeriodicTimeObserverForInterval:queue:usingBlock:来监听播放器的播放进度

/*

(1)方法传入一个CMTime结构体,每到一定时间都会回调一次,包括开始和结束播放

(2)如果block里面的操作耗时太长,下次不一定会收到回调,所以尽量减少block的操作耗时

(3)方法会返回一个观察者对象,当播放完毕时需要移除这个观察者

*/

timeObserve = [_avplayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

float current = CMTimeGetSeconds(time);

float total = CMTimeGetSeconds(songItem.duration);

NSLog(@"current-%f total-%f",current,total);

}];

[_avplayer play];

}

//歌曲播放完后处理事件

- (void)playerItemAction:(AVPlayerItem *)item {

[songItem removeObserver:self forKeyPath:@" loadedTimeRanges"];

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context {

AVPlayerItem * songItem = object;

if ([keyPath isEqualToString:@"loadedTimeRanges"]) {

NSArray * array = songItem.loadedTimeRanges;

CMTimeRange timeRange = [array.firstObject CMTimeRangeValue]; //本次缓冲的时间范围

NSTimeInterval totalBuffer = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration); //缓冲总长度

NSLog(@"共缓冲%.2f",totalBuffer);

}

}

- (void)dealloc{

if (timeObserve) {

[_avplayer removeTimeObserver:timeObserve];

timeObserve = nil;

}

[[NSNotificationCenter defaultCenter] removeObserver:self];

_avplayer = nil;

}

注意:如果_avplayer为AVPlayer的对象会造成一定的内存泄漏,所以此处用AVQueuePlayer

注意:切换上一首和下一首的另一种方法:使用AVPlayer的子类AVQueuePlayer来播放多个item.调用advanceToNextItem来播放下一首音乐  NSArray* items = @[item1,item2];

AVQueuePlayer* queuePlayer = [[AVQueuePlayer alloc]initWithItems:items]; 

注意:

AVPlayer的对象要设置为全局的,否则会播放不成功。

AVPlayer先缓冲,等缓冲完了,再播放。


AVPlayer视频播放

AVPlayer本身并不能显示视屏,他也不像MPMoviePlayerController有一个view属性。AVPlayer如果要显示视屏,需要创建一个播放器层AVPlayerLayer用于展示,这个播放器层继承于CALayer,创建AVPlayerLayer之后添加到控制器视图的layer即可。他的常用的类如下:

AVAsset:主要用于获取多媒体信息,是一个抽象类,不能直接使用。

AVURLAsset:AVAsset的子类,可以根据一个URL路径创建一个包含媒体信息的AVURLAsset对象。

AVPlayerItem:一个媒体资源管理对象,管理者视频的一些基本信息和状态,一个AVPlayerItem对应着一个视频资源。

- (void)initTV{

self.automaticallyAdjustsScrollViewInsets = NO;

NSString* str = @"http://mp3.wy520.com/%E5%AE%89%E5%92%8C%E6%A1%A5.mp4";//这是在线视屏,如果是本地视屏的话,只需把响应的路径改为本地视屏路径。

NSURL* url = [NSURL URLWithString: str];

AVPlayerItem* playItem = [AVPlayerItem playerItemWithURL:url];

self.avplayerTV = [[AVPlayer alloc]initWithPlayerItem:playItem];

[playItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];//播放完成之后需要移除观察者---也可以不添加

//创建播放器层

AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avplayerTV];

playerLayer.frame = CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, 80);

[self.view.layer addSublayer:playerLayer];

[self.avplayerTV seekToTime:CMTimeMakeWithSeconds(0, 1000)];//设置播放位置  1000位帧率

[self.avplayerTV play];

}

- (void)dealloc{

self.avplayerTV  = nil;

}

注意:AVPlayer的replaceCurrentItemWithPlayerItem方法正常是会引用住参数AVPlayerItem的,但在某些情况下导致视频播放失败,它会马上释放对这个对象的持有,假如你对AVPlayerItem的实例对象添加了监听,但是自己没有对item的计数进行管理,不知道什么时候释放这个监听,则会导致程序崩溃。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容