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
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"