16

在测试期间,一位客户注意到拔下耳机时 iPhone 中的视频播放会暂停。他想要类似的音频播放功能,也许还有弹出消息的能力。

有谁知道是否有某种事件我可以参与以使这成为可能?

4

4 回答 4

20

请参阅音频会话编程指南中的响应路由更改

于 2010-12-15T16:41:44.730 回答
8

这在 iOS 7 中发生了变化,您只需要收听名为的通知AVAudioSessionRouteChangeNotification

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
于 2015-12-03T17:10:50.127 回答
3

Swift 3.0 @snakeoil 的解决方案:

    NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.yourMethodThatShouldBeCalledOnChange), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
于 2017-01-16T12:54:56.743 回答
1

这是我最终用于在插入(和拔出)耳机时发送事件的完整实现。

我需要处理相当多的复杂性,以确保在应用程序从后台返回后仍然可以正常工作。

CVAudioSession.h 文件

#import <Foundation/Foundation.h>

#define kCVAudioInputChangedNotification @"kCVAudioInputChangedNotification"
#define kCVAudioInterruptionEnded @"kCVAudioInterruptionEnded"

@interface CVAudioSession : NSObject
+(void) setup;
+(void) destroy;
+(NSString*) currentAudioRoute;
+(BOOL) interrupted;
@end

CVAudioSession.m 文件

#import "CVAudioSession.h"
#import <AudioToolbox/AudioToolbox.h>

@implementation CVAudioSession

static BOOL _isInterrupted = NO;

+(void) setup {
    NSLog(@"CVAudioSession setup");

    // Set up the audio session for recording
    OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListener, (__bridge void*)self);

    if (error) NSLog(@"ERROR INITIALIZING AUDIO SESSION! %ld\n", error);
    if (!error) {
        UInt32 category = kAudioSessionCategory_RecordAudio; // NOTE CANT PLAY BACK WITH THIS
        error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
        if (error) NSLog(@"couldn't set audio category!");

        error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void*) self);
        if (error) NSLog(@"ERROR ADDING AUDIO SESSION PROP LISTENER! %ld\n", error);
        UInt32 inputAvailable = 0;
        UInt32 size = sizeof(inputAvailable);

        // we do not want to allow recording if input is not available
        error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);
        if (error) NSLog(@"ERROR GETTING INPUT AVAILABILITY! %ld\n", error);

        // we also need to listen to see if input availability changes
        error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void*) self);
        if (error) NSLog(@"ERROR ADDING AUDIO SESSION PROP LISTENER! %ld\n", error);

        error = AudioSessionSetActive(true);
        if (error) NSLog(@"CVAudioSession: AudioSessionSetActive (true) failed");
    }
}

+ (NSString*) currentAudioRoute {
    UInt32 routeSize = sizeof (CFStringRef);
    CFStringRef route;

    AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
                             &routeSize,
                             &route);

    NSString* routeStr = (__bridge NSString*)route;
    return routeStr;
}

+(void) destroy {
    NSLog(@"CVAudioSession destroy");

    // Very important - remove the listeners, or we'll crash when audio routes etc change when we're no longer on screen
    OSStatus stat = AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void*)self);
    NSLog(@".. AudioSessionRemovePropertyListener kAudioSessionProperty_AudioRouteChange returned %ld", stat);

    stat = AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void*)self);
    NSLog(@".. AudioSessionRemovePropertyListener kAudioSessionProperty_AudioInputAvailable returned %ld", stat);

    AudioSessionSetActive(false); // disable audio session.
    NSLog(@"AudioSession is now inactive");
}

+(BOOL) interrupted {
    return _isInterrupted;
}

// Called when audio is interrupted for whatever reason. NOTE: doesn't always call the END one..
void interruptionListener(  void *  inClientData,
                          UInt32    inInterruptionState) {

    if (inInterruptionState == kAudioSessionBeginInterruption)
    {
        _isInterrupted = YES;

        NSLog(@"CVAudioSession: interruptionListener kAudioSessionBeginInterruption. Disable audio session..");

        // Try just deactivating the audiosession..
        OSStatus rc = AudioSessionSetActive(false);
        if (rc) {
            NSLog(@"CVAudioSession: interruptionListener kAudioSessionBeginInterruption - AudioSessionSetActive(false) returned %.ld", rc);
        } else {
            NSLog(@"CVAudioSession: interruptionListener kAudioSessionBeginInterruption - AudioSessionSetActive(false) ok.");
        }



    } else if (inInterruptionState == kAudioSessionEndInterruption) {

        _isInterrupted = NO;

        // Reactivate the audiosession
        OSStatus rc = AudioSessionSetActive(true);
        if (rc) {
            NSLog(@"CVAudioSession: interruptionListener kAudioSessionEndInterruption - AudioSessionSetActive(true) returned %.ld", rc);
        } else {
            NSLog(@"CVAudioSession: interruptionListener kAudioSessionEndInterruption - AudioSessionSetActive(true) ok.");   
        }

        [[NSNotificationCenter defaultCenter] postNotificationName:kCVAudioInterruptionEnded object:(__bridge NSObject*)inClientData userInfo:nil];
    }
}

// This is called when microphone or other audio devices are plugged in and out. Is on the main thread
void propListener(  void *                  inClientData,
                  AudioSessionPropertyID    inID,
                  UInt32                  inDataSize,
                  const void *            inData)
{
    if (inID == kAudioSessionProperty_AudioRouteChange)
    {
        CFDictionaryRef routeDictionary = (CFDictionaryRef)inData;
        CFNumberRef reason = (CFNumberRef)CFDictionaryGetValue(routeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
        SInt32 reasonVal;
        CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
        if (reasonVal != kAudioSessionRouteChangeReason_CategoryChange)
        {
            NSLog(@"CVAudioSession: input changed");
            [[NSNotificationCenter defaultCenter] postNotificationName:kCVAudioInputChangedNotification object:(__bridge NSObject*)inClientData userInfo:nil];
        }
    }
    else if (inID == kAudioSessionProperty_AudioInputAvailable)
    {
        if (inDataSize == sizeof(UInt32)) {
            UInt32 isAvailable = *(UInt32*)inData;

            if (isAvailable == 0) {
                NSLog(@"AUDIO RECORDING IS NOT AVAILABLE");
            }
        }
    }
}

@end
于 2013-09-09T10:05:11.930 回答