如何使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.")
}