Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Camera: `AVCaptureAudioDataOutput` activate audio session on the fly, avoid background music stutter

I have created a Camera using AVFoundation which is able to record video and audio using AVCaptureVideoDataOutput and AVCaptureAudioDataOutput. I create my capture session, attach all inputs and the video- and audio-data outputs, and the Camera then sits idle. The user is now able to start a video recording.

Problem

The problem with this is that immediately after I start the capture session (captureSession.startRunning()), the background music stutters. I assume this is because once the capture session starts running, the AVCaptureAudioDataOutput internally activates the AVAudioSession (AVAudioSession.setActive(...)), which I don't want it doing. I want it sitting idle (and not providing any audio output buffers) until I explicitly activate the Audio Session (once the user starts recording).

This is really annoying, since the Camera is the start-screen in our app and everytime the user opens or closes the app his music stutters.

I know that this is somehow possible because Snapchat works that way - you open the App and background audio smoothly continues to play. Once you start recording, there is a small stutter on the background music, but the Camera smoothly operates and starts recording once the short stutter is over.

My code:

func configureSession() {

    captureSession.beginConfiguration()

    // Video, Photo and Audio Inputs
    ...

    // Video Output
    ...

    // Audio Output
    audioOutput = AVCaptureAudioDataOutput()
    guard captureSession.canAddOutput(audioOutput!) else {
      throw CameraError.parameter(.unsupportedOutput(outputDescriptor: "audio-output"))
    }
    audioOutput!.setSampleBufferDelegate(self, queue: audioQueue)
    captureSession.addOutput(audioOutput!)

    try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord,
                                                    options: [.mixWithOthers,
                                                              .allowBluetoothA2DP,
                                                              .defaultToSpeaker,
                                                              .allowAirPlay])

    captureSession.commitConfiguration()
}

What I tried

Delay configuring the AVAudioSession.sharedInstance()

I tried to first configure the AVAudioSession.sharedInstance with the category AVAudioSession.Category.playback, and switch to .playAndRecord once I want to start recording audio.

This didn't work and the AVCaptureSessionRuntimeError event gets invoked immediately after starting the Camera with the Error code -10851, which means kAudioUnitErr_InvalidPropertyValue. I think this means that the AVCaptureAudioDataOutput is not allowed to record from the Audio Session, but I don't event want to do that right now - it should just be idle.

Delay adding the AVCaptureAudioDataOutput output

I tried to not add the audio output (AVCaptureAudioDataOutput) in the beginning, and only add it "on-demand" once the user starts recording, and while that worked fine for the background music (no stutter when starting, only short stutter once the user starts recording, exactly how I want it), it made the Preview freeze for a short amount of time (because the Capture Session is being reconfigured via beginConfiguration + audio output adding + commitConfiguration)

Does anyone know how it's possible to achieve what I'm trying to do here - or how Snapchat does it? Any help appreciated, thanks!

like image 641
mrousavy Avatar asked Jun 02 '21 14:06

mrousavy


1 Answers

Finally figured it out. I simply created a separate AVCaptureSession specifically for the audio input/output which I synchronize with the main capture session's masterClock. I can then start/stop the secondary capture session on the fly (shortly before start recording)

like image 188
mrousavy Avatar answered Nov 15 '22 05:11

mrousavy