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的计数进行管理,不知道什么时候释放这个监听,则会导致程序崩溃。

你可能感兴趣的:(iOS---AVPlayer)