[IOS]音频的后台播放和远程控制
2015-07-29 14:31
591 查看
[IOS]音频的后台播放和远程控制
DEMO地址:http://download.csdn.net/detail/u012881779/8942623
主要实现音频的远程控制,能响应后台上/下一曲、播放、暂停功能
后台播放需要先在info表中添加键Required background modes 并设置为App plays audio or streams audio/video using AirPlay
![](https://img-blog.csdn.net/20150729130714694?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
参考代码如下:
示意图:
![](https://img-blog.csdn.net/20150729131003096?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
DEMO地址:http://download.csdn.net/detail/u012881779/8942623
主要实现音频的远程控制,能响应后台上/下一曲、播放、暂停功能
后台播放需要先在info表中添加键Required background modes 并设置为App plays audio or streams audio/video using AirPlay
参考代码如下:
#import "SMListenViewController.h" #import "MVPublicMethod.h" #import "SMListenCell.h" #import "SVPullToRefresh.h" #import "SZNGTipView.h" #import <AVFoundation/AVFoundation.h> #import <MediaPlayer/MediaPlayer.h> #import "AudioPlayer.h" @class AudioPlayer; @interface SMListenViewController ()<UITableViewDataSource , UITableViewDelegate , UIScrollViewDelegate> { AVPlayer* player; AudioPlayer *_audioPlayer; } @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (strong, nonatomic) IBOutlet UIView *headerView; //表头 @property (strong, nonatomic) IBOutlet UIView *footView; //表尾 @property (strong, nonatomic) IBOutlet UIView *emptyFooterView;//表尾空 @property (weak, nonatomic) IBOutlet UIView *loadingView; //正在加载 @property (weak, nonatomic) IBOutlet UIButton *tapLoadBut; //点击加载更多 @property (strong, nonatomic) IBOutlet UIImageView *headerImgV; @property (strong, nonatomic) NSIndexPath *indexPaths; @property (strong, nonatomic) NSMutableArray *leftDataArr; @property (strong, nonatomic) SZWaitView *waitV; //等待页面 @property (nonatomic) NSInteger leftPageNum; @property (nonatomic) NSInteger playViodoID; //当前播放音频的ID @end @implementation SMListenViewController @synthesize leftPageNum = _leftPageNum; @synthesize leftDataArr = _leftDataArr; @synthesize waitV = _waitV; @synthesize playViodoID = _playViodoID; - (void)viewDidLoad { [super viewDidLoad]; //头图大小适配 CGRect screenRect = [[UIScreen mainScreen] bounds]; float wbh = 640/300.0; CGRect headerRect = _headerView.frame; headerRect.origin.x = 0; headerRect.origin.y = 0; headerRect.size.width = screenRect.size.width; headerRect.size.height = screenRect.size.width/wbh; [_headerView setFrame:headerRect]; //等待页面 NSString* msg = @"请稍候..."; _waitV = [[SZWaitView alloc] initWithString:msg]; CGPoint theCenter = CGPointMake(UIScreen.mainScreen.bounds.size.width/2.0, UIScreen.mainScreen.bounds.size.height/2.0) ; theCenter.y = theCenter.y - 40; _waitV.center = theCenter; [_waitV setHidden:NO]; [self.view addSubview:_waitV]; _leftPageNum = 1; [_tableView setShowsHorizontalScrollIndicator:NO]; [_tableView setShowsVerticalScrollIndicator:NO]; _tableView.separatorStyle = NO; [_tableView setTableHeaderView:_headerView]; [_tableView setTableFooterView:_footView]; //点击加载更多完成 [self startLoadingAction]; if(_leftDataArr.count >= 1){ }else{ [self leftNPAction]; } if(_leftDataArr == nil || _leftDataArr.count < 20){ [_tableView setTableFooterView:_emptyFooterView]; } //下拉刷新 __weak SMListenViewController * weakVC = self; [weakVC.tableView addPullToRefreshWithActionHandler:^{ [weakVC refreshAction]; }]; //播放完一首音频后自动切换到下一首 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishPlayNextAudio:) name:@"finishPlayNextAudio" object:nil]; } //播放完一首音频后自动切换到下一首 -(void)finishPlayNextAudio:(id)sender{ NSDictionary *infoDict = (NSDictionary *)[sender userInfo]; NSInteger tempID = [[infoDict objectForKey:@"id"] integerValue]; if(tempID != _playViodoID){ NSLog(@"切换下一曲,通知ID=%d,当前播放音频ID=%d",tempID,_playViodoID); _playViodoID = tempID; [self playNextMusic]; } } -(void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; //End recieving events [[UIApplication sharedApplication] endReceivingRemoteControlEvents]; [self resignFirstResponder]; [self stopRefreshAnimation]; [_audioPlayer stop]; _audioPlayer = nil; [player pause]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; //Once the view has loaded then we can register to begin recieving controls and we can become the first responder [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; [self becomeFirstResponder]; } //避免有时候tableview莫名的往下偏移一段距离 - (void)viewDidLayoutSubviews{ if(_tableView.contentOffset.y != 0){ _tableView.contentOffset = CGPointMake(0, 0); [_tableView reloadInputViews]; } } -(BOOL)canBecomeFirstResponder{ return YES; } //响应远程音乐播放控制消息 - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent { if (receivedEvent.type == UIEventTypeRemoteControl) { switch (receivedEvent.subtype) { case UIEventSubtypeRemoteControlTogglePlayPause: [self crateAVPlay]; [_audioPlayer pause]; NSLog(@"RemoteControlEvents: pause"); break; case UIEventSubtypeRemoteControlNextTrack: [self playNextMusic]; NSLog(@"RemoteControlEvents: playModeNext"); break; case UIEventSubtypeRemoteControlPreviousTrack: NSLog(@"RemoteControlEvents: playPrev"); [self playPreviousMusic]; break; case UIEventSubtypeRemoteControlPause: [self crateAVPlay]; [_audioPlayer pause]; break; case UIEventSubtypeRemoteControlPlay: [self crateAVPlay]; [_audioPlayer play]; break; default: break; } } } //下一曲 -(void)playNextMusic{ NSIndexPath* path = [NSIndexPath indexPathForItem:_indexPaths.row+1 inSection:_indexPaths.section]; if(path.row<[_leftDataArr count]){ CGRect rect = [UIScreen mainScreen].bounds; [_tableView scrollRectToVisible:CGRectMake(0, path.row*56, rect.size.width, 112) animated:NO]; [self tableViewChanges:_tableView didSelectRowAtIndexPath:path]; }else{ return; } } //上一曲 -(void)playPreviousMusic{ NSIndexPath* path = [NSIndexPath indexPathForItem:_indexPaths.row-1 inSection:_indexPaths.section]; if(path.row>=0){ CGRect rect = [UIScreen mainScreen].bounds; [_tableView scrollRectToVisible:CGRectMake(0, path.row*56, rect.size.width, -112) animated:NO]; [self tableViewChanges:_tableView didSelectRowAtIndexPath:path]; }else{ return; } } //列表请求 -(void)leftNPAction{ //demo使用模拟数据 [self getValueAction]; } //模拟数据 -(void)getValueAction{ //点击加载更多完成 [self startLoadingAction]; //有些MP3外链可能过一段时间就无法使用,到时进入后面这个网站重新弄一些新链接就可以 http://www.csqq8.com/ NSMutableArray *urlsArr = [[NSMutableArray alloc] init]; for (int i = 0 ; i < 20*_leftPageNum ; i ++) { //链接格式基本就是这样子http://www.csqq8.com/?down/26611.mp3 NSInteger statrid = 23647;//从哪个ID开始请求 [urlsArr addObject:[NSString stringWithFormat:@"http://www.csqq8.com/?down/%d.mp3",statrid+i]];; } /*数据样式 { id = 1397; length = 47; //单位为秒 title = "标题"; url = ""; } */ NSMutableArray *valueArr = [[NSMutableArray alloc] init]; for (int i = 0; i < urlsArr.count ; i ++) { NSInteger random = arc4random()%1000; NSString *tempid = [NSString stringWithFormat:@"%d",i * random]; NSString *urlStr = [urlsArr objectAtIndex:i]; NSString *titleStr = [NSString stringWithFormat:@"[第%d首] %@",i+1,[[urlStr componentsSeparatedByString:@"/"] lastObject]]; NSString *lengthStr = [NSString stringWithFormat:@"%d",0]; NSDictionary *tempDict = [[NSDictionary alloc] initWithObjectsAndKeys:tempid,@"id",lengthStr,@"length",titleStr,@"title",urlStr,@"url", nil]; [valueArr addObject:tempDict]; } _leftDataArr = valueArr; if(_leftDataArr){ //等待页面 _waitV.hidden = YES; [_waitV removeFromSuperview]; _waitV = nil; } [_tableView reloadData]; } //停止下拉刷新 -(void)stopRefreshAnimation{ [self.tableView.pullToRefreshView stopAnimating]; } //下拉刷新 -(void)refreshAction{ [_audioPlayer stop]; _audioPlayer = nil; _leftPageNum = 1; _leftDataArr = nil; [self leftNPAction]; //点击加载更多完成 [self startLoadingAction]; [self performSelector:@selector(stopRefreshAnimation) withObject:self afterDelay:3.0]; } //正在加载 - (IBAction)loadingMoreAction:(id)sender { [_tapLoadBut setTitle:@"点击加载更多" forState:UIControlStateNormal]; [_tapLoadBut setHidden:YES]; [_loadingView setHidden:NO]; _leftPageNum = _leftPageNum + 1; [self leftNPAction]; } //点击加载更多 -(void)startLoadingAction{ [_tapLoadBut setTitle:@"点击加载更多" forState:UIControlStateNormal]; [_tapLoadBut setUserInteractionEnabled:YES]; [_tapLoadBut setHidden:NO]; [_loadingView setHidden:YES]; } //加载完成 -(void)stopLoadingAction{ [_tapLoadBut setTitle:@"加载完成" forState:UIControlStateNormal]; [_tapLoadBut setUserInteractionEnabled:NO]; [_tapLoadBut setHidden:NO]; [_loadingView setHidden:YES]; } //播放音频 - (void)playAudio:(AudioButton *)button andDict:(NSDictionary *)item{ NSString *mark = [item objectForKey:@"playMark"]; if([mark isEqualToString:@"1"]){//播放 if (_audioPlayer == nil) { _audioPlayer = [[AudioPlayer alloc] init]; AVAudioSession *session = [AVAudioSession sharedInstance]; NSError *setCategoryError = nil; BOOL success = [session setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError]; if (!success) { /* handle the error condition */ return; } NSError *activationError = nil; success = [session setActive:YES error:&activationError]; if (!success) { /* handle the error condition */ return; } } NSURL* murl = [NSURL URLWithString:[item objectForKey:@"url"]]; if(![_audioPlayer.url isEqual:murl]){ [self crateAVPlay]; [_audioPlayer stop]; } if(murl){ _audioPlayer.url = murl; [self justPlay]; } }else if([mark isEqualToString:@"3"]){//暂停 [_audioPlayer pause]; }else{//停止 [_audioPlayer stop]; _audioPlayer = nil; } } //判断播放状态 -(void)justPlay{ UIBackgroundTaskIdentifier bgTask = 0; if([UIApplication sharedApplication].applicationState== UIApplicationStateBackground) { NSLog(@"xxx后台播放"); [_audioPlayer play]; UIApplication*app = [UIApplication sharedApplication]; UIBackgroundTaskIdentifier newTask = [app beginBackgroundTaskWithExpirationHandler:nil]; if(bgTask!= UIBackgroundTaskInvalid) { [app endBackgroundTask: bgTask]; } bgTask = newTask; } else { NSLog(@"xxx前台播放"); [_audioPlayer play]; } } -(void)routeChange:(NSNotification *)notification{ NSDictionary *dic=notification.userInfo; int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue]; //等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示旧输出不可用 if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey]; AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject]; //原设备为耳机则暂停 if ([portDescription.portType isEqualToString:@"Headphones"]) { [_audioPlayer pause]; } } } //音频信息传到后台远程控制那点,需要显示标题、头像都需要这里传质过去 - (void)configNowPlayingInfoCenter:(NSDictionary*)dic { if (NSClassFromString(@"MPNowPlayingInfoCenter")) { NSMutableDictionary * dict = [[NSMutableDictionary alloc] init]; [dict setObject:[dic objectForKey:@"title"] forKey:MPMediaItemPropertyTitle]; [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict]; } } //创建一个播放器,用它一直播放一个无声的音频,确保后台远程控制时点击上一曲、下一曲、暂停、播放时控制页面不消失掉 -(void)crateAVPlay{ NSString* urlstr = [[NSBundle mainBundle] pathForResource:@"无声" ofType:@"mp3"]; NSURL* url = [[NSURL alloc] initFileURLWithPath:urlstr]; if(!player){ player = [[AVPlayer alloc] initWithURL:url]; } [player play]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); } #pragma mark UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return _leftDataArr.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = @"SMListenCell"; SMListenCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = (SMListenCell*)[[[NSBundle mainBundle] loadNibNamed:@"SMListenCell" owner:nil options:nil] objectAtIndex:0]; cell.selectionStyle = UITableViewCellSelectionStyleNone; [cell setBackgroundColor:[UIColor clearColor]]; } if(_leftDataArr.count > indexPath.row){ NSString *theMark = [[_leftDataArr objectAtIndex:indexPath.row] objectForKey:@"theMark"]; [cell valueWithDictionary:[_leftDataArr objectAtIndex:indexPath.row] andMark:theMark]; }else{ [cell valueWithDictionary:nil andMark:@"10"]; } return cell; } #pragma mark UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ NSDictionary *tempDict = [_leftDataArr objectAtIndex:indexPath.row]; NSString *title = [tempDict objectForKey:@"title"]; float widf = tableView.frame.size.width - 30; float titleF = [MVPublicMethod labelWithSting:title andWidth:widf andFontSize:15]; if(titleF > 30){ [self addMarkToDictionary:@"10" andRow:indexPath.row]; return 68-8; }else{ [self addMarkToDictionary:@"0" andRow:indexPath.row]; return 54-8; } } //添加标识 -(void)addMarkToDictionary:(NSString *)theMark andRow:(NSInteger)theRow{ NSDictionary *tempDict = [_leftDataArr objectAtIndex:theRow]; NSMutableDictionary *mutableDict = [tempDict mutableCopy]; [mutableDict setObject:theMark forKey:@"theMark"]; if(_leftDataArr.count > theRow){ [_leftDataArr insertObject:mutableDict atIndex:theRow]; if(_leftDataArr.count > theRow+1){ [_leftDataArr removeObjectAtIndex:theRow+1]; } } } //选中Cell - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ _playViodoID = 0; [self tableViewChanges:tableView didSelectRowAtIndexPath:indexPath]; } //播放上一曲/下一曲 并改变cell状态 - (void)tableViewChanges:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ SMListenCell *waterCell = (SMListenCell *)[tableView cellForRowAtIndexPath:indexPath]; NSDictionary *dic = waterCell.valueDict; _indexPaths = [indexPath copy]; //添加已读字段 NSInteger idStr = [[dic objectForKey:@"id"] integerValue]; NSString *urlSt = [dic objectForKey:@"url"]; NSURL* murl = [NSURL URLWithString:urlSt]; BOOL isMp3 = NO; if([MVPublicMethod judgeStringIsNull:urlSt]){ NSArray *subArr = [urlSt componentsSeparatedByString:@"."]; if(subArr != nil && subArr.count > 1){ NSString *lastStr = [subArr lastObject]; if([lastStr isEqualToString:@"mp3"]||[lastStr isEqualToString:@"MP3"]||[lastStr isEqualToString:@"AAC"]||[lastStr isEqualToString:@"aac"]||[lastStr isEqualToString:@"ALAC"]||[lastStr isEqualToString:@"alac"]||[lastStr isEqualToString:@"iLBC"]||[lastStr isEqualToString:@"ilbc"]||[lastStr isEqualToString:@"IMA4"]||[lastStr isEqualToString:@"ima4"]){ isMp3 = YES; } } } if(!murl){ isMp3 = NO; } for (int i = 0; i < _leftDataArr.count; i++) { NSDictionary *tempDict = [_leftDataArr objectAtIndex:i]; NSMutableDictionary *mutableDict = [tempDict mutableCopy]; if([[tempDict objectForKey:@"id"] integerValue] == idStr){ NSString *playMark = [mutableDict objectForKey:@"playMark"]; //0、停止状态 1、播放状态 3、暂停状态 2、无法播放状态 if([playMark isEqualToString:@"1"]){ [mutableDict setObject:@"3" forKey:@"playMark"]; }else if([playMark isEqualToString:@"3"]){ [mutableDict setObject:@"1" forKey:@"playMark"]; }else{ [mutableDict setObject:@"1" forKey:@"playMark"]; } if(!isMp3){ [mutableDict setObject:@"2" forKey:@"playMark"]; } waterCell.valueDict = mutableDict; }else{ [mutableDict setObject:@"0" forKey:@"playMark"]; } if(_leftDataArr.count > i){ [_leftDataArr insertObject:mutableDict atIndex:i]; if(_leftDataArr.count > i+1){ [_leftDataArr removeObjectAtIndex:i+1]; } } } [_tableView reloadData]; dic = waterCell.valueDict; if(dic != nil && dic.count > 0){ if(!isMp3){ [_audioPlayer stop]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"该音频无法播放" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; return; } //播放音频 [self configNowPlayingInfoCenter:[_leftDataArr objectAtIndex:indexPath.row]]; [self playAudio:waterCell.audioButton andDict:dic]; } } @end
示意图:
相关文章推荐
- IOS经典的书籍推荐
- ios开发--注册通知NSNotificationCenter
- ios开发--Header Search Paths
- iOS开发多线程篇—NSThread及线程的状态
- ios开发--xcode如何实现多工程联编
- iOS开发,更改状态栏(StatusBar)文字颜色为白色
- iOS实用代码片段
- iOS-动态创建按钮
- 27个提升效率的iOS开源库推荐
- IOS应用之间的跳转和数据传
- ios 图片处理( 1.按比例缩放 2.指定宽度按比例缩放
- iOS实现弹幕功能
- iOS自动处理键盘事件的第三方库:IQKeyboardManager
- iOS用AVSpeechSynthesizer合成语音
- iOS开发之如何获取各种控件的输入值
- iOS KVO概述及用法
- iOS 移动开发月报-第1期
- IOS中根据生日精确计算年龄
- 为学IOS,进击中...之OC多态练习
- OAuth认证 / 与新特性页面的配合使用