您的位置:首页 > 移动开发 > IOS开发

[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



参考代码如下:

#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
 


示意图:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: