一个音频播放器,要考虑的因素很多,首先这个播放器应该是一个单例,因为无论是哪个界面推出播放器,只能存在一个,存在就播放,不存在才能创建。先来看看效果图。
播放音频,首先会考虑到AVFoundation框架,这个是苹果专门给开发者用来播放音频的框架,本地和网络资源都可以播放。
AVPlayer:AVPlayer存在于AVFoundation中,其实它是一个视频播放器,但是用它来播放音乐是没问题的,本地音频和流媒体播放,但处理音频不够灵活;但是它对视屏有很高自由度的控制,而且能够自定义视屏播放界面。但是用来播放音频也是没有问题的。
AVPlayerItem:AVPlayerItem是一个载体,承载AVAsset,然后通过AVPlayer进行播放.我们要想对一个资源进行播放,那么就要通过AVPlayerItem和AVPlayerItemTrack来构建对应的动态内容
1、创建单例:用来承载播放器的控制器
+ (instancetype)sharedInstance{
static PlayerViewController *once_player = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
once_player = [[PlayerViewController alloc]init];
AVAudioSession *session = [AVAudioSession sharedInstance];[session setCategory:AVAudioSessionCategoryPlayback error:nil];//而且要激活,音频会话。
[session setActive:YES error:nil];//这段代码用来后台播放
});
return once_player;
}
1、创建palyer和服务
-(void)playCurrtnItem:(ShiCiDetialModel*)model{
self.model = model;
self.isPlay = YES;
self.isSXPlay = YES;
self.currtenLable.text = @"00:00:00";
self.dutionLable.text = model.duration;
self.progressView.progress = 0;
self.slider.value = 0;
[self removePlayLoadTime];
//移除监听音乐播放进度
[self removeTimeObserver];
[self removePlayStatus];
[self roatationAnimation];
[self setHeadviewImageBy:model];
if (self.player) {
//存在就播放
if (self.music_id == model.id) {
//id 相同直接播放(点击播放的时候)
[self.player play];
}else{
//不同的创建AVPlayerItem ,资源是在这里加载的
[self showBaseHudWithTitle:@""];
self.playItem = [[AVPlayerItem alloc]initWithURL:[NSURL URLWithString:model.playurl]];
[self.player replaceCurrentItemWithPlayerItem:self.playItem];//播放不同的,只需要替换掉AVPlayerItem就可以了
[self.player play];
}
}else{
//player不存在就创建
[self showBaseHudWithTitle:@""];
self.playItem = [[AVPlayerItem alloc]initWithURL:[NSURL URLWithString:model.playurl]];
self.player = [[AVPlayer alloc]initWithPlayerItem:self.playItem];
[self.player play];
}
self.music_id = model.id;
[self addNSNotificationForPlayMusicFinish];
//监听播放器状态
[self addPlayStatus];
//监听音乐缓冲进度
[self addPlayLoadTime];
//监听音乐播放的进度
[self addMusicProgressWithItem:self.player.currentItem];
}
2、各种状态的监听
-(void)addPlayStatus{
[self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
}/
/移除监听播放器状态
-(void)removePlayStatus{
if (self.model == nil) {return;
}
[self.player.currentItem removeObserver:self forKeyPath:@"status"];
}/
/KVO监听音乐缓冲状态
-(void)addPlayLoadTime{
[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
}
//移除监听音乐缓冲状态
-(void)removePlayLoadTime{
if (self.model == nil) {return;
}
[self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}
#pragma mark - NSNotification
-(void)addNSNotificationForPlayMusicFinish{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
}
-(void)playFinished:(NSNotification*)notification{
[[NSNotificationCenter defaultCenter] removeObserver:self];
//播放下一首
[self nextOneClick:nil];
}
//监听音乐播放的进度
-(void)addMusicProgressWithItem:(AVPlayerItem *)item{
__weak typeof(self) weakSelf = self;
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
//当前播放的时间
float current = CMTimeGetSeconds(time);
//总时间
float total = CMTimeGetSeconds(item.duration);
if (current) {
float progress = current / total;
//更新播放进度条
weakSelf.slider.value = progress;
weakSelf.currtenLable.text = [weakSelf timeFormatted:current];
} }];
}
//转换成时分秒
- (NSString *)timeFormatted:(NSInteger)totalSeconds{
NSInteger seconds = totalSeconds % 60;
NSInteger minutes = (totalSeconds / 60) % 60;
NSInteger hours = totalSeconds/3600;
return [NSString stringWithFormat:@"%02ld:%02ld:%02ld",hours,minutes, seconds];}
//移除监听音乐播放进度
-(void)removeTimeObserver{
if (self.timeObserver) {
[self.player removeTimeObserver:self.timeObserver];
self.timeObserver = nil; }}
//观察者回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context
{
if ([keyPath isEqualToString:@"status"]) {
switch (self.player.status) {
case AVPlayerStatusUnknown:
{
}
break;
case AVPlayerStatusReadyToPlay:
{
}
break;
case AVPlayerStatusFailed:
{
}
break;
default:
break;
}
}
if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSArray * timeRanges = self.player.currentItem.loadedTimeRanges;
//本次缓冲的时间范围
CMTimeRange timeRange = [timeRanges.firstObject CMTimeRangeValue];
//缓冲总长度
NSTimeInterval totalLoadTime = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration);
//音乐的总时间
NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration);
//计算缓冲百分比例
NSTimeInterval scale = totalLoadTime/duration;
//更新缓冲进度条
self.progressView.progress = scale;
if (scale == 1) {
[self dismissHud];
}
}
}
3、上一曲下一曲和随机播放
-(void)lastOneClick:(UIButton*)button{
if (self.currtenflag==0) {
[self showWarningHudWithWarningTitle:@"前面没有了哟"];
[self dismissHudAfter:1.f];
}else{
self.currtenflag--;
self.model = self.modelArr[self.currtenflag];
[self playCurrtnItem:self.model];
}
}
-(void)playClick:(UIButton*)button{
if (self.isPlay) {
CALayer *presentLayer = (CALayer*)self.headView.layer.presentationLayer;
[self.headView.layer removeAnimationForKey:@"playAnimation"];
self.headView.layer.transform = presentLayer.transform;
[self.playBtn setImage:ECIMAGENAME(@"pasu") forState:UIControlStateNormal];
self.isPlay = NO;
self.model = nil;
[self.player pause];
}else{
[self roatationAnimation];
self.isPlay = YES;
[self.playBtn setImage:ECIMAGENAME(@"play") forState:UIControlStateNormal];
[self.player play];
}
}
-(void)nextOneClick:(UIButton*)button{
if (self.modelArr.count - self.currtenflag == 1||self.modelArr.count - self.currtenflag == 0) {
[self showWarningHudWithWarningTitle:@"后面没有了哟"];
[self dismissHudAfter:2.f];
CALayer *presentLayer = (CALayer*)self.headView.layer.presentationLayer;
[self.headView.layer removeAnimationForKey:@"playAnimation"];
self.headView.layer.transform = presentLayer.transform;
[self.playBtn setImage:ECIMAGENAME(@"pasu") forState:UIControlStateNormal];
self.isPlay = NO;
self.player = nil;
}else{
self.currtenflag++;
self.model = self.modelArr[self.currtenflag];
[self playCurrtnItem:self.model];
}
}
-(void)showListClick:(UIButton*)button{
if (self.modelArr.count) {
self.popView = [[ZYFPopview alloc]initView:self.view rows:self.modelArr defaultSelectRow:self.currtenflag selectDone:^(NSInteger selectRow) {
self.currtenflag = selectRow;
self.model = self.modelArr[selectRow];
[self playCurrtnItem:self.model];
} canleDone:^{
}];
[self.popView showPopView];
}else{
[self showBaseHud];
[self dismissHudWithWarningTitle:@"没有歌单" After:1.f];
}
}
-(void)playStyleClick:(UIButton*)button{
NSInteger playFlag = 0;
if (self.isSXPlay) {
//随机播放
NSInteger arrCount = self.modelArr.count;
playFlag = self.currtenflag;
self.currtenflag = arc4random()%(arrCount-1);
self.isSXPlay = NO;
[self.playStyleBtn setImage:ECIMAGENAME(@"suiji") forState:UIControlStateNormal];
}else{
//顺序播放
self.currtenflag = playFlag;
self.isSXPlay = YES;
[self.playStyleBtn setImage:ECIMAGENAME(@"shunxu") forState:UIControlStateNormal];
}
}
- (void)roatationAnimation{
//dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.fromValue = @(0.0);
rotationAnimation.toValue = @(M_PI*2);
rotationAnimation.duration = 20;
rotationAnimation.repeatCount = HUGE_VAL;
[self.headView.layer addAnimation:rotationAnimation forKey:@"playAnimation"];
// });
}
里面的逻辑大概就是这样详情代码请到GitHub下载:
https://github.com/Dreamer929