AVAudioSesion和AVAudioPlayer的基本使用


 

iOS基礎篇-AVPLayerAVAudioSession

 

2018.02.27 16:17 字數 215 閱讀 1516評論 0喜歡 4

  • 作用
  • AVPLayer:可以用來播放在線及本地音視頻
  • AVAudioSession:音頻會話,主要用來管理音頻設置與硬件交互
  • 使用時需要導入

#import <AVFoundation/AVFoundation.h>

  • AVAudioSession中配置選項:

AVAudioSessionCategory

注意:除了 AVAudioSessionCategoryMultiRoute 外,其他的 Category 都遵循 last in wins 原則,即最后接入的音頻設備作為輸入或輸出的主設備。

1.AVAudioSessionCategoryAmbient

當前App的播放聲音可以和其他app播放的聲音共存,當鎖屏或按靜音時停止。

 

2.AVAudioSessionCategorySoloAmbient

只能播放當前App的聲音,其他app的聲音會停止,當鎖屏或按靜音時停止。

 

3.AVAudioSessionCategoryPlayback

只能播放當前App的聲音,其他app的聲音會停止,當鎖屏或按靜音時不會停止。

 

4.AVAudioSessionCategoryRecord

只能用於錄音,其他app的聲音會停止,當鎖屏或按靜音時不會停止

 

5.AVAudioSessionCategoryPlayAndRecord

在錄音的同時播放其他聲音,當鎖屏或按靜音時不會停止

可用於聽筒播放,比如微信語音消息聽筒播放

 

6.AVAudioSessionCategoryAudioProcessing

使用硬件解碼器處理音頻,該音頻會話使用期間,不能播放或錄音

 

7.AVAudioSessionCategoryMultiRoute

多種音頻輸入輸出,例如可以耳機、USB設備同時播放等

AVAudioSessionCategoryOptions

1.AVAudioSessionModeDefault

默認的模式,適用於所有的場景,可用於場景還原

 

2.AVAudioSessionModeVoiceChat

適用類別 :

AVAudioSessionCategoryPlayAndRecord 

應用場景VoIP

 

3.AVAudioSessionModeGameChat

適用類別:

AVAudioSessionCategoryPlayAndRecord 

應用場景游戲錄制,由GKVoiceChat自動設置,無需手動調用

 

4.AVAudioSessionModeVideoRecording

適用類別:

AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryRecord 

應用場景視頻錄制

 

5.AVAudioSessionModeMoviePlayback

適用類別:

AVAudioSessionCategoryPlayBack

應用場景視頻播放

 

6.AVAudioSessionModeVideoChat

適用類別:

AVAudioSessionCategoryPlayAndRecord

應用場景視頻通話

 

7.AVAudioSessionModeMeasurement

適用類別:

AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryRecord

AVAudioSessionCategoryPlayback

AVAudioSessionModeSpokenAudio

AVAudioSessionMode

1.AVAudioSessionCategoryOptionMixWithOthers

適用於:

AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryPlayback

AVAudioSessionCategoryMultiRoute 

用於可以和其他app進行混音

 

2.AVAudioSessionCategoryOptionDuckOthers

適用於:

AVAudioSessionCategoryAmbient

AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryPlayback

AVAudioSessionCategoryMultiRoute

用於壓低其他聲音播放的音量,使期音量變小

 

3.AVAudioSessionCategoryOptionAllowBluetooth

適用於:

AVAudioSessionCategoryRecord and AVAudioSessionCategoryPlayAndRecord

用於是否支持藍牙設備耳機等

 

4.AVAudioSessionCategoryOptionDefaultToSpeaker

適用於:

AVAudioSessionCategoryPlayAndRecord

用於將聲音從Speaker播放,外放,即免提

 

5.AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers

適用於:

AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryPlayback

AVAudioSessionCategoryMultiRoute

 

6.AVAudioSessionCategoryOptionAllowBluetoothA2DP

適用於:

AVAudioSessionCategoryPlayAndRecord

藍牙和a2dp

 

7.AVAudioSessionCategoryOptionAllowAirPlay

適用於:

AVAudioSessionCategoryPlayAndRecord

airplay

使用AVAudioSession對上下文app進行音頻控制

/**

 *  SetAVAudioSessionCategory

 */

- (void)setAVAudioSessionCategory:(NSString *)category

                          options:(AVAudioSessionCategoryOptions)options {

    NSError *error = nil;

    BOOL success = [[AVAudioSession sharedInstance] setCategory:category withOptions:options error:&error];

    

    if (!success) {

        NSLog(@"SetCategory error:%@ ",error.description);

    }

    

    AVAudioSession *audioSession = [AVAudioSession sharedInstance];

    BOOL ret = [audioSession setActive:YES error:&error];

    

    if (!ret) {

        NSLog(@"%s - activate audio session failed with error %@", __func__,[error description]);

    }

}

  • 結合AVPlayer使用

@property (strong, nonatomic) AVPlayer *avPlayer;

 

AVPlayerItem *playerItem  = [self creatAVPlayerItemWithUrlStr:AudioUrlStr];

self.playState = VoicePlayStateLoding;

    

if (self.avPlayer != nil && self.avPlayer.status == AVPlayerStatusReadyToPlay) {

   [self.avPlayer replaceCurrentItemWithPlayerItem:playerItem];

} else {

   self.avPlayer = [AVPlayer playerWithPlayerItem:playerItem];

}

    

[self setSessionLabType:@"SoloAmbient"];

  • 監聽音頻受其他事件(電話,鬧鈴,其他app占用通道)影響

- (void)customAddNotification {

    [[NSNotificationCenter defaultCenter] addObserver:self

                                             selector:@selector(audioRouteChangeListenerCallback:)

                                                 name:AVAudioSessionRouteChangeNotification object:nil];

    

    [[NSNotificationCenter defaultCenter] addObserver:self

                                             selector:@selector(otherAppAudioSessionCallBack:)

                                                 name:AVAudioSessionSilenceSecondaryAudioHintNotification object:nil];

    

    [[NSNotificationCenter defaultCenter] addObserver:self

                                             selector:@selector(systermAudioSessionCallBack:)

                                                 name:AVAudioSessionInterruptionNotification object:nil];

}

 

// 監聽外部設備改變

- (void)audioRouteChangeListenerCallback:(NSNotification*)notification {

    NSDictionary *interuptionDict = notification.userInfo;

    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];

    switch (routeChangeReason) {

        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:{

            NSLog(@"headset input");

            break;

        }

        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:{

            NSLog(@"pause play when headset output");

            [self.avPlayer pause];

            break;

        }

        case AVAudioSessionRouteChangeReasonCategoryChange:

            NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");

            break;

    }

}

 

// 其他app占用音頻通道

- (void)otherAppAudioSessionCallBack:(NSNotification *)notification {

    NSDictionary *interuptionDict = notification.userInfo;

    NSInteger interuptType = [[interuptionDict valueForKey:AVAudioSessionSilenceSecondaryAudioHintTypeKey] integerValue];

    switch (interuptType) {

        case AVAudioSessionSilenceSecondaryAudioHintTypeBegin:{

            [self.avPlayer pause];

            NSLog(@"pause play when other app occupied session");

            break;

        }

        case AVAudioSessionSilenceSecondaryAudioHintTypeEnd:{

            NSLog(@"occupied session");

            break;

        }

        default:

            break;

    }

}

 

// 電話、鬧鈴等一般性中斷通知

- (void)systermAudioSessionCallBack:(NSNotification *)notification {

    NSDictionary *interuptionDict = notification.userInfo;

    NSInteger interuptType = [[interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];

 

    switch (interuptType) { 

        case AVAudioSessionInterruptionTypeBegan:{

            [self.avPlayer pause];

            NSLog(@"pause play when phone call or alarm ");

            break;

        }

        case AVAudioSessionInterruptionTypeEnded:{

            break;

        }

        default:

            break;

    }

}

  • 總結
  • 使用AVPAVAudioSession和AVPlayer結合可靈活實現音視頻播放,可控制聽筒、前后台、音頻混播等多種播放控制。在使用中我們可以結合場景來切換不同的session.
  • Demo地址:https://github.com/MrWilsonXu/AudioSession.git

AVAudioSession

 

在上一遍學習中提到了AudioSession和AVAudioSession兩個類,在蘋果文檔中可以看到,在iOS 7以后蘋果建議使用的是AVAudioSession,所以AudioSession就等以后有時間再進行學習。

AVAudioSession是AVFoundation框架中的類,用來設置app的音頻上下文,主要的功能有:

激活或者取消app的audio session

設置系統使用音頻的方式

配置如采樣率,I/O緩存時長和信道數等音頻設置

處理Route Change(如拔出耳機后音頻停止播放)

應對其他事件

系統只允許一個session來控制audio routin。如果當前有多個sessions在運行的話,系統會根據當前的重要性來選擇一個最重要的session並且判斷該session是否允許混合。

 

獲取AVAudioSession單例

 

[AVAudioSession shareInstance];

 

錄音時請求用戶允許

 

- (void)requestRecordPermission:(PermissionBlock)response // 參數response表示用戶允許或拒絕時的回調函數

錄音需要獲取用戶的允許,系統會自動跳出提示的。

 

typedef void (^PermissionBlock)(BOOL granted)

PermissionBlock,返回YES允許錄音,NO則不允許

 

- (AVAudioSessionRecordPermission)recordPermission

返回當前錄音的許可狀態

有三個返回參數:AVAudioSessionPermissionGranted/AVAudioSessionPermissionDenied/AVAudioSessionPermissionUndetermined

 

 

管理Audio Session

 

@property(readonly) NSString *category

當前Audio session的類別,默認是AVAudioSessionCategorySoloAmbinet

 

NSString *const  AVAudioSessionCategoryAmbient ; // 該播放不是主要的。使用此類時,音頻會與其他app的進行混合。鎖屏或者靜音模式下無法播放聲音

NSString *const  AVAudioSessionCategorySoloAmbient ; // 鎖屏或者靜音模式下無法播放聲音。音頻不會與其他app混合

NSString *const  AVAudioSessionCategoryPlayback ; // 

NSString *const  AVAudioSessionCategoryRecord ;

NSString *const  AVAudioSessionCategoryPlayAndRecord ;

NSString *const  AVAudioSessionCategoryAudioProcessing ;

NSString *const  AVAudioSessionCategoryMultiRoute;

Audio Session的Category

 

@property(readonly) AVAudioSessionCategoryOptions categoryOptions

當前Category相關的選項

 

enum {

   AVAudioSessionCategoryOptionMixWithOthers  = 1, // 混合其他已激活的Session的音頻。使用這個選項激活Session的話,會打斷其他app正在播放的聲音。如果不使用的話,則會打斷其他不允許混合的session

   AVAudioSessionCategoryOptionDuckOthers  = 2, // 在本Session播放時避開其他session的音頻

   AVAudioSessionCategoryOptionAllowBluetooth  = 4, // 允許藍牙作為輸入

   AVAudioSessionCategoryOptionDefaultToSpeaker  = 8 // 接入到系統默認的揚聲器

};

typedef NSUInteger  AVAudioSessionCategoryOptions;

前兩個個Options都需要在AVAudioSessionCategoryPlayback或者AVAudioSessionPlayAndRecord下才能使用,第三個在AVAudioSessionPlayAndRecord或者AVAudioSessionRecord,最后一個需在AVAudioSessionPlayAndRecord下使用

 

- (BOOL)setCategory:(NSString *)theCategory

              error:(NSError **)outError

設置Audio Session的Category

一般會在激活之前設置好Category和mode。但是也可以在已激活的audio session中設置,不過會在發生route change之后才會發生改變

 

- (BOOL)setCategory:(NSString *)category

        withOptions:(AVAudioSessionCategoryOptions)options

              error:(NSError **)outError

跟上面的函數相似

 

@property(readonly) NSString *mode

Audio Session使用的mode

 

NSString *const  AVAudioSessionModeDefault ;

NSString *const  AVAudioSessionModeVoiceChat ;

NSString *const  AVAudioSessionModeGameChat 

NSString *const AVAudioSessionModeVideoRecording;

NSString *const  AVAudioSessionModeMeasurement ;

NSString *const  AVAudioSessionModeMoviePlayback ;

NSString *const  AVAudioSessionModeVideoChat;

Audio Session Mode

 

- (BOOL)setMode:(NSString *)theMode

          error:(NSError **)outError

設置Mode

 

- (BOOL)setActive:(BOOL)beActive

            error:(NSError **)outError

激活或者取消Audio Session

如果另外一個活動中的Audio Session的優先級高於你所定義的(比如說有電話打進來)或者有一個允許混音的Audio Session,那么激活可能會失敗。如果當前的音頻相關對象正在運行的話,取消Audio Session有可能會失敗

 

- (BOOL)setActive:(BOOL)active

      withOptions:(AVAudioSessionSetActiveOptions)options

            error:(NSError **)outError

跟上面的函數相似

第二個參數:

enum {

   AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation  = 1

};

typedef NSUInteger  AVAudioSessionSetActiveOptions;

表明當你的Audio Session被取消時,其他被你的Audio Session所打斷的Audio Session可以恢復至其激活狀態

 

 

音頻設備設定

 

@property(readonly) float outputVolume

outputVolume的范圍是0.0-1.0.系統的音量可以直接設置,想要控制音量大小的話,可以使用MPVolumeView類。

可以通過KVO來監測該值的變化

 

@property(readonly) float inputGain

輸入增益inputGain的范圍是0.0 - 1.0.

可通過KVO來監測變化

 

@property(readonly, getter=isInputGainSettable) BOOL inputGainSettable

因為不是所有設備都允許添加輸入增益,所以在設置之前要檢查是否允許設置輸入增益inputGain。

 

- (BOOL)setInputGain:(float)gain

               error:(NSError **)outError

設置inputGain為特定值。gain的范圍是0.0 - 1.0。調用此方法之前要檢查是否允許設置inputGain

 

@property(readonly) NSTimeInterval inputLatency

音頻輸入的等待時間,以秒為單位。

如果Audio Session的Category設置不允許音頻輸入,該函數會返回AVAudioSessionErrorCodeIncompatibleCategory

 

@property(readonly) NSTimeInterval outputLatency

音頻輸出的等待時間,以秒為單位

 

@property(readonly) double sampleRate

當前音頻的采樣率,以赫茲為單位

 

@property(readonly) double preferredSampleRate

首選的采樣率

 

- (BOOL)setPreferredSampleRate:(double)sampleRate

                         error:(NSError **)outError

設置輸入輸出的首選采樣率

在激活和取消Audio Session之前或之后都可以進行設置

 

@property(readonly) NSTimeInterval IOBufferDuration

I/O的緩沖時間

 

@property(readonly) NSTimeInterval preferredIOBufferDuration

首選的I/O緩沖時間

 

- (BOOL)setPreferredIOBufferDuration:(NSTimeInterval)duration

                               error:(NSError **)outError

設置首選I/O緩沖時間

I/O緩沖時間是指單音頻輸入/輸出周期的時長。比如說I/O緩沖時間為0.005s,那么在每一個音頻I/O周期里,在獲取輸入時,會收到0.005s的音頻,在輸出時,必須提供0.005s的音頻

通常來說I/O緩沖時間的范圍是0.005s至0.93s

 

@property(readonly) BOOL secondaryAudioShouldBeSilencedHint

表明另一個app是否正在播放音頻

返回YES表明另一個有着不允許混音的Audio Session的app正在播放音頻

 

 

音頻信道

 

@property(readonly) NSInteger inputNumberOfChannels

當前route的音頻輸入信道數

使用KVO來監測此值。如果Audio Session不支持錄音的話,會產生AVAudioSessionErrorCodeIncompatibleCategory

 

@property(readonly) NSInteger maximumInputNumberOfChannels

可接受的最大的音頻輸入信道數

 

@property(readonly) NSInteger preferredInputNumberOfChannels

首選的音頻輸入信道數

 

- (BOOL)setPreferredInputNumberOfChannels:(NSInteger)count

                                    error:(NSError **)outError

設置首選的音頻輸入信道數

只能在設置Audio Session的Category和mode,並在激活session后調用

 

輸出的話將input換成output即可

 

音頻輸入輸出的Route

 

@property(readonly) AVAudioSessionRouteDescription *currentRoute

描述當前音頻輸入輸出的route

 

@property(readonly, getter=isInputAvailable) BOOL inputAvailable

表明當前設備是否支持音頻輸入

 

@property(readonly, getter=isOtherAudioPlaying) BOOL otherAudioPlaying

其他app是否正在播放音頻

在iOS 8.0以后要使用secondaryAudioShouldBeSilencedHint來代替這個屬性

 

- (BOOL)overrideOutputAudioPort:(AVAudioSessionPortOverride)portOverride

                          error:(NSError **)outError

暫時更改當前的音頻路線

第一個參數:

enum {

   AVAudioSessionPortOverrideNone     = 0, // 不覆寫

   AVAudioSessionPortOverrideSpeaker  = 'spkr' // 將輸入輸出變換到內置揚聲器和話筒

};

typedef NSUInteger  AVAudioSessionPortOverride;

使用AVAudioSessionPortOverrideSpeaker和AVAudioSessionCategoryPlayAndRecord的話會無視其他設置進行話筒錄音和揚聲器播放,不過會在重新調用這個方法並且參數為AVAudioSessionPortOverrideNone時失效

 

@property(readonly) NSArray *availableInputs

Route的輸入端口數組

AVAudioSessionPortDescription對象的數組。代表了當前與系統相關的以及與當前Audio Session相關Category和mode的音頻輸入設備

 

@property(readonly) AVAudioSessionPortDescription *preferredInput

音頻線路首要的輸入端口

 

- (BOOL)setPreferredInput:(AVAudioSessionPortDescription *)inPort

                    error:(NSError **)outError

設置首要輸入端口

inPort參數必須是availableInputs中的一個AVAudioSessionPortDescription對象

只能在設置了Audio Session的category和mode並且激活了session后才能進行調用設置

 

@property(readonly) NSArray *inputDataSources

Audio session的輸入端口的可接受的數據源數組

 

@property(readonly) AVAudioSessionDataSourceDescription *inputDataSource

當前選擇的輸入數據源

 

- (BOOL)setInputDataSource:(AVAudioSessionDataSourceDescription *)dataSource

                     error:(NSError **)outError

設置選擇的數據源

 

輸出的話將input換成output即可

 

Notification

 

AVAudioSessionInterruptionNotification

當音頻中斷出現時傳遞給主線程

notification的userInfo字典包含了AVAudioSessionInterruption TypeKey,如果中斷的類型是AVAudioSessionInterruptionTypeBegan,則app的audio session會被中斷。如果是AVAudioSessionInterruptionTypeEnded,則字典會同時包含AVAudioSessionInterruptionOptionKey

 

AVAudioSessionRouteChangeNotification

當系統的音頻線路發生改變時通知主線程

notification的userInfo字典會包含AVAudioSessionRouteChangeReasonKey和AVAudioSessionRouteChangePreviousRouteKey

 

AVAudioSessionMediaServicesWereLostNotification

當媒體服務終止時通知主線程

將該通知作為提示來在重新啟動服務之前做相關處理

此notification不包含userInfo

 

AVAudioSessionMediaServicesWereResetNotification

媒體服務重新啟動時通知主線程

不包含userInfo

 

AVAudioSessionSilenceSecondaryAudioHintNotification

當其他app的首要音頻開始播放或者停止時通知主線程

userInfo中AVAudioSessionSilenceSecondaryAudioHintTypeKey的值為AVAudioSessionSilenceSecondaryAudioHintType類型

--------------------- 

 

原文:https://blog.csdn.net/kingshuo7/article/details/42588191 

 

iOS 音頻-AVAudioSession

 

2018.02.03 11:22* 字數 2157 閱讀 6624評論 9喜歡 31

1. AVAudioSession 概述

最近一年一直在做IPC Camera的iOS客戶端開發。和音頻打交道,必須要弄清楚

AVAudioSession。

先看下蘋果的官方圖:

 

 

pastedGraphic_1.png

Audio Session

可以看到AVAudioSession就是用來管理多個APP對音頻硬件設備(麥克風,揚聲器)的資源使用。

舉例一下AVAudioSession可以做這些事情

  • 設置自己的APP是否和其他APP音頻同時存在,還是中斷其他APP聲音
  • 在手機調到靜音模式下,自己的APP音頻是否可以播放出聲音
  • 電話或者其他APP中斷自己APP的音頻的事件處理
  • 指定音頻輸入和輸出的設備(比如是聽筒輸出聲音,還是揚聲器輸出聲音)
  • 是否支持錄音,錄音同時是否支持音頻播放

2. AVAudioSession Category

AVAudioSession的接口比較簡單。APP啟動的時候會自動幫激活AVAudioSession,當然我們可以手動激活代碼如下。

    //導入頭文件

    #import <AVFoundation/AVFoundation.h>

 

    //AVAudioSession是一個單例類

    AVAudioSession *session = [AVAudioSession sharedInstance];

    //AVAudioSessionCategorySoloAmbient是系統默認的category

    [session setCategory:AVAudioSessionCategorySoloAmbient error:nil];

    //激活AVAudioSession

    [session setActive:YES error:nil];

可以看到設置session這里有兩個參數,category和options

Category iOS下目前有七種,每種Category都對應是否支持下面四種能力

  • Interrupts non-mixable apps audio:是否打斷不支持混音播放的APP
  • Silenced by the Silent switch:是否會響應手機靜音鍵開關
  • Supports audio input:是否支持音頻錄制
  • Supports audio output:是否支持音頻播放

下面用圖表來直觀的看下每種category具體的能力集

Category

是否允許音頻播放/錄音

是否打斷其他不支持混音APP

是否會被靜音鍵或鎖屏鍵靜音

AVAudioSessionCategoryAmbient

只支持播放

AVAudioSessionCategoryAudioProcessing

不支持播放,不支持錄制

AVAudioSessionCategoryMultiRoute

支持播放,支持錄制

AVAudioSessionCategoryPlayAndRecord

支持播放,支持錄制

默認YES,可以重寫為NO

AVAudioSessionCategoryPlayback

只支持播放

默認YES,可以重寫為NO

AVAudioSessionCategoryRecord

只支持錄制

否(鎖屏下仍可錄制)

AVAudioSessionCategorySoloAmbient

只支持播放

  • AVAudioSessionCategoryAmbient,只支持音頻播放。這個 Category,音頻會被靜音鍵和鎖屏鍵靜音。並且不會打斷其他應用的音頻播放。
  • AVAudioSessionCategorySoloAmbient,這個是系統默認使用的 Category,只支持音頻播放。音頻會被靜音鍵和鎖屏鍵靜音。和AVAudioSessionCategoryAmbient不同的是,這個會打斷其他應用的音頻播放
  • AVAudioSessionCategoryPlayback,只支持音頻播放。你的音頻不會被靜音鍵和鎖屏鍵靜音。適用於音頻是主要功能的APP,像網易雲這些音樂app,鎖屏后依然可以播放。

需要注意一下,選擇支持在靜音鍵切到靜音狀態以及鎖屏鍵切到鎖屏狀態下仍然可以播放音頻 Category 時,必須在應用中開啟支持后台音頻功能,詳見 UIBackgroundModes

  • AVAudioSessionCategoryRecord,只支持音頻錄制。不支持播放。
  • AVAudioSessionCategoryPlayAndRecord,支持音頻播放和錄制。音頻的輸入和輸出不需要同步進行,也可以同步進行。需要音頻通話類應用,可以使用這個 Category。
  • AVAudioSessionCategoryAudioProcessing,只支持本地音頻編解碼處理。不支持播放和錄制。
  • AVAudioSessionCategoryMultiRoute,支持音頻播放和錄制。允許多條音頻流的同步輸入和輸出。(比如USB連接外部揚聲器輸出音頻,藍牙耳機同時播放另一路音頻這種特殊需求)

我們也可以通過AVAudioSession的屬性來讀取當前設備支持的Category

@property(readonly) NSArray<NSString *> *availableCategories;

這樣可以保證設備兼容性。

設置Category的代碼示例如下

NSError *setCategoryError = nil;

BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&setCategoryError];

if (!success) { 

    //這里可以讀取setCategoryError.localizedDescription查看錯誤原因

}

3. AVAudioSession Mode&&Options

剛剛介紹的Category定義了七種主場景,實際開發需求中有時候需要對Category進行微調整,我們發現這個接口還有兩個參數Mode和Options。

/* set session category and mode with options */

- (BOOL)setCategory:(NSString *)category mode:(NSString *)mode options:(AVAudioSessionCategoryOptions)options error:(NSError **)outError API_AVAILABLE(ios(10.0), watchos(3.0), tvos(10.0));

AVAudioSession Mode

我們通過讀取下面這條屬性獲取當前設備支持的Mode

@property(readonly) NSArray<NSString *> *availableModes;

iOS下有七種mode來定制我們的Category行為

模式

兼容的 Category

場景

AVAudioSessionModeDefault

All

默認模式

AVAudioSessionModeVoiceChat

AVAudioSessionCategoryPlayAndRecord

VoIP

AVAudioSessionModeGameChat

AVAudioSessionCategoryPlayAndRecord

游戲錄制,GKVoiceChat自動設置

AVAudioSessionModeVideoRecording

AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord

錄制視頻

AVAudioSessionModeMoviePlayback

AVAudioSessionCategoryPlayback

視頻播放

AVAudioSessionModeMeasurement

AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback

最小系統

AVAudioSessionModeVideoChat

AVAudioSessionCategoryPlayAndRecord

視頻通話

下面逐一介紹下每個Mode

  • AVAudioSessionModeDefault,默認模式,與所有的 Category 兼容
  • AVAudioSessionModeVoiceChat,適用於VoIP 類型的應用。只能是 AVAudioSessionCategoryPlayAndRecord Category下。在這個模式系統會自動配置AVAudioSessionCategoryOptionAllowBluetooth 這個選項。系統會自動選擇最佳的內置麥克風組合支持語音聊天。
  • AVAudioSessionModeVideoChat,用於視頻聊天類型應用,只能是 AVAudioSessionCategoryPlayAndRecord Category下。適在這個模式系統會自動配置 AVAudioSessionCategoryOptionAllowBluetooth 和 AVAudioSessionCategoryOptionDefaultToSpeaker 選項。系統會自動選擇最佳的內置麥克風組合支持視頻聊天。
  • AVAudioSessionModeGameChat,適用於游戲類應用。使用 GKVoiceChat 對象的應用會自動設置這個模式和 AVAudioSessionCategoryPlayAndRecord Category。實際參數和AVAudioSessionModeVideoChat一致
  • AVAudioSessionModeVideoRecording,適用於使用攝像頭采集視頻的應用。只能是 AVAudioSessionCategoryPlayAndRecord 和 AVAudioSessionCategoryRecord 這兩個 Category下。這個模式搭配 AVCaptureSession API 結合來用可以更好地控制音視頻的輸入輸出路徑。(例如,設置 automaticallyConfiguresApplicationAudioSession 屬性,系統會自動選擇最佳輸出路徑。
  • AVAudioSessionModeMeasurement,最小化系統。只用於 AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryRecord、AVAudioSessionCategoryPlayback 這幾種 Category。
  • AVAudioSessionModeMoviePlayback,適用於播放視頻的應用。只用於 AVAudioSessionCategoryPlayback 這個Category。

AVAudioSession Options

我們還可以使用options去微調Category行為,如下表

Option

Option功能說明

兼容的 Category

AVAudioSessionCategoryOptionMixWithOthers

支持和其他APP音頻 mix

AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute

AVAudioSessionCategoryOptionDuckOthers

系統智能調低其他APP音頻音量

AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute

AVAudioSessionCategoryOptionAllowBluetooth

支持藍牙音頻輸入

AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryOptionDefaultToSpeaker

設置默認輸出音頻到揚聲器

AVAudioSessionCategoryPlayAndRecord

調優我們的Category

通過Category和合適的Mode和Options的搭配我們可以調優出我們的效果,下面舉兩個應用場景:

用過高德地圖的都知道,在后台播放QQ音樂的時候,如果導航語音出來,QQ音樂不會停止,而是被智能壓低和混音,等導航語音播報完后,QQ音樂正常播放,這里我們需要后台播放音樂,所以Category使用AVAudioSessionCategoryPlayback,需要混音和智能壓低其他APP音量,所以Options選用 AVAudioSessionCategoryOptionMixWithOthers和AVAudioSessionCategoryOptionDuckOthers

代碼示例如下

 BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDuckOthers error:&setCategoryError];

又或者我希望AVAudioSessionCategoryPlayAndRecord這個Category默認的音頻由揚聲器播放,那么可以調用這個接口去調整Category

- (BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError

通過選擇合適和Category,mode和options,就可以調優音頻的輸入輸出,來滿足日常開發需求(需要注意的是Category,mode,option是搭配使用的,而不是簡單組合,也就是說某種Category支持某些mode和option,從上面的表中也可以看出這一點)

4. 音頻中斷處理

其他APP或者電話會中斷我們的APP音頻,所以相應的我們要做出處理。

我們可以通過監聽AVAudioSessionInterruptionNotification這個key獲取音頻中斷事件

回調回來Userinfo有鍵值

  • AVAudioSessionInterruptionTypeKey:
  • 取值AVAudioSessionInterruptionTypeBegan表示中斷開始
  • 取值AVAudioSessionInterruptionTypeEnded表示中斷結束

中斷開始:我們需要做的是保存好播放狀態,上下文,更新用戶界面等

中斷結束:我們要做的是恢復好狀態和上下文,更新用戶界面,根據需求准備好之后選擇是否激活我們session。

選擇不同的音頻播放技術,處理中斷方式也有差別,具體如下:

  • System Sound Services:使用 System Sound Services 播發音頻,系統會自動處理,不受APP控制,當中斷發生時,音頻播放會靜音,當中斷結束后,音頻播放會恢復。
  • AV Foundation framework:AVAudioPlayer 類和 AVAudioRecorder 類提供了中斷開始和結束的 Delegate 回調方法來處理中斷。中斷發生,系統會自動停止播放,需要做的是記錄播放時間等狀態,更新用戶界面,等中斷結束后,再次調用播放方法,系統會自動激活session。
  • Audio Queue Services, I/O audio unit:使用aduio unit這些技術需要處理中斷,需要做的是記錄播放或者錄制的位置,中斷結束后自己恢復audio session。
  • OpenAL:使用 OpenAL 播放時,同樣需要自己監聽中斷。管理 OpenAL上下文,用戶中斷結束后恢復audio session。

需要注意的是:1. 有中斷開始事件,不一定對應有中斷結束事件,所以需要在用戶進入前台,點擊UI操作的時候,需要保存好播放狀態和對Audio Session管理,以便不影響APP的音頻功能。2.音頻資源競爭上,一定是電話優先。3. AVAudioSession同樣可以監聽外設音頻狀態,比如耳機拔入拔出。這里不做累述

5. AVAudioSession總結

AVAudioSession的作用就是管理音頻這一唯一硬件資源的分配,通過調優合適的AVAudioSession來適配我們的APP對於音頻的功能需求。切換音頻場景時候,需要相應的切換AVAudioSession。

參考文獻:Audio Session Programming Guide

 

ios播放聲音中斷后台音樂的問題

 

 今天遇到一個ios播放聲音中斷后台音樂的問題,在我的app中如果調用AVAudioSession 播放完聲音,后台的qq音樂偶爾不能恢復,而網易雲音樂一次都不能恢復播放,研究了一下AVAudioSession ,我之前調用[audioSession setActive:NO error:&err];還有一個方法

/* Set the session active or inactive. Note that activating an audio session is a synchronous (blocking) operation.

 Therefore, we recommend that applications not activate their session from a thread where a long blocking operation will be problematic.

 Note that this method will throw an exception in apps linked on or after iOS 8 if the session is set inactive while it has running or 

 paused I/O (e.g. audio queues, players, recorders, converters, remote I/Os, etc.).

*/

- (BOOL)setActive:(BOOL)active error:(NSError **)outError;

- (BOOL)setActive:(BOOL)active withOptions:(AVAudioSessionSetActiveOptions)options error:(NSError **)outError NS_AVAILABLE_IOS(6_0);

而AVAudioSessionSetActiveOptions這個枚舉

/*  options for use when calling setActive:withOptions:error: 

AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation -- 

Notify an interrupted app that the interruption has ended and it may resume playback. Only valid on 

session deactivation. */

typedef NS_OPTIONS(NSUInteger, AVAudioSessionSetActiveOptions)

{

AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation = 1

} NS_AVAILABLE_IOS(6_0);

注釋的意思是:“通知中斷程序中斷已經結束,可以恢復播放。。。。“,

看來這里應該是造成播放聲音導致后台的qq音樂偶爾不能恢復,而網易雲音樂一次都不能恢復播放的地方

 

 之后改成調用   [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];果然網易雲音樂也可以恢復了,看了蘋果官方的文章,上面有張圖

AVAudioPlayer在AVFoundation框架下,所以我們要導入AVFoundation.framework。

AVAudioPlayer類封裝了播放單個聲音的能力。播放器可以用NSURL或者NSData來初始化,要注意的是NSURL並不可以是網絡url而必須是本地文件URL,

因為 AVAudioPlayer不具備播放網絡音頻的能力,如果要播放網絡URL,需要先轉化為NSData.但是此法並不可取,

因為AVAudioPlayer只能播放一個完整的文件,並不支持流式播放,所以必須是緩沖完才能播放,所以如果網絡文件過大抑或是網速不夠豈不是要等很久?

所以播放網絡音頻我們一般用音頻隊列。

注意:需要添加AVFoundation.framework

    AVAudioPlayer不支持邊下邊播,所以只能下載到本地再播放

 

    1.實例化方法

    - (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;

    

    2.預加載資源

    - (BOOL)prepareToPlay;

 

    3.遵守協議

    AVAudioPlayerDelegate

 

    4.播放完成之后回調以下方法

    - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;

先搭建UI吧,直接上圖:

pastedGraphic_2.png

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

#import "AVAudioPlayerViewController.h"

#import <AVFoundation/AVFoundation.h>

 

@interface AVAudioPlayerViewController ()

{

    //聲明一個播放器

    AVAudioPlayer *_musicPlay;//音樂播放器

}

 

- (IBAction)playAction:(id)sender;//播放

- (IBAction)pauseAction:(id)sender;//暫停

- (IBAction)voiceAction:(UISlider *)sender;//音量

- (IBAction)progressAction:(UISlider *)sender;//進度

 

@property (weak, nonatomic) IBOutlet UISlider *progressSlider;//進度條

@property (nonatomic,strong)NSTimer *myTimer;//聲明一個定時器類型的成員變量

 

 

@end

 

@implementation AVAudioPlayerViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

 

    //獲取資源路徑  需要事先導入本地音頻內容

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"mp3"];

    NSLog(@"%@",filePath);

     

    NSURL *URL =[NSURL fileURLWithPath:filePath];

    _musicPlay = [[AVAudioPlayer alloc] initWithContentsOfURL:URL error:nil];

    //實例化音樂播放器

//    _musicPlay = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:nil];

     

    //設置音量大小處於中等強度

    _musicPlay.volume = 0.5;<br> //循環次數,默認是1<br>    _musicPlay.numberOfLoops = 1;<br>   //    聲道數<br>    NSUInteger channels = _musicPlay.numberOfChannels;//只讀屬性<br><br><br>

     

    //預加載資源

    if ([_musicPlay prepareToPlay]) {

         

        NSLog(@"准備完畢");

         

    }

     

    //設置總進度大小

    self.progressSlider.maximumValue = _musicPlay.duration;

     

}

#pragma mark- 各類觸發事件

-(void)change:(NSTimer *)sender{

     

    //每一秒鍾設置一下進度值  播放位置

    self.progressSlider.value = _musicPlay.currentTime;

     

}

 

#pragma mark- AVAudioPlayer相關的方法

- (IBAction)playAction:(id)sender {

    //播放

    [_musicPlay play];

     

    self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(change:) userInfo:nil repeats:YES];  

}

 

- (IBAction)pauseAction:(id)sender {

    //暫停

    [_musicPlay pause];

    //停止定時器

    [self.myTimer invalidate];

    NSLog(@"%f",_musicPlay.currentTime);

}

 

- (IBAction)voiceAction:(UISlider *)sender {

     

    //改變音量大小

    _musicPlay.volume = sender.value;

     

}

 

- (IBAction)progressAction:(UISlider *)sender {

     

    //設置進度

    _musicPlay.currentTime = sender.value;

     

}

代理方法

          加入播放出現異常,或者被更高級別的系統任務打斷,我們的程序還沒來得及收場就掛了,怎么辦?不急,我們可以通過幾個委托方法很好地處理所有的情形。

         首先給player設置委托是必須的:

  1. player.delegate = self; 
  2. - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)player successfully:(BOOL)flag{  
  3.     //播放結束時執行的動作  
  4. }  
  5. - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer*)player error:(NSError *)error{  
  6.     //解碼錯誤執行的動作  
  7. }  
  8. - (void)audioPlayerBeginInteruption:(AVAudioPlayer*)player{  
  9.     //處理中斷的代碼  
  10. }  
  11. - (void)audioPlayerEndInteruption:(AVAudioPlayer*)player{  
  12.     //處理中斷結束的代碼  

iOSAVAudioPlayer

pastedGraphic_3.png 請輸入賬號名 關注

2016.06.04 01:38* 字數 1316 閱讀 3596評論 1喜歡 6

蘋果系統帶有一個音頻播放器,這就是AVAudioPlayer,要使用這個播放器進行播放音頻,首先需要創建這個播放器對象,而這個播放器的創建所需的頭文件並不是在foundation的頭文件下而是在播放器自己專屬的頭文件下,如下:

#import <AVFoundation/AVFoundation.h>

而AV開頭的文件里包含了許多的和音視頻相關的頭文件。

一、創建播放器對象

我們先聲明一個播放器的全局變量,而且只能聲明稱全局變量。

AVAudioPlayer *_audioPlayer;

此時我們創建播放器對象:

_audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];

這里有兩個參數:

參數1:需要播放的音頻的地址,這里可以是本地或者是網頁的文件

參數2:錯誤信息

這里就暫且使用本地資源,需要創建本地資源的路徑,然后轉為url路徑

NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:nil];

這里有兩個參數:

參數1:文件的名字

參數2:文件的類型

注意的是:在參數1中如果包含了名字和類型,在參數2中可以不用謝類型,直接寫nil就可以了。

然后將string類型的路徑用url來接收

NSURL *url = [NSURL fileURLWithPath:path];

這里的參數就是之前創建的NSString類型的路徑。

完整的代碼如下:

NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:nil];

NSURL *url = [NSURL fileURLWithPath:path];

_audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];

 

二、播放

音頻播放之前需要准備播放,這里需要調用一個方法;

[_audioPlayer prepareToPlay];

就緒后直接播放

[_audioPlayer play];

以上就是和音頻最主要的功能的介紹。

三、音頻播放器的相關功能

下面是對音頻播放器的擴展,音頻播放器一般會有的功能如:開始播放、暫停播放、調整音頻的音量的大小、調整音頻的進度、顯示音頻文件的內部附有的圖片信息及協議代理等。

1.開始和暫停播放音頻

首先,為了方便,我們在storyBoard去拖一個按鈕控件,主要功能是讓音頻暫停和播放。

這里我們可以用到自動布局的相關知識,在storyBoard里運用約束條件,使得可以進行適配手機。這里對按鈕的左右下和高度進行約束。

讓按鈕的初始文字顯示為播放,點擊播放后,顯示為STOP,暫停后顯示為play;隨便設個背景顏色用於區分

然后直接在storyBoard里面拖個方法到ViewController.m文件的@implementation里,然后就在這里這個按鈕的點擊事件里執行音頻的播放和暫停

代碼如下:

#pragma mark - 開始暫停按鈕

- (IBAction)button:(UIButton *)sender {

    // 用一個靜態變量對按鈕的狀態進行控制

    static int i = 0;

    if (i == 0) {

        // 開始播放

        [_audioPlayer play];

        // 開始后按鈕文字變為stop,按鈕的狀態為正常

        [sender setTitle:@"stop" forState:UIControlStateNormal];

        i = 1;

    }

    else {

        // 4.暫停播放

        [_audioPlayer pause];

        //[_audioPlayer stop];

        [sender setTitle:@"play" forState:UIControlStateNormal];

        i = 0;

    }

}

2.調整音量大小

我們還是可以在storyBoard中拖一個控件出來,這里需要一個步進器,然后把相關的方法拖到文件中去實現。

#pragma mark - 改變音量

- (IBAction)volumeChange:(UIStepper *)sender {

    // 設置音量(0 ~ 1);我們設置的步進的音量值為0.1

    _audioPlayer.volume = sender.value;

}

3.調整音頻播放進度

3.1更新進度

調整進度就需要用到滑塊控件,然后向之前的在storyBoard里去拖個滑塊的控件,還有相關的屬性和方法。

這里我們可能會運用到定時器,所以我們需要創建定時器,而這個定時器的創建應該放到准備播放音頻的時候就創建出來。

// 添加定時器

[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];

這里的參數為:

參數1:定時器的監測時間,單位是S

參數2:執行的對象

參數3:選擇器(需要執行的方法)

參數4:用戶的信息,一般為nil

參數5:是否需要重復執行,這里是需要的,為YES

實現選擇器中的方法;

#pragma mark - 更新進度updateProgress

- (void)updateProgress {

    // 當前時間

    double currentTime = _audioPlayer.currentTime;

    // 總時間

    double totalTime = _audioPlayer.duration;

    // 計算進度

    float progress = currentTime / totalTime;

    // 展示到進度條上

    self.sliderProgress.value = progress;

}

3.2更改滑塊的進度

這里就需要實現滑塊拖出來的方法了

#pragma mark - 改變進度條

- (IBAction)sliderProgress:(UISlider *)sender {

    // 當前時間 = 總時間 * 爆發進度

    double currentTime = _audioPlayer.duration * _sliderProgress.value;

    // 是否讓進度條實時更新。

    _sliderProgress.continuous = YES;

    // 重新設置播放器的當前時間

    _audioPlayer.currentTime = currentTime;

}

4.獲取專輯的圖片

同樣的需要在storyBoard上拖一個imageView的控件,展示出其屬性。

#pragma mark - 獲取專輯圖片

- (void)showPlayerImage {

    AVURLAsset *asset = [[AVURLAsset alloc]initWithURL:_audioPlayer.url options:nil];

    NSString *format = [asset availableMetadataFormats].firstObject;

    NSArray *array = [asset metadataForFormat:format];

    for (AVMutableMetadataItem *item in array) {

        // 這里的artwork是需要在音頻文件里去得到的。可以吧array打印出來,得到里面的值

        if ([item.commonKey isEqualToString:@"artwork"]) {

            NSData *data = (NSData *)item.value;

            UIImage *image = [UIImage imageWithData:data];

            _imageView.image = image;

        }

    }

}

5.協議代理

5.1當前音頻播放器播放的音頻結束后會自動調用這個方法

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {

    NSLog(@"播放結束");

    // 切換下一首

    [self preparePlayerWithName:@"春天里.mp3"];

    [_audioPlayer play];

}

5.2音頻播放被中斷的時候會調用這個方法

一般有電話的接入和有短信接收的時候,一般會有中斷,這需要調用這個方法。我們一般執行的都是暫停音頻的播放。

- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {

    NSLog(@"一般這個方法中會暫停音頻");

    [player stop];

}

5.3中斷結束后

一般就在來電結束后會調用這個方法。一般在這個方法中執行的是會繼續音頻的播放。

// 中斷結束的時候會調用這個方法

- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags {

    // 一般在這個方法中繼續播放音頻

    [player play];

    NSLog(@"繼續播放");

}

5.4解碼出錯

// 解碼錯誤的時候會調用這個方法

- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {

    NSLog(@"文件出錯");

}

 

總結:

1.這里需要導入的頭文件一定是:AVFoundation/AVFoundation.h;

2.音頻播放器AVAudioPlayer作為一個對象,要使用必須要先創建對象,創建對象前需要給一個音頻文件的路徑(一般都是url);

3.聲明對象一定是要全局變量,不然運行可能失敗,因為會調用到偏硬件的;

4.就是在storyBoard里拖控件的時候需要注意自動布局的約束條件,為的是適配;

5.在有代理的設置的時候,一定要遵循代理:AVAudioPlayerDelegate


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM