AVPlayer播放网络音乐


AVPlayer播放网络音乐

144 
作者 Wy_chris 
2016.09.23 17:23* 字数 411 阅读 355评论 6

下载地址:AVPlayer播放网络音乐

音乐播放并展示到界面上需要我们实现的功能如下:

1、(核心)播放器通过一个网络链接播放音乐
2、(基本)播放器的常用操作:暂停、播放、上一首、下一首等等
3、(基本)监听该音乐的播放进度、获取音乐的总时间、当前播放时间
4、(基本)监听改播放器状态:
     (1)媒体加载状态
     (2)数据缓冲状态
     (3)播放完毕状态
5、(可选)Remote Control控制音乐的播放
6、(可选)Now Playing Center展示正在播放的音乐

功能实现

我自己写了一个管理类 (里面封装了大部分播放逻辑)
已完成功能:播放、暂停、上下一首、单曲循环、随机播放、后台播放、锁屏信息。
第一次写这种管理类,可能会有点复杂,不太容易看懂 。 我把歌曲的信息也存在里面了。

.h 文件

#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> #import "SongsMessageModel.h" typedef NS_ENUM(NSInteger, playerType) { AVPlayerTypeNormal = 0, //正常循环播放状态 AVPlayerTypeRandom, //随机播放 AVPlayerTypeSingle, //单曲循环 }; /**返回缓冲进度*/ typedef void(^loadTime)(NSString *str); /**返回 当前进度、总时长、slider 最大值和当前值*/ typedef void(^returnTime)(NSString *current,NSString *total,float max,float value); /**返回 AVPlayerItem的序号(换背景图、歌手、歌曲)*/ typedef void(^returnImage)(NSInteger index); @interface AVPlayerManager : NSObject /**存放AVplayerItem的数组*/ @property(nonatomic,strong)NSMutableArray *musicArray; //**歌曲地址数组*/ @property(nonatomic,strong)NSMutableArray *songArray;// 是否进入的已存在的歌单! /**海报数组*/ @property(nonatomic,strong)NSMutableArray *imageArray; /**歌手数组*/ @property(nonatomic,strong)NSMutableArray *singerArray; /**歌曲名数组*/ @property(nonatomic,strong)NSMutableArray *songNameArray; /**AVPlayer播放器*/ @property (nonatomic, strong) AVPlayer *player; /**播放模式*/ @property (nonatomic,assign) NSInteger playerType; /**当前播放时间*/ @property(nonatomic,assign) float currentPlayTime; /**当前缓冲时间*/ @property(nonatomic,assign)NSTimeInterval currentLoadTime; /**slider最大值*/ @property(nonatomic,assign) float silderMaxValue; /**slider当前值*/ @property(nonatomic,assign) float silderValue; /***/ @property(nonatomic,strong) NSString *playTime; /**总时长*/ @property(nonatomic,strong) NSString *totalTime; /**缓冲提示*/ @property(nonatomic,strong) NSString *loadTime; @property(nonatomic,strong)loadTime block; @property(nonatomic,strong)returnTime block1; @property(nonatomic,strong) returnImage block2; /**通知 获取当前播放状态 控制按钮状态*/ @property(nonatomic,strong) NSNotificationCenter * center; /**判断是否是从歌单列表进入播放器 还是 直接在进入播放器*/ @property(nonatomic,assign) BOOL listInto; + (instancetype)shareManager; //**播放*/ - (void)musicPlayerWithArray:(NSArray *)musicArray andIndex:(NSInteger )index; //**正在播放时 播放指定歌曲, */ - (void)musicPlayerWithIndex:(NSInteger )index; //*播放 -(void)playMusic; //*暂停 -(void)pasueMusic; //**下一首*/ - (void)nextSong; //**上一首*/ - (void)lastSong; //**进度条 调节 */ - (void)playerProgressWithProgressFloat:(CGFloat)progressFloat; //**移除观察者*/ -(void)removeObserver; /**获取当前播放序号*/ -(NSInteger )getcurrentItem;

.m 文件

#import "AVPlayerManager.h" #import <MediaPlayer/MediaPlayer.h> @implementation AVPlayerManager { id timeObserve;// 为 当前AVPlayerItem 添加观察者获取各种播放状态 } +(instancetype)shareManager{ static AVPlayerManager *manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [AVPlayerManager new]; }); return manager; } -(instancetype)init{ if (self =[super init]) { _playerType = AVPlayerTypeNormal; } return self; } //播放器懒加载 -(AVPlayer *)player{ if (!_player) { _player = [AVPlayer new]; } return _player; } -(NSMutableArray *)musicArray{ if (!_musicArray) { _musicArray = [NSMutableArray array]; _center = [NSNotificationCenter defaultCenter]; } return _musicArray; } //播放指定url - (void)musicPlayerWithURL:(NSURL *)playerItemURL{ //创建要播放的资源 AVPlayerItem *playerItem = [[AVPlayerItem alloc]initWithURL:playerItemURL]; //添加观察者 //播放当前资源 [self.player replaceCurrentItemWithPlayerItem:playerItem]; } -(void)musicPlayerWithIndex:(NSInteger)index{ // 播放AVPlayerItem数组中指定序号歌曲 (此操作之前需要将上一个item 的 观察者移除 并将 seekTime 设置为0 确保下次播放从头开始) [self.player replaceCurrentItemWithPlayerItem:self.musicArray[index]]; self.block2([self.musicArray indexOfObject:self.player.currentItem]); [self addObserver]; } //重新添加新的歌单列表进来 - (void)musicPlayerWithArray:(NSArray *)musicArray andIndex:(NSInteger )index{ //首先移除之前的歌单列表 再 重新添加 [self.songArray removeAllObjects]; [self.songArray addObjectsFromArray:musicArray]; for (NSString * obj in musicArray) { AVPlayerItem *songItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:obj]]; [self.musicArray addObject:songItem]; } //播放歌单列表序号对应的歌曲 [self.player replaceCurrentItemWithPlayerItem:self.musicArray[index]]; //对当前item 添加观察者 [self addObserver]; } //播放 -(void)playMusic{ [self.player play]; } //暂停 -(void)pasueMusic{ [self.player pause]; } //上一首 -(void)lastSong{ [self removeObserver];//先移除当前 观察者 [self.player.currentItem seekToTime:(kCMTimeZero) completionHandler:^(BOOL finished) { if (self.playerType == AVPlayerTypeNormal) {//顺序循环播放 //判断当前歌曲是否是第一首歌曲,如果是跳转到最后一首歌 if ([self.musicArray indexOfObject:self.player.currentItem] ==0) { [self.player replaceCurrentItemWithPlayerItem:self.musicArray.lastObject]; }else{ [self.player replaceCurrentItemWithPlayerItem:self.musicArray[[self.musicArray indexOfObject:self.player.currentItem]-1]]; } }else if (self.playerType == AVPlayerTypeRandom){//随机播放 NSInteger random = arc4random_uniform((int)self.musicArray.count); NSLog(@"%ld",random); [self.player replaceCurrentItemWithPlayerItem:self.musicArray[arc4random_uniform((int)self.musicArray.count)]]; }else if (self.playerType ==AVPlayerTypeSingle){ //单曲循环在自动切换歌曲时进行判断 //判断当前歌曲是否是第一首歌曲,如果是跳转到最后一首歌 if ([self.musicArray indexOfObject:self.player.currentItem] ==0) { [self.player replaceCurrentItemWithPlayerItem:self.musicArray.lastObject]; }else{ [self.player replaceCurrentItemWithPlayerItem:self.musicArray[[self.musicArray indexOfObject:self.player.currentItem]-1]]; } } [self playMusic]; //放回对应歌曲的信息 self.block2([self.musicArray indexOfObject:self.player.currentItem]); [self addObserver];//添加观察者 }]; } //下一首 -(void)nextSong{ [self removeObserver]; [self.player.currentItem seekToTime:(kCMTimeZero) completionHandler:^(BOOL finished) { if (self.playerType == AVPlayerTypeNormal) { //判断当前歌曲是否是最后一首歌曲,如果是跳转到第一首歌 if ([self.musicArray indexOfObject:self.player.currentItem]+1 == self.musicArray.count) { [self.player replaceCurrentItemWithPlayerItem:self.musicArray.firstObject]; }else{ [self.player replaceCurrentItemWithPlayerItem:self.musicArray[[self.musicArray indexOfObject:self.player.currentItem]+1]]; } }else if (self.playerType == AVPlayerTypeRandom){ [self.player replaceCurrentItemWithPlayerItem:self.musicArray[arc4random_uniform((int)self.musicArray.count)]]; }else if (self.playerType == AVPlayerTypeSingle){ //判断当前歌曲是否是最后一首歌曲,如果是跳转到第一首歌 if ([self.musicArray indexOfObject:self.player.currentItem]+1 == self.musicArray.count) { [self.player replaceCurrentItemWithPlayerItem:self.musicArray.firstObject]; }else{ [self.player replaceCurrentItemWithPlayerItem:self.musicArray[[self.musicArray indexOfObject:self.player.currentItem]+1]]; } } [self playMusic]; self.block2([self.musicArray indexOfObject:self.player.currentItem]); [self addObserver]; }]; } //获取 当前播放歌曲在AVPlayerItem中的 序号 -(NSInteger )getcurrentItem{ return [self.musicArray indexOfObject:self.player.currentItem]; } //将slider的值传入使 歌曲进度 前进或后退 -(void)playerProgressWithProgressFloat:(CGFloat)progressFloat{ if (progressFloat > self.currentLoadTime) { NSDictionary *dict = @{@"播放状态":@0}; [self.center postNotificationName:@"playOrPasue" object:self userInfo:dict]; } [self.player seekToTime:CMTimeMake(progressFloat, 1.0)]; } //接受播放完成的通知 开始下一首 - (void)playbackFinished:(NSNotification *)notice { //判断是否是单曲循环 是的话 移除观察者 将当前进度调为0 重新播放 if (self.playerType == AVPlayerTypeSingle) { [self removeObserver]; [self.player.currentItem seekToTime:kCMTimeZero]; [self musicPlayerWithIndex:[self getcurrentItem]]; [self playMusic]; }else{ [self nextSong]; } } //为当前播放的item 添加观察者 -(void)addObserver{ __weak typeof(self) weekSelf = self; __weak AVPlayer *weekPlayer = self.player; //观察该item 是否能播放 [self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; //观察该item 加载进度 [self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; //AVPlayer 自带方法:返回歌曲 播放进度 信息 timeObserve = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { [weekSelf updateLockedScreenMusic:[weekSelf getcurrentItem]]; float current = CMTimeGetSeconds(time); float total = CMTimeGetSeconds(weekPlayer.currentItem.duration); weekSelf.currentPlayTime = current; if (total > 0) { weekSelf.silderMaxValue = total; }else{ weekSelf.silderMaxValue = 300; }; weekSelf.silderValue = current; if (current) { weekSelf.playTime =[weekSelf timetranstation:[NSString stringWithFormat:@"%.f",current]]; weekSelf.totalTime =[weekSelf timetranstation:[NSString stringWithFormat:@"%.f",total]]; //判断歌曲播完 后发出通知 -132行 接受通知 if (current == total) { // total - current <=1 有时会导致不能正常切换歌曲 [[NSNotificationCenter defaultCenter] addObserver:weekSelf selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:weekPlayer.currentItem]; } //返回 播放界面所需要的内容。 weekSelf.block1(weekSelf.playTime,weekSelf.totalTime,weekSelf.silderMaxValue,weekSelf.silderValue); } }]; } //移除对当前item 的观察 通知 -(void)removeObserver{ if (timeObserve) { [[NSNotificationCenter defaultCenter] removeObserver:self]; [self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; [self.player.currentItem removeObserver:self forKeyPath:@"status"]; [self.player removeTimeObserver:timeObserve]; } } //观察者 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ AVPlayerItem * songItem = object; NSDictionary *dict = @{@"播放状态":@1}; if ([keyPath isEqualToString:@"status"]) { switch (self.player.status) { case AVPlayerStatusUnknown: // BASE_INFO_FUN(@"KVO:未知状态,此时不能播放"); break; case AVPlayerStatusReadyToPlay: // self.status = SUPlayStatusReadyToPlay; // BASE_INFO_FUN(@"KVO:准备完毕,可以播放"); [self.center postNotificationName:@"playOrPasue" object:self userInfo:dict]; [self.player play]; break; case AVPlayerStatusFailed: // BASE_INFO_FUN(@"KVO:加载失败,网络或者服务器出现问题"); break; default: break; } }else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { NSArray * array = songItem.loadedTimeRanges; CMTimeRange timeRange = [array.firstObject CMTimeRangeValue]; //本次缓冲的时间范围 NSTimeInterval totalBuffer = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration); //缓冲总长度 self.currentLoadTime = totalBuffer; float total = CMTimeGetSeconds(songItem.duration); // NSLog(@"共缓冲%.0f%",totalBuffer/total*100); 计算缓冲进度 self.loadTime =[NSString stringWithFormat:@"正在缓冲%.0f%",totalBuffer/total*100]; if ([[NSString stringWithFormat:@"%.0f",totalBuffer/total*100] isEqualToString:@"100"]) { self.loadTime =@""; }else{ self.loadTime =[NSString stringWithFormat:@"正在缓冲%.0f%",totalBuffer/total*100]; } CGFloat currentPlayTime = self.currentPlayTime; //缓冲完成时间大于当前播放时间5s时通知播放器继续播放 否则暂停 if (totalBuffer > currentPlayTime +5) { // NSDictionary *dict = @{@"播放状态":@1}; // [self.center postNotificationName:@"playOrPasue" object:self userInfo:dict]; }else{ NSDictionary *dict = @{@"播放状态":@0}; [self.center postNotificationName:@"playOrPasue" object:self userInfo:dict]; } NSLog(@"%f ----%f",total,totalBuffer); if (totalBuffer + 1 > total) { NSDictionary *dict = @{@"播放状态":@1}; [self.center postNotificationName:@"playOrPasue" object:self userInfo:dict]; } self.block(self.loadTime); } } #pragma 秒数转为分钟 -(NSString *)timetranstation:(NSString *)time{ NSString *returnTime =[NSString string]; NSInteger total = [time integerValue]; returnTime =[NSString stringWithFormat:@"%02ld:%02ld",total/60,total%60]; return returnTime; } #pragma mark - 锁屏时候的设置,效果需要在真机上才可以看到 - (void)updateLockedScreenMusic:(NSInteger )index{ // 播放信息中心 MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter]; // 初始化播放信息 NSMutableDictionary *info = [NSMutableDictionary dictionary]; // 专辑名称 // info[MPMediaItemPropertyAlbumTitle] = [sel]; // 歌手 info[MPMediaItemPropertyArtist] = self.singerArray[index]; // 歌曲名称 info[MPMediaItemPropertyTitle] = self.songNameArray[index]; // 设置图片 info[MPMediaItemPropertyArtwork] = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@",self.imageArray[index]]]]]]; // 设置持续时间(歌曲的总时间) [info setObject:[NSNumber numberWithFloat:CMTimeGetSeconds([self.player.currentItem duration])] forKey:MPMediaItemPropertyPlaybackDuration]; // 设置当前播放进度 [info setObject:[NSNumber numberWithFloat:CMTimeGetSeconds([self.player.currentItem currentTime])] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; // 切换播放信息 center.nowPlayingInfo = info; }

第一次写,如果哪位大神耐心看了,希望多给一点指点,不足的地方指出来。我要多学习学习!!!

demo中的网络地址来自于 阿里星球 , 因为是在线播放,所以在真机上面跑会有点卡 ,希望大神指点!需要怎么处理?

参考链接:iOS音频篇:使用AVPlayer播放网络音乐


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM