基于AVPlayer的音乐播放器

RGPlayManafer.h
#import
#import

@protocol RGPlayManagerDelegate 

/**
 获取播放信息

 @param curTime   当前时间
 @param totleTime 总时间
 @param progress  当前播放进度
 @param netValue  缓冲进度百分比
 */
-(void)getCurTiem:(NSString *)curTime Totle:(NSString *)totleTime Progress:(CGFloat)progress netValue:(NSTimeInterval)netValue;

/**
 播放结束之后, 如何操作由外部决定.
 */
-(void)endOfPlayAction;

@end

@interface RGPlayManager : NSObject

@property (nonatomic, strong) AVPlayer *player;
@property (weak, nonatomic)   id delegate;


+(instancetype)sharedPlayManager;

/**播放音乐*/
-(void)musicPlay;

/**暂停音乐*/
-(void)musicPause;


/**
 准备播放
 
 @param urlStr 音乐地址
 @param isOnline 是在线, 否本地
 */
-(void)musicPrePlay:(NSString*)urlStr isOnline:(BOOL)isOnline;

/**
 前进或者后退

 @param value silder的值
 */
-(void)seekToTimeWithValue:(CGFloat)value;

/**
 设置音量

 @param value silder的值
 */
- (void)setupCurrentVolume:(CGFloat)value;

@end

RGPlayManager.m
#import "RGPlayManager.h"

@interface RGPlayManager()

@property (nonatomic, strong) AVPlayerItem *currentItem;    //当前的音乐item
@property (nonatomic, strong) NSTimer *timer;               //定时器, 记录播放
@property (assign, nonatomic) NSTimeInterval scale;         //缓冲百分比例

@end

@implementation RGPlayManager

+ (instancetype)sharedPlayManager {
    static RGPlayManager *_playManger;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _playManger = [[self alloc] init];
    });
    return _playManger;
}

- (instancetype)init{
    
    if (self = [super init]) {
        _player = [[AVPlayer alloc] init];
        
        //播放完毕的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endOfPlay:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
        
        //后台播放
//        [self updateLockedScreenMusic];
    }
    return self;
}

//MARK: - 通知方法
- (void)endOfPlay:(NSNotification *)sender{
    [self musicPause];
    
    if ([self.delegate respondsToSelector:@selector(endOfPlayAction)]) {
        [self.delegate endOfPlayAction];
    }
}

//MARK: - 准备播放
-(void)musicPrePlay:(NSString*)urlStr isOnline:(BOOL)isOnline{
    
    //先移除item的status添加观察者.
    if (self.player.currentItem) {
        [self.player.currentItem removeObserver:self forKeyPath:@"status"];
        [self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
    }
    
    [self addPlayerItem:urlStr isOnline:isOnline];
}

//MARK: - AVPlayerItem
-(void)addPlayerItem:(NSString*)urlStr isOnline:(BOOL)isOnline{
    //判断在线和本地
    NSURL *url = nil;
    if (isOnline) {
        //在线
        url = [NSURL URLWithString:urlStr];
    }else{
        //本地
        url = [NSURL fileURLWithPath:urlStr];
    }
    
    // 根据传入的URL(MP3歌曲地址),创建一个item对象
    // initWithURL的初始化方法建立异步链接. 什么时候连接建立完成我们不知道.
    // 但是它完成连接之后,会修改自身内部的属性status.
    // 所以,我们要观察这个属性,当它的状态变为AVPlayerItemStatusReadyToPlay时,
    // 我们便能得知,播放器已经准备好,可以播放了.
    AVPlayerItem * item = [[ AVPlayerItem alloc] initWithURL:url];
    
    
    //监听播放状态
    [item addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:nil];
    //KVO监听音乐缓冲状态
    [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    
    // 替换AVPlayer之前的item.
    [self.player replaceCurrentItemWithPlayerItem:item];
}

//MARK: - 观察的是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 musicPlay];
                break;
            case AVPlayerItemStatusFailed:
                //NSLog(@"准备失败");
                break;
            default:
                break;
        }
    }else 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.scale = scale;
    }
}

//MARK: - 播放
-(void)musicPlay{
    // 如果计时器已经存在了,说明已经在播放中,直接返回.
    // 对于已经存在的计时器,只有musicPause方法才会使之停止和注销.
    if (self.timer != nil) {
        return;
    }
    
    // 播放后,我们开启一个计时器.
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
    
    [self.player play];
}
-(void)timerAction:(NSTimer * )sender{
    // 计时器的处理方法中,不断的调用代理方法,将播放进度返回出去.
    [self.delegate getCurTiem:[self valueToString:[self getCurTime]] Totle:[self valueToString:[self getTotleTime]] Progress:[self getProgress] netValue: self.scale];
}
//MARK: - 暂停方法
-(void)musicPause{
    [self.timer invalidate];
    self.timer = nil;
    [self.player pause];
}

//MARK: - 滑动silder
-(void)seekToTimeWithValue:(CGFloat)value{
    // 先暂停
    [self musicPause];
    
    // 跳转
    [self.player seekToTime:CMTimeMake(value * [self getTotleTime], 1) completionHandler:^(BOOL finished) {
        if (finished == YES) {
            [self musicPlay];
        }
    }];
}

//MARK: - 设置音量
-(void)setupCurrentVolume:(CGFloat)value{
    [self.player setVolume:value];
}

//MARK: - 获取当前的播放时间
-(NSInteger)getCurTime{
    
    if (self.player.currentItem) {
        // 用value/scale,就是AVPlayer计算时间的算法
        // 下同.
        return self.player.currentTime.value / self.player.currentTime.timescale;
    }
    return 0;
}

//MARK: - 获取总时长
-(NSInteger)getTotleTime{
    CMTime totleTime = [self.player.currentItem duration];
    if (totleTime.timescale == 0) {
        return 1;
    } else {
        return totleTime.value /totleTime.timescale;
    }
}
//MARK: - 获取当前播放进度
-(CGFloat)getProgress{
    return (CGFloat)[self getCurTime]/ (CGFloat)[self getTotleTime];
}

//MARK: - 将整数秒转换为 00:00 格式的字符串
-(NSString *)valueToString:(NSInteger)value{
    return [NSString stringWithFormat:@"%.2ld:%.2ld",value/60,value%60];
}

@end

你可能感兴趣的:(基于AVPlayer的音乐播放器)