在網上看到有人說AVplay就可以播放流媒體,便拿過來使用,,結果發現無論如何它都是要把MP3緩存完成之後才開始播放,,音頻長的就要等好久,對于M4A 格式的還有好些,MP3就不行了,,這樣體驗非常差。于是在網上搜羅方法,,找到了FreeStreamer 庫 可以實作邊緩存邊播放MP3檔案,,網上資料比較少。。在這裡大緻做下總結。。我的代碼寫的并不好 隻是給需要的做個參考 支援mp3和m4a格式 後面還有一篇DOUAudioStreamer 的總結 可以參考下
1.使用cocoapods 添加FreeStreamer庫
pod 'FreeStreamer','~> 3.7.2'
2.
#import <FSAudioStream.h>
- (void)viewWillAppear:(BOOL)animated{
[superviewWillAppear:animated];
// 開始接受遠端控制 為添加到音頻中心背景播放做準備
[[UIApplicationsharedApplication]beginReceivingRemoteControlEvents];
[selfbecomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated{
[superviewWillDisappear:animated];
// 結束遠端控制 為添加到音頻中心背景播放做準備
[[UIApplicationsharedApplication]endReceivingRemoteControlEvents];
[selfresignFirstResponder];
}
// 重寫父類成為響應者方法 為添加到音頻中心背景播放做準備
- (BOOL)canBecomeFirstResponder {
returnYES;
}
#pragma mark 重寫父類方法,接受外部事件的處理 為添加到音頻中心背景播放做準備
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
if (receivedEvent.type ==UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {//得到事件類型
caseUIEventSubtypeRemoteControlTogglePlayPause://暫停 ios6
self.play =YES;//調用你所在項目的暫停按鈕的響應方法下面的也是如此
[selfplayAction];
break;
caseUIEventSubtypeRemoteControlPreviousTrack: //上一首
[selflastButtonAction];
break;
caseUIEventSubtypeRemoteControlNextTrack://下一首
[selfnextButtonAction];
break;
caseUIEventSubtypeRemoteControlPlay://播放
self.play =NO;//調用你所在項目的暫停按鈕的響應方法下面的也是如此
[selfplayAction];
break;
caseUIEventSubtypeRemoteControlPause://暫停 ios7
self.play =YES;//調用你所在項目的暫停按鈕的響應方法下面的也是如此
[selfplayAction];
break;
default:
break;
}
}
}
//Now Playing Center可以在鎖屏界面展示音樂的資訊,也達到增強使用者體驗的作用。
#pragma mark 傳遞資訊到鎖屏狀态下此方法在播放歌曲與切換歌曲時調用即可
- (void)configNowPlayingCenter:(YiPinXiangXiBody *)body {
NSMutableDictionary * info = [NSMutableDictionarydictionary];
//音樂的标題
[info setObject:body.titleforKey:MPMediaItemPropertyTitle];
//音樂的藝術家
[info setObject:body.columnNameforKey:MPMediaItemPropertyArtist];
//音樂的封面
UIImage *image = [[UIImagealloc]initWithData:[NSDatadataWithContentsOfURL:[NSURLURLWithString: [NSStringstringWithFormat:@"%@%@",URLADDRESS,body.cover]]]];
MPMediaItemArtwork * artwork = [[MPMediaItemArtworkalloc]initWithImage:image];
[info setObject:artworkforKey:MPMediaItemPropertyArtwork];
//完成設定
[[MPNowPlayingInfoCenterdefaultCenter]setNowPlayingInfo:info];
// NSLog(@"----%lld--------%f------",self.player.currentTime.value,self.totalTime);
}
#pragma mark 初始化播放器
- (void)playerInit:(NSURL *)url{
if (!_audioStream) {
// 建立FSAudioStream對象
_audioStream=[[FSAudioStreamalloc]init];
// 設定聲音
[_audioStreamsetVolume:1];
}
_audioStream.url = url;
[_audioStream play];
_audioStream.onFailure=^(FSAudioStreamError error,NSString *description){
NSLog(@"播放出現問題%@",description);
};
__weaktypeof(self) weakSelf =self;
_audioStream.onCompletion=^(){
[weakSelf nextButtonAction];// 播放完成後繼續播放下一個
};
// 計時器 擷取播放進度和緩存進度
self.playerTimer = [NSTimerscheduledTimerWithTimeInterval:1target:selfselector:@selector(playProgressAction)userInfo:nilrepeats:YES];
// 将視訊資訊添加到Playing Center 鎖屏顯示播放資訊 控制播放暫停
[selfconfigNowPlayingCenter:self.yiPinXiangXi.body];
}
#pragma mark 音頻緩存和播放進度提示
- (void)playProgressAction{
FSStreamPosition cur =self.audioStream.currentTimePlayed;
self.playbackTime =cur.playbackTimeInSeconds/1;
double minutesElapsed =floor(fmod(self.playbackTime/60.0,60.0));
double secondsElapsed =floor(fmod(self.playbackTime,60.0));
self.nowTimeLabel.text = [NSStringstringWithFormat:@"%02.0f:%02.0f",minutesElapsed, secondsElapsed];
self.sliderProgress.value = cur.position;//播放進度
// 擷取視訊的總時長
self.totalTime =self.playbackTime/cur.position;
NSLog(@"-=-=-=-=-=%f",self.totalTime);
if ([[NSStringstringWithFormat:@"%f",self.totalTime]isEqualToString:@"nan"]) {
self.totalTimeLabel.text =@"00:00";
}else{
double minutesElapsed1 =floor(fmod(self.totalTime/60.0,60.0));
double secondsElapsed1 =floor(fmod(self.totalTime,60.0));
self.totalTimeLabel.text = [NSStringstringWithFormat:@"%02.0f:%02.0f",minutesElapsed1, secondsElapsed1];
}
float prebuffer = (float)self.audioStream.prebufferedByteCount;
float contentlength = (float)self.audioStream.contentLength;
if (contentlength>0) {
self.playerProgress.progress = prebuffer /contentlength;//緩存進度
}
/// 更新airPlay背景和鎖屏播放進度====================
NSMutableDictionary *dict = [NSMutableDictionarydictionaryWithDictionary:[[MPNowPlayingInfoCenterdefaultCenter]nowPlayingInfo]];
[dict setObject:@(self.playbackTime)forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
//音樂的總時間
[dict setObject:@(self.totalTime)forKey:MPMediaItemPropertyPlaybackDuration];
[[MPNowPlayingInfoCenterdefaultCenter] setNowPlayingInfo:dict];
}
#pragma mark 播放暫停按鈕=================
- (void)playAction{
//先将未到時間執行前的任務取消 防止變态點選導緻崩潰
[[selfclass]cancelPreviousPerformRequestsWithTarget:selfselector:@selector(theplayAction)object:nil];
[selfperformSelector:@selector(theplayAction)withObject:nilafterDelay:0.2f];// 0.2不可改
}
- (void)theplayAction{
if (self.play ==YES) {// 如果正在播放 則暫停
[self.audioStreampause];
[self.playerTimersetFireDate:[NSDatedistantFuture]];// 計時器暫停
[_playButtonsetImage:[UIImageimageNamed:@"bofang"]forState:UIControlStateNormal];// 按鈕換狀态
}else{
[self.audioStreampause];// 如果是暫停,則播放。。這裡并不是使用[self.audioStream play];不信你可以試試...不知道為什麼會這樣
[self.playTimersetFireDate:[NSDatedistantPast]];
[self.playerTimersetFireDate:[NSDatedistantPast]];
[_playButtonsetImage:[UIImageimageNamed:@"audioPause"]forState:UIControlStateNormal];
}
self.play = !self.play;
}
#pragma mark 拖動進度條到指定位置播放,重新添加播放進度。 事先建立好進度條添加滑動方法
- (void)durationSliderTouchEnded:(UISlider *)slider{
[selfslidertoPlay:slider.value];
}
#pragma mark 滑動進度條跳到指定位置,播放狀态
- (void)slidertoPlay:(CGFloat)time{
if (time ==1) {// 當滑動到頭的時候自動播放下一曲。。
[selfnextButtonAction];
}elseif (time >=0) {
FSStreamPosition pos = {0};
pos.position = time;
[self.audioStreamseekToPosition:pos];// 到指定位置播放
}
}
#pragma mark 上一曲按鈕點選方法
- (void)lastButtonAction{
//先将未到時間執行前的任務取消 防止變态點選
[[selfclass]cancelPreviousPerformRequestsWithTarget:selfselector:@selector(theLastButtonAction)object:nil];
[selfperformSelector:@selector(theLastButtonAction)withObject:nilafterDelay:0.2f];// 0.2不可改
}
- (void)theLastButtonAction{ // 我這邊上一曲下一曲是通過網絡擷取 根據具體情況進行更改
if (self.lastIdentifier ==nil) {
}else{
[self.playTimersetFireDate:[NSDatedistantFuture]];
self.playerProgress.progress =0.0;
self.play =YES;
self.sliding =NO;
[selfplayerItemDealloc];// 播放器銷毀
self.identifier =self.lastIdentifier;
[selfaddData]; // 擷取到上一曲後,,建立播放器
}
}
#pragma mark 下一曲按鈕點選方法
- (void)nextButtonAction{
//先将未到時間執行前的任務取消
[[selfclass]cancelPreviousPerformRequestsWithTarget:selfselector:@selector(thenextButtonAction)object:nil];
[selfperformSelector:@selector(thenextButtonAction)withObject:nilafterDelay:0.2f];// 0.2不可改
}
- (void)thenextButtonAction{
if (self.nextIdentifier ==nil) {
}else{
[self.playTimersetFireDate:[NSDatedistantFuture]];
self.playerProgress.progress =0.0;
self.play =YES;
self.sliding =NO;
[selfplayerItemDealloc];
self.identifier =self.nextIdentifier;
[selfaddData];
}
}
#pragma mark 銷毀播放器,,清除緩存的音頻(freestreamer 會自動下載下傳音頻,可以不清除,,但可能檔案越來越大,,我這裡不保留,,基本是緩沖一點點馬上可以播放,,離線的話就播不了了)
- (void)playerItemDealloc{
NSArray *arr = [[NSFileManagerdefaultManager]contentsOfDirectoryAtPath:_audioStream.configuration.cacheDirectoryerror:nil];
for (NSString *filein arr) {
if ([filehasPrefix:@"FSCache-"]) {
NSString *path = [NSStringstringWithFormat:@"%@/%@",_audioStream.configuration.cacheDirectory, file];
[[NSFileManagerdefaultManager]removeItemAtPath:patherror:nil];
}
}
[_audioStreamstop];
_audioStream =nil;
[_playerTimerinvalidate];
_playerTimer =nil;
}
#pragma mark 解決slider小範圍滑動不能觸發的問題(也曾困擾很久的問題)
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if([gestureRecognizerlocationInView:gestureRecognizer.view].y >=_sliderProgress.frame.origin.y && !_sliderProgress.hidden)
returnNO;
returnYES;
}
// 系統自帶API解析MP3時長
- (CGFloat)getMusicTime:(NSURL *)url{
AVURLAsset *avURLAsset = [[AVURLAssetalloc]initWithURL:urloptions:nil];
// 擷取音頻總時長
CGFloat time = avURLAsset.duration.value / avURLAsset.duration.timescale;
return time;
}
https://github.com/muhku/FreeStreamer