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

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

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

【问题讨论】:

    标签: iphone ios objective-c audiotoolbox headphones


    【解决方案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)
      

      【讨论】:

      • 太棒了!非常感谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-17
      • 2016-06-10
      • 2014-02-06
      • 1970-01-01
      • 2014-12-30
      • 2013-11-11
      相关资源
      最近更新 更多