繁体   English   中英

可可-相机开始记录时检测事件

[英]Cocoa - detect event when camera started recording

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

  [[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];

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

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

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

我知道这个问题确实很老,但是当我寻找同样的问题时,我也偶然发现了这个问题,因此我找到了答案。

对于初学者来说,AVFoundation的级别太高,您需要将其降至更低的级别,即CoreMediaIO。 关于此的文档并不多,但是基本上您需要执行几个查询。

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

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

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

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

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

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)

一旦在执行过程中超过了这一点, devices将指向潜在的许多设备。 我们需要遍历它们,如下所示:

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

最后清理devices

free(devices)

现在,您将要使用上面保存的对象ID进行另一个查询。 我们需要一个新的物业地址:

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

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

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来通知事件,以通知我们上面使用的使用中的属性地址。

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

用户jer的先前答案肯定是正确的答案,但是我只想添加一个其他重要信息。

如果在CMIOObjectAddPropertyListenerBlock注册了一个侦听器块,则必须运行当前的运行循环,否则将不会接收任何事件,并且该侦听器块将永远不会触发。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM