0

I have an app that records video, but I need it to show pitch levels of the sounds captured on the microphone in real-time to the user. I have been able to successfully record audio and video to MP4 using AVCaptureSession. However, when I add AVCaptureAudioDataOutput to the session and assign the AVCaptureAudioDataOutputSampleBufferDelegate I receive no errors, and yet the captureOutput function is never called once the session starts.

Here is the code:

import UIKit
import AVFoundation
import CoreLocation


class ViewController: UIViewController, 
AVCaptureVideoDataOutputSampleBufferDelegate, 
AVCaptureFileOutputRecordingDelegate, CLLocationManagerDelegate , 
AVCaptureAudioDataOutputSampleBufferDelegate {

var videoFileOutput: AVCaptureMovieFileOutput!
let session = AVCaptureSession()
var outputURL: URL!
var timer:Timer!
var locationManager:CLLocationManager!
var currentMagnitudeValue:CGFloat!
var defaultMagnitudeValue:CGFloat!
var visualMagnitudeValue:CGFloat!
var soundLiveOutput: AVCaptureAudioDataOutput!


override func viewDidLoad() {
    super.viewDidLoad()
    self.setupAVCapture()
}


func setupAVCapture(){

    session.beginConfiguration()

    //Add the camera INPUT to the session
    let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
                                              for: .video, position: .front)
    guard
        let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!),
        session.canAddInput(videoDeviceInput)
        else { return }
    session.addInput(videoDeviceInput)

    //Add the microphone INPUT to the session
    let microphoneDevice = AVCaptureDevice.default(.builtInMicrophone, for: .audio, position: .unspecified)
    guard
        let audioDeviceInput = try? AVCaptureDeviceInput(device: microphoneDevice!),
        session.canAddInput(audioDeviceInput)
        else { return }
    session.addInput(audioDeviceInput)

    //Add the video file OUTPUT to the session
    videoFileOutput = AVCaptureMovieFileOutput()
    guard session.canAddOutput(videoFileOutput) else {return}
    if (session.canAddOutput(videoFileOutput)) {
        session.addOutput(videoFileOutput)
    }

    //Add the audio output so we can get PITCH of the sounds
    //AND assign the SampleBufferDelegate
    soundLiveOutput = AVCaptureAudioDataOutput()
    soundLiveOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "test"))
    if (session.canAddOutput(soundLiveOutput)) {
        session.addOutput(soundLiveOutput)
        print ("Live AudioDataOutput added")
    } else
    {
        print("Could not add AudioDataOutput")
    }



    //Preview Layer
    let previewLayer = AVCaptureVideoPreviewLayer(session: session)
    let rootLayer :CALayer = self.cameraView.layer
    rootLayer.masksToBounds=true
    previewLayer.frame = rootLayer.bounds
    rootLayer.addSublayer(previewLayer)
    previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill;

    //Finalize the session
    session.commitConfiguration()

   //Begin the session
    session.startRunning()


}

func captureOutput(_: AVCaptureOutput, didOutput: CMSampleBuffer, from: 
AVCaptureConnection) {
    print("Bingo")
}

}

Expected output:

Bingo
Bingo
Bingo
...

I have read:

StackOverflow: captureOutput not being called - The user was not declaring the captureOutput method correctly.

StackOverflow: AVCaptureVideoDataOutput captureOutput not being called - The user was not declaring the captureOutput method at all.

Apple - AVCaptureAudioDataOutputSampleBufferDelegate - Apple's documentation on the delegate and it's method - the method matches the method I have declared.

Other common errors I have encountered online:

  • Using the declaration for older versions of Swift (I am using v4.1)
  • Apparently on one article after Swift 4.0, AVCaptureMetadataOutput replaces AVCaptureAudioDataOutput - Although I couldn't find this in Apple's documentation, I tried this also, but similarly, the metadataOutput function is never called.

I am fresh out of ideas. Am I missing something obvious?

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
JCutting8
  • 732
  • 9
  • 29

3 Answers3

1

Ok, nobody got back to me but after playing around with it I worked out the correct way to declare the captureOutput method for Swift4 is as follows:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    //Do your stuff here
}

Unfortunately, the documentation for this online is very poor. I guess you just need to get it exactly right - there are no errors thrown if you mispell or misname the variables as it is an optional function.

JCutting8
  • 732
  • 9
  • 29
  • Omg after wasting several days to fix this problem this actually was the issue. Thanks a lot. I'm currently porting my app from Android to iOS and I'm really surprised that recording microphone data is such a pain here on iOS. – grill2010 Mar 20 '22 at 17:48
  • 2
    @grill2010 funnily enough, in 2022, here I am searching the same problem and found my question/answer from years earlier. Unfortunately this time, it isn't the same issue. – JCutting8 Sep 10 '22 at 08:53
1

The method you're using is been updated with this one, Which will get called for both AVCaptureAudioDataOutput & AVCaptureVideoDataOutput. you make sure you check the output before writing the sample buffers to asset writer.

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {

    //Make sure you check the output before using sample buffer
    if output == audioDataOutput {
      //Use sample buffer for audio 
   }
}
Tushar Katyal
  • 412
  • 5
  • 12
  • While this code may answer the question, providing additional context regarding *how* and *why* it solves the problem would improve the answer's long-term value. – Alexander Aug 08 '18 at 16:58
1

The problem for me turned out to be this, and that the AVAudioSession and AVCaptureSession where declared as local variables and when I started the session, it just went away. Once I moved them to class level variables, everything worked great!

Gregg Harrington
  • 123
  • 1
  • 11