I'm trying to record video and audio and sending them over the network so that they can be played back in real time on other clients. I've managed to record and play back video successfully, but audio still cannot be played back (see AVAudioPlayer at the bottom of the code below). What am I doing wrong or what is missing? (There are a couple of other StackOverflow questions which seem to address the same issue, but even if the comments there show that some people were able to make it work, none of them show an explicit, working answer.) Thank you in advance for any input.
let captureSession = AVCaptureSession()
private func startVideoFeed() {
let sessionPreset = AVCaptureSession.Preset.low
if captureSession.canSetSessionPreset(sessionPreset) {
captureSession.sessionPreset = sessionPreset
}
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { success in
self.startVideoFeed()
}
case .authorized:
captureSession.beginConfiguration()
let captureVideoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)!
let captureVideoInput = try! AVCaptureDeviceInput(device: captureVideoDevice)
if captureSession.canAddInput(captureVideoInput) {
captureSession.addInput(captureVideoInput)
}
let captureVideoOutput = AVCaptureVideoDataOutput()
captureVideoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
if captureSession.canAddOutput(captureVideoOutput) {
captureSession.addOutput(captureVideoOutput)
}
captureSession.commitConfiguration()
captureSession.startRunning()
default:
break
}
}
private func startAudioFeed() {
switch AVCaptureDevice.authorizationStatus(for: .audio) {
case .notDetermined:
AVCaptureDevice.requestAccess(for: .audio) { success in
self.startAudioFeed()
}
case .authorized:
captureSession.beginConfiguration()
let captureAudioDevice = AVCaptureDevice.default(for: .audio)!
let captureAudioInput = try! AVCaptureDeviceInput(device: captureAudioDevice)
if captureSession.canAddInput(captureAudioInput) {
captureSession.addInput(captureAudioInput)
}
let captureAudioOutput = AVCaptureAudioDataOutput()
captureAudioOutput.audioSettings = [AVFormatIDKey: kAudioFormatLinearPCM, AVNumberOfChannelsKey: NSNumber(value: 1), AVSampleRateKey: NSNumber(value: 44100)]
captureAudioOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
if captureSession.canAddOutput(captureAudioOutput) {
captureSession.addOutput(captureAudioOutput)
}
captureSession.commitConfiguration()
default:
break
}
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
if let imageBuffer = sampleBuffer.imageBuffer {
let ciImage = CIImage(cvPixelBuffer: imageBuffer)
let cgImage = CIContext().createCGImage(ciImage, from: ciImage.extent)!
let data = CFDataCreateMutable(nil, 0)!
let imageDestination = CGImageDestinationCreateWithData(data, kUTTypeJPEG, 1, nil)!
CGImageDestinationAddImage(imageDestination, cgImage, [kCGImageDestinationLossyCompressionQuality: NSNumber(value: 0)] as CFDictionary)
CGImageDestinationFinalize(imageDestination)
play(data: data as Data)
} else if let dataBuffer = sampleBuffer.dataBuffer {
let data = try! dataBuffer.dataBytes()
play(data: data)
}
}
private func play(data: Data) {
if let image = CGImage(jpegDataProviderSource: CGDataProvider(data: data as CFData)!, decode: nil, shouldInterpolate: false, intent: .defaultIntent) {
// image is a valid image
} else if let audioPlayer = try? AVAudioPlayer(data: data) {
audioPlayer.play()
// audioPlayer is always nil with error: Error Domain=NSOSStatusErrorDomain Code=1954115647 "(null)"
}
}