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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容