【问题标题】:Playing back audio recorded with AVCaptureAudioDataOutput播放使用 AVCaptureAudioDataOutput 录制的音频
【发布时间】:2021-05-11 04:58:38
【问题描述】:

我正在尝试录制视频和音频并通过网络发送它们,以便它们可以在其他客户端上实时播放。我已成功录制和播放视频,但仍然无法播放音频(请参阅下面代码底部的 AVAudioPlayer)。我做错了什么或缺少什么? (还有几个其他 StackOverflow 问题似乎解决了同样的问题,但即使那里的 cmets 表明有些人能够使其工作,但他们都没有给出明确的、有效的答案。)提前谢谢你任何输入。

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)"
    }
}

【问题讨论】:

    标签: ios swift macos avfoundation avkit


    【解决方案1】:

    https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer

    调用者不拥有返回的 dataBuffer,如果调用者需要维护对它的引用,则必须显式保留它。

    您需要对数据执行CFRetain 和 CFRelease。

    播放器是 nil,因为您正在使用它初始化它的数据。 Convert sampleBuffer to Data.

    NSOSStatusErrorDomain Code=1954115647

    【讨论】:

    • 非常感谢您的帮助,但似乎 ARC 不存在 CFRetain。另外,由于我使用的是dataBuffer.dataBytes(),它被记录为This function is used to copy bytes out of a CMBlockBuffer,由于数据被复制,是否真的有必要在此处保留任何内容?
    • 我不熟悉 dataBytes()。我编辑了答案并包含了一个评论链接,该评论解释了如何将 sampleBuffer 转换为 Data。 @Nickkk
    • 谢谢,我试过了。现在奇怪的是,我的原始代码会产生与您的建议相同的输出,而无需更改它。我收到这些错误:[] CMIOHardware.cpp:379:CMIOObjectGetPropertyData Error: 2003332927, failed [] CMIO_Unit_Converter_Audio.cpp:590:RebuildAudioConverter AudioConverterSetProperty(dbca) failed (1886547824) [ac] ACMP3Decoder.cpp:229:SetCurrentInputFormat: (0x7fb601287440) input sample rates must be either 8, 11.025, 12, 16, 22.05, 24, 32, 44.1 or 48 kHz
    • [ac] ACMP3Decoder.cpp:229:SetCurrentInputFormat: (0x7fb601287440) input sample rates must be either 8, 11.025, 12, 16, 22.05, 24, 32, 44.1 or 48 kHz [AQ] AudioQueueObject.cpp:388:AudioQueueObject: AudioConverterNew from AudioQueueNew returned 1718449215 io: 2 ch, 12000 Hz, Float32, non-inter client: 2 ch, 12000 Hz, '.mp2' (0x00000000) 0 bits/channel, 0 bytes/packet, 1152 frames/packet, 0 bytes/frame MP4::LATMHeader::GetStreamFormatInfo Failed LOASAudioFile::ParseAudioFile failed EC3AudioFile::ParseAudioFile : ParseOneCycle failed
    • EC3AudioFile::ParseAudioFile failed [] CMIOHardware.cpp:323:CMIOObjectGetPropertyData the System is exiting [] CMIOHardware.cpp:379:CMIOObjectGetPropertyData Error: 1970171760, failed [] CMIO_DALA_System.cpp:264:GetPropertyData error 1970171760 (unop) getting property selector (inot) scope (glob) element 0
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    相关资源
    最近更新 更多