拔下耳机时是不是有事件?

Posted

技术标签:

【中文标题】拔下耳机时是不是有事件?【英文标题】:Is there an event for when the headphones are unplugged?拔下耳机时是否有事件? 【发布时间】:2011-05-26 01:04:17 【问题描述】:

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

有谁知道我是否可以参与某种活动来实现这一切?

【问题讨论】:

【参考方案1】:

请参阅音频会话编程指南中的 Responding to Route Changes。

【讨论】:

谢谢!这个页面,从那里链接,developer.apple.com/library/ios/#documentation/Audio/Conceptual/…。也有示例代码“当用户插入或拔出耳机时,系统调用回调,或者对接或取消对接设备 - 从而添加或删除音频连接”。还没试过,但看起来很完美。为单挑干杯。 太棒了:)。回调不用头疼【参考方案2】:

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

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

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

【讨论】:

【参考方案3】:

iOS 7 改变了这一点,你只需要收听名为AVAudioSessionRouteChangeNotification 的通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChanged:) name:AVAudioSessionRouteChangeNotification object:nil];

【讨论】:

这确实适用于视频播放器,但前提是您使用 AVAudioSession【参考方案4】:

Swift 3.0 @snakeoil 的解决方案:

    NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.yourMethodThatShouldBeCalledOnChange), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)

【讨论】:

太棒了!非常感谢!

以上是关于拔下耳机时是不是有事件?的主要内容,如果未能解决你的问题,请参考以下文章

拔下耳机时暂停ExoPlayer

拔下耳机时 MPMoviePlayerViewController 停止播放

通话过程中拔下耳机时 AVAudioEngine 崩溃

Android怎样监听蓝牙耳机的按键事件

iOS开发系列--触摸事件手势识别摇晃事件耳机线控

指挥中心事件,仅响应无线耳机播放/暂停