How to switch between multiple AVAudioSession configs in one app?

Viewed 53

I have a project in react-native and I'm using react-native-video library for providing videos in my app. I have many videos components within app, but they must have different AVAudioSession category and options. But when I first time set category and options to AVAudioSession, then it applies to every video entity in my app..

The code for configuring audio is below:

- (void)configureAudio
{
    AVAudioSession *session = [AVAudioSession sharedInstance];
    AVAudioSessionCategory category = nil;
    AVAudioSessionCategoryOptions options = nil;

    if([_ignoreSilentSwitch isEqualToString:@"ignore"]) {
      category = AVAudioSessionCategoryPlayback;
    } else if([_ignoreSilentSwitch isEqualToString:@"obey"]) {
      category = AVAudioSessionCategoryAmbient;
    }

    if([_mixWithOthers isEqualToString:@"mix"]) {
      options = AVAudioSessionCategoryOptionMixWithOthers;
    } else if([_mixWithOthers isEqualToString:@"duck"]) {
      options = AVAudioSessionCategoryOptionDuckOthers;
    } 

    if (category != nil && options != nil) {
      [session setCategory:category withOptions:options error:nil];
    } else if (category != nil && options == nil) {
      [session setCategory:category error:nil];
    } else if (category == nil && options != nil) {
      [session setCategory:session.category withOptions:options error:nil];
    }
}

The code where I call this method:

- (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch
{
  _ignoreSilentSwitch = ignoreSilentSwitch;
  [self configureAudio];
  [self applyModifiers];
}

- (void)setPaused:(BOOL)paused
{
  if (paused) {
    [_player pause];
    [_player setRate:0.0];
    
  } else {
    [self configureAudio];

    if (@available(iOS 10.0, *) && !_automaticallyWaitsToMinimizeStalling) {
      [_player playImmediatelyAtRate:_rate];
    } else {
      [_player play];
      [_player setRate:_rate];
    }
    [_player setRate:_rate];
  }
  
  _paused = paused;
}

Config must change every time when user call setIgnoreSilentSwitch('some value') or setPaused(false), but it doesn't happen..

1 Answers

It seems somehow impossible to use multiple AVAudioSession in an app. I confirmed it by unit testing.

class ASession: AVAudioSession {
    private static let instance = ASession()
    override class func sharedInstance() -> AVAudioSession { instance }

    override var category: AVAudioSession.Category { .playAndRecord }
    override var categoryOptions: AVAudioSession.CategoryOptions { [.duckOthers, .allowBluetooth] }
}

class BSession: AVAudioSession {
    private static let instance = BSession()
    override class func sharedInstance() -> AVAudioSession { instance }

    override var category: AVAudioSession.Category { .playback }
    override var categoryOptions: AVAudioSession.CategoryOptions { [.defaultToSpeaker, .duckOthers] }
}

class AVAudioSessionTests: XCTestCase {

    func tesIftMultipleAudioSessionsAvailable() {
        XCTAssertEqual(ASession.sharedInstance().category, .playAndRecord)  // Passed
        XCTAssertEqual(ASession.sharedInstance().categoryOptions, [.mixWithOthers, .duckOthers, .allowBluetooth])  // Passed

        // XCTAssertEqual failed: ("AVAudioSessionCategory(_rawValue: AVAudioSessionCategoryPlayAndRecord)") is not equal to ("AVAudioSessionCategory(_rawValue: AVAudioSessionCategoryPlayback)")
        XCTAssertEqual(BSession.sharedInstance().category, .playback)
        // XCTAssertEqual failed: ("AVAudioSessionCategoryOptions(rawValue: 7)") is not equal to ("AVAudioSessionCategoryOptions(rawValue: 11)") 
        XCTAssertEqual(BSession.sharedInstance().categoryOptions, [.defaultToSpeaker, .mixWithOthers, .duckOthers])
    }
}

Also, I found a mention in another question that says "Each app running in iOS has a single audio session". I can no longer find the same sentence in the Apple document mentioned in the answer, but it was probably there and functionality itself hasn't been changed as of today.
https://stackoverflow.com/a/31815855/4834226

Related