感觉没啥说的,直接看代码。
WebMusicTool是控制单个音频的类。
WebMusicOperationTool是控制流程播放控制的类。
#import <Foundation/Foundation.h> #import "DenglSingleton.h" #import "WebMusicTool.h" #import "NLessonM.h" #import "NMusicMessageModel.h" @interface WebMusicOperationTool : NSObject DenglSingletonH(WebMusicOperationTool) @property(nonatomic,copy) NSArray *musicMs; -(void)playMusic:(NLessonM *)musicM; -(void)playCurrentMusic; -(void)pauseCurrentMusic; -(void)seekToTime:(float)p; -(NMusicMessageModel*)getMusicMessageModel; @end
#import "WebMusicOperationTool.h" @interface WebMusicOperationTool () { } @property(nonatomic,strong) WebMusicTool *tool; @property(nonatomic,assign) NSInteger currentPlayIndex; @property(nonatomic,strong) NMusicMessageModel *musicModel; @end @implementation WebMusicOperationTool DenglSingletonM(WebMusicOperationTool) #pragma mark - GET SET -(WebMusicTool *)tool{ if (!_tool) { _tool = [[WebMusicTool alloc]init]; } return _tool; } -(NMusicMessageModel *)musicModel{ if (!_musicModel) { _musicModel = [[NMusicMessageModel alloc]init]; } return _musicModel; } -(NMusicMessageModel*)getMusicMessageModel{ // 在这里, 保持, 数据的最新状态, 就可以 // 当前正在播放的歌曲信息 self.musicModel.lessonmodel = self.musicMs[_currentPlayIndex]; NSTimeInterval costTime = CMTimeGetSeconds(self.tool.player.currentItem.currentTime); if (isnan(costTime)) { costTime = 0; } self.musicModel.costTime = costTime; NSTimeInterval totalTime = CMTimeGetSeconds(self.tool.player.currentItem.duration); if (isnan(totalTime)) { totalTime = 0; } self.musicModel.totalTime = totalTime; self.musicModel.status = self.tool.status; self.musicModel.isPlaying = self.tool.isPlaying; return self.musicModel; } #pragma mark - play -(void)playMusic:(NLessonM *)musicM{ // 播放数据模型对应的音乐 [self.tool playMusic:musicM.main_audio[@"src"]]; _currentPlayIndex = [self.musicMs indexOfObject:musicM]; } -(void)playCurrentMusic{ // 取出需要播放的音乐数据模型 NLessonM * model = self.musicMs[_currentPlayIndex]; // 播放音乐模型 [self playMusic:model]; } -(void)pauseCurrentMusic{ [self.tool pauseMusic]; } -(void)seekToTime:(float)p{ [self.tool seekToTime:p]; } @end
#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> //播放状态枚举值 typedef NS_ENUM(NSInteger,NPlayerStatus){ NPlayerStatusFailed, NPlayerStatusReadyToPlay, NPlayerStatusUnknown, NPlayerStatusBuffering, NPlayerStatusPlaying, NPlayerStatusStopped, }; @interface WebMusicTool : NSObject @property(nonatomic,strong) AVPlayer *player; @property(nonatomic,assign) BOOL isPlaying; //播放状态 @property (nonatomic,assign,readonly) NPlayerStatus status; -(void)playMusic:(NSString *)musicUrl; -(void)seekToTime:(float)p; -(void)pauseMusic; @end
#import "WebMusicTool.h" @interface WebMusicTool (){ id playbackTimerObserver; } //记录当前正在播放的曲目url @property(nonatomic,strong) NSString *currentUrl; //当前歌曲进度监听者 @property(nonatomic,strong) id timeObserver; @end @implementation WebMusicTool - (instancetype)init { self = [super init]; if (self) { //后台播放三步走:1.background mode 2.setCategory 3.setActive AVAudioSession *session = [AVAudioSession sharedInstance]; [session setCategory:AVAudioSessionCategoryPlayback error:nil]; [session setActive:YES error:nil]; // // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemTimeJumpedNotification object:nil]; // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:nil]; // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemPlaybackStalledNotification object:nil]; // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemFailedToPlayToEndTimeErrorKey object:nil]; // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemNewErrorLogEntryNotification object:nil]; // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemNewAccessLogEntryNotification object:nil]; } return self; } -(void)playMusic:(NSString *)musicUrl{ // 1. 创建播放器 if (!musicUrl) { return; } //2. if ([musicUrl isEqualToString:_currentUrl]) { [self.player play]; return; } //3. [self stop]; AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:musicUrl]]; self.player = nil; self.player = [AVPlayer playerWithPlayerItem:item]; _currentUrl = musicUrl; [self.player play]; [self addKVO]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; } -(void)seekToTime:(float)p{ NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * p; [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; [self.player play]; } -(void)pauseMusic{ [self.player pause]; } #pragma mark - Notification -(void)AVPlayerItemNotification:(NSNotification* )noti{ // if (noti.object == self.player.currentItem) { if ([noti.name isEqualToString:@"AVPlayerItemDidPlayToEndTimeNotification"]) { [self stop]; [[NSNotificationCenter defaultCenter] postNotificationName:@"NPlayerFinishNotification" object:nil]; } } } #pragma mark - 监听音乐各种状态 KVO //添加KVO -(void)addKVO{ //监听状态属性 [self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; //监听网络加载情况属性 [self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; //监听播放的区域缓存是否为空 [self.player.currentItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; //缓存可以播放的时候调用 [self.player.currentItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; //监听暂停或者播放中 [self.player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil]; } -(void)stop{ [self.player.currentItem removeObserver:self forKeyPath:@"status"]; // [self.player removeTimeObserver:playbackTimerObserver]; [self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; [self.player.currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"]; [self.player.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; [self.player removeObserver:self forKeyPath:@"rate"]; [[NSNotificationCenter defaultCenter]removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[self.player currentItem]]; if (self.player) { _currentUrl = nil; self.player = nil; } } //KVO -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ if ([keyPath isEqualToString:@"status"]) { AVPlayerItemStatus itemStatus = [[change objectForKey:NSKeyValueChangeNewKey]integerValue]; switch (itemStatus) { case AVPlayerItemStatusUnknown: { _status = NPlayerStatusUnknown; NSLog(@"AVPlayerItemStatusUnknown"); } break; case AVPlayerItemStatusReadyToPlay: { _status = NPlayerStatusReadyToPlay; [self.player play]; NSLog(@"AVPlayerItemStatusReadyToPlay"); } break; case AVPlayerItemStatusFailed: { _status = NPlayerStatusFailed; NSLog(@"AVPlayerItemStatusFailed"); [self stop]; } break; default: break; } }else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { //监听播放器的下载进度 NSArray *loadedTimeRanges = [self.player.currentItem loadedTimeRanges]; CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域 float startSeconds = CMTimeGetSeconds(timeRange.start); float durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval timeInterval = startSeconds + durationSeconds;// 计算缓冲总进度 CMTime duration = self.player.currentItem.duration; CGFloat totalDuration = CMTimeGetSeconds(duration); //缓存值 CGFloat p = timeInterval / totalDuration; NSLog(@"%f",p); } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { //监听播放器在缓冲数据的状态 _status = NPlayerStatusBuffering; NSLog(@"缓冲中"); // if (!self.activityIndeView.isAnimating) { // [self.activityIndeView startAnimating]; // } } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { NSLog(@"缓冲达到可播放"); // [self.activityIndeView stopAnimating]; } else if ([keyPath isEqualToString:@"rate"]){//当rate==0时为暂停,rate==1时为播放,当rate等于负数时为回放 if ([[change objectForKey:NSKeyValueChangeNewKey]integerValue]==1) { _isPlaying=YES; _status = NPlayerStatusPlaying; }else{ _isPlaying=NO; _status = NPlayerStatusStopped; } } }