CFReadStream - 在后台启动

时间:2012-01-23 02:26:30

标签: iphone ios core-audio core-foundation

有一个音频应用程序通过网络传输文件,一切正常,但有一件事。为了在后台自动播放下一首曲目,在调用AudioQueueStop之后初始化CFReadStream(我可以在日志中看到它),但是从不调用回调(编辑:实际上被调用一次),直到应用程序进入前台。流init的代码片段:

    //also tried main runloop just for test, no luck 
CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);

有线的是,在队列停止后,应用程序正常运行,正在初始化流,但只有在前台模式下初始化流时才正确调用回调。这是一段回调代码:

    CFReadStreamSetClient(stream,
                      kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
                      MyReadStreamCallBack,
                      &context);

另一方面,当应用程序在后台并且不是自动触发下一曲目而是使用app-delegate(具有相同功能)时,将调用回调。

我不完全理解这三种情况之间的区别,请帮忙。

编辑。

OSStatus status = AudioFileStreamOpen(self, MyAudioListener ...

调用MyAudioListener回调,而MyReadStreamCallBack只调用

编辑2

ReadStream回调通常甚至不被调用一次,一次是我能够看到的最大值。

另一方面,这让我误解了正在发生的事情,在之前的AudioQueue停止并且下一首曲目是本地文件之后,那么打开另一个AudioQueue ,它使用AudioFileReadPackets读取文件,我不必从后台唤醒应用程序以开始下一首曲目播放,因为它在后台播放。< / p>

2 个答案:

答案 0 :(得分:1)

在适当的背景模式下,音频队列将继续在后台运行。但一旦停止,似乎音频队列将无法在后台运行。一个回调可能只是填充缓冲区,因此队列可以在被带到前台后立即启动。

我发现的唯一解决方案是在后台不停止上一个音频队列,但以某种方式将新的音频数据提供给左侧运行的旧音频队列,而不会在其间中断任何回调。

答案 1 :(得分:0)

当前的解决方案是添加一个后台作业,等待有数据开始一个新队列(开始队列没有数据给出-50错误,我相信挂起一个hw解码器)。

让我们说在流媒体播放功能中有openNetworkStream函数,如下所示:

// ...whatever we need to be happy 


// start the background job
UIDevice* device = [UIDevice currentDevice];
BOOL isBackgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
    isBackgroundSupported = device.multitaskingSupported;

if(isBackgroundSupported && waitForQueueToStartBackgroundTask == UIBackgroundTaskInvalid) {
    waitForQueueToStartbackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:waitForQueueToStartBackgroundTask];
        waitForQueueToStartBackgroundTask = UIBackgroundTaskInvalid;
    }];
}

// Open the stream
if (!CFReadStreamOpen(stream)) {

你应该在某个地方有一个AudioQueueStart调用,对我来说,StreamEnqueueBuffer函数用于处理下载的数据片段,因为缓冲区已经准备好播放或者文件结束了(AudioQueueEnqueueBuffer),这个函数也是我们需要填充缓冲区以启动它,因此是AudioQueue的初始启动程序。我的解决方案是在队列启动时结束已初始化的启动后台作业:

OSStatus StreamEnqueueBuffer(AudioNetworkStreamer* aStreamer)

// wahtever you need to be happy with a filled buffer

if (!aStreamer->didStart) {     // start the queue if it has not been started already
    aStreamer->isBuffering = NO;
    [aStreamer startQueue];

    UIDevice* device = [UIDevice currentDevice];
    BOOL isBackgroundSupported = NO;
    if ([device respondsToSelector:@selector(isMultitaskingSupported)])
        isBackgroundSupported = device.multitaskingSupported;

    if(isBackgroundSupported && aStreamer->waitForQueueToStartBackgroundTask == UIBackgroundTaskInvalid) {
        [[UIApplication sharedApplication] endBackgroundTask:aStreamer->waitForQueueToStartBackgroundTask];
        aStreamer->waitForQueueToStartBackgroundTask = UIBackgroundTaskInvalid;
    }
}

    // nandle mutexes, flags ...