macoscocoascreencapturekitscshareablecontent

How to capture screenshot at screen resolution with ScreenCaptureKit


I am trying to get a screenshot of whole display. However I am being returned different resolution which is also non retina. Instead of 3456x2234 CGImageRef I am being given 1920x1080.

Note that the old API CGWindowListCreateImage has been made obsolete in macOS 15.0 and triggers unlimited privacy popup warning. Old API gives expected result.

CG_EXTERN CGImageRef __nullable CGWindowListCreateImage(CGRect screenBounds,
    CGWindowListOption listOption, CGWindowID windowID,
    CGWindowImageOption imageOption)
    SCREEN_CAPTURE_OBSOLETE(10.5,14.0,15.0);

Code:

[SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent * _Nullable shareableContent, NSError * _Nullable error) {
    if (error) { return; }
    
    SCDisplay *display = [[shareableContent displays] firstObject];
    SCContentFilter *filter = [[SCContentFilter alloc] initWithDisplay:display excludingWindows:@[]];
    SCStreamConfiguration *configuration = [[SCStreamConfiguration alloc] init];
    configuration.capturesAudio = NO;
    configuration.excludesCurrentProcessAudio = YES;
    configuration.preservesAspectRatio = YES;
    configuration.showsCursor = NO;
    configuration.captureResolution = SCCaptureResolutionBest;
    //configuration.pixelFormat = 'BGRA';
    
    [SCScreenshotManager captureImageWithFilter:filter configuration:configuration completionHandler:^(CGImageRef  _Nullable cgImage, NSError * _Nullable error) {
        if (error) {
            
        } else {
            NSLog(@"%@", cgImage);
        }
    }];
}];

Log:

Printing description of cgImage:
<CGImage 0x11fe306c0> (DP)
    <<CGColorSpace 0x600002ba0a80> (kCGColorSpaceDeviceRGB)>
        width = 1920, height = 1080, bpc = 8, bpp = 32, row bytes = 7680 
        kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little  | kCGImagePixelFormatPacked 
        is mask? No, has masking color? No, has soft mask? No, has matte? No, should interpolate? Yes
(lldb) po [NSScreen mainScreen]
<NSScreen: 0x600002bac3c0; name="Built-in Retina Display"; backingScaleFactor=2.000000; frame={{0, 0}, {1728, 1117}}; visibleFrame={{0, 48}, {1728, 1031}}>

Old API would return:

Printing description of cgImage:
<CGImage 0x146e8ad90> (DP)
    <<CGColorSpace 0x600000c949c0> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Color LCD)>
        width = 3456, height = 2234, bpc = 8, bpp = 32, row bytes = 13824 
        kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little  | kCGImagePixelFormatPacked 
        is mask? No, has masking color? No, has soft mask? No, has matte? No, should interpolate? No

Solution

  • The SCStreamConfiguration defaults to 1920x1080. It has to be configured manually.

    configuration.width = NSWidth(filter.contentRect) * filter.pointPixelScale;
    configuration.height = NSHeight(filter.contentRect) * filter.pointPixelScale;
    

    Taking a display screenshot:

    [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent * _Nullable shareableContent, NSError * _Nullable error) {
        if (error) { return; }
        
        SCDisplay *display = [[shareableContent displays] firstObject]; //or match displayID to window's screen deviceDescription NSScreenNumber
        SCContentFilter *filter = [[SCContentFilter alloc] initWithDisplay:display excludingWindows:@[]];
        SCStreamConfiguration *configuration = [[SCStreamConfiguration alloc] init];
        configuration.capturesAudio = NO;
        configuration.excludesCurrentProcessAudio = YES;
        configuration.preservesAspectRatio = YES;
        configuration.showsCursor = NO;
        configuration.captureResolution = SCCaptureResolutionBest;
        configuration.width = NSWidth(filter.contentRect) * filter.pointPixelScale;
        configuration.height = NSHeight(filter.contentRect) * filter.pointPixelScale;
        //configuration.pixelFormat = 'BGRA';
        
        [SCScreenshotManager captureImageWithFilter:filter configuration:configuration completionHandler:^(CGImageRef  _Nullable cgImage, NSError * _Nullable error) {
            if (error) {
                
            } else {
                NSLog(@"%@", cgImage);
            }
        }];
    }];
            
    

    NOTE: You need to retain the cgImage or pass it as NSImage object.