AVPlayer可以用來播放音頻和視頻稿辙,今天要說的就是用AVPlayer的一個子類 AVQueuePlayer來播放音頻、這個類可以用來播放隊列气忠,當前AVPlayerItem播放完了邓深,如果nextItem存在就會自動播放下一個Item,比AVPlayer要方便一點笔刹。
一芥备、后臺播放設置
先導入兩個框架
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
設置后臺播放
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
二、AVQueuePlayer初始化
-(void)initPlayer{
//準備數據 一個本地MP3和一個URL MP3
AVPlayerItem*firstItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:@"那些花兒" ofType:@"mp3"]]];
AVPlayerItem*secondItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:@"http://sc1.111ttt.com/2015/1/06/06/99060941326.mp3"]];
SongModel*firstModel = [[SongModel alloc]init];
firstModel.songName = @"那些花兒";
firstModel.singer = @"樸樹";
firstModel.picture = [UIImage imageNamed:@"pushu.jpg"];
firstModel.duration = 294;
firstModel.item = firstItem;
SongModel*secondModel = [[SongModel alloc]init];
secondModel.songName = @"演員";
secondModel.singer = @"薛之謙";
secondModel.picture = [UIImage imageNamed:@"xue.png"];
secondModel.duration = 262;
secondModel.item = secondItem;
_songInfoArray = @[firstModel,secondModel];
//這里初始化設置一個或多個都是可以的舌菜,這里我設置一個的原因主要是上一曲和下一曲的時候會清空items
self.player = [[AVQueuePlayer alloc]initWithItems:@[firstItem]];
_index = 0;
// item和player都有status 屬性 通常我們觀察item的status 是因為可以檢測資源是否可以播放萌壳,當然這里直接調用play方法也是可以直接播放的
[firstItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playeyEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
初始化完成之后直接調用[AVPlayer play]也是可以直接播放的,但是資源出現(xiàn)問題的話還是需要手動處理的所以我們在這里對Item添加了一個觀察者,并對AVPlayer播放完成添加了一個通知袱瓮。
通知和觀察者模式實現(xiàn)代碼
///播放結束
-(void)playeyEnd:(NSNotification*)notify{
NSLog(@"end");
[self nextSong];
}
///AVPlayerItem observer
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
AVPlayerItem*item = (AVPlayerItem*)object;
if ([keyPath isEqualToString:@"status"]) {
if (item.status==AVPlayerItemStatusReadyToPlay) {
NSLog(@"play");
[self setLockScreenNowPlayingInfo];
[item removeObserver:self forKeyPath:@"status"];
[self.player play];
}
if (item.status==AVPlayerItemStatusFailed) {
NSLog(@"filad");
[self setLockScreenNowPlayingInfo];
[item removeObserver:self forKeyPath:@"status"];
[self nextSong];
}
}
}
切歌控制
#warning 在同一時間內一個item只能占用一個位置缤骨、所以這里是先刪除,再添加
-(void)nextSong{
//下一首
if (_index==_songInfoArray.count-1) {
_index =0;
}else{
_index++;
}
SongModel*model = _songInfoArray[_index];
[model.item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[self.player removeAllItems];
[self.player insertItem:model.item afterItem:nil];
[self.player seekToTime:kCMTimeZero];
[self setLockScreenNowPlayingInfo];
}
-(void)lastSong{
//上一首
if (_index==0) {
_index =_songInfoArray.count-1;
}else{
_index--;
}
SongModel*model = _songInfoArray[_index];
[model.item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[self.player removeAllItems];
[self.player insertItem:model.item afterItem:nil];
[self.player seekToTime:kCMTimeZero];
[self setLockScreenNowPlayingInfo];
}
這里有一個小坑尺借、AVQueuePlayer提供了跳轉到下一個Item的方法
- (void)advanceToNextItem;
卻沒有提供LastItem的方法绊起,所以要實現(xiàn)LastItem的方法我是通過來實現(xiàn)的
[self.player insertItem:model.item afterItem:nil];
特別提醒在同一時間同一個Item只能加入一次,所以在insert之前需要將之前的Item刪除掉燎斩、我選擇了刪除全部虱歪、這樣同一時間PlayerQueue中就只存在一個Item了、我覺得這樣更容易控制一點栅表。而關于 [self.player seekToTime:kCMTimeZero]; 這個是為了讓Item上一次的播放時間置零笋鄙,如果不置零就會繼續(xù)播放(感覺這個功能播放視頻的時候這個還是比較好用的);
三怪瓶、注冊遠程控制(鎖屏界面控制和耳機控制)
需要先讓應用能夠接受遠程控制萧落,并成為第一響應者
#pragma mark - 遠程控制接收方法的設置
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) { //判斷是否為遠程控制
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
{
if (!_isPlaying) {
[self.player play];
}
_isPlaying=!_isPlaying;
}
break;
case UIEventSubtypeRemoteControlPause:
{
if (_isPlaying) {
[self.player pause];
}
_isPlaying = !_isPlaying;
}
break;
case UIEventSubtypeRemoteControlNextTrack:
{
[self nextSong];
}
break;
case UIEventSubtypeRemoteControlPreviousTrack:
{
[self lastSong];
}
break;
default:
break;
}
}
}
四、設置鎖屏信息
期間我看了網易云音樂的鎖屏效果洗贰、設置了lyrics,結果我這里設置了并沒有什么效果找岖。。敛滋。
///設置鎖屏信息
- (void)setLockScreenNowPlayingInfo
{
SongModel*model = self.songInfoArray[_index];
//更新鎖屏時的歌曲信息
MPMediaItemArtwork *artWork = [[MPMediaItemArtwork alloc] initWithImage:model.picture];
NSDictionary *dic = @{MPMediaItemPropertyTitle:model.songName,
MPMediaItemPropertyArtist:model.singer,
// MPMediaItemPropertyLyrics:@"hello lyrics break ",
// MPMediaItemPropertyReleaseDate:@"2017-08-23",//唱片發(fā)布日期
MPMediaItemPropertyPlaybackDuration:@(model.duration),//設置鎖屏界面歌曲時間
MPMediaItemPropertyArtwork:artWork//鎖屏界面圖片
};
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dic];
}
最后附上真機鎖屏的效果以及Demo地址
https://github.com/LuoCongMing/AVQueuePlayerDemo.git