我正在开发一种快速图像扫描应用程序
在-captureOutput:didOutputSampleBuffer:fromConnection:
方法中,我选取CVPixelBuffer并将它们添加到NSOperation
子类中。 NSOperation
获取缓冲区并将其转换为保存在文件系统上的图像
-isConcurrent
NSOperation
方法返回YES。创建操作后,将添加到NSOperationQueue
除了影响扫描帧速率的一个问题外,一切运行正常
使用时间分析器我发现一些NSOperation运行在我创建的AVCaputureVideoOutput委托的同一个线程上:
dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_SERIAL);
[dataOutput setSampleBufferDelegate:(id)self queue:queue];
当一个操作在AV会话队列的同一个线程上运行时,它会影响帧速率,它只发生在它们中,可能GCD正在决定在活动线程上调度。
唯一的方法我发现要解决这个问题,就是创建一个单独的线程并将其传递给单个操作,强制它们在它上面运行。
还有另一种方法,强制在不同的线程上运行操作吗?
的 [编辑]
按照@impcc的建议,我做了一些测试
即使有些不一致,结果也很有趣
测试平台是一个通过MBP与调试模式连接的iPhone 5,具有16GB的RAM四核i7。会话使用RosyWriter apple示例代码中的算法测试了60fps的输出。
AVSession队列同步针对高优先级队列
26 fps,有时队列的线程与该队列共享 的操作。委托方法内部的时间有一个 平均0.02s
14 fps,主要是为操作创建的线程
在此线程上没有调用方法,将强制启动执行
在那个特定的线程上。这个线程创建一次并保留
用虚拟端口活着..代表内部的时间是
0.008。
AVSession队列并发目标为高优先级队列
13.5 fps,有时队列的线程与其中一个操作共享。委托方法内部所花费的时间 平均0.02s,基本等于同步的对应物 队列中。
14 fps,主要是为操作创建的线程
在此线程上没有调用方法,将强制启动执行
在那个特定的线程上。这个线程创建一次并保留
活着的虚拟端口。代表内部的时间是
0.008点。
结论
并发或串行队列似乎没有太大的区别,但是对我而言并发是不正确的,因为我需要采取时间戳来保留单个图片的序列。让我感到惊讶的事实是使用ad-hoc线程的帧丢弃,即使委托方法内部所花费的时间相当少,帧速率也会下降大约10fps。只是信任GCD,帧速率更好,但委托方法需要2次以上才能完成,这可能是因为有时AV队列也被nsoperations使用。我真的不明白为什么代表内部的时间似乎与fps无关。不应该越快越好?
通过进一步调查,似乎在添加和可能在队列中执行操作的过程中窃取时间si是什么。
答案 0 :(得分:8)
我认为你可能会误解isConcurrent
的含义。这完全可以理解,因为它的命名非常糟糕。从YES
返回-isConcurrent
时,它的真正含义是“我将处理与此操作相关的任何并发需求,否则将异步运行。”在这种情况下,NSOperationQueue
可以在您添加操作的线程上同步调用-start
您的操作。 NSOperationQueue
期待这一点,因为您已声明将管理自己的并发性,因此您的-start
方法将启动异步进程并立即返回。我怀疑这是你问题的根源。
如果您通过覆盖-main
来实施操作,那么您几乎肯定希望从NO
返回isConcurrent
。为了使问题更复杂,与isConcurrent
相关的行为多年来发生了变化(但官方文档中涵盖了所有这些行为。)梦幻解释如何正确实施退货 - 可以找到YES
- 来自 - isConcurrent
NSOperation
here.
我在这里阅读你的问题的方式,听起来不像你的NSOperation
子类实际上需要“管理它自己的并发”,而是你只是想让它以异步方式执行,“后台线程“,可能与其他操作同时”。
在确保AVCaptureVideoDataOutputSampleBufferDelegate
回调的最佳性能方面,我建议将传递给-setSampleBufferDelegate:queue:
的队列(本身)并发,并将其作为高优先级全局并发队列的目标,像这样:
dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
[dataOutput setSampleBufferDelegate:(id)self queue:queue];
然后你应该让委托回调方法尽可能轻量级 - 只需打包制作NSOperation
所需的信息并将其添加到NSOperationQueue
。
这应确保回调始终优先于NSOperations
。 (我的理解是NSOperationQueue
定位主队列(对于与主线程和运行循环相关联的NSOperationQueue
)或默认优先级后台队列。)这应该允许您的回调完全跟上与帧率。
另一个重要的事情就是拿走(另一位评论者得到的)是,如果你使用GCD进行所有并发操作,那么只有一个特殊线程 - 主线程。除此之外,线程只是GCD可以(并且将)彼此互换使用的通用资源。事实上,一个点使用了一个线程ID为X的线程来为你的委托回调服务,而在另一个点上用来进行处理的事实本身并不表示存在问题。
希望这有帮助!