iOS开发 - AVPlayer MPRemoteCommandCenter实现后台音乐播放

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];
    }
}

六 显示效果图

在这里插入图片描述

七 后台显示效果图

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/gloryFlow/article/details/131679612