使用時:可以傳入一個播放隊列,他會自動列表循環,也可以在外部自己管理播放隊列,每次傳入一個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
點個贊再走唄。。。
如有疑問,聯系作者
博客園:這個我不知道誒
