转到后台并返回前台后无法继续从AVAssetReaderOutput读取

Dan*_*nra 11 core-audio audiounit ios avassetreader

我正在使用a AVAssetReaderOutput来读取a中的样本AVAsset,对它们进行一些处理,并使用RemoteIO AU播放结果.

问题是,在调用AudioOutputUnitStop暂停播放后,在转到后台并返回前台后,调用后音频将不会再次启动AudioOutputUnitStart.这是由于copyNextSampleBuffer方法返回的错误,该错误AVAssetReaderOutput被称为渲染管道的一部分.

调用之后的status属性是,其属性为Error Domain = AVFoundationErrorDomain Code = -11847"Operation Interrupted"UserInfo = 0x1d8b6100 {NSLocalizedRecoverySuggestion =停止其他操作并再试一次.,NSLocalizedDescription = Operation Interrupted}AVAssetReadercopyNextSampleBufferAVAssetReaderStatusFailederror

我正在寻找一种解决方案,它不会强迫我在回到前台后重新初始化整个管道 - 希望有这样一个解决方案,AVAssetReaders可以在应用程序进入后台并返回...

笔记

  • 该应用程序有权在后台播放音频.
  • 我正在处理音频中断 - AVAudioSessionAVAudioSessionDelegates endInterruptionWithFlags:事件和应用程序变为活动状态时将我设置为活动状态.无论我是否这样做都没有区别,得到同样的错误.

一些代码:

AudioPlayer

@implementation AudioPlayer
    ...
// Audio Unit Setup
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;

AudioComponent defaultOutput = AudioComponentFindNext(NULL, &desc);

AudioComponentInstanceNew(defaultOutput, &_audioUnit);

AudioStreamBasicDescription audioFormat;
    FillOutASBDForLPCM(audioFormat, 44100, 2, 16, 16, false, false);

AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat));

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = RenderCallback;
callbackStruct.inputProcRefCon = (__bridge void*)self;
AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callbackStruct, sizeof(callbackStruct));

AudioUnitInitialize(self.audioUnit);
Run Code Online (Sandbox Code Playgroud)

AudioReader设置

@implementation AudioReader
    ...
NSError* error = nil;
self.reader = [AVAssetReader assetReaderWithAsset:self.asset error:&error];
NSDictionary *outputSettings = ...
self.readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[self.asset.tracks objectAtIndex:0] outputSettings:outputSettings];
[self.reader addOutput:self.readerOutput];
[self.reader startReading];
Run Code Online (Sandbox Code Playgroud)

AudioReader渲染方法,最终由RenderCallback函数调用

-(BOOL)readChunkIntoBuffer
{
     CMSampleBufferRef sampleBuffer = [self.readerOutput copyNextSampleBuffer];
     if ( sampleBuffer == NULL )
     {
         NSLog(@"Couldn't copy next sample buffer, reader status=%d error=%@, self.reader.status, self.reader.error);
         return NO;
     }
 ...
}
Run Code Online (Sandbox Code Playgroud)

Dre*_*ara 4

图表和 AVReader 基础没有任何连接/链接。令人困惑的可能是,如果 iOS 看到音频图正在运行,它就不会“后台”(休眠)进程(因为这样就无法生成音频数据)。这就是为什么当您停止音频图时,2-3 分钟后,iOS 会将您的进程置于后台(从 iOS 9 开始)。据推测,iOS 会查看您的进程中发生的情况,并决定何时应强制您的进程进入后台(通过beginBackgroundTaskWithName:expirationHandler)。

苹果开发人员出于某种原因决定让 AVReader 在进入后台模式时停止(我最好的猜测是 QA)。好消息是,当您的进程退出后台模式时,您可以检测到它并恢复,但您将需要再次重新启动 reader 和 readerOutput。首先,当 AVAssetReaderOutputcopyNextSampleBuffer返回 NULL 时,AVAssetReader.error.code检查AVErrorOperationInterrupted.

我们在这里实现无缝恢复的方法是,当我们到达该点时,我们首先计算我们离开的确切时间(很容易做到——只需维护已输出的总样本的计数器)。然后,您将需要重新启动您的AVAssetReader流程AVAssetReaderOutput。但这没问题,因为良好的开发实践会将其封装在一个以查找时间作为参数的函数中。在该功能中,使用AVAssetReader.timeRange来查找您需要恢复的时间并立即!