4

I would like to get raw audio data from the iPhone mic (in NSData format) to stream over a socket. This is not a situation where I can use twilio/etc, as it is a research project. The socket implementation is done (I can send audio files), but I'm having trouble getting the streaming mic data.

Here is my attempt:

class ViewController: UIViewController, AVCaptureAudioDataOutputSampleBufferDelegate
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.setupMicrophone()
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func setupMicrophone()
    {
        let session = AVCaptureSession()
        session.sessionPreset = AVCaptureSessionPresetMedium

        let mic = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
        var mic_input: AVCaptureDeviceInput!

        let audio_output = AVCaptureAudioDataOutput()
        audio_output.setSampleBufferDelegate(self, queue: dispatch_get_main_queue())

        do
        {
            mic_input = try AVCaptureDeviceInput(device: mic)
        }
        catch
        {
            return
        }

        session.addInput(mic_input)
        session.addOutput(audio_output)

        session.startRunning()
    }

    func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!)
    {
        // Do something here
    }
}

Problems:

  • The delegate function is never called.

  • The data given to the delegate (if it were being called) is not NSData, is there another function that would provide NSData? Is there a way to convert CMSampleBuffer to NSData?

Any help is appreciated.

Cheers

Connor Hicks
  • 755
  • 2
  • 8
  • 25

1 Answers1

2

Your AVCaptureSession is going out of scope and being deallocated. That's why your delegate isn't being called. You can fix this by moving session to class scope:

class ViewController: UIViewController, AVCaptureAudioDataOutputSampleBufferDelegate {

   let session = AVCaptureSession()

   override func viewDidLoad() {

Once you have an audio CMSampleBuffer, you can copy the audio data into an NSData object like this:

let block = CMSampleBufferGetDataBuffer(sampleBuffer)
var length = 0
var data: UnsafeMutablePointer<Int8> = nil
let status = CMBlockBufferGetDataPointer(block!, 0, nil, &length, &data)    // TODO: check for errors
let result = NSData(bytes: data, length: length)

p.s. if you're careful and want to avoid copying, you can use NSData(bytesNoCopy: data, length: length, freeWhenDone: false)

Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
  • That looks like it could be the issue. I will test and get back to you! – Connor Hicks Nov 23 '15 at 05:16
  • @rythmic-fistman First part works perfectly! However, when I use the second part, I receive the error: malloc: *** error for object 0x1035662c0: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug Any idea why this would be? – Connor Hicks Nov 23 '15 at 22:09
  • @rythmic-fistman It was happening because I was trying the bytesNoCopy: version - switching back to bytes: fixed it! – Connor Hicks Nov 23 '15 at 22:27
  • My bad - `bytesNoCopy:` calls `free` when finished. You need `bytesNoCopy:length:freeWhenDone:`. Updating answer. – Rhythmic Fistman Nov 23 '15 at 22:29
  • 1
    @rythmic-fistman beautiful! Now we just have to get it to play the streaming audio on the other side! :S – Connor Hicks Nov 23 '15 at 22:45
  • @rythmic-fistman any chance you can shed some light on the next part? http://stackoverflow.com/questions/33906649/play-audio-from-avaudiopcmbuffer-with-avaudioengine - cheers! – Connor Hicks Nov 25 '15 at 00:47