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的計數進行管理,不知道什么時候釋放這個監聽,則會導致程序崩潰。