SCStreamUpdateFrameContentRect X coordinate always returns 48 instead of expected 0
Environment
Device: MacBook Pro 13-inch
macOS: Sequoia 15.6.1
Xcode: 16.4
Framework: Screen Capture Kit
Issue Description
I'm experiencing an unexpected behavior with Screen Capture Kit where the SCStreamUpdateFrameContentRect X coordinate consistently returns 48 instead of the expected 0.
Code Context
I'm using SCContentSharingPicker to capture screen content and implementing the SCStreamOutput protocol to receive frame data. In my stream(_:didOutputSampleBuffer:of:) method, I'm extracting the content rect information from the sample buffer attachments:
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
switch type {
case .screen:
guard let attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: false) as? [[SCStreamFrameInfo: Any]] else {
return
}
guard let attachments = attachmentsArray.first else { return }
if !attachments.keys.contains(.contentRect) {
return
}
print(attachments) // X coordinate always shows 48
/*
}, __C.SCStreamFrameInfo(_rawValue: SCStreamUpdateFrameContentRect): {
Height = 540;
Width = 864;
X = 48; <<-- unexpected value
Y = 0;
}]
*/
return
// ... other cases
}
}
Expected vs Actual Behavior
Expected: X coordinate should be 0 (indicating the content starts at the left edge of the screen)
Actual: X coordinate is consistently 48
Visual verification: When I display the captured screen content, it appears correctly without any offset, suggesting the actual content should indeed start at X=0
Main ViewModel Class
import Foundation
import ScreenCaptureKit
import SwiftUICore
class VM: NSObject, ObservableObject, SCContentSharingPickerObserver, SCStreamDelegate, SCStreamOutput {
@State var isRecording = false
// Error handling delegate
func stream(_ stream: SCStream, didStopWithError error: Error) {
DispatchQueue.main.async {
self.isRecording = false
}
}
var picker: SCContentSharingPicker?
func createPicker() -> SCContentSharingPicker {
if let p = picker {
return p
}
let picker = SCContentSharingPicker.shared
var config = SCContentSharingPicker.shared.defaultConfiguration //SCContentSharingPickerConfiguration()
config.allowedPickerModes = .singleDisplay
config.allowsChangingSelectedContent = false
config.excludedBundleIDs.append(Bundle.main.bundleIdentifier!)
picker.add(self)
picker.isActive = true
SCContentSharingPicker.shared.present(using: .display)
return picker
}
var stream: SCStream?
let videoSampleBufferQueue = DispatchQueue(label: "com.example.apple-samplecode.VideoSampleBufferQueue")
// observer call back for picker
func contentSharingPicker(_ picker: SCContentSharingPicker, didUpdateWith filter:
SCContentFilter, for stream: SCStream?) {
if let stream = stream {
stream.updateContentFilter(filter)
} else {
let config = SCStreamConfiguration()
config.capturesAudio = false
config.captureMicrophone = false
config.captureResolution = .automatic
config.captureDynamicRange = .SDR
config.showMouseClicks = false
config.showsCursor = false
// Set the frame rate for screen capture
config.minimumFrameInterval = CMTime(value: 1, timescale: 5)
self.stream = SCStream(filter: filter, configuration: config, delegate: self)
do {
try self.stream?.addStreamOutput(self, type: .screen, sampleHandlerQueue: self.videoSampleBufferQueue)
} catch {
print("\(error)")
}
self.stream?.updateContentFilter(filter)
DispatchQueue.main.async {
self.stream?.startCapture()
}
}
}
func contentSharingPicker(_ picker: SCContentSharingPicker, didCancelFor stream: SCStream?) {}
func contentSharingPickerStartDidFailWithError(_ error: any Error) {
print(error)
}
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
switch type {
case .screen:
guard let attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer,
createIfNecessary: false) as? [[SCStreamFrameInfo: Any]] else {
return
}
guard let attachments = attachmentsArray.first else { return }
if !attachments.keys.contains(.contentRect) {
return
}
print(attachments)
return
case .audio:
return
case .microphone:
return
@unknown default:
return
}
}
func outputVideoEffectDidStart(for stream: SCStream) {
print("outputVideoEffectDidStart")
}
func outputVideoEffectDidStop(for stream: SCStream) {
print("outputVideoEffectDidStop")
}
func streamDidBecomeActive(_ stream: SCStream) {
print("streamDidBecomeActive")
}
func streamDidBecomeInactive(_ stream: SCStream) {
print("streamDidBecomeInactive")
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
SCStreamUpdateFrameContentRect X coordinate always returns 48 instead of expected 0
Environment
Device: MacBook Pro 13-inch
macOS: Sequoia 15.6.1
Xcode: 16.4
Framework: Screen Capture Kit
Issue Description
I'm experiencing an unexpected behavior with Screen Capture Kit where the SCStreamUpdateFrameContentRect X coordinate consistently returns 48 instead of the expected 0.
Code Context
I'm using SCContentSharingPicker to capture screen content and implementing the SCStreamOutput protocol to receive frame data. In my stream(_:didOutputSampleBuffer:of:) method, I'm extracting the content rect information from the sample buffer attachments:
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
switch type {
case .screen:
guard let attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: false) as? [[SCStreamFrameInfo: Any]] else {
return
}
guard let attachments = attachmentsArray.first else { return }
if !attachments.keys.contains(.contentRect) {
return
}
print(attachments) // X coordinate always shows 48
/*
, __C.SCStreamFrameInfo(_rawValue: SCStreamUpdateFrameContentRect): {
Height = 540;
Width = 864;
X = 48; <<-- unexpected offset
Y = 0;
}]
*/
return
// ... other cases
}
}
Expected vs Actual Behavior
Expected: X coordinate should be 0 (indicating the content starts at the left edge of the screen)
Actual: X coordinate is consistently 48
Visual verification: When I display the captured screen content, it appears correctly without any offset, suggesting the actual content should indeed start at X=0
Additional Information
The picker is configured with .singleDisplay mode
I'm excluding the current app's bundle ID from capture
The captured content visually appears correct, only the reported coordinates seem off
Main ViewModel Class
import Foundation
import ScreenCaptureKit
import SwiftUICore
class VM: NSObject, ObservableObject, SCContentSharingPickerObserver, SCStreamDelegate, SCStreamOutput {
@State var isRecording = false
// Error handling delegate
func stream(_ stream: SCStream, didStopWithError error: Error) {
DispatchQueue.main.async {
self.isRecording = false
}
}
var picker: SCContentSharingPicker?
func createPicker() -> SCContentSharingPicker {
if let p = picker {
return p
}
let picker = SCContentSharingPicker.shared
picker.add(self)
picker.isActive = true
SCContentSharingPicker.shared.present(using: .display)
return picker
}
var stream: SCStream?
let videoSampleBufferQueue = DispatchQueue(label: "com.example.apple-samplecode.VideoSampleBufferQueue")
// observer call back for picker
func contentSharingPicker(_ picker: SCContentSharingPicker, didUpdateWith filter:
SCContentFilter, for stream: SCStream?) {
if let stream = stream {
stream.updateContentFilter(filter)
} else {
let config = SCStreamConfiguration()
config.capturesAudio = false
config.captureMicrophone = false
config.captureResolution = .automatic
config.captureDynamicRange = .SDR
config.showMouseClicks = false
config.showsCursor = false
// Set the frame rate for screen capture
config.minimumFrameInterval = CMTime(value: 1, timescale: 5) // 10 FPS
self.stream = SCStream(filter: filter, configuration: config, delegate: self)
do {
try self.stream?.addStreamOutput(self, type: .screen, sampleHandlerQueue: self.videoSampleBufferQueue)
} catch {
print("\(error)")
}
self.stream?.updateContentFilter(filter)
DispatchQueue.main.async {
self.stream?.startCapture()
}
}
}
func contentSharingPicker(_ picker: SCContentSharingPicker, didCancelFor stream: SCStream?) {}
func contentSharingPickerStartDidFailWithError(_ error: any Error) {
print(error)
}
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
switch type {
case .screen:
guard let attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer,
createIfNecessary: false) as? [[SCStreamFrameInfo: Any]] else {
return
}
guard let attachments = attachmentsArray.first else { return }
if !attachments.keys.contains(.contentRect) {
return
}
print(attachments)
return
case .audio:
return
case .microphone:
return
@unknown default:
return
}
}
func outputVideoEffectDidStart(for stream: SCStream) {
print("outputVideoEffectDidStart")
}
func outputVideoEffectDidStop(for stream: SCStream) {
print("outputVideoEffectDidStop")
}
func streamDidBecomeActive(_ stream: SCStream) {
print("streamDidBecomeActive")
}
func streamDidBecomeInactive(_ stream: SCStream) {
print("streamDidBecomeInactive")
}
}