前言:
? ? ? ? ? ? ?在iOS中無論哪個框架,只要能把它的屬性志珍、方法鬼佣、代理方法搞清楚,就基本上能夠進行簡單的開發(fā)。當(dāng)然如果想更高性能更好體驗的話就需要了解每個屬性和方法的內(nèi)在關(guān)系够庙,以及加上自身掌握的處理事件的經(jīng)驗恭应。無論哪一種情況,我建議大家講框架的類全部整理出來耘眨,將每一個類的屬性和方法整理出來暮屡,iOS的API因為命名的規(guī)范大都能夠通過字面意思了解,但是其他的我建議仔細翻譯毅桃,或者去文檔中讀取英文注釋褒纲。 ?言歸正傳,下面我給大家介紹一下AVPlayer開發(fā)音樂模塊钥飞。
音樂概述:
先給大家擼兩張圖片莺掠,然后我仔細介紹一下:
1、AudioToolbox.framework的音頻播放時間不能超過30s,數(shù)據(jù)必須是PCM或者IMA4格式,音頻文件必須打包成.caf读宙、.aif彻秆、.wav中的一種(注意這是官方文檔的說法,實際測試發(fā)現(xiàn)一些.mp3也可以播放. 它的主要用途可以用作app的音效(不是背景音).
2结闸、MediaPlayer.framework框架下有兩個常用的系統(tǒng)封裝好的播放器:MPMoviePlayController 和 MPMoviePlayViewController, 二者的區(qū)別在于, 后者的視頻圖像需要一張View視圖作為載體, 你可以自己創(chuàng)建這個View, 那么也可以自由的控制它. 最明顯的例子就是你可以用它做個浮窗播放器.
3唇兑、AVFoundation.framework 目前被AVKit框架替代了, 但是我沒有跟進, 我就用它:
? ? ? AVAudioRecorder播放器, 提供錄音, 錄音的的代碼加起來沒你jj長.AVPlayer播放器, 一個能 ? ? 播放網(wǎng)絡(luò)和本地視頻/音頻的播放器, 和MediaPlayer.framework框架下的兩個播放器不同, 系統(tǒng)并未提供它的UI界面, 我們需要自己實現(xiàn), 往好聽了說: 這是一個可以高度自定義的播放器.
AVAudioPlayer與 AVPlayer播放器的區(qū)別在于, 這貨只能播放本地音樂.
好了讓我開始音樂播放的開發(fā)之旅吧!h氤扎附!
1、框架的設(shè)計结耀。
? ? ? ? 因為我們模塊需要播放本地音樂留夜、網(wǎng)絡(luò)音樂播放、藍牙播放图甜、車載SD卡播放碍粥,所以綜合情況比一般的音樂播放要麻煩很多。但是呢黑毅,大家也不用怕啦嚼摩,只要框架設(shè)計的好就很大程度上減少我們的工作量。
? ? ? 所以呢矿瘦,我把本地音樂和網(wǎng)絡(luò)音樂放到一個管理類(MusicPlayer)里面枕面,然后藍牙播放和車載SD卡播放放到另外一個管理類(BLEMusicPlayer)里,兩個管理類都是繼承BasePlayer.
#importtypedef enum : NSInteger {
PLAYER_ORDER,? ? ? // 順序
PLAYER_REPEAT,? ? // 循環(huán)
PLAYER_RANDOM,? ? // 隨機
} PLAYERTYPEMODE;
@interface BasePlayer : NSObject
- (void)play;
- (void)pause;
- (void)stop;
- (void)next;
- (void)previous;
- (float)getSystomVolume;
- (void)adjustVolumeOfplayer:(float)value;
- (void)selectTypeMode:(PLAYERTYPEMODE)typeMode;
#import "BasePlayer.h"
@implementation BasePlayer
- (void)play{}
- (void)pause{}
- (void)stop{}
- (void)next{}
- (void)previous{}
- (float)getSystomVolume{
return 0;
}
- (void)adjustVolumeOfplayer:(float)value{}
- (void)selectTypeMode:(PLAYERTYPEMODE)typeMode{}
@end
可能名字起得不是太規(guī)范,大家見諒我語文老師是數(shù)學(xué)老師教的(別當(dāng)真哈)7朔病膊畴!
下面是MusicPlayer類,這個類我是封裝的AVPlayer,將播放的狀態(tài)病游、索引、歌曲緩存進度、播放進度衬衬、item等通過代理的方式全部傳出去买猖。下面我只提供一些核心代碼,如果有不明白的地方大家可以在下面提問滋尉,或者直接看我的代碼玉控。
NSKeyValueObservingOptions opations = NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew;
// 查看播放狀態(tài)
[self.player.currentItem addObserver:self forKeyPath:@"status" options:opations context:nil];
// 查看加載進度
[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:opations context:nil];
// 監(jiān)聽音樂是否完成
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(musicPlayDidFinished) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
// 監(jiān)聽音樂是否跳躍
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(musicPlayJumped) name:AVPlayerItemTimeJumpedNotification object:self.player.currentItem];
// 監(jiān)聽時間進度
__weak typeof(self) wself = self;
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
// TODO
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds(wself.player.currentItem.duration);
float progress = current/total;
if (current) {
//? ? ? ? ? ? NSLog(@"\n 音樂加載的進度:%f \n 當(dāng)前的時間:%f \n 總時間:%f" ,current/total,current,total);
if (wself.delegate&&[wself.delegate respondsToSelector:@selector(updataPlayProgress:currentTime:totalTime:)]) {
[wself.delegate updataPlayProgress:progress currentTime:current totalTime:total];
}
}
}];
#pragma mark --- KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context{? ? ? ?
AVPlayerItem *item = (AVPlayerItem*)object;? ?
if ([keyPath isEqualToString:@"status"]) {? ? ? ?
switch ([change[@"new"] integerValue]) {? ? ? ? ? ?
case AVPlayerItemStatusReadyToPlay: ? ? ? ? ?
{
[self play];? ? ? ? ? ? ? ?
if ([self.delegate respondsToSelector:@selector(playerStartPlayWithPlayer:)]) { ? ? ?[self.delegate playerStartPlayWithPlayer:self.player];? ? ? ? ? ? ? ?
} ? ? ? ? ? } ? ? ? ? ? ? ?
break; ? ? ? ? ??
case AVPlayerItemStatusFailed:? ? ? ? ? ? {? ? ? ? ? ? ? ?
NSLog(@"播放失敗");? ? ? ? ? ? ? ?
self.playing = NO;? ? ? ? ? ? ? ?
if ([self.delegate respondsToSelector:@selector(player:failure:)]) {? ? ? ? ? ? ? ? ? ? [self.delegate player:self.player failure:nil];? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? ? ? ? ?
break;? ? ? ? ? ?
case AVPlayerItemStatusUnknown:? ? ? ? ? ? {? ? ? ? ? ? ? ? NSLog(@"出現(xiàn)未知的原因");? ? ? ? ? ? ? ? self.playing = NO;? ? ? ? ? ? }? ? ? ? ? ? ? ?
break;? ? ? ? ? ?
default:? ? ? ? ? ? ? ? break;? ? ? ?
}?
? }? ? ?
? if ([keyPath isEqualToString:@"loadedTimeRanges"]) {? ? ? ? ? ? ? ?
? NSArray*array = item.loadedTimeRanges;
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];? //本次緩沖的時間范圍
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
float totalDuration = CMTimeGetSeconds(self.player.currentItem.asset.duration);
float bufferProgerss = durationSeconds / totalDuration;
NSTimeInterval totalBuffer = startSeconds + durationSeconds;
//? ? ? ? NSLog(@"\n音樂緩存的進度:%f \n 當(dāng)前時間:%f \n 總的時間:%f",bufferProgerss ,durationSeconds,totalBuffer);
if ([self.delegate respondsToSelector:@selector(updataBufferProgress:)]) {
[self.delegate updataBufferProgress:bufferProgerss];
}
}
if ([keyPath isEqualToString:@"rate"]) {
// 0~1 表示正在播放 , 1 表示播放
float rate = self.player.rate;
if (self.delegate && [self.delegate respondsToSelector:@selector(player:rateOfPlayer:)]) {
[self.delegate player:self.player rateOfPlayer:rate];
}
}
if ([keyPath isEqualToString:@"currentItem"]) {
if (self.delegate && [self.delegate respondsToSelector:@selector(playerCurrentItemOfPlayerChanged:)]) {
[self.delegate playerCurrentItemOfPlayerChanged:self.player];
}
}
}
播放 狮惜,暫停實現(xiàn)很簡單高诺,下面我說一下音樂播放列表、上一首碾篡、下一首虱而、播放模式的切換的基本思路。
在進入播放界面的時候开泽,隨之傳遞的是一大溜的序列化的音樂數(shù)據(jù)牡拇,也就是你放到數(shù)組的模型類,因為我們沒有后臺穆律,用的是第三方的LeanCloud儲存數(shù)據(jù)惠呼,因此表格需要我自己建,在這里呢我給大家的建議是:將 我的收藏峦耘、喜歡等屬于個人的屬性全部歸屬到一個表中剔蹋,這樣查詢的時候也是方便的。上面的上一首辅髓、下一首滩租、播放模式其實都是操作的索引值。
切換歌曲用這個方法:
[self.player replaceCurrentItemWithPlayerItem:item];
拖拽進度用這個方法:
[self.player seekToTime:seekTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {}];
好了利朵,上面的只要研讀AVPlayer 律想、AVPlayerItem、AVAsset的方法和屬性不難實現(xiàn)绍弟,下篇文章我來專門講一下上面這幾個類技即,然后是緩存,在然后就是整個的藍牙模塊啦樟遣。