Is there a way to tell using device / stream properties if a camera extension is being actively used by a client?
CMIOExtension Camera Extension query if being used
I use a custom property for this:
let CMIOExtensionPropertyCustomPropertyData_just: CMIOExtensionProperty = CMIOExtensionProperty(rawValue: "4cc_just_glob_0000")
In you cameraDeviceSource class:
func myStreamingCounter() -> String {
return "sc=\(_streamingCounter)"
}
In your cameraStreamSource class:
public var just: String = "toto"
func streamProperties(forProperties properties: Set<CMIOExtensionProperty>) throws -> CMIOExtensionStreamProperties {
let streamProperties = CMIOExtensionStreamProperties(dictionary: [:])
if properties.contains(.streamActiveFormatIndex) {
streamProperties.activeFormatIndex = 0
}
if properties.contains(.streamFrameDuration) {
let frameDuration = CMTime(value: 1, timescale: Int32(kFrameRate))
streamProperties.frameDuration = frameDuration
}
if properties.contains(CMIOExtensionPropertyCustomPropertyData_just) {
streamProperties.setPropertyState(CMIOExtensionPropertyState(value: self.just as NSString), forProperty: CMIOExtensionPropertyCustomPropertyData_just)
}
return streamProperties
}
func setStreamProperties(_ streamProperties: CMIOExtensionStreamProperties) throws {
if let activeFormatIndex = streamProperties.activeFormatIndex {
self.activeFormatIndex = activeFormatIndex
}
if let state = streamProperties.propertiesDictionary[CMIOExtensionPropertyCustomPropertyData_just] {
if let newValue = state.value as? String {
self.just = newValue
if let deviceSource = device.source as? cameraDeviceSource {
self.just = deviceSource.myStreamingCounter()
}
}
}
Then in your main app, 2 functions:
func getJustProperty(streamId: CMIOStreamID) -> String? {
let selector = FourCharCode("just")
var address = CMIOObjectPropertyAddress(selector, .global, .main)
let exists = CMIOObjectHasProperty(streamId, &address)
if exists {
var dataSize: UInt32 = 0
var dataUsed: UInt32 = 0
CMIOObjectGetPropertyDataSize(streamId, &address, 0, nil, &dataSize)
var name: CFString = "" as NSString
CMIOObjectGetPropertyData(streamId, &address, 0, nil, dataSize, &dataUsed, &name);
return name as String
} else {
return nil
}
}
func setJustProperty(streamId: CMIOStreamID, newValue: String) {
let selector = FourCharCode("just")
var address = CMIOObjectPropertyAddress(selector, .global, .main)
let exists = CMIOObjectHasProperty(streamId, &address)
if exists {
var settable: DarwinBoolean = false
CMIOObjectIsPropertySettable(streamId,&address,&settable)
if settable == false {
return
}
var dataSize: UInt32 = 0
CMIOObjectGetPropertyDataSize(streamId, &address, 0, nil, &dataSize)
var newName: CFString = newValue as NSString
CMIOObjectSetPropertyData(streamId, &address, 0, nil, dataSize, &newName)
}
}
Finally, poll at regular intervals the value of this property: notice that you FIRST need to write to it and then read it:
@objc func veryslowTimer() {
if let screegleStream = screegleStream {
self.setJustProperty(streamId: screegleStream, newValue: "\(count)")
let just = self.getJustProperty(streamId: screegleStream)
if let just = just {
if just == "sc=1" {
needToStream = true
} else {
needToStream = false
}
}
}
}
And this FourCharCode extension:
extension FourCharCode: ExpressibleByStringLiteral {
public init(stringLiteral value: StringLiteralType) {
var code: FourCharCode = 0
// Value has to consist of 4 printable ASCII characters, e.g. '420v'.
// Note: This implementation does not enforce printable range (32-126)
if value.count == 4 && value.utf8.count == 4 {
for byte in value.utf8 {
code = code << 8 + FourCharCode(byte)
}
}
else {
print("FourCharCode: Can't initialize with '\(value)', only printable ASCII allowed. Setting to '????'.")
code = 0x3F3F3F3F // = '????'
}
self = code
}
public init(extendedGraphemeClusterLiteral value: String) {
self = FourCharCode(stringLiteral: value)
}
public init(unicodeScalarLiteral value: String) {
self = FourCharCode(stringLiteral: value)
}
public init(_ value: String) {
self = FourCharCode(stringLiteral: value)
}
public var string: String? {
let cString: [CChar] = [
CChar(self >> 24 & 0xFF),
CChar(self >> 16 & 0xFF),
CChar(self >> 8 & 0xFF),
CChar(self & 0xFF),
0
]
return String(cString: cString)
}
}
You can keep a counter of the client connection and disconnection events.