CMIOExtension Camera Extension query if being used

Is there a way to tell using device / stream properties if a camera extension is being actively used by a client?

Replies

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.