Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/43.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Iphone CFReadStream-在后台启动_Iphone_Ios_Core Audio_Core Foundation - Fatal编程技术网

Iphone CFReadStream-在后台启动

Iphone CFReadStream-在后台启动,iphone,ios,core-audio,core-foundation,Iphone,Ios,Core Audio,Core Foundation,有一个音频应用程序可以通过网络传输文件,除了一件事,一切都很好。为了在后台自动播放下一首曲目,在调用AudioQueueStop后,CFReadStream将被初始化(我可以在日志中看到它),但在应用程序进入前台之前,不会调用回调(编辑:实际上调用一次)。stream init的一段代码: //also tried main runloop just for test, no luck CFReadStreamScheduleWithRunLoop(stream, CFRunLoopG

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

    //also tried main runloop just for test, no luck 
CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
连线的事情是,在队列停止后,应用程序可以正常工作,流正在初始化,但只有在流在前台模式下初始化时,才能正确调用回调。下面是一段回调代码:

    CFReadStreamSetClient(stream,
                      kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
                      MyReadStreamCallBack,
                      &context);
另一方面,当应用程序在后台时调用回调,并且下一个曲目不是自动触发的,而是通过app委托(使用相同的函数)触发的

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

编辑

正在调用MyAudioListener回调,而MyReadStreamCallBack只调用一次

编辑2

ReadStream回调通常不会被调用一次,一次是我所能看到的最大次数


另一方面,这让我对正在发生的事情产生误解,前一个音频队列停止后,下一个曲目是本地文件,然后打开另一个音频队列,它使用AudioFileReadPackets读取文件,我不必从后台唤醒应用程序来开始下一个曲目播放,因为它在后台播放自己。

如果采用适当的后台模式,音频队列将继续在后台运行。但一旦停止,音频队列似乎不会在后台开始运行。一个回调可能只是为了初始化缓冲区,这样队列在被带到前台后就可以立即启动


我找到的唯一解决方案是,在后台时不停止以前的音频队列,而是以某种方式将新音频数据提供给保持运行的旧音频队列,而不会在其间发生任何回调。

当前的解决方案是添加一个后台作业,以等待有数据启动新队列(不带数据的启动队列给出-50错误,我相信挂起了一个硬件解码器)

让我们假设在streamer Play函数中有一个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调用,对我来说,它是一个StreamQueueBuffer函数,用于在缓冲区准备播放或到达文件末尾时处理下载的数据片段(AudioQueuenQueueBuffer),此函数也是AudioQueue的初始启动程序,因为我们需要填充缓冲区来启动它。我的解决方案是在队列启动时在此处结束初始化的a started后台作业:

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 ...

这是非常有用的,任何关于AudioQueue为什么在后台启动和工作的注释,尽管下一个曲目是本地存储的,并且使用了另一个拖缆(它不等待网络回调,而是使用递归的AudioFileReadPackets)?只是猜测,但如果下一首曲目的音频数据在AQ真正完全停止之前立即可用(而不是被要求停止),它并没有真正停止。非常有帮助,最后我添加了一个后台任务,让网络拖缆等待回调收到足够的数据来填充其中一个缓冲区并启动AudioQueue(它还没有真正启动-AudioQueueStart没有被调用,因为没有数据可播放)。每个感兴趣的人都将遵循代码方案。
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 ...