[英]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.