題記:檢測蘋果手機的物理靜音按鍵的開關狀態
最近業務上有個需求就是以靜音鍵的狀態來做一些邏輯顯示,但是在iOS5以後,蘋果就沒有開放現成的api來獲取靜音鍵的狀態。只要遇到這樣的情況,基本都是“曲線救國”。
目前網上基本獲取靜音鍵的狀態大體有三種方式:
第一種:使用對應的api獲取
CFStringRef state = nil;
UInt32 propertySize = sizeof(CFStringRef);
AudioSessionInitialize(NULL, NULL, NULL, NULL);
OSStatus status = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);
if (status == kAudioSessionNoError){
return (CFStringGetLength(state) == 0); // YES = silent
}
return NO;
試了試,但是獲取狀態不正確。
第二種方式:
通過播放一段短的白噪音,根據回調時間差來計數,然後根據計數的大小來判斷是否靜音。參考文獻.
但是這種方式的獲取也存在一些誤差,在某些情況下,由於runloop的時機,以及計數的誤差,加上判斷閥值的設定,總會有些誤差導致有那麼幾次靜音鍵的狀態獲取不對。
第三種方式:
國外大佬的解決方案,SoundSwitch.zip.但是此地址打開失敗。
綜合以上情況,最後選擇了第二種方式來獲取靜音鍵的開關狀態。所以一直在查找那些爲什麼狀態獲取失敗的原因。
在我調試的時候,發現在靜音鍵關閉的時候,獲取狀態爲開的時候,獲取音量的值始終爲0.
[[AVAudioSession sharedInstance] outputVolume]
至此,可以知道有兩個地方的影響,導致獲取靜音鍵按鈕的開光狀態的誤差原因。
1、在某些情況下,由於runloop的時機,以及計數的誤差,加上判斷閥值的設定,導致判斷出錯。
2、當前獲取音量的值爲0影響了獲取結果。
優化方案:
1、摒棄runloop技術策略,改用播放時間戳的判斷。因爲上面demo的思想就是播放一個短音效。所以根據前後播放時間差的判斷,規避由於計數器的判斷誤差。
2、針對獲取音量的值爲0情況,一般都是AVFoundation獲取音量的時候,播放器沒有激活。或者播放器聲道資源被佔用。所以在每一次播放白噪音時,提前設置AVAudioSession,並且播放一次音頻,確保在獲取前聲道沒有被第三方或者APP裏面的其他播放業務打斷。
猜測,靜音鍵按鈕的狀態 是根據聲卡、獲取音量的值有聯繫。如果獲取音量的值爲0(不管是獲取正確的音量值是0,還是因爲獲取錯誤的音量值爲0),那麼默認就是靜音的狀態。
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];//保證不會打斷第三方app的音頻播放 導致獲取不準確
NSString *itemVideoPath = [[NSBundle mainBundle] pathForResource:@"detection" ofType:@"aiff"];
AVPlayer *player = [AVPlayer playerWithURL:[NSURL URLWithString:itemVideoPath]];
[player play];
最終解決方案代碼:
demo地址