可可-相机开始记录时检测事件 [英] Cocoa - detect event when camera started recording

查看:127
本文介绍了可可-相机开始记录时检测事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的OSX应用程序中,我使用下面的代码显示来自摄像机的预览。

In my OSX application I'm using code below to show preview from camera.

  [[self session] beginConfiguration];

  NSError *error = nil;
  AVCaptureDeviceInput *newVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];

  if (captureDevice != nil) {
    [[self session] removeInput: [self videoDeviceInput]];
    if([[self session] canAddInput: newVideoDeviceInput]) {
      [[self session] addInput:newVideoDeviceInput];
      [self setVideoDeviceInput:newVideoDeviceInput];
    } else {
      DLog(@"WTF?");
    }
  }

  [[self session] commitConfiguration];

但是,我需要检测从摄像机预览开始可用的确切时间。

Yet, I need to detect the exact time when the preview from the camera becomes available.

换句话说,我试图检测与OSX下的Facetime相同的时刻,一旦相机提供预览,动画即会开始。

In other words I'm trying to detect the same moment like in Facetime under OSX, where animation starts once the camera provides the preview.

实现此目标的最佳方法是什么?

What is the best way to achieve this?

推荐答案

I知道这个问题确实很老,但是当我寻找相同的问题时,我也偶然发现了这个问题,并且找到了答案,所以就去了。

I know this question is really old, but I stumbled upon it too when I was looking for this same question, and I have found answers so here goes.

对于初学者来说,AVFoundation级别太高,您需要将级别降到CoreMediaIO。有关此内容的文档并不多,但是基本上您需要执行几次查询。

For starters, AVFoundation is too high level, you'll need to drop down to a lower level, CoreMediaIO. There's not a lot of documentation on this, but basically you need to perform a couple queries.

为此,我们将结合使用多个调用。首先, CMIOObjectGetPropertyDataSize 让我们获取下一个要查询的数据的大小,然后在调用 CMIOObjectGetPropertyData 。要设置get属性数据大小调用,我们需要使用以下属性地址从顶部开始:

To do this, we'll use a combination of calls. First, CMIOObjectGetPropertyDataSize lets us get the size of the data we'll query for next, which we can then use when we call CMIOObjectGetPropertyData. To set up the get property data size call, we need to start at the top, using this property address:

var opa = CMIOObjectPropertyAddress(
    mSelector: CMIOObjectPropertySelector(kCMIOHardwarePropertyDevices),
    mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
    mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementMaster)
)

接下来,我们将设置一些变量以保留所需的数据:

Next, we'll set up some variables to keep the data we'll need:

var (dataSize, dataUsed) = (UInt32(0), UInt32(0))
var result = CMIOObjectGetPropertyDataSize(CMIOObjectID(kCMIOObjectSystemObject), &opa, 0, nil, &dataSize)
var devices: UnsafeMutableRawPointer? = nil

从这一点开始,我们需要等待,直到获得一些数据,因此让我们忙个循环:

From this point on, we'll need to wait until we get some data out, so let's busy loop:

repeat {
    if devices != nil {
        free(devices)
        devices = nil
    }
    devices = malloc(Int(dataSize))
    result = CMIOObjectGetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &opa, 0, nil, dataSize, &dataUsed, devices);
} while result == OSStatus(kCMIOHardwareBadPropertySizeError)

执行中,设备将指向可能有许多设备。我们需要像这样循环遍历它们:

Once we get past this point in our execution, devices will point to potentially many devices. We need to loop through them, somewhat like this:

if let devices = devices {
    for offset in stride(from: 0, to: dataSize, by: MemoryLayout<CMIOObjectID>.size) {
        let current = devices.advanced(by: Int(offset)).assumingMemoryBound(to: CMIOObjectID.self)
        // current.pointee is your object ID you will want to keep track of somehow
    }
}

最后,清理个设备

free(devices)

现在,您将要使用上面保存的对象ID来创建另一个对象查询。我们需要一个新的属性地址:

Now at this point, you'll want to use that object ID you saved above to make another query. We need a new property address:

var CMIOObjectPropertyAddress(
    mSelector: CMIOObjectPropertySelector(kCMIODevicePropertyDeviceIsRunningSomewhere),
    mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeWildcard),
    mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementWildcard)
)

这告诉CoreMediaIO我们想知道设备当前是否正在某处运行(读取:在任何应用程序中),通配符其余字段。接下来,我们进入查询的实质,下面的 camera 对应于您之前保存的ID:

This tells CoreMediaIO that we want to know if the device is currently running somewhere (read: in any app), wildcarding the rest of the fields. Next we get to the meat of the query, camera below corresponds to the ID you saved before:

var (dataSize, dataUsed) = (UInt32(0), UInt32(0))
var result = CMIOObjectGetPropertyDataSize(camera, &opa, 0, nil, &dataSize)
if result == OSStatus(kCMIOHardwareNoError) {
    if let data = malloc(Int(dataSize)) {
        result = CMIOObjectGetPropertyData(camera, &opa, 0, nil, dataSize, &dataUsed, data)
        let on = data.assumingMemoryBound(to: UInt8.self)
        // on.pointee != 0 means that it's in use somewhere, 0 means not in use anywhere
    }
}

使用上述代码示例,您应该有足够的能力来测试相机是否正在使用中。您只需要获得一次设备(答案的第一部分);检查是否正在使用中,则您需要在任何时候想要此信息。作为额外的练习,请考虑使用 CMIOObjectAddPropertyListenerBlock 来通知我们上面使用的使用中属性地址的事件更改。

With the above code samples you should have enough to test whether or not the camera is in use. You only need to get the device once (the first part of the answer); the check for if it's in use however, you'll have to do at any time you want this information. As an extra exercise, consider playing with CMIOObjectAddPropertyListenerBlock to be notified on event changes for the in use property address we used above.

尽管对于OP来说,这个答案还差3年,但我希望它对将来的人有所帮助。 Swift 3.0给出了示例。

While this answer is nearly 3 years too late for the OP, I hope it helps someone in the future. Examples here are given with Swift 3.0.

这篇关于可可-相机开始记录时检测事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆