App crash in macOS API "CGDisplayStreamCreateWithDispatchQueue" on macOS Ventura

We use “CGDisplayStreamCreateWithDispatchQueue” function to create a display stream, sometimes the application show crash on macOS Ventura. It can be use normally on macOS Monterey/Big Sur/Catalina/… I can see this bug by other Application(Through the Console->Crash Report), which is also use this function. The relevant code is as follows:


    CGDisplayStreamRef _displayStream;

    CGDirectDisplayID displayID = CGMainDisplayID();

    unsigned long modeWidth;

    unsigned long modeHeight;

    modeWidth = CGDisplayPixelsWide(displayID);

    modeHeight = CGDisplayPixelsHigh(displayID);

    CGDisplayModeRef displayModeRef = CGDisplayCopyDisplayMode(displayID);

    if (displayModeRef){

        modeWidth = CGDisplayModeGetPixelWidth(displayModeRef);

        modeHeight = CGDisplayModeGetPixelHeight(displayModeRef);

    }

    CGDisplayModeRelease(displayModeRef);

    NSLog(@"enable stream, displayID[0x%x], mode[%lu x %lu]\n", displayID, modeWidth, modeHeight);

    const int var_prop = 2;

    const void* keys[var_prop] = { kCGDisplayStreamDestinationRect, kCGDisplayStreamShowCursor};

    const void* values[var_prop] = { CGRectCreateDictionaryRepresentation(CGRectMake(0, 0, modeWidth, modeHeight)) , kCFBooleanTrue};

    CFDictionaryRef properties = CFDictionaryCreate(NULL, keys, values, var_prop, NULL, NULL);

    _displayStream = CGDisplayStreamCreateWithDispatchQueue(displayID, modeWidth, modeHeight, 'BGRA', properties, dispatchQueue, ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {

            if(status == kCGDisplayStreamFrameStatusStopped)

            {

                NSLog(@"kCGDisplayStreamFrameStatusStopped is received!");

                bIsStreamStatusStopped = true;

                return;

            }

            if(status == kCGDisplayStreamFrameStatusFrameComplete && frameSurface)

            {

                CFRetain(frameSurface);

                IOSurfaceIncrementUseCount(frameSurface);

                CFRetain(updateRef);

                [[NSOperationQueue mainQueue] addOperationWithBlock:^{

                    if(!bIsStreamStatusStopped) {

                        self.view.layer.contents = (__bridge id _Nullable)(frameSurface);

                    }

                }];

              CFRelease(updateRef);

                IOSurfaceDecrementUseCount(frameSurface);

                CFRelease(frameSurface);

            }

    });

    if (_displayStream)

    {

        CGError err = CGDisplayStreamStart(_displayStream);

        bIsStreamStatusStopped = false;

        if (err != CGDisplayNoErr)

        {

            NSLog(@"Error %u starting display stream", (unsigned)err);

            CFRelease(_displayStream);

            _displayStream = 0;

        }

    }

    else

        NSLog(@"create stream failed.\n");

The crash log is as follows:

0   CoreFoundation                	       0x189889e48 CFGetTypeID + 148
1   CoreFoundation                	       0x1898ac970 __CFPropertyListIsValidAux + 60
2   CoreFoundation                	       0x1898aedd4 __CFPropertyListIsDictPlistAux + 188
3   CoreFoundation                	       0x1898eab40 __CFDictionaryApplyFunction_block_invoke + 28
4   CoreFoundation                	       0x1898b2c38 CFBasicHashApply + 148
5   CoreFoundation                	       0x1898a4814 CFDictionaryApplyFunction + 320
6   CoreFoundation                	       0x1898acb9c __CFPropertyListIsValidAux + 616
7   CoreFoundation                	       0x1898beea8 CFPropertyListWrite + 92
8   CoreFoundation                	       0x1898de418 CFPropertyListCreateData + 144
9   SkyLight                      	       0x18e4aba04 CGSPropertyListCreateSerializedData + 72
10  SkyLight                      	       0x18e4b72ac CGSPropertyListCreateSerializedBytes + 68
11  SkyLight                      	       0x18e699e14 CGSPropertyListPerformWithSerializedBytes + 64
12  SkyLight                      	       0x18e5c25c4 SLDisplayStreamCreate + 296
13  SkyLight                      	       0x18e5c3008 SLDisplayStreamCreateWithDispatchQueue + 52
14  macOS InstantView             	       0x1043617f8 0x104328000 + 235512
15  macOS InstantView             	       0x1043619fc 0x104328000 + 236028
16  macOS InstantView             	       0x104361444 0x104328000 + 234564
17  SkyLight                      	       0x18e4b9f8c displayConfigFinalizedProc + 276
18  SkyLight                      	       0x18e4b1558 CGSPostLocalNotification + 172
19  SkyLight                      	       0x18e4b1148 (anonymous namespace)::notify_datagram_handler(unsigned int, CGSDatagramType, void*, unsigned long, void*) + 116
20  SkyLight                      	       0x18e7d3bec CGSDatagramReadStream::dispatchMainQueueDatagrams() + 228
21  SkyLight                      	       0x18e7d3ae8 invocation function for block in CGSDatagramReadStream::mainQueueWakeup() + 28
22  libdispatch.dylib             	       0x189680a48 _dispatch_call_block_and_release + 32
23  libdispatch.dylib             	       0x189682570 _dispatch_client_callout + 20
24  libdispatch.dylib             	       0x189690d28 _dispatch_main_queue_drain + 928
25  libdispatch.dylib             	       0x189690978 _dispatch_main_queue_callback_4CF + 44
26  CoreFoundation                	       0x18992a77c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
27  CoreFoundation                	       0x1898e82d0 __CFRunLoopRun + 2036
28  CoreFoundation                	       0x1898e7388 CFRunLoopRunSpecific + 612
29  HIToolbox                     	       0x192f06a68 RunCurrentEventLoopInMode + 292
30  HIToolbox                     	       0x192f068ac ReceiveNextEventCommon + 672
31  HIToolbox                     	       0x192f065f4 _BlockUntilNextEventMatchingListInModeWithFilter + 72
32  AppKit                        	       0x18cb2621c _DPSNextEvent + 632
33  AppKit                        	       0x18cb253ac -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 728
34  AppKit                        	       0x18cb1972c -[NSApplication run] + 464
35  AppKit                        	       0x18caf0a24 NSApplicationMain + 880
36  macOS InstantView             	       0x1043546cc 0x104328000 + 181964
37  dyld   

Accepted Reply

This line is wrong:

CFDictionaryRef properties = CFDictionaryCreate(NULL, keys, values, var_prop, NULL, NULL);

When creating a CFDictionary with CFType values, you must pass in kCFTypeDictionaryKeyCallBacks and kCFTypeDictionaryValueCallBacks for those last two parameters.

However…

Using CFDictionary is a royal pain and I encourage you to instead take advantage of the toll-free bridge between CFDictionary and NSDictionary. So:

NSDictionary * properties = @{
    (__bridge NSString *) kCGDisplayStreamDestinationRect: CFBridgingRelease(
        CGRectCreateDictionaryRepresentation(CGRectMake(0, 0, modeWidth, modeHeight))
    ),
    (__bridge NSString *) kCGDisplayStreamShowCursor: @YES,
};

_displayStream = CGDisplayStreamCreateWithDispatchQueue(
    displayID,
    modeWidth,
    modeHeight,
    'BGRA',
    (__bridge CFDictionaryRef) properties,
    dispatchQueue,
    ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
        // … stuff …
    }
);

One key advantage here is that all the memory management of NSDictionary and friends is done by ARC.

Share and Enjoy

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

Replies

This line is wrong:

CFDictionaryRef properties = CFDictionaryCreate(NULL, keys, values, var_prop, NULL, NULL);

When creating a CFDictionary with CFType values, you must pass in kCFTypeDictionaryKeyCallBacks and kCFTypeDictionaryValueCallBacks for those last two parameters.

However…

Using CFDictionary is a royal pain and I encourage you to instead take advantage of the toll-free bridge between CFDictionary and NSDictionary. So:

NSDictionary * properties = @{
    (__bridge NSString *) kCGDisplayStreamDestinationRect: CFBridgingRelease(
        CGRectCreateDictionaryRepresentation(CGRectMake(0, 0, modeWidth, modeHeight))
    ),
    (__bridge NSString *) kCGDisplayStreamShowCursor: @YES,
};

_displayStream = CGDisplayStreamCreateWithDispatchQueue(
    displayID,
    modeWidth,
    modeHeight,
    'BGRA',
    (__bridge CFDictionaryRef) properties,
    dispatchQueue,
    ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
        // … stuff …
    }
);

One key advantage here is that all the memory management of NSDictionary and friends is done by ARC.

Share and Enjoy

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