實(shí)例講解iOS音樂(lè)播放器DOUAudioStreamer用法
好久沒(méi)有寫(xiě)東西了,最近加班太嚴(yán)重,今天抽空把用到的音樂(lè)播放器DOUAudioStreamer整理一下,由于項(xiàng)目之前用的是AVPlayer,這個(gè)也可以,但是就是要先緩存一段時(shí)間再播放,老板看了之后要求,要變緩存變播放(有網(wǎng)時(shí),點(diǎn)擊播放按鈕就立刻播放),怎么不早說(shuō)!怎么不早說(shuō)!怎么不早說(shuō)!還能怎樣?只能原諒他,繼續(xù)敲代碼。。。。。。(還是直接上代碼吧)
一、導(dǎo)入三方庫(kù)
pod 'DOUAudioStreamer'
或者GitHup下載地址:https://github.com/douban/DOUAudioStreamer
二、使用
1.從demo中獲取NAKPlaybackIndicatorView文件和MusicIndicator.h和MusicIndicator.m 文件,并導(dǎo)入頭文件
//音樂(lè)播放
#import "DOUAudioStreamer.h"
#import "NAKPlaybackIndicatorView.h"
#import "MusicIndicator.h"
#import "Track.h"
如圖:
2.創(chuàng)建一個(gè)Track類(lèi),用于音樂(lè)播放的URL存放
3.需要的界面.h中,添加DOUAudioStreamer,并用單利來(lái)初始化
+ (instancetype)sharedInstance ; @property (nonatomic, strong) DOUAudioStreamer *streamer;
如圖:
在.m中實(shí)現(xiàn):
static void *kStatusKVOKey = &kStatusKVOKey; static void *kDurationKVOKey = &kDurationKVOKey; static void *kBufferingRatioKVOKey = &kBufferingRatioKVOKey; @property (strong, nonatomic) MusicIndicator *musicIndicator; @property (nonatomic, strong) Track *audioTrack; + (instancetype)sharedInstance { static HYNEntertainmentController *_sharedMusicVC = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedMusicVC = [[HYNEntertainmentController alloc] init]; _sharedMusicVC.streamer = [[DOUAudioStreamer alloc] init]; }); return _sharedMusicVC; }
播放按鈕事件
#pragma mark ---音樂(lè)播放按鈕 -(void)playMusicStart:(UIButton *)sender { //通過(guò)按鈕獲取cell MusicCollectionViewCell *musicCell = (MusicCollectionViewCell *)[[sender superview] superview]; if(_playFirst == 0){//_playFirst == 0首次播放,其他為暫停 NSURL *url = [NSURL URLWithString:HttpImgUrl(musicCell.model.musicUrl)]; _audioTrack.audioFileURL = url; @try { [self removeStreamerObserver]; } @catch(id anException){ } //在DOUAudioStreamer進(jìn)行播放時(shí),必須先置為nil _streamer = nil; _streamer = [DOUAudioStreamer streamerWithAudioFile:_audioTrack]; [self addStreamerObserver]; [_streamer play]; } if([_streamer status] == DOUAudioStreamerPaused || [_streamer status] == DOUAudioStreamerIdle){ [sender setBackgroundImage:[UIImage imageNamed:@"music_play_icon"] forState:UIControlStateNormal]; [_streamer play]; }else{ [sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal]; [_streamer pause]; } _playFirst++; }
對(duì)添加監(jiān)聽(tīng)
- (void)addStreamerObserver { [_streamer addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:kStatusKVOKey]; [_streamer addObserver:self forKeyPath:@"duration" options:NSKeyValueObservingOptionNew context:kDurationKVOKey]; [_streamer addObserver:self forKeyPath:@"bufferingRatio" options:NSKeyValueObservingOptionNew context:kBufferingRatioKVOKey]; } /// 播放器銷(xiāo)毀 - (void)dealloc{ if (_streamer !=nil) { [_streamer pause]; [_streamer removeObserver:self forKeyPath:@"status" context:kStatusKVOKey]; [_streamer removeObserver:self forKeyPath:@"duration" context:kDurationKVOKey]; [_streamer removeObserver:self forKeyPath:@"bufferingRatio" context:kBufferingRatioKVOKey]; _streamer =nil; } } - (void)removeStreamerObserver { [_streamer removeObserver:self forKeyPath:@"status"]; [_streamer removeObserver:self forKeyPath:@"duration"]; [_streamer removeObserver:self forKeyPath:@"bufferingRatio"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == kStatusKVOKey) { [self performSelector:@selector(updateStatus) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO]; } else if (context == kDurationKVOKey) { [self performSelector:@selector(updateSliderValue:) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO]; } else if (context == kBufferingRatioKVOKey) { [self performSelector:@selector(updateBufferingStatus) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO]; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } - (void)updateSliderValue:(id)timer { } -(void)updateBufferingStatus { } - (void)updateStatus { //self.musicIsPlaying = NO; _musicIndicator.state = NAKPlaybackIndicatorViewStateStopped; switch ([_streamer status]) { case DOUAudioStreamerPlaying: // self.musicIsPlaying = YES; _musicIndicator.state = NAKPlaybackIndicatorViewStatePlaying; break; case DOUAudioStreamerPaused: break; case DOUAudioStreamerIdle: break; case DOUAudioStreamerFinished: break; case DOUAudioStreamerBuffering: _musicIndicator.state = NAKPlaybackIndicatorViewStatePlaying; break; case DOUAudioStreamerError: break; } }
這樣就能播放了。
鎖屏?xí)r的音樂(lè)顯示、拔出耳機(jī)后暫停播放、監(jiān)聽(tīng)音頻打斷事件
-(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; //接受遠(yuǎn)程控制 [self becomeFirstResponder]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; } //這個(gè)不能忘記了 -(BOOL)canBecomeFirstResponder{ return YES; } - (void)viewDidLoad { [super viewDidLoad]; //音樂(lè)播放器 [self initPlayer]; } #pragma mark =========================音樂(lè)播放============================== //音樂(lè)播放器 -(void)initPlayer { _audioTrack = [[Track alloc] init]; AVAudioSession *session = [AVAudioSession sharedInstance]; [session setActive:YES error:nil]; [session setCategory:AVAudioSessionCategoryPlayback error:nil]; //讓app支持接受遠(yuǎn)程控制事件 [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; //添加通知,拔出耳機(jī)后暫停播放 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil]; // 監(jiān)聽(tīng)音頻打斷事件 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionWasInterrupted:) name:AVAudioSessionInterruptionNotification object:session]; } // 監(jiān)聽(tīng)音頻打斷事件 - (void)audioSessionWasInterrupted:(NSNotification *)notification { //被打斷時(shí) if (AVAudioSessionInterruptionTypeBegan == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue]) { [_streamer pause]; UIButton *btn = (UIButton *)[self.view viewWithTag:2000]; [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal]; } else if (AVAudioSessionInterruptionTypeEnded == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue]) { } } // 拔出耳機(jī)后暫停播放 -(void)routeChange:(NSNotification *)notification{ NSDictionary *dic=notification.userInfo; int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue]; //等于A(yíng)VAudioSessionRouteChangeReasonOldDeviceUnavailable表示舊輸出不可用 if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey]; AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject]; //原設(shè)備為耳機(jī)則暫停 if ([portDescription.portType isEqualToString:@"Headphones"]) { [_streamer pause]; UIButton *btn = (UIButton *)[self.view viewWithTag:2000]; [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal]; } } } //鎖屏?xí)r音樂(lè)顯示(這個(gè)方法可以在點(diǎn)擊播放時(shí),調(diào)用傳值) - (void)setupLockScreenInfoWithSing:(NSString *)sign WithSigner:(NSString *)signer WithImage:(UIImage *)image { // 1.獲取鎖屏中心 MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter]; //初始化一個(gè)存放音樂(lè)信息的字典 NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary]; // 2、設(shè)置歌曲名 if (sign) { [playingInfoDict setObject:sign forKey:MPMediaItemPropertyAlbumTitle]; } // 設(shè)置歌手名 if (signer) { [playingInfoDict setObject:signer forKey:MPMediaItemPropertyArtist]; } // 3設(shè)置封面的圖片 //UIImage *image = [self getMusicImageWithMusicId:self.currentModel]; if (image) { MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image]; [playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork]; } // 4設(shè)置歌曲的總時(shí)長(zhǎng) //[playingInfoDict setObject:self.currentModel.detailDuration forKey:MPMediaItemPropertyPlaybackDuration]; //音樂(lè)信息賦值給獲取鎖屏中心的nowPlayingInfo屬性 playingInfoCenter.nowPlayingInfo = playingInfoDict; // 5.開(kāi)啟遠(yuǎn)程交互 [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; } //鎖屏?xí)r操作 - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent { if (receivedEvent.type == UIEventTypeRemoteControl) { UIButton *sender = (UIButton *)[self.view viewWithTag:2000]; switch (receivedEvent.subtype) {//判斷是否為遠(yuǎn)程控制 case UIEventSubtypeRemoteControlPause: [[HYNEntertainmentController sharedInstance].streamer pause]; [sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal]; break; case UIEventSubtypeRemoteControlStop: break; case UIEventSubtypeRemoteControlPlay: [[HYNEntertainmentController sharedInstance].streamer play]; [sender setBackgroundImage:[UIImage imageNamed:@"music_play_icon"] forState:UIControlStateNormal]; break; case UIEventSubtypeRemoteControlTogglePlayPause: break; case UIEventSubtypeRemoteControlNextTrack: break; case UIEventSubtypeRemoteControlPreviousTrack: break; default: break; } } }
整體圖片:
上圖為未播放
上圖為播放中
上圖為鎖屏?xí)r狀態(tài)
應(yīng)該沒(méi)有什么要添加的了,暫時(shí)告一段落,有不足之處,可以在下方的留言區(qū)討論,感謝對(duì)腳本之家的支持。
- ios 流媒體播放器實(shí)現(xiàn)流程及FreeStreamer的使用的示例
- iOS之基于FreeStreamer的簡(jiǎn)單音樂(lè)播放器示例
- 運(yùn)用iOS教你輕松制作音樂(lè)播放器
- ios開(kāi)發(fā):一個(gè)音樂(lè)播放器的設(shè)計(jì)與實(shí)現(xiàn)案例
- iOS中視頻播放器的簡(jiǎn)單封裝詳解
- iOS中的音頻服務(wù)和音頻AVAudioPlayer音頻播放器使用指南
- 實(shí)例解析iOS中音樂(lè)播放器應(yīng)用開(kāi)發(fā)的基本要點(diǎn)
- iOS開(kāi)發(fā)中音頻工具類(lèi)的封裝以及音樂(lè)播放器的細(xì)節(jié)控制
- iOS音樂(lè)播放器實(shí)現(xiàn)代碼完整版
相關(guān)文章
React Native搭建iOS開(kāi)發(fā)環(huán)境
React Native的門(mén)檻不管是對(duì)于前端開(kāi)發(fā)者還是移動(dòng)端開(kāi)發(fā)者來(lái)說(shuō)都是很高的,既要懂原生又要懂js,技術(shù)棧是相當(dāng)長(zhǎng)的。但是沒(méi)有關(guān)系,下面我們一步步來(lái)學(xué)習(xí),慢慢成長(zhǎng)吧!2016-09-09iOS?StoreKit?2?新特性盤(pán)點(diǎn)解析
這篇文章主要為大家介紹了iOS?StoreKit?2?新特性盤(pán)點(diǎn)及要點(diǎn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07iOS開(kāi)發(fā)中使用Picker View實(shí)現(xiàn)一個(gè)點(diǎn)菜應(yīng)用的UI示例
這篇文章主要介紹了iOS開(kāi)發(fā)中使用Picker View實(shí)現(xiàn)一個(gè)點(diǎn)菜應(yīng)用的UI示例,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-01-01iOS浮點(diǎn)類(lèi)型精度問(wèn)題的原因與解決辦法
在iOS開(kāi)發(fā)中,我們經(jīng)常要使用浮點(diǎn)類(lèi)型去接收后臺(tái)返回過(guò)來(lái)的的數(shù)據(jù),這時(shí)往往會(huì)遇到精度問(wèn)題,這篇文章主要給大家介紹了關(guān)于iOS浮點(diǎn)類(lèi)型精度問(wèn)題的原因與解決辦法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01iOS 獲取當(dāng)前的ViewController的方法
本篇文章主要介紹了iOS 獲取當(dāng)前的ViewController的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09iOS開(kāi)發(fā)使用GDataXML框架解析網(wǎng)絡(luò)數(shù)據(jù)
GDataXML是Google開(kāi)發(fā)的一個(gè)XML解析庫(kù),輕便,特點(diǎn)使用非常簡(jiǎn)單,支持XPath。今天把前兩天弄的IOS XML解析記錄下來(lái),也供大家參考。2016-02-02IOS中(Xcode) DEBUG模式(RELEASE模式)控制NSLog輸出,NSLog輸出方式
這篇文章主要介紹了IOS中(Xcode) DEBUG模式(RELEASE模式)控制NSLog輸出,NSLog輸出方式的相關(guān)資料,需要的朋友可以參考下2016-11-11Flutter?模型動(dòng)態(tài)化賦值研究分析
這篇文章主要為大家介紹了Flutter?模型動(dòng)態(tài)化賦值研究分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03