【问题标题】:AVCaptureVideoDataOutput on iOS 8 does not post sample buffers on the specified dispatch queueiOS 8 上的 AVCaptureVideoDataOutput 不会在指定的调度队列上发布样本缓冲区
【发布时间】:2014-10-02 12:13:37
【问题描述】:

当使用 AVCaptureVideoDataOutput 并使用调度队列 (setSampleBufferDelegate:queue) 定义样本缓冲区委托时,我们在 iOS 8 上遇到 AVFoundation 不会将样本缓冲区发布到指定的调度队列,而是始终使用“com.apple. avfoundation.videodataoutput.bufferqueue"。

这在 iOS7 上按预期工作。

有其他人经历过吗?

一个明显的解决方法是在回调中手动调用 dispatch_sync 以将处理同步到自定义调度队列,但是奇怪的是,这会导致死锁......

产生此问题的示例代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    session.sessionPreset = AVCaptureSessionPresetMedium;

    AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
    captureVideoPreviewLayer.frame = self.view.bounds;
    [self.view.layer addSublayer:captureVideoPreviewLayer];

    [session addInput:[AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil]];

    AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];

    queue = dispatch_queue_create("our.dispatch.queue", DISPATCH_QUEUE_SERIAL);

    [output setSampleBufferDelegate:self queue:queue];

    [session addOutput:output];

    [session startRunning];
}

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    NSLog(@"Running on queue %@, queue that was set is %@, this is %s", dispatch_get_current_queue(),
      [captureOutput performSelector:@selector(sampleBufferCallbackQueue)],
      queue == dispatch_get_current_queue() ? "our queue" : "not our queue!!!");
}

【问题讨论】:

    标签: ios ios8 avfoundation grand-central-dispatch avcapturesession


    【解决方案1】:

    这里可能发生的情况是,他们的队列 @9​​87654321@ 已设置为使用 dispatch_set_target_queue 定位您的队列。这在功能上等同于分派到您的队列,但会解释名称,并且还会在您尝试分派回队列时解释死锁。

    换句话说,仅仅因为队列名称不等于您的队列名称并不意味着该块没有在您的队列上执行。

    【讨论】:

    • 是的,这也让我们想到了,但是最初发现了整个问题,因为我们遇到了多个线程访问共享资源的问题/竞争条件,而共享资源应该只是从我们的处理队列中访问。堆栈跟踪显示一个线程在我们的处理队列上运行作业,另一个运行“com.apple.avfoundation.videodataoutput.bufferqueue”。如果 avfoundation 队列是 dispatch_sync-ing 到我们的队列,这不应该发生,因为我们的队列是串行的
    • 您可以通过提前暂停队列来确定。然后,如果您最终进入了障碍,则可以确定这是一个错误。
    • 暂停“我们的”队列实际上会停止处理队列,所以看起来你是正确的,“com.apple.avfoundation.videodataoutput.bufferqueue”正在使用我们的队列作为目标队列。尽管如此,我们有两个同时运行的线程应该在同一个串行队列中排队的操作的原因(一个来自“com.apple.avfoundation.videodataoutput.bufferqueue”,另一个来自“our.dispatch.queue” ) 对我们来说是个谜。这不应该发生,否则唯一的解释是在 avfoundation 的队列处理中存在某种同步疏忽。
    • 您还可以使用dispatch_queue_set_specificdispatch_queue_get_specific 来确定定位关系。在您的串行队列上设置一个特定值,然后从两个呼叫站点查询它。如果你从两个调用站点取回你的标签值并且它们同时被调用,那么有些事情正在发生。
    • 感谢您的建议和帮助!由于这种情况不会经常发生,我们将放置一个带有当前调度队列标签的 NSLog 和我们在共享资源位置的队列上设置的“特定”,这样我们就可以看到是谁调用了它(它只能由来自的代码调用“我们的”队列)。我会将您的答案标记为已接受,因为它确实回答了我最初的问题。
    【解决方案2】:

    为了解决这个问题,我不得不修改我的-captureOutput:

    - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
    {
        dispatch_queue_t queue = ((MyAppDelegate *)UIApplication.sharedApplication.delegate).videoDataOutputQueue;
        CFRetain(sampleBuffer);
        dispatch_async(queue, ^{
    
            for (id<AVCaptureVideoDataOutputSampleBufferDelegate> target in captureTargets.copy)
                [target captureOutput:captureOutput didOutputSampleBuffer:sampleBuffer fromConnection:connection];
            CFRelease(sampleBuffer);
        });
    }
    

    【讨论】:

    • 这里的 captureTarget.copy 是什么?
    • 我有许多不同的用户使用来自相机的图像。这些“用户”存储在数组 captureTargets 中。由于数组可以在另一个队列中更改,因此我使用数组的副本循环并将相机输出发送到每个队列。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 2014-06-05
    • 2012-05-03
    • 2019-03-09
    • 1970-01-01
    • 2018-05-30
    相关资源
    最近更新 更多