obs-studio icon indicating copy to clipboard operation
obs-studio copied to clipboard

mac-virtualcam: Compare camera UUIDs using CFUUID

Open jcm93 opened this issue 1 year ago • 6 comments

Description

Resolves a small issue related to custom OBS virtual camera builds.

When starting virtual camera output on macOS, a UUID string comparison is used to locate the CMIO extension associated with OBS. This UUID string, as stored in the app bundle, can be either lowercase or uppercase depending on the parameters originally provided to CMake. The variant returned from CMIO internals is always uppercase. ~~Since OBS is using a string comparison to compare the UUIDs, this PR changes the comparison to be case insensitive.~~ This PR changes the comparison to use CFUUIDs created from the strings and CFEqual to check equality, rendering any questions of case sensitivity moot.

Motivation and Context

When debugging the Mac virtual camera with a separate code signing identity from OBS, it is necessary to generate new UUIDs for the virtual camera to successfully differentiate the custom virtual camera from the official OBS virtual camera. People building OBS in this fashion who are not familiar with this intricacy might define generate a lowercase UUID string and be confused when OBS cannot find its own virtual camera extension.

How Has This Been Tested?

Tested locally on my machine to verify the virtual camera output can still be started.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • [x] My code has been run through clang-format.
  • [x] I have read the contributing document.
  • [x] My code is not on the master branch.
  • [x] The code has been tested.
  • [x] All commit messages are properly formatted and commits squashed where appropriate.
  • [x] I have included updates to all appropriate documentation.

jcm93 avatar Jan 11 '24 02:01 jcm93

I feel that our CMake flow shouldn't allow generating with a UUID that doesn't match our convention. I'd be curious what protections we provide already when it comes to building the camera extension, rather than modifying the detections in OBS itself. This change is fine in isolation, and probably worth including anyway, but I'd prefer that the issue should be fixed on the CMake side first.

WizardCM avatar Jan 13 '24 23:01 WizardCM

I feel that our CMake flow shouldn't allow generating with a UUID that doesn't match our convention. I'd be curious what protections we provide already when it comes to building the camera extension, rather than modifying the detections in OBS itself. This change is fine in isolation, and probably worth including anyway, but I'd prefer that the issue should be fixed on the CMake side first.

Forgot to reply sooner - I would rather just upgrade lowercase UUIDs automatically to the required uppercase variant.

PatTheMav avatar Jan 14 '24 00:01 PatTheMav

It doesn't really matter I suppose, but I was also going to propose another solution, a bit less quick and dirty, that actually compares the UUIDs as CFUUIDs instead of using the string representations:

        vcam->deviceID = 0;
        NSString *OBSVirtualCamUUIDString = [[NSBundle bundleWithIdentifier:@"com.jcm.obstesting.mac-virtualcam"]
            objectForInfoDictionaryKey:@"OBSCameraDeviceUUID"];
        CFUUIDRef OBSVirtualCamUUID = CFUUIDCreateFromString(kCFAllocatorDefault, (CFStringRef)OBSVirtualCamUUIDString);

        size_t num_elements = size / sizeof(CMIOObjectID);
        for (size_t i = 0; i < num_elements; i++) {
            CMIOObjectID cmioDevice;
            [cmioDevices getBytes:&cmioDevice range:NSMakeRange(i * sizeof(CMIOObjectID), sizeof(CMIOObjectID))];

            address.mSelector = kCMIODevicePropertyDeviceUID;
            UInt32 device_name_size;
            CMIOObjectGetPropertyDataSize(cmioDevice, &address, 0, NULL, &device_name_size);
            CFStringRef uid;
            CMIOObjectGetPropertyData(cmioDevice, &address, 0, NULL, device_name_size, &used, &uid);
            CFUUIDRef deviceUUID = CFUUIDCreateFromString(kCFAllocatorDefault, uid);
            
            if (CFEqual(OBSVirtualCamUUID, deviceUUID)) {
                vcam->deviceID = cmioDevice;
                CFRelease(uid);
                CFRelease(deviceUUID);
                break;
            } else {
                CFRelease(uid);
                CFRelease(deviceUUID);
            }
        }
        CFRelease(OBSVirtualCamUUID);

I tested this to work with mixed case UUID strings; my two cents was that it shouldn't matter how precisely the UUIDs are stored if they're all meant to represent the same 128 bits, and the important thing is to compare them *as UUIDs*. But that was before Pat replied :P

jcm93 avatar Jan 14 '24 00:01 jcm93

It doesn't really matter I suppose, but I was also going to propose another solution, a bit less quick and dirty, that actually compares the UUIDs as CFUUIDs instead of using the string representations:

        vcam->deviceID = 0;
        NSString *OBSVirtualCamUUIDString = [[NSBundle bundleWithIdentifier:@"com.jcm.obstesting.mac-virtualcam"]
            objectForInfoDictionaryKey:@"OBSCameraDeviceUUID"];
        CFUUIDRef OBSVirtualCamUUID = CFUUIDCreateFromString(kCFAllocatorDefault, (CFStringRef)OBSVirtualCamUUIDString);

        size_t num_elements = size / sizeof(CMIOObjectID);
        for (size_t i = 0; i < num_elements; i++) {
            CMIOObjectID cmioDevice;
            [cmioDevices getBytes:&cmioDevice range:NSMakeRange(i * sizeof(CMIOObjectID), sizeof(CMIOObjectID))];

            address.mSelector = kCMIODevicePropertyDeviceUID;
            UInt32 device_name_size;
            CMIOObjectGetPropertyDataSize(cmioDevice, &address, 0, NULL, &device_name_size);
            CFStringRef uid;
            CMIOObjectGetPropertyData(cmioDevice, &address, 0, NULL, device_name_size, &used, &uid);
            CFUUIDRef deviceUUID = CFUUIDCreateFromString(kCFAllocatorDefault, uid);
            
            if (CFEqual(OBSVirtualCamUUID, deviceUUID)) {
                vcam->deviceID = cmioDevice;
                CFRelease(uid);
                CFRelease(deviceUUID);
                break;
            } else {
                CFRelease(uid);
                CFRelease(deviceUUID);
            }
        }
        CFRelease(OBSVirtualCamUUID);

I tested this to work with mixed case UUID strings; my two cents was that it shouldn't matter how precisely the UUIDs are stored if they're all meant to represent the same 128 bits, and the important thing is to compare them as UUIDs. But that was before Pat replied :P

Using CFUUID to compare them would still be more "correct" IMO and beneficial in any case. As for how the camera extension should report itself to macOS (whether to use uppercase or lowercase UUIDs) that might depend on whether there are popular applications people use the virtual camera with that might then run into issues (because they might also only use a string comparison).

PatTheMav avatar Jan 16 '24 17:01 PatTheMav

Updated the PR to use CFUUID / CFEqual for comparisons so that's there, at least.

jcm93 avatar Jan 16 '24 23:01 jcm93

Yes; when testing this originally I made sure the virtual camera works with UUIDs passed to CMake in both uppercase and lowercase.

jcm93 avatar Aug 17 '24 15:08 jcm93