17

I am playing the audio from an url using AVPlayer, but when the iPhone is connected to a Bluetooth device it is not playing via Bluetooth, how to play via Bluetooth if it is connected, i see some posts in SO, but none of those are clearly explained. Below is my code.

    -(void)playselectedsong{

    AVPlayer *player = [[AVPlayer alloc]initWithURL:[NSURL URLWithString:urlString]];
    self.songPlayer = player;
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerItemDidReachEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:[songPlayer currentItem]];
    [self.songPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateProgress:) userInfo:nil repeats:YES];

    [self.songPlayer play];

}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    if (object == songPlayer && [keyPath isEqualToString:@"status"]) {
        if (songPlayer.status == AVPlayerStatusFailed) {
            NSLog(@"AVPlayer Failed");

        } else if (songPlayer.status == AVPlayerStatusReadyToPlay) {
            NSLog(@"AVPlayerStatusReadyToPlay");


        } else if (songPlayer.status == AVPlayerItemStatusUnknown) {
            NSLog(@"AVPlayer Unknown");

        }
    }
}

- (void)playerItemDidReachEnd:(NSNotification *)notification {

 //  code here to play next sound file

}
Sudheer Kumar Palchuri
  • 2,919
  • 1
  • 28
  • 38

3 Answers3

10

For Swift 3.1

Short version:

let audioSession = AVAudioSession.sharedInstance()
do {
    try audioSession.setCategory(AVAudioSessionCategoryRecord, with: [.allowBluetooth])
    try audioSession.setActive(true)
} catch {
    fatalError("Error Setting Up Audio Session")
}

Extended version to be sure, that you're using correct input:

/**
    Check availability of recording and setups audio session
    with prioritized input.
 */
func setupSessionForRecording() {
    let audioSession = AVAudioSession.sharedInstance()
    do {
        try audioSession.setCategory(AVAudioSessionCategoryRecord, with: [.allowBluetooth])
    } catch {
        fatalError("Error Setting Up Audio Session")
    }
    var inputsPriority: [(type: String, input: AVAudioSessionPortDescription?)] = [
        (AVAudioSessionPortLineIn, nil),
        (AVAudioSessionPortHeadsetMic, nil),
        (AVAudioSessionPortBluetoothHFP, nil),
        (AVAudioSessionPortUSBAudio, nil),
        (AVAudioSessionPortCarAudio, nil),
        (AVAudioSessionPortBuiltInMic, nil),
    ]
    for availableInput in audioSession.availableInputs! {
        guard let index = inputsPriority.index(where: { $0.type == availableInput.portType }) else { continue }
        inputsPriority[index].input = availableInput
    }
    guard let input = inputsPriority.filter({ $0.input != nil }).first?.input else {
        fatalError("No Available Ports For Recording")
    }
    do {
        try audioSession.setPreferredInput(input)
        try audioSession.setActive(true)
    } catch {
        fatalError("Error Setting Up Audio Session")
    }
}

/**
    Check availability of playing audio and setups audio session
    with mixing audio.
 */
func setupSessionForPlaying() {
    let audioSession = AVAudioSession.sharedInstance()
    do {
        try audioSession.setCategory(AVAudioSessionCategoryPlayback, with: [.mixWithOthers])
        try audioSession.setActive(true)
    } catch {
        fatalError("Error Setting Up Audio Session")
    }
}

The main idea that you have 2 functions for changing audio session settings. Use setupSessionForRecording right before recording, and setupSessionForPlaying before playing audio.

Important to use AVAudioSessionCategoryRecord and AVAudioSessionCategoryPlayback, but not AVAudioSessionCategoryPlayAndRecord, because of it's buggy. Use AVAudioSessionCategoryPlayAndRecord only if you really need to play and record audio same time.

Vasilii Muravev
  • 3,063
  • 17
  • 45
  • Tried the above code and looked for almost every solution online. Everything is set correctly. But the bluetooth headset input/output is just not being taken by the app. The phone mic also stops as the input gets successfully set to bluetoothHFP, but when you speak, it doesn't catch. wired headsets work perfectly though. The same bluetooth headset works perfectly with Siri. Is there some other configuration setting that I am missing - like permissions or capabilities? Device used : iphone 5c (10.3.2) – Sayalee Pote Oct 09 '17 at 13:43
  • @SayaleePote Did you try to set AV category to `AVAudioSessionCategoryPlayAndRecord`? – Vasilii Muravev Oct 11 '17 at 18:16
  • Yes it was set to PlayAndRecord earlier. Didn't work. Then I set it separately as suggested here. Still no success. – Sayalee Pote Oct 12 '17 at 06:32
9

Try this

UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory,
                         sizeof(sessionCategory),&sessionCategory);

// Set AudioSession
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&sessionError];

UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
SwathiK
  • 364
  • 3
  • 13
  • @PoojaSrivastava write this code in this method "didFinishLaunchingWithOptions" of this file "AppDelegate.m". note:- write this code before this line "[self.window makeKeyAndVisible]; ". – Pramod Oct 27 '17 at 09:19
  • 2
    "You can set AVAudioSessionCategoryOptionAllowBluetooth option only if the audio session category is AVAudioSessionCategoryPlayAndRecord or AVAudioSessionCategoryRecord." Source: https://developer.apple.com/documentation/avfoundation/avaudiosessioncategoryoptions/avaudiosessioncategoryoptionallowbluetooth?language=objc – Avi Dubey Jan 10 '19 at 02:13
2

You need to set a category and options on the AVAudioSession.

Try this when the app starts:

//configure audio session
NSError *setCategoryError = nil;
BOOL setCategorySuccess = [[AVAudioSession sharedInstance]
                           setCategory:AVAudioSessionCategoryPlayback
                           withOptions:AVAudioSessionCategoryOptionAllowBluetooth
                           error:&setCategoryError];

if (setCategorySuccess) {
    NSLog(@"Audio Session options set.");
} else {
    NSLog(@"WARNING: Could not set audio session options.");
}
picciano
  • 22,341
  • 9
  • 69
  • 82