简体   繁体   English

更改AVAudioSession后,AVCaptureSession没有音频

[英]No audio from AVCaptureSession after changing AVAudioSession

I'm making an app that supports both video playback and recording. 我正在制作一个支持视频播放和录制的应用。 I always want to allow background audio mixing except for during video playback (during playback, background audio should be muted). 我总是希望允许背景音频混合,除了在视频播放期间(在播放期间,背景音频应该静音)。 Therefore, I use the two methods below while changing the state of playback. 因此,我在改变播放状态时使用以下两种方法。 When my AVPlayer starts loading, I call MuteBackgroundAudio , and when I dismiss the view controller containing it, I call ResumeBackgroundAudio . 当我的AVPlayer开始加载时,我调用MuteBackgroundAudio ,当我关闭包含它的视图控制器时,我调用ResumeBackgroundAudio This works as expected, and the audio returns successfully after leaving playback. 这按预期工作,音频在离开播放后成功返回。

The issue is that after doing this at least once, whenever I record anything using AVCaptureSession , no sounds gets recorded. 问题是,在执行此操作至少一次后,每当我使用AVCaptureSession录制任何内容时,都不会录制任何声音。 My session is configured like so: 我的会话配置如下:

AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];

if (error)
{
    NSLog(@"%@", error);
}

if ([self.session canAddInput:audioDeviceInput])
{
    [self.session addInput:audioDeviceInput];
}

[self.session setAutomaticallyConfiguresApplicationAudioSession:NO];

// ... videoDeviceInput

Note that I have not set usesApplicationAudioSession , so it defaults to YES . 请注意,我没有设置usesApplicationAudioSession ,因此它默认为YES

void MuteBackgroundAudio(void)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        if ([[AVAudioSession sharedInstance] isOtherAudioPlaying] && !isMuted)
        {
            isMuted = YES;

            NSError *error = nil;
            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord
                                             withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker
                                                   error:&error];
            if (error)
            {
                NSLog(@"DEBUG - Set category error %ld, %@", (long)error.code, error.localizedDescription);
            }

            NSError *error2 = nil;
            [[AVAudioSession sharedInstance] setActive:YES
                                           withOptions:0
                                                 error:&error2];
            if (error2)
            {
                NSLog(@"DEBUG - Set active error 2 %ld, %@", (long)error.code, error.localizedDescription);
            }
        }
    });
}

void ResumeBackgroundAudio(void)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        if (isMuted)
        {
            AVAudioSession *audioSession = [AVAudioSession sharedInstance];

            NSError *deactivationError = nil;
            [audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&deactivationError];

            if (deactivationError)
            {
                NSLog(@"DEBUG - Failed at deactivating audio session, retrying...");
                ResumeBackgroundAudio();
                return;
            }

            isMuted = NO;
            NSLog(@"DEBUG - Audio session deactivated");

            NSError *categoryError = nil;
            [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
                          withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers
                                error:&categoryError];

            if (categoryError)
            {
                NSLog(@"DEBUG - Failed at setting category");
                return;
            }

            NSLog(@"DEBUG - Audio session category set to mix with others");

            NSError *activationError = nil;
            [audioSession setActive:YES error:&activationError];

            if (activationError)
            {
                NSLog(@"DEBUG - Failed at activating audio session");
                return;
            }

            NSLog(@"DEBUG - Audio session activated");
        }
    });
}

Debugging 调试

I have noticed that the audioSession always needs two tries to successfully deactivate after calling ResumeBackgroundAudio . 我注意到在调用ResumeBackgroundAudio之后,audioSession总是需要两次尝试才能成功停用。 It seems my AVPlayer does not get deallocated or stopped in time, due to this comment in AVAudioSession.h : 由于AVAudioSession.h这个注释,我的AVPlayer似乎没有及时解除分配或停止:

Note that this method will throw an exception in apps linked on or after iOS 8 if the session is set inactive while it has running or paused I/O (eg audio queues, players, recorders, converters, remote I/Os, etc.). 请注意,如果会话在运行或暂停I / O(例如音频队列,播放器,录像机,转换器,远程I / O等)时设置为非活动状态,则此方法将在iOS 8上或之后链接的应用中引发异常。 。

The fact that no sound gets recorded bring me to believe the audioSession does not actually get activated, but my logging says it does (always in the second iteration of the recursion). 没有声音被记录的事实让我相信audioSession实际上没有被激活,但是我的日志记录说它确实(总是在递归的第二次迭代中)。

I got the idea of using recursion to solve this problem from this post . 我从这篇文章中得到了使用递归来解决这个问题的想法。

To clarify, the flow that causes the problem is the following: 澄清一下,导致问题的流程如下:

  1. Open app with Spotify playing 使用Spotify播放打开应用程序
  2. Begin playback of any content in the app 开始播放应用中的任何内容
  3. Spotify gets muted, playback begins ( MuteBackgroundAudio ) Spotify MuteBackgroundAudio静音,播放开始( MuteBackgroundAudio
  4. Playback ends, Spotify starts playing again ( ResumeBackgroundAudio ) 播放结束,Spotify再次开始播放( ResumeBackgroundAudio
  5. Start recording 开始录制
  6. Stop recording, get mad that there is no audio 停止录音,生气,没有音频

I've had the exact same issue as you're describing, down to the very last detail ( [audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&deactivationError]; failed the first time no matter what) 我和你描述的问题完全相同,直到最后一个细节( [audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&deactivationError];第一次失败,无论如何)

The only acceptable way, for my case, was to stop then start the AVCaptureSession. 对于我的情况,唯一可接受的方法是停止然后启动AVCaptureSession。 I'm not entirely sure but I think this is the same exact way Whatsapp handles it - their camera behaves exactly like mine with the solution I'm suggesting. 我不完全确定,但我认为这与Whatsapp处理它的方式完全相同 - 他们的相机与我建议的解决方案完全一样。

Removing and adding the same audio input on the already running session also seemed to work but I got a terrible camera freeze - nothing compared to the session start / stop. 在已经运行的会话中删除和添加相同的音频输入似乎也可以工作,但我的相机冻结很可怕 - 与会话开始/停止相比没什么。

The recursion solution is nice, but I think it would be a great idea to 'throttle' that recursion call (add a short delay & a max retry count of some sort) in case for a legit reason the set fails every time. 递归解决方案很不错,但我认为“限制”递归调用(添加一个短暂的延迟和某种类型的最大重试计数)是一个好主意,以防由于合法的原因每次都会失败。 Otherwise your stack will overflow and your app will crash. 否则你的堆栈将溢出,你的应用程序将崩溃。

If you or anyone found a better solution to this I would love to know it. 如果你或任何人找到了更好的解决方案,我很想知道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM