在网上看到有人说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;
}