在我的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一样检测同一时刻,一旦相机提供预览,动画就会启动。
实现这一目标的最佳方法是什么?
答案 0 :(得分:3)
我知道这个问题真的很老了,但当我在寻找同样的问题时,我偶然发现了这个问题,而且我已经找到了答案,所以这里也是如此。
对于初学者来说,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。
答案 1 :(得分:1)
用户jer之前的回答肯定是正确的答案,但我只是想添加一个额外的重要信息。
如果一个监听器块注册了CMIOObjectAddPropertyListenerBlock
,则必须运行当前的运行循环,否则不会收到任何事件,并且监听器块永远不会触发。