在进入后台并返回前台后,无法继续从AVAssetReaderOutput读取内容。

13
我正在使用AVAssetReaderOutputAVAsset中读取样本,对其进行处理,并使用RemoteIO AU播放结果。问题在于,在调用AudioOutputUnitStop暂停播放后,然后进入后台并返回前台,调用AudioOutputUnitStart后音频将无法再次启动。这是由于在渲染管道的一部分中调用了AVAssetReaderOutputcopyNextSampleBuffer方法时返回的错误。
在调用copyNextSampleBuffer后,AVAssetReaderstatus属性为AVAssetReaderStatusFailed,其error属性为Error Domain = AVFoundationErrorDomain Code = -11847 "操作已中断" UserInfo = 0x1d8b6100 {NSLocalizedRecoverySuggestion=停止其他操作,然后重试。, NSLocalizedDescription=操作已中断} 我正在寻找一种解决方案,不会强制我在回到前台后重新初始化整个管道-希望有这样的解决方案,即AVAssetReader可以在应用程序进入后台和返回时保持存在...
注意:
- 应用程序有权在后台播放音频。 - 我正在处理音频中断-在AVAudioSessionDelegateendInterruptionWithFlags:事件和应用程序变为活动状态时,设置我的AVAudioSession作为活动会话。无论是否这样做,都会得到相同的错误。
一些代码:
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);

音频阅读器设置
@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];

音频读取器渲染方法,最终由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;
     }
 ...
}

这是用于压缩音频类型的吗?据报道,某些设备型号上的一些硬件音频解压缩单元在没有被重启的情况下无法被中断。 - hotpaw2
有趣。这是用于从用户的iTunes库中检索的MPMediaItem读取AVAsset的代码。因此,我相信mp3/m4a都是压缩的。关于“设备型号”-这是在iPhone 5上的问题。您是否有任何可用的参考资料来描述您所说的内容? - Danra
2个回答

4

图形和AVReader的基础完全没有连接/链接。混淆可能来自于iOS不会在音频图运行时“后台”(休眠)进程(否则无法生成音频数据)。这就是为什么当你停止音频图后,2-3分钟后,iOS将后台处理你的进程(截至iOS 9)。据推测,iOS查看进程中正在发生的事情,并决定何时通过beginBackgroundTaskWithName:expirationHandler强制将其转到后台。

由于某些原因,苹果开发人员决定使AVReader在被放入后台模式时停止(我最好的猜测是QA)。好消息是,您可以检测到它并在进程退出后台模式时恢复,但是您需要重新启动您的阅读器和readerOutput。首先,在AVAssetReaderOutput的copyNextSampleBuffer返回NULL时,检查AVAssetReader.error.code是否为AVErrorOperationInterrupted

我们实现无缝恢复的方法是:当我们到达那个点时,我们首先计算我们离开的确切时间(很容易做到 - 只需维护已输出的总样本计数器)。然后,您需要重新启动您的AVAssetReaderAVAssetReaderOutput流。但这没有问题,因为您的良好开发实践将其封装在一个具有搜索时间参数的单个函数中。在该函数中,使用AVAssetReader.timeRange来搜索需要恢复的时间,然后即可完成!


3

我建议您尝试的第一件事是:

不要调用 AudioOutputUnitStop?为什么不让图形继续运行,当音频暂停时,只需不调用该函数即可。

[self.readerOutput copyNextSampleBuffer]

只需将缓冲区填充为0和/或使用kAudioUnitRenderAction_OutputIsSilence的渲染操作来填充缓冲区。启动和停止图表需要时间并导致不响应的图表。我从不在我的应用程序运行时停止图表。(除非发生严重的中断)这可能会使资产阅读器保持良好状态。
但是我有一个预感,因为AVAssetReader与音频图表没有直接连接(您只需请求样本并将其放置在呈现回调提供的缓冲区中),所以问题根本不在于停止和重新启动音频图表。因此,我尝试的下一步攻击是从AVAsset请求样本,将应用程序放入后台并将其带回前台,完全不使用AUGraph。这将确定问题是否与AUGraph或应用程序进入后台状态有关。
如果发生了什么事情,迫使您拆除AVAssetReader,则必须重新连接到资产。因此,由于您必须编写该代码,我将查看您不想执行的选项。重新连接到AVAssetReader,将其定位到离开的位置并继续进行。

实际上,我现在正在暂停的操作正是你所写的,用0填充缓冲区,这样当转到后台并返回前台并重新开始播放时,播放会正常继续。这种方法的问题是状态栏中仍然会保留“播放”图标。 - Danra
那么你的意思是AVAssetReader与AUGraph有关联吗? - Aran Mulholland
不确定您所说的“linked”是什么意思?我在AU的回放回调中从中读取。 - Danra
我觉得很奇怪,如果停止AUGraph,当你重新启动图时,AVAssetReader不允许你读取样本。你设置的方式意味着这两个应该是独立的。即使您不请求样本,保持图形运行,AVAssetReader仍然保持活动状态,但是如果停止图形,则会死机。有没有可能AVAssetReader的类被ARC垃圾回收了? - Aran Mulholland
机会很小,但如果将ioActionFlags设置为kAudioUnitRenderAction_OutputIsSilence,您是否仍然会得到播放指示? - Aran Mulholland
资产阅读器没有被ARC垃圾回收。此外,按照您建议的设置ioActionFlags仍然会在状态栏中显示播放指示器。我同意,这很奇怪!我认为这与苹果管理播放音频所需资源有关,当您进入后台而没有音频播放时,iOS可能不会保留一些继续播放所需的资源,以便在返回前台后继续播放。 - Danra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接