基于AVPlayer的音樂播放器

RGPlayManafer.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@protocol RGPlayManagerDelegate <NSObject>

/**
 獲取播放信息

 @param curTime   當(dāng)前時(shí)間
 @param totleTime 總時(shí)間
 @param progress  當(dāng)前播放進(jìn)度
 @param netValue  緩沖進(jìn)度百分比
 */
-(void)getCurTiem:(NSString *)curTime Totle:(NSString *)totleTime Progress:(CGFloat)progress netValue:(NSTimeInterval)netValue;

/**
 播放結(jié)束之后, 如何操作由外部決定.
 */
-(void)endOfPlayAction;

@end

@interface RGPlayManager : NSObject

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


+(instancetype)sharedPlayManager;

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

/**暫停音樂*/
-(void)musicPause;


/**
 準(zhǔn)備播放
 
 @param urlStr 音樂地址
 @param isOnline 是在線, 否本地
 */
-(void)musicPrePlay:(NSString*)urlStr isOnline:(BOOL)isOnline;

/**
 前進(jìn)或者后退

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

/**
 設(shè)置音量

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

@end

RGPlayManager.m
#import "RGPlayManager.h"

@interface RGPlayManager()

@property (nonatomic, strong) AVPlayerItem *currentItem;    //當(dāng)前的音樂item
@property (nonatomic, strong) NSTimer *timer;               //定時(shí)器, 記錄播放
@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];
        
        //后臺(tái)播放
//        [self updateLockedScreenMusic];
    }
    return self;
}

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

//MARK: - 準(zhǔn)備播放
-(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];
    }
    
    // 根據(jù)傳入的URL(MP3歌曲地址),創(chuàng)建一個(gè)item對(duì)象
    // initWithURL的初始化方法建立異步鏈接. 什么時(shí)候連接建立完成我們不知道.
    // 但是它完成連接之后,會(huì)修改自身內(nèi)部的屬性status.
    // 所以,我們要觀察這個(gè)屬性,當(dāng)它的狀態(tài)變?yōu)锳VPlayerItemStatusReadyToPlay時(shí),
    // 我們便能得知,播放器已經(jīng)準(zhǔn)備好,可以播放了.
    AVPlayerItem * item = [[ AVPlayerItem alloc] initWithURL:url];
    
    
    //監(jiān)聽播放狀態(tài)
    [item addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:nil];
    //KVO監(jiān)聽音樂緩沖狀態(tài)
    [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    
    // 替換AVPlayer之前的item.
    [self.player replaceCurrentItemWithPlayerItem:item];
}

//MARK: - 觀察的是Item的status狀態(tài).
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    //播放狀態(tài)
    if ([keyPath isEqualToString:@"status"]) {
        switch ([[change valueForKey:@"new"] integerValue]) {
            case AVPlayerItemStatusUnknown:
                //NSLog(@"不知道什么錯(cuò)誤");
                break;
            case AVPlayerItemStatusReadyToPlay:
                //只有觀察到status變?yōu)檫@種狀態(tài),才會(huì)真正的播放.
                [self musicPlay];
                break;
            case AVPlayerItemStatusFailed:
                //NSLog(@"準(zhǔn)備失敗");
                break;
            default:
                break;
        }
    }else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {//緩沖進(jìn)度
        
        NSArray * timeRanges = self.player.currentItem.loadedTimeRanges;
        //本次緩沖的時(shí)間范圍
        CMTimeRange timeRange = [timeRanges.firstObject CMTimeRangeValue];
        //緩沖總長度
        NSTimeInterval totalLoadTime = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration);
        //音樂的總時(shí)間
        NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration);
        //計(jì)算緩沖百分比例
        NSTimeInterval scale = totalLoadTime/duration;
        self.scale = scale;
    }
}

//MARK: - 播放
-(void)musicPlay{
    // 如果計(jì)時(shí)器已經(jīng)存在了,說明已經(jīng)在播放中,直接返回.
    // 對(duì)于已經(jīng)存在的計(jì)時(shí)器,只有musicPause方法才會(huì)使之停止和注銷.
    if (self.timer != nil) {
        return;
    }
    
    // 播放后,我們開啟一個(gè)計(jì)時(shí)器.
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
    
    [self.player play];
}
-(void)timerAction:(NSTimer * )sender{
    // 計(jì)時(shí)器的處理方法中,不斷的調(diào)用代理方法,將播放進(jìn)度返回出去.
    [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: - 滑動(dòng)silder
-(void)seekToTimeWithValue:(CGFloat)value{
    // 先暫停
    [self musicPause];
    
    // 跳轉(zhuǎn)
    [self.player seekToTime:CMTimeMake(value * [self getTotleTime], 1) completionHandler:^(BOOL finished) {
        if (finished == YES) {
            [self musicPlay];
        }
    }];
}

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

//MARK: - 獲取當(dāng)前的播放時(shí)間
-(NSInteger)getCurTime{
    
    if (self.player.currentItem) {
        // 用value/scale,就是AVPlayer計(jì)算時(shí)間的算法
        // 下同.
        return self.player.currentTime.value / self.player.currentTime.timescale;
    }
    return 0;
}

//MARK: - 獲取總時(shí)長
-(NSInteger)getTotleTime{
    CMTime totleTime = [self.player.currentItem duration];
    if (totleTime.timescale == 0) {
        return 1;
    } else {
        return totleTime.value /totleTime.timescale;
    }
}
//MARK: - 獲取當(dāng)前播放進(jìn)度
-(CGFloat)getProgress{
    return (CGFloat)[self getCurTime]/ (CGFloat)[self getTotleTime];
}

//MARK: - 將整數(shù)秒轉(zhuǎn)換為 00:00 格式的字符串
-(NSString *)valueToString:(NSInteger)value{
    return [NSString stringWithFormat:@"%.2ld:%.2ld",value/60,value%60];
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容