Принудительное использование микрофона iPhone в качестве аудиовхода - PullRequest
10 голосов
/ 23 октября 2010

Я разрабатываю приложение для iOS (на данный момент предназначенное специально для iPhone), для которого требуется, чтобы приложение записывало звук только с внутреннего микрофона iPhone (даже если наушники / гарнитура подключены) и воспроизводилона наушники (предположим, что наушники подключены).

Мне интересно, возможно ли это в настоящее время с доступными API?Если да, то кто-нибудь может пролить свет на то, как мне это сделать?

Спасибо!

Ответы [ 5 ]

8 голосов
/ 14 февраля 2011

Я считаю, что ответом на этот вопрос будет «нет». Для экспериментов я использовал iPhone 4 и новый для iOS 4 AVFoundation, сосредоточившись на классе AVCaptureDevice .

Я добавил в приложение следующее:

NSLog(@"%@", [AVCaptureDevice devices]);

Таким образом, запрашивается список всех устройств, которые можно использовать для захвата аудио и / или видео. Без наушников я получаю:

(
    "Back Camera",
    "Front Camera",
    "iPhone Microphone" 
)

С подключенными наушниками я получаю:

(
    "Back Camera",
    "Front Camera",
    Headphones
)

Таким образом, микрофон iPhone исчезает из списка доступных устройств AVCaptureDevices, как только наушники становятся доступными. Чтобы проверить это далее, я добавил небольшой фрагмент кода, чтобы получить экземпляр AVCaptureDevice для доступного аудиоустройства и распечатать его уникальный идентификатор. И для устройства, идентифицирующего себя как «iPhone Microphone», и для устройства, идентифицирующего себя как «Наушники», я получил:

com.apple.avfoundation.avcapturedevice.built-in_audio:0

Мне кажется очевидным, что два устройства не могут иметь один и тот же уникальный идентификатор, поэтому очевидно, что это одно и то же устройство, меняющее свое состояние. Несмотря на то, что AVCaptureDevices имеет множество возможностей для настройки состояния, оно ограничено визуальными элементами, такими как фокус, экспозиция, вспышка и баланс белого.

4 голосов
/ 04 апреля 2013

Похоже, это действительно невозможно.

Моя цель - отправлять вывод на гарнитуру Bluetooth и записывать входные данные с него. Насколько я вижу, мои лучшие варианты: свойство PlayAndRecord + AllowBluetoothInput (iphone 4, гарнитура Nokia BH-214)

ВАЖНО, согласно документации Apple, вы всегда должны переопределять свою аудио категорию при изменении аудио маршрута!

Это мой метод LISTENER ИЗМЕНЕНИЯ МАРШРУТА, который печатает: RouteChangeReasons, outputRoute, audioRout :

void RouteChangeListener(void                    *inClientData,
                     AudioSessionPropertyID  inID,
                     UInt32                  inDataSize,
                     const void              *inData) {


if (inID == kAudioSessionProperty_AudioRouteChange) {

    NSLog(@"]-----------------[ Audio Route Change ]--------------------[");

    // ************************************************************************************************
    // Check route change reason **********************************************************************
    // ************************************************************************************************
    CFDictionaryRef routeDict = (CFDictionaryRef)inData;
    NSNumber* reasonValue = (NSNumber*)CFDictionaryGetValue(routeDict, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
    int reason = [reasonValue intValue];

    if (reason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
        NSLog(@"] Logic: audio route change reason: OldDeviceUnavailable");
    }else if (reason == kAudioSessionRouteChangeReason_NewDeviceAvailable ) {
        NSLog(@"] Logic: audio route change reason: NewDeviceAvailable");
    }else if (reason == kAudioSessionRouteChangeReason_Unknown ) {
        NSLog(@"] Logic: audio route change reason: Unknown");
    }else if (reason == kAudioSessionRouteChangeReason_CategoryChange ) {
        NSLog(@"] Logic: audio route change reason: CategoryChange");
    }else if (reason == kAudioSessionRouteChangeReason_Override ) {
        NSLog(@"] Logic: audio route change reason: Override");
    }else if (reason == kAudioSessionRouteChangeReason_WakeFromSleep ) {
        NSLog(@"] Logic: audio route change reason: WakeFromSleep");
    }else if (reason == kAudioSessionRouteChangeReason_NoSuitableRouteForCategory ) {
        NSLog(@"] Logic: audio route chang reasone: NoSuitableRouteForCategory");
    }

    // ************************************************************************************************
    // Check output type ******************************************************************************
    // ************************************************************************************************
    CFDictionaryRef currentRouteDescriptionDictionary = nil;
    UInt32 dataSize = sizeof(currentRouteDescriptionDictionary);
    AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &dataSize, &currentRouteDescriptionDictionary);
    if (currentRouteDescriptionDictionary) {
        CFArrayRef outputs = CFDictionaryGetValue(currentRouteDescriptionDictionary, kAudioSession_AudioRouteKey_Outputs);
        if(CFArrayGetCount(outputs) > 0) {
            CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0);
            CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type);

            if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo) ) {                // if Airplay
                NSLog(@"] Logic: output changed to Airplay");
            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BluetoothA2DP, 0) == kCFCompareEqualTo) ) {     // if Bluetooth A2DP
                NSLog(@"] Logic: output changed to A2DP");

                // Mix with others category
                UInt32 doSetProperty = 1;
                AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);

                // Bluetooth support enable
                UInt32 allowBluetoothInput = 1;
                AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);


            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BluetoothHFP, 0) == kCFCompareEqualTo) ) {     // if Bluetooth HFP
                NSLog(@"] Logic: output changed to HFP");
                // Mix with others category
                UInt32 doSetProperty = 1;
                AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);

                // Bluetooth support enable
                UInt32 allowBluetoothInput = 1;
                AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);
            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_LineOut, 0) == kCFCompareEqualTo) ) {           // if Line Out
                NSLog(@"] Logic: output changed to Line Out");
            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_Headphones, 0) == kCFCompareEqualTo) ) {        // if Headphones
                NSLog(@"] Logic: output changed to Headphone");

                // Mix with others category
                UInt32 doSetProperty = 1;
                AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);

                // Bluetooth support disable
                UInt32 allowBluetoothInput = 0;
                AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);

            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BuiltInSpeaker, 0) == kCFCompareEqualTo) ) {    // if Built In Speaker
                NSLog(@"] Logic: output changed to Built In Speaker");

                // Mix with others category
                UInt32 doSetProperty = 1;
                AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);

            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_USBAudio, 0) == kCFCompareEqualTo) ) {          // if USB audio
                NSLog(@"] Logic: output changed to USB Audio");
            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_HDMI, 0) == kCFCompareEqualTo) ) {              // if HDMI
                NSLog(@"] Logic: output changed to HDMI");
            }
            else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BuiltInReceiver, 0) == kCFCompareEqualTo) ) {   // if Built in Reciever
                NSLog(@"] Logic: output changed to Built in Reciever");

                // Mix with others category
                UInt32 doSetProperty = 1;
                AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
            }
            else {                                                                                                          // Unknown audio type
                NSLog(@"] Logic: WARNING: Unknown audio type: %@",(NSString*)outputType);
            }
        }



    }

    // ************************************************************************************************
    // Check audio route ******************************************************************************
    // ************************************************************************************************
    UInt32 routeSize = sizeof(CFStringRef);
    CFStringRef route;
    AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &routeSize, &route);
    NSLog(@"] Logic: the audio route is: %@",(NSString*)route);


    // ************************************************************************************************
    NSLog(@"]--------------------------[  ]-----------------------------[");        

}

}

3 голосов
/ 14 октября 2014

Поскольку apple снова изменила аудиосистему, начиная с 7.0, я собираюсь опубликовать обновленный код здесь:

#pragma mark Route change listener
// *********************************************************************************************************
// *********** Route change listener ***********************************************************************
// *********************************************************************************************************
-(void)routeChanged:(NSNotification*)notification {

    NSLog(@"]-----------------[ Audio Route Change ]--------------------[");

    AVAudioSession *session = [AVAudioSession sharedInstance];

    //AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];

    // Reason
    NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (reason) {
        case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
            NSLog(@"] Audio Route: The route changed because no suitable route is now available for the specified category.");
            break;
        case AVAudioSessionRouteChangeReasonWakeFromSleep:
            NSLog(@"] Audio Route: The route changed when the device woke up from sleep.");
            break;
        case AVAudioSessionRouteChangeReasonOverride:
            NSLog(@"] Audio Route: The output route was overridden by the app.");
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            NSLog(@"] Audio Route: The category of the session object changed.");
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            NSLog(@"] Audio Route: The previous audio output path is no longer available.");
            break;
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            NSLog(@"] Audio Route: A preferred new audio output path is now available.");
            break;
        case AVAudioSessionRouteChangeReasonUnknown:
            NSLog(@"] Audio Route: The reason for the change is unknown.");
            break;
        default:
            NSLog(@"] Audio Route: The reason for the change is very unknown.");
            break;
    }

    // Output
    AVAudioSessionPortDescription *output = [[session.currentRoute.outputs count]?session.currentRoute.outputs:nil objectAtIndex:0];
    if ([output.portType isEqualToString:AVAudioSessionPortLineOut]) {
        NSLog(@"] Audio Route: Output Port: LineOut");
    }
    else if ([output.portType isEqualToString:AVAudioSessionPortHeadphones]) {
        NSLog(@"] Audio Route: Output Port: Headphones");
    }
    else if ([output.portType isEqualToString:AVAudioSessionPortBluetoothA2DP]) {
        NSLog(@"] Audio Route: Output Port: BluetoothA2DP");
    }
    else if ([output.portType isEqualToString:AVAudioSessionPortBuiltInReceiver]) {
        NSLog(@"] Audio Route: Output Port: BuiltInReceiver");
    }
    else if ([output.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
        NSLog(@"] Audio Route: Output Port: BuiltInSpeaker");
    }
    else if ([output.portType isEqualToString:AVAudioSessionPortHDMI]) {
        NSLog(@"] Audio Route: Output Port: HDMI");
    }
    else if ([output.portType isEqualToString:AVAudioSessionPortAirPlay]) {
        NSLog(@"] Audio Route: Output Port: AirPlay");
    }
    else if ([output.portType isEqualToString:AVAudioSessionPortBluetoothLE]) {
        NSLog(@"] Audio Route: Output Port: BluetoothLE");
    }
    else {
        NSLog(@"] Audio Route: Output Port: Unknown: %@",output.portType);
    }

    // Input
    AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count] ? session.currentRoute.inputs:nil objectAtIndex:0];

    if ([input.portType isEqualToString:AVAudioSessionPortLineIn]) {
        NSLog(@"] Audio Route: Input Port: LineIn");
    }
    else if ([input.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
        NSLog(@"] Audio Route: Input Port: BuiltInMic");
    }
    else if ([input.portType isEqualToString:AVAudioSessionPortHeadsetMic]) {
        NSLog(@"] Audio Route: Input Port: HeadsetMic");
    }
    else if ([input.portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
        NSLog(@"] Audio Route: Input Port: BluetoothHFP");
    }
    else if ([input.portType isEqualToString:AVAudioSessionPortUSBAudio]) {
        NSLog(@"] Audio Route: Input Port: USBAudio");
    }
    else if ([input.portType isEqualToString:AVAudioSessionPortCarAudio]) {
        NSLog(@"] Audio Route: Input Port: CarAudio");
    }
    else {
        NSLog(@"] Audio Input Port: Unknown: %@",input.portType);
    }

    NSLog(@"]--------------------------[  ]-----------------------------[");

}

Не забудьте добавить наблюдателя, поскольку делегат аудио-сессии также устарел:

[[NSNotificationCenter defaultCenter] addObserver: self
                                                 selector: @selector(audioInterruption:)
                                                     name: AVAudioSessionInterruptionNotification
                                                   object: nil];

PS: вам не нужно сбрасывать категорию здесь (как в 6.0)

2 голосов
/ 28 августа 2012

Это невозможно, я пытаюсь разобраться с прослушивателем измененного маршрута (с AudioSession). Мой результат: вы не можете настроить отдельно ввод или вывод из-за категорий , предоставленных Apple. Я пытаюсь * PlayAndRecord, когда я подключаю устройство Bluetooth, маршрут меняется следующим образом:

old route : HeadsetBT
new route : SpeakerAndMicrophone

На самом деле мой блютуз не гарнитура, просто динамики ... Так что для меня нет решения.

2 голосов
/ 02 февраля 2011

Я вполне уверен, что это возможно с помощью аудиосеанса вашего приложения:

Аудио сеанс является посредником между вашим приложением и iOS. каждый Приложение для iPhone имеет ровно один аудио сессия. Вы настраиваете это выразить аудио вашего приложения Намерения. Для начала, вы отвечаете на некоторые вопросы о том, как вы хотите, чтобы ваш приложение к поведению:

  • Как вы хотите, чтобы ваше приложение реагировать на прерывания, такие как телефонный звонок?
  • Собираетесь ли вы смешать ваши звуки приложения с теми из другие запущенные приложения, или вы намерены заставить их замолчать?
  • Как следует ваше приложение отвечает на аудио изменение маршрута, например, когда пользователь подключить или отключить гарнитуру?

С ответы в руке, вы используете аудио интерфейс сеанса (объявлен в AudioToolbox / AudioServices.h) для настроить аудио сеанс и ваш применение.

Копайте в этих документах:

И дай мне знать, как оно идет!

...