Suggestions

close search
Speed up your development
Create a Fast Track

Add Messaging, Voice, and Authentication to your apps with Vonage Communications APIs

Visit the Vonage API Developer Portal

Back to Tutorials

Custom Audio Driver (iOS)

Overview

The OpenTok iOS SDK lets you set up a custom audio driver for publishers and subscribers. You can use a custom audio driver to customize publisher and subscriber stream audio. You will use the custom audio driver when you want to start and stop the audio, and play your own audio file. When you want to do "anything" with audio, other than the SDK default behavoir of live video chat, you would use custom audio drivers.

Setting up your project

The code for this section is in the Ringtones sample in the opentok-ios-sdk-samples repo. If you haven't already, you'll need to clone the repo into a local directory — this can be done using the command line:

git clone https://github.com/opentok/opentok-ios-sdk-samples.git

Open the project in XCode to follow along

Exploring the code

This sample application uses the custom audio driver — defined by the OTDefaultAudioDevice included in the OpenTok iOS SDK. The audio driver is used by both publishers and subscribers. In this app, the subscriber stream is same as the publisher stream (a self-subscribing stream). We first publish, then stop capturing publisher audio, so we can play a ringtone exclusively on an Apple AVAudioPlayer. When the subscriber joins (10 seconds after the app publishes the stream), the ringtone player stops playing. The custom audio driver then reverts back to capturing and rendering audio.

The OTAudioDeviceRingtone class inherits from OTDefaultAudioDevice. Most of the low-level audio processing is done in OTDefaultAudioDevice. It uses Apple Audio Units to process, record and play audio. OTDefaultAudioDevice also supports Bluetooth, handles route changes, etc. This tutorial does not cover these advanced features; it just shows a simple use of the OTAudioDevice lifecycle protocol to start and stop audio.

If you want to do any device(iPhone) related audio processing like enabling CallKit, playing MP3 files, ringtones, audio frame processing, etc., you should use custom audio drivers. Refer to the Custom-Audio-Driver project in the opentok-ios-sdk-samples repo for more details.

If you want to solely capture, process, and play audio frame by frame, you can use webrtc audio transformers, for ease of use. Refer to the Media-Transformers project in the opentok-ios-sdk-samples repo for more details.

Ringtones app

A ringtone is an external audio played using the Apple Audio AVAudioPlayer API. Since the iPhone Audio system is a global singleton object, playing the ringtone on it without stopping our audio flow, leads to intermingling of sounds. So, the app first needs stop capturing the publisher audio, then it plays the ringtone, and then it enables the audio capturing and rendering when a subscriber joins. We initialize the Ringtone app as follows:


- (instancetype)initWithRingtone:(NSURL *)url {
    self = [super init];
    if (self) {
        ringtoneURL = url;
    }
    return self;
}

The ringtone (represented as a URL) will be played indefinitely using the AVAudioPlayer APIs. The following code shows the stop, play, and start flow:


- (void)playRingtoneFromURL:(NSURL*)url
{
    [self stopCapture];
    [self stopRendering];
    // Stop & replace existing audio player
    if (_audioPlayer) {
        [_audioPlayer stop];
        _audioPlayer = nil;
    }
    
    NSError* error = nil;
    _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url
                         error:&error];
    if (error) {
        NSLog(@"Ringtone audio player initialization failure %@", error);
        _audioPlayer = nil;
        return;
    }
    [_audioPlayer setDelegate:self];
    
    // Tell player to loop indefinitely
    [_audioPlayer setNumberOfLoops:-1];

    // setup timer to vibrate device with some frequency
    if (_vibratesWithRingtone) {
        _vibrateTimer =
        [NSTimer scheduledTimerWithTimeInterval:VIBRATE_FREQUENCY_SECONDS
            target:self
            selector:@selector(buzz:)
            userInfo:nil
            repeats:YES];
    }
    
    // finally, begin playback
    [_audioPlayer play];
}

OTAudioDevice lifecycle methods

The [self startCapture] method starts the publisher audio capturing, and the [self startRendering] method starts the subscriber audio rendering.

Similarly, the [self stopCapture] method stops the publisher from capturing, and the [self stopRendering] method stops the subscriber from rendering its audio. These overridden methods don't do much in the sample app except call the parent method.

Our custom audio exposes three ringtone-related helper methods as follows:


#pragma mark - Exposed interface methods
- (void)startRingtone {
    if (_audioPlayer == nil) {
        [self playRingtoneFromURL:ringtoneURL];
    }
}

- (void)stopRingtone {
    // Stop Audio
    [_audioPlayer stop];
    _audioPlayer = nil;
    
    // Stop vibration
    [_vibrateTimer invalidate];
    _vibrateTimer = nil;
    
    [self startCapture];
    [self startRendering];

}

- (BOOL)isRingTonePlaying {
    return _audioPlayer != nil;
}    

These methods are used in the ViewController. After publishing the video, the doPublish method starts the ringtone. When the self-subscribing stream connects, the subscriberDidConnectToStream method stops the ringtone.

Congratulations! You've finished the Custom Audio Driver Tutorial for iOS.
You can continue to play with and adjust the code you've developed here, or check out the Next Steps below.