一、AVAudioPlayer播放本地音樂
簡介:AVAudioPlayer是AVFoundation.framework中的一個類胆描,它支持多種音頻格式,而且能夠進(jìn)行進(jìn)度国夜、音量短绸、播放速度等控制醋闭,詳細(xì)屬性可以看
原文 介紹非常詳細(xì)。
注意點:AVAudioPlayer只能播放本地文件乐埠,并且是一次性加載所有音頻數(shù)據(jù)瑟曲,初始化AVAudioPlayer時指定的URL也只能是File URL而不能是HTTP URL,AVAudioPlayer一次只能播放一個音頻文件,如果想實現(xiàn)上一曲烦衣、下一曲的功能可以通過創(chuàng)建多個播放器對象來完成.
AVAudioPlayer簡單使用
1.初始化一個AVAudioPlayer
//獲取本地音樂的路徑
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"235319" ofType:@"mp3"];
NSURL *url2 = [NSURL URLWithString:filePath];
NSError *error = nil;
//創(chuàng)建音樂播放器掩浙,此URL不能是HTTP URL
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url2 error:&error];
//設(shè)置為0不循環(huán)厨姚,可以設(shè)置循環(huán)次數(shù)
_audioPlayer.numberOfLoops=3;
//根據(jù)URL地址來讀取音樂文件(寫在ViewDidLoad中會自動播放)
[_audioPlayer prepareToPlay];
//設(shè)置代理,監(jiān)聽音樂是否播放完成
_audioPlayer.delegate = self;
//error存在今布,初始化失敗
if (error) {
NSLog(@"初始化錯誤");
}
2.在需要播放的地方開始播放就行了
-(void)playClick:(UIButton*)button{
if (!button.selected) {
[self play];
}else{
[self pause];
}
button.selected = !button.selected;
}
-(void)play{
if (![_audioPlayer isPlaying]) {
[_audioPlayer play];
self.timer.fireDate=[NSDate distantPast];//恢復(fù)定時器
}
}
/**
* 暫停播放
*/
-(void)pause{
if ([_audioPlayer isPlaying]) {
[_audioPlayer pause];
self.timer.fireDate=[NSDate distantFuture];//暫停定時器拭抬,注意不能調(diào)用invalidate方法造虎,此方法會取消,之后無法恢復(fù)
}
}
3.滑動UISlider設(shè)置當(dāng)前播放
音樂播放進(jìn)度的實現(xiàn)主要依靠一個定時器實時計算當(dāng)前播放時長和音頻總時長的比例
//開始播放時份蝴,開始定時器,暫停播放時暫停定時器
-(NSTimer *)timer{
if (!_timer) {
_timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:true];
}
return _timer;
}
-(void)updateProgress{
//當(dāng)前播放音樂的進(jìn)度時間/音樂總時間波桩,算出值賦值給Slider
float progress= _audioPlayer.currentTime /_audioPlayer.duration;
if (!self.isTap) {
[self.slider setValue:progress animated:true];
}
}
-(void)playToTime:(float)value{
if (![_audioPlayer isPlaying]) {
_audioPlayer.currentTime = value*_audioPlayer.duration;
[_audioPlayer play];
self.timer.fireDate=[NSDate distantPast];//恢復(fù)定時器
}else{
_audioPlayer.currentTime = value*_audioPlayer.duration;
}
}
4.完成播放回掉代理
#pragma mark - 播放器代理方法
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(@"音樂播放完成...");
//切換歌曲可以在此操作
self.timer.fireDate=[NSDate distantFuture];//恢復(fù)定時器
}
二镐躲、AVPlayer播放網(wǎng)絡(luò)侍筛,本地音樂
參考文章鏈接:http://www.cnblogs.com/kenshincui/p/4186022.html
簡介:上面說了AVAudioPlayer 不支持網(wǎng)絡(luò)音頻在線播放匣椰,你可以使用一些第三方框架來實現(xiàn)在線播放例如AudioStreamer、FreeStreamer入录。如果不想使用第三方的框架佳镜,AVPlayer可以幫你實現(xiàn)在線播放的功能,AVPlayer也是存在于AVFoundation中蚀同,但是它功能要更加強(qiáng)大蠢络,可以播放本地音視頻和網(wǎng)絡(luò)音視頻迟蜜,有時當(dāng)你需要自定義一些播放樣式時,蘋果提供的一些封裝好的API例如MPMoviePlayerController可能并不能滿足你的需求髓霞,這時使用AVPlayer可以幫你完成這些需求微姊。
常用的類:
- AVAsset:主要用于獲取多媒體信息兢交,是一個抽象類,不能直接使用酪穿。
- AVURLAsset:AVAsset的子類,可以根據(jù)一個URL路徑創(chuàng)建一個包含媒體信息的AVURLAsset對象救赐。
- AVPlayerItem:一個媒體資源管理對象只磷,管理者視頻的一些基本信息和狀態(tài),一個AVPlayerItem對應(yīng)著一個視頻資源预厌。
暫停轧叽、播放功能
首先說一下音頻的播放刊棕、暫停功能,這也是最基本的功能网严,AVPlayer對應(yīng)著兩個方法play蜈膨、pause來實現(xiàn)翁巍。但是關(guān)鍵問題是如何判斷當(dāng)前音頻是否在播放休雌,在前面的內(nèi)容中無論是音頻播放器還是音頻播放器都有對應(yīng)的狀態(tài)來判斷,但是AVPlayer卻沒有這樣的狀態(tài)屬性驰凛,通常情況下可以通過判斷播放器的播放速度來獲得播放狀態(tài)恰响。如果rate為0說明是停止?fàn)顟B(tài)涌献,1是則是正常播放狀態(tài)。
///判斷是否正在播放
-(BOOL)isPlaying{
if (self.rate==1) {
return YES;
}else{
return NO;
}
}
播放進(jìn)度的控制和音頻的切換
其次要展示播放進(jìn)度就沒有其他播放器那么簡單了枢劝。在前面的播放器中通常是使用通知來獲得播放器的狀態(tài),媒體加載狀態(tài)等烙常,但是無論是AVPlayer還是AVPlayerItem(AVPlayer有一個屬性currentItem是AVPlayerItem類型蚕脏,表示當(dāng)前播放的音頻對象)都無法獲得這些信息侦锯。當(dāng)然AVPlayerItem是有通知的率触,但是對于獲得播放狀態(tài)和加載狀態(tài)有用的通知只有一個(其他真的沒啥用):播放完成通知AVPlayerItemDidPlayToEndTimeNotification。在播放音頻時穴张,特別是播放網(wǎng)絡(luò)音頻往往需要知道音頻加載情況两曼、緩沖情況、播放情況偿枕,這些信息可以通過KVO監(jiān)控AVPlayerItem的status渐夸、loadedTimeRanges屬性來獲得渔欢。當(dāng)AVPlayerItem的status屬性為AVPlayerStatusReadyToPlay是說明正在播放,只有處于這個狀態(tài)時才能獲得音頻時長等信息苫幢;當(dāng)loadedTimeRanges的改變時(每緩沖一部分?jǐn)?shù)據(jù)就會更新此屬性)可以獲得本次緩沖加載的音頻范圍(包含起始時間垫挨、本次加載時長)九榔,這樣一來就可以實時獲得緩沖情況涡相。然后就是依靠AVPlayer的方法獲得播放進(jìn)度漾峡,這個方法會在設(shè)定的時間間隔內(nèi)定時更新播放進(jìn)度喻旷,通過time參數(shù)通知客戶端。相信有了這些視頻信息播放進(jìn)度就不成問題了槽袄,事實上通過這些信息就算是平時看到的其他播放器的緩沖進(jìn)度顯示以及拖動播放的功能也可以順利的實現(xiàn)遍尺。
最后就是音頻切換的功能涮拗,在前面介紹的所有播放器中每個播放器對象一次只能播放一個音頻,如果要切換音頻只能重新創(chuàng)建一個對象鼓择,但是AVPlayer卻提供了- (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item方法用于在不同的音頻之間切換(事實上在AVFoundation內(nèi)部還有一個AVQueuePlayer專門處理播放列表切換呐能,有興趣的朋友可以自行研究抑堡,這里不再贅述)首妖。
添加通知和觀察者
///添加通知和觀察者
-(void)addNotificationAndObserver{
[self addNotification];
[self addProgressObserver];
[self addObserverToPlayerItem:self.currentItem];
}
///移除所有的通知和觀察者
-(void)removeNotificationAndObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.currentItem];
[self removeObserverFromPlayerItem:self.currentItem];
[self removeTimeObserver:self.obeserObject];
}
-(void)addProgressObserver{
__weak typeof(self) weakSelf = self;
//這里設(shè)置每秒執(zhí)行一次,這個方法會在設(shè)定的時間間隔內(nèi)定時更新播放進(jìn)度有缆,通過time參數(shù)通知客戶端。
self.obeserObject = [self addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
if ([weakSelf.delegate respondsToSelector:@selector(msPlyaer:cmTime:)]) {
[weakSelf.delegate msPlyaer:weakSelf cmTime:time];
}
}];
}
-(void)addNotification{
//給AVPlayerItem添加播放完成通知(音樂播放完成后會回調(diào)通知方法)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.currentItem];
}
-(void)playbackFinished:(NSNotification *)notification{
self.isPlayComplete = YES;
if ([self.delegate respondsToSelector:@selector(msPlyaerPlayEnd:)]) {
[self.delegate msPlyaerPlayEnd:self];
}
}
-(void)addObserverToPlayerItem:(AVPlayerItem *)playerItem{
//監(jiān)控狀態(tài)屬性,注意AVPlayer也有一個status屬性灌曙,通過監(jiān)控它的status也可以獲得播放狀態(tài)
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
//監(jiān)控網(wǎng)絡(luò)加載情況屬性
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem{
[playerItem removeObserver:self forKeyPath:@"status"];
[playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
AVPlayerItem *playerItem=object;
if ([keyPath isEqualToString:@"status"]) {
AVPlayerStatus status= [[change objectForKey:@"new"] intValue];
if(status==AVPlayerStatusReadyToPlay){
///獲取音頻長度
NSLog(@"正在播放...在刺,視頻總長度:%.2f",CMTimeGetSeconds(playerItem.duration));
if ([self.delegate respondsToSelector:@selector(msPlyaer:currentPlayerItem:)]) {
[self.delegate msPlyaer:self currentPlayerItem:playerItem];
}
}else{
///更新音頻播放時間
if ([self.delegate respondsToSelector:@selector(msPlyaerPlayError:currentPlayerItem:)]) {
[self.delegate msPlyaerPlayError:self currentPlayerItem:playerItem];
}
}
}else if([keyPath isEqualToString:@"loadedTimeRanges"]){
NSArray *array=playerItem.loadedTimeRanges;
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次緩沖時間范圍
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩沖總長度
NSLog(@"共緩沖:%.2f",totalBuffer);
//
}
}
音量控制
使用player.volume屬性可以控制播放的音量蚣驼,但是此屬性僅改變app的音量颖杏,不要用它來制作slider的聲音控制開關(guān),可以使用MPVolumeView來制作翼抠,這里 就不介紹啦获讳,有興趣的朋友可以自己去看看丐膝。
注意點
1.切換音樂時可以手動判斷當(dāng)前是否在播放,以防止上一曲暫唾怂啵或者播放完成后處于暫停狀態(tài)下直接調(diào)用下面方法時不播放损晤。
///調(diào)用此方法切換播放源
[self replaceCurrentItemWithPlayerItem:item];
2.切換音頻時要清除上一個item的通知和觀察者
///移除所有的通知和觀察者
-(void)removeNotificationAndObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.currentItem];
[self removeObserverFromPlayerItem:self.currentItem];
[self removeTimeObserver:self.obeserObject];
}
3.使用全局變量標(biāo)記進(jìn)度觀察者返回的對象,切換音頻時移除觀察
@interface MSPlayer ()
@property(nonatomic,strong) id obeserObject;
@end
-(void)addProgressObserver{
__weak typeof(self) weakSelf = self;
//這里設(shè)置每秒執(zhí)行一次,這個方法會在設(shè)定的時間間隔內(nèi)定時更新播放進(jìn)度尤勋,通過time參數(shù)通知客戶端最冰。
self.obeserObject = [self addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
if ([weakSelf.delegate respondsToSelector:@selector(msPlyaer:cmTime:)]) {
[weakSelf.delegate msPlyaer:weakSelf cmTime:time];
}
}];
}
[self removeTimeObserver:self.obeserObject];
4.音頻播放失敗后一定要重新處理player和item,具體的好辦法我也不清楚赌朋,只是通過下面的代碼實現(xiàn)了重新播放沛慢。
-(void)msPlyaerPlayError:(MSPlayer*)player currentPlayerItem:(AVPlayerItem*)playerItem{
self.player = nil;
self.playerItme = nil;
}