1

我正在研究一个应用程序,该应用程序捕获来自摄像机的帧,并混合由另一个线程更新的图像。我的问题是,每500ms调用一次的图像创建线程会消耗影响其他线程的峰值计算能力。GCD设置调度/线程之间的优先级

这里是一些日志说明:

captureOutput(): used time: 0.027741014957428 , frame rate: 36.0477077545513 
captureOutput(): used time: 0.0285720229148865 , frame rate: 34.9992719444091 
captureOutput(): used time: 0.0310209989547729 , frame rate: 32.2362281581567 
captureOutput(): used time: 0.0268059968948364 , frame rate: 37.3050852733863 
captureOutput(): used time: 0.0263729691505432 , frame rate: 37.9176115624965 
motionCallback(): starting drawing: 
captureOutput(): used time: 0.0376390218734741 , frame rate: 26.5681718127947 
motionCallback(): elapesed time: 0.0835540294647217 
captureOutput(): used time: 0.0581380128860474 , frame rate: 17.2004502795793 
captureOutput(): used time: 0.0364410281181335 , frame rate: 27.4415967836645 
captureOutput(): used time: 0.0278580188751221 , frame rate: 35.8963070734734 
captureOutput(): used time: 0.0283130407333374 , frame rate: 35.3194137435949 
captureOutput(): used time: 0.0271909832954407 , frame rate: 36.7768972947616 
captureOutput(): used time: 0.0268760323524475 , frame rate: 37.2078730552999 

正如你可以看到captureOutput拥有超过30 fps的帧速率。只要motionCallback创建图像,帧速率就会下降,然后再次上升。请注意,motionCallback()仅在每个第15个捕获帧中调用,因此平均计算能力应该足够了。

captureOutput()是在这样创造了一个队列运行:

required init?(coder aDecoder: NSCoder) { 
    super.init(coder: aDecoder) 
    self.m_captureSessionQueue = DispatchQueue(label: "CaptureSessionOutputQueue", attributes: DispatchQueueAttributes.serial) 
} 

对我设置AVCapture后来是这样的:

videoDataOutput.setSampleBufferDelegate(self, queue: self.m_captureSessionQueue) 

motionCallback()设置这样的:

let interval = 0.4 
if m_manager.isDeviceMotionAvailable { 
    m_manager.showsDeviceMovementDisplay = true 
    m_manager.deviceMotionUpdateInterval = interval 
    // XArbitraryCorrectedZVertical 
    m_manager.startDeviceMotionUpdates(using: CMAttitudeReferenceFrame.xArbitraryCorrectedZVertical , to: OperationQueue.main, withHandler: motionCallback) 
    print("viewDidLoad(): CM initialized !") 
} 

及更高版本:

func motionCallback(_ motion: CMDeviceMotion?, error: NSError?) -> Void { 
    guard let mot = motion else {return} 
    print("motionCallback(): starting drawing: ") 

    let start = NSDate() // <<<<<<<<<< Start time 
    self.m_instrview?.update(attitude: mot.attitude) 
    self.m_overlayImage = CIImage(image: self.m_instrview!.generateImage()!) 

    let end = NSDate() // <<<<<<<<<< end time 
    let timeInterval: Double = end.timeIntervalSince(start as Date) 
    print("motionCallback(): elapesed time: ", timeInterval) 
} 

和generateImage()这样的:

func generateImage() -> UIImage { 
    UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main().scale) 
    drawHierarchy(in: self.bounds, afterScreenUpdates: true) 
    let image = UIGraphicsGetImageFromCurrentImageContext() 
    UIGraphicsEndImageContext() 
    return image! 
} 

的想法是创建每次CoreMotion系统让我更新,我会生成一个新的形象。

我的问题是我如何平均motionCallback()的计算时间,以便它不会消耗导致帧速率下降的峰值CPU功率?所以我会接受它运行10倍,但在此期间只消耗CPU的十分之一。

任何想法如何可以控制?

感谢 克里斯

+0

您的动作回调位于主队列中,其中包括对“generateImage”的调用,这似乎是CPU密集型的功能。请注意,如果您将其分派到后台队列中,您仍然必须在主队列上执行UI更新。 – Paulw11

+0

是的,这正是我的问题,必须在主线程上调用CPU密集型功能。因此,如果我不能对motionCallBack进行优先级排序,那么优先捕获线程的优先顺序是什么?我怎样才能做到这一点 ? – Chris

+0

为什么说cpu密集函数必须在主线程上调用? generateImage是cpu密集型的,但可以在后台线程上运行。一旦完成,在主线程上分派CIImage的分配。使用低优先级线程处理核心运动事件 – Paulw11

回答

1

比,做你的帧捕捉了一个较低的优先级创建一个异步队列(使用dispatch_queue_create)。在这个较低优先级队列上运行您的motionCallback。

您的motionCapture方法在主队列上运行,主队列在主线程上运行其任务。不要这样做。

此行是错误的:

m_manager.startDeviceMotionUpdates(
    using: CMAttitudeReferenceFrame.xArbitraryCorrectedZVertical , 
    to: OperationQueue.main, 
    withHandler: motionCallback) 

更改到:参数。

您需要创建一个新的串行队列,其运行优先级低于您创建的CaptureSessionOutputQueue,并将其传递给上面的to:参数。

我刚开始学习Swift 3,所以我没有创建调度队列的新代码的语法。

+0

我已经做的是将该代码DispatchQueue.global(属性:.qosBackground).async {...},但这会导致竞争图形用户界面的冻结。在主线程中是否必须调用drawHierarchy? – Chris

+2

因此,编辑您的问题以包含您的队列设置的详细信息以及两个队列正在运行的代码的相关部分。 –

+0

问题已相应更新 – Chris