iOS音視頻學習3——播放音樂(后臺播放+拔耳機控制)

#######要想開啟后臺播放,需要兩步

  1. 開啟后臺模式:
開啟后臺模式
  1. 設(shè)置AVAudioSession的類型為AVAudioSessionCategoryPlayback并且調(diào)用setActive::方法啟動(激活)會話。
AVAudioSession *audioSession=[AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
    [audioSession setActive:YES error:nil];

#######為了支持拔耳機以后,自動暫停播放顽耳,需要添加遠程控制事件

前兩步是后臺播放所必須設(shè)置的,第三步主要用于接收遠程事件,如果這一步不設(shè)置雖讓也能夠在后臺播放烛亦,但是無法獲得音頻控制權(quán)(如果在使用當前應用之前使用其他播放器播放音樂的話,此時如果按耳機播放鍵或者控制中心的播放按鈕則會播放前一個應用的音頻)懂拾,并且不能使用耳機進行音頻控制煤禽。
第一步操作相信大家都很容易理解,如果應用程序要允許運行到后臺必須設(shè)置岖赋,正常情況下應用如果進入后臺會被掛起檬果,通過該設(shè)置可以讓應用程序繼續(xù)在后臺運行。

拔耳機監(jiān)聽操作

增加通知:

//添加通知唐断,拔出耳機后暫停播放
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];

通知方法:

/**
 *  一旦輸出改變則執(zhí)行此方法
 *
 *  @param notification 輸出改變通知對象
 */
-(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];
        //原設(shè)備為耳機則暫停
        if ([portDescription.portType isEqualToString:@"Headphones"]) {
            [self pause];
        }
    }
    
//    [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
//        NSLog(@"%@:%@",key,obj);
//    }];
}

記得要取消監(jiān)聽:


-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
}
接收遠程事件

在appDelegate中的didFinishLaunchingWithOptions方法中添加:

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

來接收遠程控制事件

然后在viewController中选脊,實現(xiàn)代理:



- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    if(event.type==UIEventTypeRemoteControl){
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPlay:
                [self.audioPlayer play];
                break;
            case UIEventSubtypeRemoteControlPause:
                [self.audioPlayer pause];
                break;
            case UIEventSubtypeRemoteControlStop:
                [self.audioPlayer stop];
                break;
            case UIEventSubtypeRemoteControlTogglePlayPause:
                if (self.audioPlayer.isPlaying) {
                    [self.audioPlayer pause];
                }else{
                    [self.audioPlayer play];
                }
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                NSLog(@"Next...");
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                NSLog(@"Previous...");
                break;
            case UIEventSubtypeRemoteControlBeginSeekingForward:
                NSLog(@"Begin seek forward...");
                break;
            case UIEventSubtypeRemoteControlEndSeekingForward:
                NSLog(@"End seek forward...");
                break;
            case UIEventSubtypeRemoteControlBeginSeekingBackward:
                NSLog(@"Begin seek backward...");
                break;
            case UIEventSubtypeRemoteControlEndSeekingBackward:
                NSLog(@"End seek backward...");
                break;
            default:
                break;
        }
    }
}

ViewController類實現(xiàn)如下:

//
//  ViewController.m
//  prac
//
//  Created by Realank on 16/3/23.
//  Copyright ? 2016年 realank. All rights reserved.
//

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface ViewController () <AVAudioPlayerDelegate>

@property (nonatomic, strong) AVAudioPlayer *audioPlayer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if ([self.audioPlayer isPlaying]) {
        [self.audioPlayer stop];
        AVAudioSession *audioSession=[AVAudioSession sharedInstance];
        [audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
        
    }else{
        [self musicPlayback];
    }
    
}

- (AVAudioPlayer *)audioPlayer {
    if (!_audioPlayer) {
        NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"liang.mp3" ofType:nil];
        NSURL *url=[NSURL fileURLWithPath:urlStr];
        NSError *error=nil;
        //初始化播放器,注意這里的Url參數(shù)只能時文件路徑脸甘,不支持HTTP Url
        _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
        //設(shè)置播放器屬性
        _audioPlayer.numberOfLoops=0;//設(shè)置為0不循環(huán)
        _audioPlayer.delegate=self;
        [_audioPlayer prepareToPlay];//加載音頻文件到緩存
        if(error){
            NSLog(@"初始化播放器過程發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);
            return nil;
        }
        AVAudioSession *audioSession=[AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
        [audioSession setActive:YES error:nil];
        //添加通知恳啥,拔出耳機后暫停播放
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
    }
    
    return _audioPlayer;
}


- (void)musicPlayback {
    if (!self.audioPlayer.isPlaying) {
        [self.audioPlayer play];
    }
    
}

-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    NSLog(@"音樂播放完成...");
}

/**
 *  一旦輸出改變則執(zhí)行此方法
 *
 *  @param notification 輸出改變通知對象
 */
-(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];
        //原設(shè)備為耳機則暫停
        if ([portDescription.portType isEqualToString:@"Headphones"]) {
            [self.audioPlayer pause];
        }
    }

}

-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
}


- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    if(event.type==UIEventTypeRemoteControl){
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPlay:
                [self.audioPlayer play];
                break;
            case UIEventSubtypeRemoteControlPause:
                [self.audioPlayer pause];
                break;
            case UIEventSubtypeRemoteControlStop:
                [self.audioPlayer stop];
                break;
            case UIEventSubtypeRemoteControlTogglePlayPause:
                if (self.audioPlayer.isPlaying) {
                    [self.audioPlayer pause];
                }else{
                    [self.audioPlayer play];
                }
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                NSLog(@"Next...");
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                NSLog(@"Previous...");
                break;
            case UIEventSubtypeRemoteControlBeginSeekingForward:
                NSLog(@"Begin seek forward...");
                break;
            case UIEventSubtypeRemoteControlEndSeekingForward:
                NSLog(@"End seek forward...");
                break;
            case UIEventSubtypeRemoteControlBeginSeekingBackward:
                NSLog(@"Begin seek backward...");
                break;
            case UIEventSubtypeRemoteControlEndSeekingBackward:
                NSLog(@"End seek backward...");
                break;
            default:
                break;
        }
    }
}

@end
AVAudioSession有必要進行一下詳細的說明:

在iOS中每個應用都有一個音頻會話,這個會話就通過AVAudioSession來表示丹诀。
AVAudioSession同樣存在于AVFoundation框架中钝的,它是單例模式設(shè)計,通過sharedInstance進行訪問铆遭。
在使用Apple設(shè)備時大家會發(fā)現(xiàn)有些應用只要打開其他音頻播放就會終止硝桩,而有些應用卻可以和其他應用同時播放,在多種音頻環(huán)境中如何去控制播放的方式就是通過音頻會話來完成的枚荣。
下面是音頻會話的幾種會話模式:

會話模式

類似的碗脊,如果一個應用已經(jīng)在播放音頻,打開我們的應用之后設(shè)置了在后臺播放的會話類型棍弄,此時其他應用的音頻會停止而播放我們的音頻望薄,如果希望我們的程序音頻播放完之后(關(guān)閉或退出到后臺之后)能夠繼續(xù)播放其他應用的音頻的話則可以調(diào)用setActive::方法關(guān)閉會話:

 AVAudioSession *audioSession=[AVAudioSession sharedInstance];
[audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];

注意,setActive為NO的時候呼畸,一定要配上參數(shù)痕支,不然的話沒有什么卵用。
播放結(jié)束以后蛮原,setActive為NO是一個比較負責任的做法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卧须,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌花嘶,老刑警劉巖笋籽,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異椭员,居然都是意外死亡车海,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門隘击,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侍芝,“玉大人,你說我怎么就攤上這事埋同≈莸” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵凶赁,是天一觀的道長咧栗。 經(jīng)常有香客問我,道長虱肄,這世上最難降的妖魔是什么致板? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮浩峡,結(jié)果婚禮上可岂,老公的妹妹穿的比我還像新娘。我一直安慰自己翰灾,他們只是感情好缕粹,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纸淮,像睡著了一般平斩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咽块,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天绘面,我揣著相機與錄音,去河邊找鬼侈沪。 笑死揭璃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的亭罪。 我是一名探鬼主播瘦馍,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼应役!你這毒婦竟也來了情组?” 一聲冷哼從身側(cè)響起燥筷,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎院崇,沒想到半個月后肆氓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡底瓣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年谢揪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片濒持。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡键耕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柑营,到底是詐尸還是另有隱情,我是刑警寧澤村视,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布官套,位于F島的核電站,受9級特大地震影響蚁孔,放射性物質(zhì)發(fā)生泄漏奶赔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一杠氢、第九天 我趴在偏房一處隱蔽的房頂上張望站刑。 院中可真熱鬧,春花似錦鼻百、人聲如沸绞旅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫩实。三九已至厚者,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晃琳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工琐鲁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卫旱,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓围段,卻偏偏與公主長得像顾翼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蒜撮,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容