iOS音乐播放封装(锁屏后台播放,歌曲切换)

使用时:可以传入一个播放队列,他会自动列表循环,也可以在外部自己管理播放队列,每次传入一个item,监听到是切换,传入自定义列表的下一个item

注意,需要锁屏后台播放需在项目设置

 

 具体代码
MusicsPlayer.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MusicPlayerInfo : NSObject
@property(nonatomic,strong) NSString * filePath;//歌曲路径(可以是本地路径,也可以是网络路径)
@property(nonatomic,strong) NSString * name;//歌曲名称
@property(nonatomic,strong) NSString * playerName;//歌手名称
@property(nonatomic,strong) NSString * picPath;//歌曲图片路径
@property(nonatomic,assign) BOOL needBack;//是否后台播放
@property(nonatomic,assign) NSInteger slider;//进度,已经播放多少秒
@property(nonatomic,assign) BOOL isplay;// 是否正在播放
@end


@interface MusicsPlayer : NSObject
+(instancetype) sharedInstance;

//设置播放列表//默认会播放第一首
//alltime 多少秒后结束播放,,大于0则会定时关闭 小于0不会定时关闭
//completion 播放状态改变的回调通知 ischange 是否切换歌曲  ischange=-1 暂停 ; ischange=1 播放 ; ischange=-2 上一首 ; ischange=2 下一首
-(void)playMusics:(NSArray<MusicPlayerInfo*> *)musicInfos endTime:(NSInteger)alltime stateChange:(void (^)(int ischange))completion;

//更改当前音乐的播放进度,从多少秒开始播放(time 秒)
-(void)playMusicWithSlider:(NSInteger)time;

//停止//会直接销毁
-(void)stopMusic;

//继续//上次播放
-(void)continuePlayMusic;

//暂停//后面可以继续播放
-(void)pauseMusic;

//下一首
-(void)nextMusic;

//上一首
-(void)upMusic;

//获取当前播放信息
-(MusicPlayerInfo *)playingMusicInfo;
//回调
//通知前端播放状态改变,///前端可以调用playingMusicInfo获取最新信息

+(void)applicationWillEnterForeground;
+(void)applicationDidBecomeActive;
@end

NS_ASSUME_NONNULL_END

MusicsPlayer.m

#import "MusicsPlayer.h"
#import <AVKit/AVKit.h>
#import <MediaPlayer/MediaPlayer.h>


@interface MusicsPlayer ()

@property (nonatomic,strong)AVPlayer  *  musicPlayer;
@property (nonatomic,strong)NSArray<MusicPlayerInfo*> * musicInfos;
@property(nonatomic,assign)NSInteger playingIndex;//当前播放歌曲索引
@property(nonatomic,assign)BOOL isplaying;//是否正在播放
@property (copy, nonatomic) void (^playerChangeBlock)(int ischange);//播放器状态改变回调
@property(nonatomic,strong) NSTimer * changeTimer;//自动关闭计时器
@property(nonatomic,assign)NSInteger endPlayTime;//停止播放倒计时
@property(nonatomic,assign)BOOL isBackPlayer;//是否是后台播放

@end

@implementation MusicsPlayer

static MusicsPlayer * _instance = nil;

+(instancetype) sharedInstance{
   static dispatch_once_t onceToken ;
   dispatch_once(&onceToken, ^{
       _instance = [[self alloc]init];
   }) ;
   return _instance ;
}
-(instancetype)init{
   self = [super init];
   if(self){
       [self loadBackFunction];
   }
   return self;
}

-(void)loadBackFunction{
   AVAudioSession  *session  =  [AVAudioSession  sharedInstance];
   [session setActive:YES error:nil];
   [session setCategory:AVAudioSessionCategoryPlayback error:nil];
   // *让app接受远程事件控制,及锁屏是控制版会出现播放按钮
   [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
   
   MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
   
   MPRemoteCommand *playCommand = commandCenter.playCommand;
   playCommand.enabled = YES;
   [playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
       NSLog(@"点击播放");
       [self continuePlayMusic];
       return MPRemoteCommandHandlerStatusSuccess;
   }];
   MPRemoteCommand *pauseCommand = commandCenter.pauseCommand;
   pauseCommand.enabled = YES;
   [pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
       NSLog(@"点击暂停");
       [self pauseMusic];
       return MPRemoteCommandHandlerStatusSuccess;
   }];
   MPRemoteCommand *nextTrackCommand = commandCenter.nextTrackCommand;
   nextTrackCommand.enabled = YES;
   [nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
       NSLog(@"点击下一首");
       [self nextMusic];
       return MPRemoteCommandHandlerStatusSuccess;
   }];
   MPRemoteCommand *previousTrackCommand = commandCenter.previousTrackCommand;
   previousTrackCommand.enabled = YES;
   [previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
       NSLog(@"点击上一首");
       [self upMusic];
       return MPRemoteCommandHandlerStatusSuccess;
   }];
   
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endOfPlay:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}

-(void)playMusic{
   MusicPlayerInfo * musicInfo = self.musicInfos[self.playingIndex];
   NSString * filePath = musicInfo.filePath;
   self.isBackPlayer = musicInfo.needBack;
   
   if(self.musicPlayer){
       [self.musicPlayer.currentItem removeObserver:self forKeyPath:@"status"];//老的item移除监听
   }else{
       self.musicPlayer = [[AVPlayer alloc] init];
   }
   NSURL * url ;
   if([filePath containsString:@"http"]){
       url = [NSURL URLWithString:filePath];
   }else{
       url = [NSURL fileURLWithPath:filePath];
   }
   AVPlayerItem * item = [[AVPlayerItem alloc] initWithURL:url];
   // 为item的status添加观察者.
   [item addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:nil];
   // 用新创建的item,替换AVPlayer之前的item.新的item是带着观察者的哦.
   [self.musicPlayer replaceCurrentItemWithPlayerItem:item];
}

-(void)loadLockViewInfo{
   MusicPlayerInfo * musicInfo = self.musicInfos[self.playingIndex];
   NSString * name = musicInfo.name;
   NSString * playerName = musicInfo.playerName;
   NSString * picPath = musicInfo.picPath;
   //设置锁屏状态下屏幕显示音乐信息
   //设置后台播放时显示的东西,例如歌曲名字,图片等
   UIImage *image;
   if([picPath containsString:@"http"]){
       NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:picPath]];
       image = [UIImage imageWithData:imageData];
   }else{
       image = [UIImage imageWithContentsOfFile:picPath];
   }
   MPMediaItemArtwork *artWork ;
   if ([UIDevice currentDevice].systemVersion.floatValue >= 10.0) {
       artWork = [[MPMediaItemArtwork alloc] initWithBoundsSize:CGSizeMake(512, 512) requestHandler:^UIImage * _Nonnull(CGSize size) {
           return image;
       }];
   }else{
        artWork = [[MPMediaItemArtwork alloc] initWithImage:image];
   }
   NSDictionary *dic = @{MPMediaItemPropertyTitle:name,
                         MPMediaItemPropertyArtist:playerName,
                         MPMediaItemPropertyArtwork:artWork,
                         MPNowPlayingInfoPropertyElapsedPlaybackTime:@([self playingSlider]),
                         MPNowPlayingInfoPropertyPlaybackRate:@(1.0),
                         MPMediaItemPropertyPlaybackDuration:@([self musicDuration]),
                         };
   [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dic];
   
}

// 播放结束后的方法,
-(void)endOfPlay:(NSNotification *)sender
{
   NSLog(@"播放完成,下一首");
   [self nextMusic];
}

-(void)nextMusic{
   //下一首
   [self pauseMusic];
   self.playingIndex ++ ;
   if(self.playingIndex >= self.musicInfos.count){
       self.playingIndex = 0;
   }
   [self playMusic];
   
   if(self.playerChangeBlock){
       self.playerChangeBlock(2);
   }
}

-(void)upMusic{
   //上一首
   [self pauseMusic];
   self.playingIndex -- ;
   if(self.playingIndex < 0){
       self.playingIndex = self.musicInfos.count-1;
   }
   [self playMusic];
   
   if(self.playerChangeBlock){
       self.playerChangeBlock(-2);
   }
}

// 观察者的处理方法, 观察的是Item的status状态.
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
   if ([keyPath isEqualToString:@"status"]) {
       switch ([[change valueForKey:@"new"] integerValue]) {
           case AVPlayerItemStatusUnknown:
               NSLog(@"不知道什么错误");
               //回调下一首
               break;
           case AVPlayerItemStatusReadyToPlay:
               // 只有观察到status变为这种状态,才会真正的播放.
               [self continuePlayMusic];
               break;
           case AVPlayerItemStatusFailed:
               // mini设备不插耳机或者某些耳机会导致准备失败.
               NSLog(@"准备失败");
               break;
           default:
               break;
       }
   }
}

//停止//会直接销毁
-(void)stopMusic{
   if(self.musicPlayer){
       [self pauseMusic];
       [self.musicPlayer.currentItem removeObserver:self forKeyPath:@"status"];//移除监听
       self.musicPlayer  =  nil;
   }
}

//继续播放
-(void)continuePlayMusic
{
   if(self.musicPlayer){
       if(self.musicPlayer.currentItem){
           [self loadLockViewInfo];
           [self.musicPlayer play];
           self.isplaying = YES;
       }
   }
}
//暂停//后面可以继续播放
-(void)pauseMusic{
   if(self.musicPlayer){
       [self.musicPlayer pause];
       self.isplaying = NO;
   }
}

//更改当前音乐的播放进度,从多少秒开始播放(time 秒)
-(void)playMusicWithSlider:(NSInteger)time{
   if(self.musicPlayer){
        // 先暂停
          [self pauseMusic];
          // 跳转
          [self.musicPlayer seekToTime:CMTimeMake(time * self.musicPlayer.currentTime.value, 1) completionHandler:^(BOOL finished) {
              if (finished == YES) {
                  [self continuePlayMusic];
              }
          }];
   }
}

-(BOOL)isPlaying{
   if(self.musicPlayer){
       return self.isplaying;
   }
   return NO;
}

//获取播放进度;秒(当前歌曲已经播放了多少秒)
-(NSInteger)playingSlider{
   if (self.musicPlayer.currentItem) {
       return CMTimeGetSeconds(self.musicPlayer.currentTime);
   }
   return 0;
}
//获取歌曲时长;秒(当前歌曲已经播放了多少秒)
-(NSInteger)musicDuration{
   if (self.musicPlayer.currentItem) {
       return CMTimeGetSeconds(self.musicPlayer.currentItem.duration);
   }
   return 0;
}

-(void)playMusics:(NSArray<MusicPlayerInfo*> *)musicInfos endTime:(NSInteger)alltime stateChange:(void (^)(int ischange))completion{
   self.playerChangeBlock = completion;
   self.musicInfos = musicInfos;
   self.endPlayTime = alltime;
   if(!self.changeTimer){
       self.changeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(changeplaytime) userInfo:nil repeats:YES];//开始计时
   }
   [self stopMusic];
   self.playingIndex = 0;
   [self playMusic];
}

-(MusicPlayerInfo *)playingMusicInfo{
   MusicPlayerInfo * musicInfo = self.musicInfos[self.playingIndex];
   MusicPlayerInfo * result = [[MusicPlayerInfo alloc]init];
   result.filePath = musicInfo.filePath;
   result.name = musicInfo.name;
   result.picPath = musicInfo.picPath;
   result.playerName = musicInfo.playerName;
   result.needBack = musicInfo.needBack;
   result.slider = [self playingSlider];
   result.isplay = [self isPlaying];
   return musicInfo;
}

-(void)setIsplaying:(BOOL)isplaying{
   _isplaying = isplaying;
   //通知播放状态改变
   if(self.playerChangeBlock){
       self.playerChangeBlock(isplaying?1:-1);
   }
}


-(void)changeplaytime{
   self.endPlayTime --;
   [self loadLockViewInfo];
   if(self.endPlayTime == 0){
       [self pauseMusic];//暂停
   }
}

+(void)applicationWillEnterForeground{
   if(_instance && !_instance.isBackPlayer){//不是后台播放,切换后台需暂停
       [_instance pauseMusic];
   }
}
+(void)applicationDidBecomeActive{
   if(_instance && !_instance.isBackPlayer){//不是后台播放
       [_instance continuePlayMusic];
   }
}
@end
@implementation MusicPlayerInfo

@end

 

 

点个赞再走呗。。。

 

如有疑问,联系作者 

博客园:这个我不知道诶

posted @ 2020-05-18 16:54  这个我知道  阅读(899)  评论(0编辑  收藏  举报