Post

Replies

Boosts

Views

Activity

Reply to is XPC from app to CMIOExtension possible?
it would be really really helpful to have official sample code that shows how the camera extension can receive frame data from the main app in which it was embedded. Also, it’d be really useful if the camera extension could use some window server APIs such as CGWindowListCreateImage or the newer ScreenCaptureKit API: assuming the rights to access the screen were granted to the bundling app, could the camera extension inherit that right? that’s the approch I’m using currently in my old DAL plugin for Screegle app to capture frames from an NSWindow that the main app has.
Jun ’22
Reply to is XPC from app to CMIOExtension possible?
@smith_c is correct: we can add a sink stream to the same device and the app can feed CMSampleBuffers to this sink stream by connecting to it using the CoreMediaIO C API. In my code, the cameraStreamSink (modeled after cameraStreamSource) simply consumes buffers by waiting on self._streamSink.stream.consumeSampleBuffer(buf,...) and sends them immediately to the source stream by calling self._streamSource.stream.send(buf!,...) I can provide source code if needed.
Jun ’22
Reply to 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)   } }
Jul ’22