如何使AVCaptureSession和AVPlayer尊重AvaudioSessionCategoryBient?

如何使AVCaptureSession和AVPlayer尊重AvaudioSessionCategoryBient?,ios,swift,avfoundation,avplayer,avaudiosession,Ios,Swift,Avfoundation,Avplayer,Avaudiosession,我正在制作一个可以录制(AVCaptureSession)和播放(AVPlayerLayer)视频的应用程序。我希望能够做到这一点,而不暂停其他应用程序的背景音频,我希望播放尊重静音开关 在AppDelegate中,我设置了AvaudioSessionCategoryByent,根据文档,这应该: 应用程序的类别,其中声音播放是非主要的,也就是说,您的应用程序可以在关闭声音的情况下成功使用 该类别也适用于“随行播放”风格的应用程序,例如用户在音乐应用程序播放时播放的虚拟钢琴。当您使用此类别时,来

我正在制作一个可以录制(
AVCaptureSession
)和播放(
AVPlayerLayer
)视频的应用程序。我希望能够做到这一点,而不暂停其他应用程序的背景音频,我希望播放尊重静音开关

在AppDelegate中,我设置了
AvaudioSessionCategoryByent
,根据文档,这应该:

应用程序的类别,其中声音播放是非主要的,也就是说,您的应用程序可以在关闭声音的情况下成功使用

该类别也适用于“随行播放”风格的应用程序,例如用户在音乐应用程序播放时播放的虚拟钢琴。当您使用此类别时,来自其他应用程序的音频将与您的音频混合。您的音频通过屏幕锁定和静音开关(iPhone上称为铃声/静音开关)静音

这完美地描述了我正在寻找的行为。但它不起作用

我知道它已设置,因为如果我尝试在任何视图控制器中打印(AVAudioSession.sharedInstance().category),它将返回
avaudioSessionCategoryByent

有什么想法吗?我正在使用Swift,但即使是一个模糊的方向也会很感激。

如何将背景音频与AVCapture会话混合使用: 如果您有麦克风输入,默认情况下,AVCapture会话会将您的应用程序AVAudioSession设置为
AVAudioSessionCategoryPlayAndRecord
。你必须告诉它不要:

AVCaptureSession.automaticallyConfiguresApplicationAudioSession = false
然而,这样做只是冻结了应用程序。因为不幸的是,
AVAudioSessionCategoryAmbient
不能与
AVCaptureSession
一起使用

解决方案是使用选项将应用程序
AVAudioSession
设置为
AVAudioSessionCategoryPlayAndRecord

do {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: [.MixWithOthers, .AllowBluetooth, .DefaultToSpeaker])
    try AVAudioSession.sharedInstance().setActive(true)

} catch let error as NSError {
    print(error)
}
.MixWithOthers
是最重要的一种。可以播放来自其他应用程序的音频。但它把它换成了从耳机里出来,这真是太奇怪了(起初我还以为它被躲开了)。因此,
.DefaultToSpeaker
将其移动到底部扬声器,并且
.AllowBluetooth
允许您保持蓝牙音频从耳机中传出,但也可以启用蓝牙麦克风。不确定这是否可以改进,但它们似乎是所有相关的选项

如何在播放时尊重静音开关: 在录制过程中,您将
AVAudioSession
设置为
AVAudioSessionCategoryPlayAndRecord
,但这与静音开关无关

因为当有麦克风输入时,您无法设置
AVAudioSessionCategoryAmbient
。诀窍是从
AVCaptureSession
中取出麦克风,然后将
AVAudioSession
设置为
AVAudioSessionCategoryAmbient

do {
    captureSession.removeInput(micInput)
    try AVAudioSession.sharedInstance().setActive(false)
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
    try AVAudioSession.sharedInstance().setActive(true)
} catch let error as NSError { print(error) }
完成播放并需要返回录音后,需要再次设置
AVAudioSessionCategoryPlayAndRecord
(再次使用选项以继续播放背景音频):


do
块中的第一行是让我花了很长时间才理解的东西。我不需要将音频会话设置为inactive以切换到
AVAudioSessionCategoryAmbient
,但当返回到
AVAudioSessionCategoryPlayAndRecord
编辑时,它暂停了背景音频:这可能仅适用于使用AVAssetWriter使用示例缓冲区录制视频的人,这是顺序直接操作和渲染相机的逐帧输出时更易于管理的大小。

我为此挣扎了一段时间,因为我正在构建一个复杂的应用程序,它必须在(1)播放视频(使用其他应用程序的音频)和(2)播放和录制视频(使用其他应用程序的音频)之间进行委派。在我的上下文中,如果有一个AVPlayer对象正在播放视频,以及一个AVCaptureSession正在从输入设备录制视频,则在播放视频之前必须添加以下内容:

接下来,要录制有/没有来自其他应用程序的音频的视频,或使用音频播放视频的AVPlayer对象,您必须执行以下操作:

准备音频 使用AVCaptureSession设置音频设备输入/输出 当这些方法被分解时,它们基本上如下所示: 安装AVAssetWriter 准备好所有这些之后,您需要设置并配置AVAssetWriter,以开始将视频数据写入文件

删除音频输入和输出
确保在完成视频/音频数据处理后,将AVAssetWriter的视频和音频输入标记为已完成。

简而言之,当麦克风处于活动状态时,似乎无法保持
AVAudioSessionCategoryPlayAndRecord
模式。要更正我上面的评论,当麦克风处于活动状态时,您不能使用
AVAudioSessionCategoryAmbient
模式。5s有任何问题吗?我们的代码与您在这里发布的代码非常相似,它在6s和4s上正常工作,但当我们在5s上运行时,我们的捕获屏幕冻结,无法正常工作。只有当我们在一个有AVPlayer的屏幕上导航到捕获屏幕时,问题才会出现。当我使用此代码时,它会将AVCaptureSession冻结约1-2秒。有什么解决方法吗?请告诉我如何处理这种情况,例如如果音乐正在其他应用程序中播放,而不是我的录音机应接受该音乐,如果当前没有播放音乐,则我的录音机应启用麦克风进行录音。短暂性脑缺血发作
do {
    try AVAudioSession.sharedInstance().setActive(false)
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: [.MixWithOthers, .AllowBluetooth, .DefaultToSpeaker])
    try AVAudioSession.sharedInstance().setActive(true)
} catch let error as NSError { print(error) }

captureSession.automaticallyConfiguresApplicationAudioSession = false
captureSession.addInput(micInput!)
do {
        // MARK: - AVAudioSession
        try AVAudioSession.sharedInstance().setActive(false, options: [])
        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
        try AVAudioSession.sharedInstance().setActive(true, options: [])
    } catch let error {
        print("\(#file)/\(#line) - Error setting the AVAudioSession for mixing audio play back: \(error.localizedDescription as Any).")
    }
// Prepare the audio session to allow simultaneous audio-playback while recording video
    do {
        // MARK: - AVAudioSession
        try AVAudioSession.sharedInstance().setActive(false, options: [.notifyOthersOnDeactivation])
    } catch let error {
        print("\(#file)/\(#line) - Error deactivating AVAudioSession: \(error.localizedDescription as Any).")
    }
    do {
        // MARK: - AVAudioSession
        try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.mixWithOthers, .defaultToSpeaker, .allowBluetoothA2DP])
    } catch let error {
        print("\(#file)/\(#line) - Error setting the AVAudioSession category: \(error.localizedDescription as Any).")
    }
    do {
        // MARK: - AVAudioSession
        try AVAudioSession.sharedInstance().setActive(true, options: [])
    } catch let error {
        print("\(#file)/\(#line) - Error activating the AVAudioSession: \(error.localizedDescription as Any).")
    }
        // Setup the audio device input
    setupAudioDeviceInput { (error: NGError?) in
        if error == nil {
            print("\(#file)/\(#line) - Successfully added audio-device input.")
        } else {
            print("\(#file)/\(#line) - Error: \(error?.localizedDescription as Any)")
        }
    }

    // Setup the audio data output
    setupAudioDataOutput { (error: NGError?) in
        if error == nil {
            print("\(#file)/\(#line) - Successfully added audio-data output.")
        } else {
            print("\(#file)/\(#line) - Error: \(error?.localizedDescription as Any)")
        }
    }
/// (1) Initializes the AVCaptureDevice for audio, (2) creates the associated AVCaptureDeviceInput for audio, and (3) adds the audio device input to the AVCaptureSession.
/// - Parameter error: An optional NGError object returned if the setup for the audio device input fails.
func setupAudioDeviceInput(completionHandler: @escaping (_ error: NGError?) -> ()) {
    // Setup the AVCaptureDevice for audio input
    self.audioCaptureDevice = AVCaptureDevice.default(for: .audio)

    // Unwrap the AVCaptureDevice for audio input
    guard audioCaptureDevice != nil else {
        print("\(#file)/\(#line) - Couldn't unwrap the AVCaptureDevice for audio.")
        return
    }

    do {
        /// Create the AVCaptureDeviceInput for the AVCaptureDevice for audio
        self.audioCaptureDeviceInput = try AVCaptureDeviceInput(device: audioCaptureDevice)

        // Add the AVCaptureDeviceInput for the audio
        if self.captureSession.canAddInput(self.audioCaptureDeviceInput) {
            self.captureSession.addInput(self.audioCaptureDeviceInput)

            // Pass the values in the completion handler
            completionHandler(nil)

        } else {
            // MARK: - NGError
            let error = NGError(message: "\(#file)/\(#line) - Couldn't add the AVCaptureDeviceInput to the capture session.")
            // Pass the values in the completion handler
            completionHandler(error)
        }

    } catch let error {
        // MARK: - NGError
        let error = NGError(message: "\(#file)/\(#line) - Error setting up audio input for the capture session \(error.localizedDescription as Any)")
        // Pass the values in the completion handler
        completionHandler(error)
    }
}

/// (1) Initializes the AVCaptureAudioDataOutput, (2) sets its AVCaptureAudioDataOutputSampleBufferDelegate and adds it to the AVCaptureSession.
/// - Parameter error: An optional NGError object returned if the setup fails.
func setupAudioDataOutput(completionHandler: @escaping (_ error: NGError?) -> ()) {
    // Setup the AVCaptureAudioDataOutput
    self.audioDataOutput = AVCaptureAudioDataOutput()

    // Determine if the AVCaptureSession can add the audio data output
    if self.captureSession.canAddOutput(self.audioDataOutput) {
        // Setup the AVCaptureAudioDataOutput's AVCaptureAudioDataOutputSampleBufferDelegate and add it to the AVCaptureSession
        self.audioDataOutput.setSampleBufferDelegate(self, queue: self.outputQueue)
        self.captureSession.addOutput(self.audioDataOutput)

        // Pass the values to the completion handler
        completionHandler(nil)
    } else {
        // MARK: - NGError
        let error = NGError(message: "\(#file)/\(#line) - Couldn't add the AVCaptureAudioDataOutput to the AVCaptureSession.")
        // Pass the values to the completion handler
        completionHandler(error)
    }
}
        // Remove the audio device input and its audio data output.
    if let audioInput = audioCaptureDeviceInput, let audioOutput = audioDataOutput {
        captureSession.removeInput(audioInput)
        captureSession.removeOutput(audioOutput)
    } else {
        print("\(#file)/\(#line) - Couldn't remove the AVCaptureDeviceInput for audio and the AVCaptureAudioDataOutput.")
    }