Value of type 'SCRecordingOutput' has no member 'delegate'

Hello, I am trying to capture screen recording ( output.mp4 ) using ScreenCaptureKit and also the mouse positions during the recording ( mouse.json ). The recording and the mouse positions ( tracked based on mouse movements events only ) needs to be perfectly synced in order to add effects in post editing.

I started off by using the await stream?.startCapture() and after that starting my mouse tracking function :-

try await captureEngine.startCapture(configuration: config, filter: filter, recordingOutput: recordingOutput)

let captureStartTime = Date()
            
mouseTracker?.startTracking(with: captureStartTime)

But every time I tested, there is a clear inconsistency in sync between the recorded video and the recorded mouse positions.

The only thing I want is to know when exactly does the recording "actually" started so that I can start the mouse capture at that same time, and thus I tried using the Delegates, but being able to set them up perfectly.


import Foundation
import AVFAudio
import ScreenCaptureKit
import OSLog
import Combine

class CaptureEngine: NSObject, @unchecked Sendable {
    
    private let logger = Logger()

    private(set) var stream: SCStream?
    private var streamOutput: CaptureEngineStreamOutput?
    private var recordingOutput: SCRecordingOutput?
    
    private let videoSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.VideoSampleBufferQueue")
    private let audioSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.AudioSampleBufferQueue")
    private let micSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.MicSampleBufferQueue")

    func startCapture(configuration: SCStreamConfiguration, filter: SCContentFilter, recordingOutput: SCRecordingOutput) async throws {
        
        // Create the stream output delegate.
        let streamOutput = CaptureEngineStreamOutput()
        self.streamOutput = streamOutput

        do {
            stream = SCStream(filter: filter, configuration: configuration, delegate: streamOutput)
            
            try stream?.addStreamOutput(streamOutput, type: .screen, sampleHandlerQueue: videoSampleBufferQueue)
            try stream?.addStreamOutput(streamOutput, type: .audio, sampleHandlerQueue: audioSampleBufferQueue)
            try stream?.addStreamOutput(streamOutput, type: .microphone, sampleHandlerQueue: micSampleBufferQueue)
            
            self.recordingOutput = recordingOutput
            recordingOutput.delegate = self
            
            try stream?.addRecordingOutput(recordingOutput)
            
            try await stream?.startCapture()
            
        } catch {
            logger.error("Failed to start capture: \(error.localizedDescription)")
            throw error
        }
    }
    
    func stopCapture() async throws {
        do {
            try await stream?.stopCapture()
        } catch {
            logger.error("Failed to stop capture: \(error.localizedDescription)")
            throw error
        }
    }
    
    func update(configuration: SCStreamConfiguration, filter: SCContentFilter) async {
        do {
            try await stream?.updateConfiguration(configuration)
            try await stream?.updateContentFilter(filter)
        } catch {
            logger.error("Failed to update the stream session: \(String(describing: error))")
        }
    }
    
    func stopRecordingOutputForStream(_ recordingOutput: SCRecordingOutput) throws {
        try self.stream?.removeRecordingOutput(recordingOutput)
    }
}

// MARK: - SCRecordingOutputDelegate
extension CaptureEngine: SCRecordingOutputDelegate {
    
    func recordingOutputDidStartRecording(_ recordingOutput: SCRecordingOutput) {
        let startTime = Date()
        logger.info("Recording output did start recording \(startTime)")
    }
    
    func recordingOutputDidFinishRecording(_ recordingOutput: SCRecordingOutput) {
        logger.info("Recording output did finish recording")
    }
    
    func recordingOutput(_ recordingOutput: SCRecordingOutput, didFailWithError error: any Error) {
        logger.error("Recording output failed with error: \(error.localizedDescription)")
    }
}

private class CaptureEngineStreamOutput: NSObject, SCStreamOutput, SCStreamDelegate {
    
    private let logger = Logger()
    
    override init() {
        super.init()
    }
    
    func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of outputType: SCStreamOutputType) {
        
        
        guard sampleBuffer.isValid else { return }
        switch outputType {
        case .screen:
            break
        case .audio:
            break
        case .microphone:
            break
        @unknown default:
            logger.error("Encountered unknown stream output type:")
        }
    }
    
    func stream(_ stream: SCStream, didStopWithError error: Error) {
        logger.error("Stream stopped with error: \(error.localizedDescription)")
    }
}

I am getting error

Value of type 'SCRecordingOutput' has no member 'delegate'

Even though I am targeting macOs 15+ ( macOs 26 actually ) and macOs only.

What is the best way to achieving the desired result? Is there any other / better way to do it?

Answered by DTS Engineer in 863119022

As the error says, SCRecordingOutput has no delegate property. Rather, you set the delegate when you initialise the object using the init(configuration:delegate:) initialiser. You’ll have to restructure your code to account for that reality, meaning that your startCapture(…) method won’t be able to accept an SCRecordingOutput parameter but rather take a description of how to create such an object, allowing you to then supply your delegate.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

As the error says, SCRecordingOutput has no delegate property. Rather, you set the delegate when you initialise the object using the init(configuration:delegate:) initialiser. You’ll have to restructure your code to account for that reality, meaning that your startCapture(…) method won’t be able to accept an SCRecordingOutput parameter but rather take a description of how to create such an object, allowing you to then supply your delegate.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Value of type 'SCRecordingOutput' has no member 'delegate'
 
 
Q