iOS AVPlayer 实现后台音乐播放
最近听网易云音乐,当进入后台时候,播放音乐的感觉,于是找到了AVPlayer与MPRemoteCommandCenter来实现后台音乐播放。
什么是MPNowPlayingInfoCenter呢?
MPNowPlayingInfoCenter:即时播放中心能够用于播放APP中正在播放的媒体信息. 播放的信息会显示在锁屏页面和多任务管理页面.如果用户是用airplay播放的话 会自动投射到相应的设备上.
app进入后台的情况?
当 App 退到后台时,会进入 suspend 状态,若此时在播放视频,则会自动暂停。我们需要实现的效果是,当 App 退到后台时,视频中的声音还能继续播放。另外,我们还同时实现视频的连续播放功能,和在锁屏界面控制视频播放的功能。
一、设置App支持后台运行
设置app支持后台运行,开启background modes。Required background modes -> App plays audio。也可以修改info.plist,在里面添加Required background modes,并在下面添加一项 App plays audio or streams audio/video using AirPlay。
二、设置AVAudioSessionCategoryPlayback
在app启动的时候,在AppDelegate中设置AVAudioSessionCategoryPlayback
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 告诉app支持后台播放
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
return YES;
}
三、AVPlayer
使用AVPlayer来播放音频mp3
// 初始化播放器和视图
self.player = [[AVPlayer alloc] init];
// 监听播放进度
__weak typeof(self) weakSelf = self;
[self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1, NSEC_PER_SEC)
queue:NULL
usingBlock:^(CMTime time) {
[weakSelf updateProgressView];
}];
// 播放视频
[self playWithUrl:_urlList[_currentIndex]];
[self updateMusicInfo];
三、MPNowPlayingInfoCenter
- 1、设置播放中心能够用于播放APP中正在播放的媒体信息
// 更新锁屏界面信息
- (void)refreshLockScreenInfo {
if (!_player) {
return;
}
// 1.获取锁屏中心
MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
// 初始化一个存放音乐信息的字典
NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];
// 2、设置歌曲名
[playingInfoDict setObject:[NSString stringWithFormat:@"歌曲%ld", (long)_currentIndex + 1]
forKey:MPMediaItemPropertyTitle];
[playingInfoDict setObject:[NSString stringWithFormat:@"专辑%ld", (long)_currentIndex + 1]
forKey:MPMediaItemPropertyAlbumTitle];
// 3、设置封面的图片
UIImage *image = [UIImage imageNamed:self.coverList[_currentIndex]];
if (image) {
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
[playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork];
}
// 4、设置歌曲的时长和已经消耗的时间
NSNumber *playbackDuration = @(CMTimeGetSeconds(_player.currentItem.duration));
NSNumber *elapsedPlaybackTime = @(CMTimeGetSeconds(_player.currentItem.currentTime));
if (!playbackDuration || !elapsedPlaybackTime) {
return;
}
[playingInfoDict setObject:playbackDuration
forKey:MPMediaItemPropertyPlaybackDuration];
[playingInfoDict setObject:elapsedPlaybackTime
forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[playingInfoDict setObject:@(_player.rate) forKey:MPNowPlayingInfoPropertyPlaybackRate];
//音乐信息赋值给获取锁屏中心的nowPlayingInfo属性
playingInfoCenter.nowPlayingInfo = playingInfoDict;
}
- 2、当app进入后台的时候,添加App退到后台和进入前台的监听
- (void)addObservers {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(removePlayerOnPlayerLayer)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[center addObserver:self
selector:@selector(resetPlayerToPlayerLayer)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)removePlayerOnPlayerLayer {
}
- (void)resetPlayerToPlayerLayer {
}
- 3、当进入后台时候,锁屏后台的一些操作,上一首、暂停、下一首等操作
// 添加远程控制
- (void)createRemoteCommandCenter {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
MPRemoteCommand *pauseCommand = [commandCenter pauseCommand];
[pauseCommand setEnabled:YES];
[pauseCommand addTarget:self action:@selector(remotePauseEvent)];
MPRemoteCommand *playCommand = [commandCenter playCommand];
[playCommand setEnabled:YES];
[playCommand addTarget:self action:@selector(remotePlayEvent)];
MPRemoteCommand *nextCommand = [commandCenter nextTrackCommand];
[nextCommand setEnabled:YES];
[nextCommand addTarget:self action:@selector(remoteNextEvent)];
MPRemoteCommand *previousCommand = [commandCenter previousTrackCommand];
[previousCommand setEnabled:YES];
[previousCommand addTarget:self action:@selector(remotePreviousEvent)];
if (@available(iOS 9.1, *)) {
MPRemoteCommand *changePlaybackPositionCommand = [commandCenter changePlaybackPositionCommand];
[changePlaybackPositionCommand setEnabled:YES];
[changePlaybackPositionCommand addTarget:self action:@selector(remoteChangePlaybackPosition:)];
}
}
- (void)remotePlayEvent {
[self playVideo];
}
- (void)remotePauseEvent {
[self pauseVideo];
}
- (void)remoteNextEvent {
[self playNextVideo];
}
- (void)remotePreviousEvent {
[self playPreviousVideo];
}
- (void)remoteChangePlaybackPosition:(MPRemoteCommandEvent *)event {
MPChangePlaybackPositionCommandEvent * playbackPositionEvent = (MPChangePlaybackPositionCommandEvent *)event;
[self seekToTime:playbackPositionEvent.positionTime];
}
四、更新界面
- 1、更新封面信息及显示
- (void)updateMusicInfo {
self.teamAnnounceView.nameLabel.text = self.musicNameList[_currentIndex];
[UIView transitionWithView:self.teamAnnounceView.bgImageView duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.teamAnnounceView.bgImageView.image = [UIImage imageNamed:self.coverList[_currentIndex]];
} completion:nil];
[UIView transitionWithView:self.teamAnnounceView.coverImageView duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.teamAnnounceView.coverImageView.image = [UIImage imageNamed:self.coverList[_currentIndex]];
} completion:nil];
}
- 2、更新播放进度
// 更新进度条进度
- (void)updateProgressView {
self.currentDuration = CMTimeGetSeconds(_player.currentItem.duration);
CGFloat progress = CMTimeGetSeconds(_player.currentItem.currentTime) / _currentDuration;
// 可以通过监听playerItem结束的通知来切换歌曲
// 当结束时需要移除对当前playerItem的监听,然后添加下一个playerItem的监听
// 这里直接通过判断进度条是否完成,来切换歌曲
//
// 监听播放通知的写法如下:
// [[NSNotificationCenter defaultCenter] addObserver:self
// selector:@selector(playerItemDidReachEnd:)
// name:AVPlayerItemDidPlayToEndTimeNotification
// object:self.playerItem];
if (progress == 1.0f) {
[self playNextVideo];
} else {
[self.teamAnnounceView.progressSlider setValue:progress];
}
}
- 3、拖动进度操作
// 拖动进度条监听
- (void)videoProgressDidChanged:(UISlider *)sender {
if (sender.value == 1.0f) {
[self playNextVideo];
return;
}
NSTimeInterval duration = CMTimeGetSeconds(_player.currentItem.duration);
[self seekToTime:duration * sender.value];
}
五 释放,移除CommandCenter的targets
- (void)removeCommandCenterTargets
{
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[[commandCenter playCommand] removeTarget:self];
[[commandCenter pauseCommand] removeTarget:self];
[[commandCenter nextTrackCommand] removeTarget:self];
[[commandCenter previousTrackCommand] removeTarget:self];
if (@available(iOS 9.1, *)) {
[commandCenter.changePlaybackPositionCommand removeTarget:self];
}
}