美文网首页音视频
ios音乐后台播放和远程控制

ios音乐后台播放和远程控制

作者: IreneWu | 来源:发表于2017-10-31 16:45 被阅读1085次

    最近在做音乐播放器,要静音状态下播放,后台播放,远程控制,锁屏显示,上拉菜单控制,来电中断处理等。

    开启后台模式:
    在TARGETS-Capabilities-Background Modes 勾选Audio,AirPay and Picture in Picture

    在AppDelegate中

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        //静音状态下播放
        [[AVAudioSession sharedInstance] setActive:YES error:nil];
        //处理电话打进时中断音乐播放
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interruptionNotificationHandler:) name:AVAudioSessionInterruptionNotification object:nil];
        //后台播放
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    
        // 在App启动后开启远程控制事件, 接收来自锁屏界面和上拉菜单的控制
        [application beginReceivingRemoteControlEvents];
        // 处理远程控制事件
        [self remoteControlEventHandler];
        
        self.window= [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        [self.window makeKeyAndVisible];
        
        MusicListTableVC * vc = [[MusicListTableViewController alloc] init];
        UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController:vc];
        self.window.rootViewController = nc;
        
        return YES;
    }
    

    来电中断处理:

    //来电中断处理
    - (void)interruptionNotificationHandler:(NSNotification*)notification
    {
        NSDictionary *interuptionDict = notification.userInfo;
        NSString *type = [NSString stringWithFormat:@"%@", [interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey]];
        NSUInteger interuptionType = [type integerValue];
    
        if (interuptionType == AVAudioSessionInterruptionTypeBegan) {
            //获取中断前音乐是否在播放
            _played = [MusicPlayViewController shareMusicPlay].isPlaying;
            NSLog(@"AVAudioSessionInterruptionTypeBegan");
        }else if (interuptionType == AVAudioSessionInterruptionTypeEnded) {
            NSLog(@"AVAudioSessionInterruptionTypeEnded");
        }
        
        if(_played)
        {
            //停止播放的事件
            [[MusicPlayTools shareMusicPlay] musicPause];
            _played=NO;
        }else {
            //继续播放的事件
            [[MusicPlayTools shareMusicPlay] musicPlay];
            _played=YES;
        }
    }
    
    - (void)applicationWillResignActive:(UIApplication *)application
    {
        NSLog(@"要挂起了。。。");
        //更新锁屏信息
        [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
    }
    
    - (void)applicationWillTerminate:(UIApplication *)application
    {
        // 在App要终止前结束接收远程控制事件, 也可以在需要终止时调用该方法终止
        [application endReceivingRemoteControlEvents];
    }
    

    远程控制事件处理:

    // 在需要处理远程控制事件的具体控制器或其它类中实现
    - (void)remoteControlEventHandler
    {
        // 直接使用sharedCommandCenter来获取MPRemoteCommandCenter的shared实例
        MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
        // 启用播放命令 (锁屏界面和上拉快捷功能菜单处的播放按钮触发的命令)
        commandCenter.playCommand.enabled = YES;
        // 为播放命令添加响应事件, 在点击后触发
        [commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            [[MusicPlayTools shareMusicPlay] musicPlay];
            [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        // 播放, 暂停, 上下曲的命令默认都是启用状态, 即enabled默认为YES
        [commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            //点击了暂停
            [[MusicPlayTools shareMusicPlay] musicPause];
            [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        [commandCenter.previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            //点击了上一首
            [[MusicPlayViewController shareMusicPlay] lastSongAction];
            [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        [commandCenter.nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            //点击了下一首
            [[MusicPlayViewController shareMusicPlay] nextSongButtonAction:nil];
            [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
        // 启用耳机的播放/暂停命令 (耳机上的播放按钮触发的命令)
        commandCenter.togglePlayPauseCommand.enabled = YES;
        // 为耳机的按钮操作添加相关的响应事件
        [commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
            // 进行播放/暂停的相关操作 (耳机的播放/暂停按钮)
            [[MusicPlayViewController shareMusicPlay] playPauseButtonAction:nil];
            [[MusicPlayViewController shareMusicPlay] configNowPlayingInfoCenter];
            return MPRemoteCommandHandlerStatusSuccess;
        }];
    }
    

    锁频界面上所显示的歌曲播放信息和图片,控制中心上显示的歌曲播放信息等,这些信息的显示都由MPNowPlayingInfoCenter类来控制。
    首先#import <MediaPlayer/MPNowPlayingInfoCenter.h>然后调用MPNowPlayingInfoCenter的单例方法获取实例,再把需要显示的信息组织成Dictionary并赋值给nowPlayingInfo属性就完成了。

    其中常用的是MPNowPlayingInfoPropertyElapsedPlaybackTime和MPNowPlayingInfoPropertyPlaybackRate:

    • MPNowPlayingInfoPropertyElapsedPlaybackTime表示已经播放的时间,用这个属性可以让NowPlayingCenter显示播放进度;
    • MPNowPlayingInfoPropertyPlaybackRate表示播放速率。通常情况下播放速率为1.0,即真是时间的1秒对应播放时间中的1秒;

    这里需要解释的是,NowPlayingCenter中的进度刷新并不是由app不停的更新nowPlayingInfo来做的,而是根据app传入的ElapsedPlaybackTime和PlaybackRate进行自动刷新。例如传入ElapsedPlaybackTime=120s,PlaybackRate=1.0,那么NowPlayingCenter会显示2:00并且在接下来的时间中每一秒把进度加1秒并刷新显示。如果需要暂停进度,传入PlaybackRate=0.0即可。

    所以每次播放暂停和继续都需要更新NowPlayingCenter并正确设置ElapsedPlaybackTime和PlaybackRate否则NowPlayingCenter中的播放进度无法正常显示。

    NowPlayingCenter的刷新时机

    频繁的刷新NowPlayingCenter并不可取,特别是在有Artwork的情况下。所以需要在合适的时候进行刷新。

    依照我自己的经验下面几个情况下刷新NowPlayingCenter比较合适:

    • 当前播放歌曲进度被拖动时
    • 当前播放的歌曲变化时
    • 播放暂停或者恢复时
    • 当前播放歌曲的信息发生变化时(例如Artwork,duration等)

    在刷新时可以适当的通过判断app是否active来决定是否必须刷新以减少刷新次数。

    /**
     *  设置锁屏信息
     */
    -(void)configNowPlayingInfoCenter
    {
        Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
        
        if (playingInfoCenter) {
            MusicInfoModel * model = [MusicPlayTools shareMusicPlay].model;
            NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
            UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld", self.index + 1]];
            MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithBoundsSize:image.size requestHandler:^UIImage * _Nonnull(CGSize size) {
                return image;
            }];
            //歌曲名称
            [songInfo setObject:model.name forKey:MPMediaItemPropertyTitle];
            //演唱者
            [songInfo setObject:model.singer forKey:MPMediaItemPropertyArtist];
            //专辑名
            [songInfo setObject:@"专辑名" forKey:MPMediaItemPropertyAlbumTitle];
            //专辑缩略图
            [songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
            //音乐当前已经播放时间
            NSInteger currentTime = [[MusicPlayTools shareMusicPlay] getCurTime];
            [songInfo setObject:[NSNumber numberWithInteger:currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
            //进度光标的速度 (这个随 自己的播放速率调整,我默认是原速播放)
            [songInfo setObject:[NSNumber numberWithFloat:1.0] forKey:MPNowPlayingInfoPropertyPlaybackRate];
            //歌曲总时间设置
            NSInteger duration = [model.duration integerValue];
            [songInfo setObject:[NSNumber numberWithInteger:duration] forKey:MPMediaItemPropertyPlaybackDuration];
            //设置锁屏状态下屏幕显示音乐信息
            [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
        }
    }
    

    相关文章

      网友评论

        本文标题:ios音乐后台播放和远程控制

        本文链接:https://www.haomeiwen.com/subject/igebpxtx.html