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