// // PlugInInterface.mm // obs-mac-virtualcam // // This file implements the CMIO DAL plugin interface // // Created by John Boiles on 4/9/20. // // obs-mac-virtualcam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // obs-mac-virtualcam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with obs-mac-virtualcam. If not, see . #import "OBSDALPlugInInterface.h" #import #import "OBSDALPlugin.h" #import "OBSDALDevice.h" #import "OBSDALStream.h" #import "Logging.h" #pragma mark Plug-In Operations static UInt32 sRefCount = 0; ULONG HardwarePlugIn_AddRef(CMIOHardwarePlugInRef self) { UNUSED_PARAMETER(self); sRefCount += 1; DLogFunc(@"sRefCount now = %d", sRefCount); return sRefCount; } ULONG HardwarePlugIn_Release(CMIOHardwarePlugInRef self) { UNUSED_PARAMETER(self); sRefCount -= 1; DLogFunc(@"sRefCount now = %d", sRefCount); return sRefCount; } HRESULT HardwarePlugIn_QueryInterface(CMIOHardwarePlugInRef self, REFIID uuid, LPVOID *interface) { UNUSED_PARAMETER(self); DLogFunc(@""); if (!interface) { DLogFunc(@"Received an empty interface"); return E_POINTER; } // Set the returned interface to NULL in case the UUIDs don't match *interface = NULL; // Create a CoreFoundation UUIDRef for the requested interface. CFUUIDRef cfUuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, uuid); CFStringRef uuidString = CFUUIDCreateString(NULL, cfUuid); CFStringRef hardwarePluginUuid = CFUUIDCreateString(NULL, kCMIOHardwarePlugInInterfaceID); if (CFEqual(uuidString, hardwarePluginUuid)) { // Return the interface; sRefCount += 1; *interface = OBSDALPlugInRef(); return kCMIOHardwareNoError; } else { DLogFunc(@"ERR Queried for some weird UUID %@", uuidString); } return E_NOINTERFACE; } // I think this is deprecated, seems that HardwarePlugIn_InitializeWithObjectID gets called instead OSStatus HardwarePlugIn_Initialize(CMIOHardwarePlugInRef self) { DLogFunc(@"ERR self=%p", self); return kCMIOHardwareUnspecifiedError; } OSStatus HardwarePlugIn_InitializeWithObjectID(CMIOHardwarePlugInRef self, CMIOObjectID objectID) { DLogFunc(@"self=%p", self); OSStatus error = kCMIOHardwareNoError; OBSDALPlugin *plugIn = [OBSDALPlugin SharedPlugIn]; plugIn.objectId = objectID; [[OBSDALObjectStore SharedObjectStore] setObject:plugIn forObjectId:objectID]; OBSDALDevice *device = [[OBSDALDevice alloc] init]; CMIOObjectID deviceId; error = CMIOObjectCreate(OBSDALPlugInRef(), kCMIOObjectSystemObject, kCMIODeviceClassID, &deviceId); if (error != noErr) { DLog(@"CMIOObjectCreate Error %d", error); return error; } device.objectId = deviceId; device.pluginId = objectID; [[OBSDALObjectStore SharedObjectStore] setObject:device forObjectId:deviceId]; OBSDALStream *stream = [[OBSDALStream alloc] init]; CMIOObjectID streamId; error = CMIOObjectCreate(OBSDALPlugInRef(), deviceId, kCMIOStreamClassID, &streamId); if (error != noErr) { DLog(@"CMIOObjectCreate Error %d", error); return error; } stream.objectId = streamId; [[OBSDALObjectStore SharedObjectStore] setObject:stream forObjectId:streamId]; device.streamId = streamId; plugIn.stream = stream; // Tell the system about the Device error = CMIOObjectsPublishedAndDied( OBSDALPlugInRef(), kCMIOObjectSystemObject, 1, &deviceId, 0, 0); if (error != kCMIOHardwareNoError) { DLog(@"CMIOObjectsPublishedAndDied plugin/device Error %d", error); return error; } // Tell the system about the Stream error = CMIOObjectsPublishedAndDied(OBSDALPlugInRef(), deviceId, 1, &streamId, 0, 0); if (error != kCMIOHardwareNoError) { DLog(@"CMIOObjectsPublishedAndDied device/stream Error %d", error); return error; } return error; } OSStatus HardwarePlugIn_Teardown(CMIOHardwarePlugInRef self) { DLogFunc(@"self=%p", self); OSStatus error = kCMIOHardwareNoError; OBSDALPlugin *plugIn = [OBSDALPlugin SharedPlugIn]; [plugIn teardown]; return error; } #pragma mark CMIOObject Operations void HardwarePlugIn_ObjectShow(CMIOHardwarePlugInRef self, CMIOObjectID objectID) { UNUSED_PARAMETER(objectID); DLogFunc(@"self=%p", self); } Boolean HardwarePlugIn_ObjectHasProperty(CMIOHardwarePlugInRef self, CMIOObjectID objectID, const CMIOObjectPropertyAddress *address) { UNUSED_PARAMETER(self); NSObject *object = [OBSDALObjectStore GetObjectWithId:objectID]; if (object == nil) { DLogFunc(@"ERR nil object"); return false; } Boolean answer = [object hasPropertyWithAddress:*address]; // Disabling Noisy logs // DLogFunc(@"%@(%d) %@ self=%p hasProperty=%d", NSStringFromClass([object class]), objectID, [ObjectStore StringFromPropertySelector:address->mSelector], self, answer); return answer; } OSStatus HardwarePlugIn_ObjectIsPropertySettable( CMIOHardwarePlugInRef self, CMIOObjectID objectID, const CMIOObjectPropertyAddress *address, Boolean *isSettable) { NSObject *object = [OBSDALObjectStore GetObjectWithId:objectID]; if (object == nil) { DLogFunc(@"ERR nil object"); return kCMIOHardwareBadObjectError; } *isSettable = [object isPropertySettableWithAddress:*address]; DLogFunc(@"%@(%d) %@ self=%p settable=%d", NSStringFromClass([object class]), objectID, [OBSDALObjectStore StringFromPropertySelector:address->mSelector], self, *isSettable); return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_ObjectGetPropertyDataSize( CMIOHardwarePlugInRef self, CMIOObjectID objectID, const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize, const void *qualifierData, UInt32 *dataSize) { UNUSED_PARAMETER(self); NSObject *object = [OBSDALObjectStore GetObjectWithId:objectID]; if (object == nil) { DLogFunc(@"ERR nil object"); return kCMIOHardwareBadObjectError; } *dataSize = [object getPropertyDataSizeWithAddress:*address qualifierDataSize:qualifierDataSize qualifierData:qualifierData]; // Disabling Noisy logs // DLogFunc(@"%@(%d) %@ self=%p size=%d", NSStringFromClass([object class]), objectID, [ObjectStore StringFromPropertySelector:address->mSelector], self, *dataSize); return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_ObjectGetPropertyData( CMIOHardwarePlugInRef self, CMIOObjectID objectID, const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize, const void *qualifierData, UInt32 dataSize, UInt32 *dataUsed, void *data) { UNUSED_PARAMETER(self); NSObject *object = [OBSDALObjectStore GetObjectWithId:objectID]; if (object == nil) { DLogFunc(@"ERR nil object"); return kCMIOHardwareBadObjectError; } [object getPropertyDataWithAddress:*address qualifierDataSize:qualifierDataSize qualifierData:qualifierData dataSize:dataSize dataUsed:dataUsed data:data]; // Disabling Noisy logs // if ([ObjectStore IsBridgedTypeForSelector:address->mSelector]) { // id dataObj = (__bridge NSObject *)*static_cast(data); // DLogFunc(@"%@(%d) %@ self=%p data(id)=%@", NSStringFromClass([object class]), objectID, [ObjectStore StringFromPropertySelector:address->mSelector], self, dataObj); // } else { // UInt32 *dataInt = (UInt32 *)data; // DLogFunc(@"%@(%d) %@ self=%p data(int)=%d", NSStringFromClass([object class]), objectID, [ObjectStore StringFromPropertySelector:address->mSelector], self, *dataInt); // } return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_ObjectSetPropertyData( CMIOHardwarePlugInRef self, CMIOObjectID objectID, const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize, const void *qualifierData, UInt32 dataSize, const void *data) { NSObject *object = [OBSDALObjectStore GetObjectWithId:objectID]; if (object == nil) { DLogFunc(@"ERR nil object"); return kCMIOHardwareBadObjectError; } UInt32 *dataInt = (UInt32 *)data; DLogFunc(@"%@(%d) %@ self=%p data(int)=%d", NSStringFromClass([object class]), objectID, [OBSDALObjectStore StringFromPropertySelector:address->mSelector], self, *dataInt); [object setPropertyDataWithAddress:*address qualifierDataSize:qualifierDataSize qualifierData:qualifierData dataSize:dataSize data:data]; return kCMIOHardwareNoError; } #pragma mark CMIOStream Operations OSStatus HardwarePlugIn_StreamCopyBufferQueue( CMIOHardwarePlugInRef self, CMIOStreamID streamID, CMIODeviceStreamQueueAlteredProc queueAlteredProc, void *queueAlteredRefCon, CMSimpleQueueRef *queue) { OBSDALStream *stream = (OBSDALStream *)[OBSDALObjectStore GetObjectWithId:streamID]; if (stream == nil) { DLogFunc(@"ERR nil object"); return kCMIOHardwareBadObjectError; } *queue = [stream copyBufferQueueWithAlteredProc:queueAlteredProc alteredRefCon:queueAlteredRefCon]; DLogFunc(@"%@ (id=%d) self=%p queue=%@", stream, streamID, self, (__bridge NSObject *)*queue); return kCMIOHardwareNoError; } #pragma mark CMIODevice Operations OSStatus HardwarePlugIn_DeviceStartStream(CMIOHardwarePlugInRef self, CMIODeviceID deviceID, CMIOStreamID streamID) { DLogFunc(@"self=%p device=%d stream=%d", self, deviceID, streamID); OBSDALStream *stream = (OBSDALStream *)[OBSDALObjectStore GetObjectWithId:streamID]; if (stream == nil) { DLogFunc(@"ERR nil object"); return kCMIOHardwareBadObjectError; } [[OBSDALPlugin SharedPlugIn] startStream]; return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_DeviceSuspend(CMIOHardwarePlugInRef self, CMIODeviceID deviceID) { UNUSED_PARAMETER(deviceID); DLogFunc(@"self=%p", self); return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_DeviceResume(CMIOHardwarePlugInRef self, CMIODeviceID deviceID) { UNUSED_PARAMETER(deviceID); DLogFunc(@"self=%p", self); return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_DeviceStopStream(CMIOHardwarePlugInRef self, CMIODeviceID deviceID, CMIOStreamID streamID) { DLogFunc(@"self=%p device=%d stream=%d", self, deviceID, streamID); OBSDALStream *stream = (OBSDALStream *)[OBSDALObjectStore GetObjectWithId:streamID]; if (stream == nil) { DLogFunc(@"ERR nil object"); return kCMIOHardwareBadObjectError; } [[OBSDALPlugin SharedPlugIn] stopStream]; return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_DeviceProcessAVCCommand(CMIOHardwarePlugInRef self, CMIODeviceID deviceID, CMIODeviceAVCCommand *ioAVCCommand) { UNUSED_PARAMETER(deviceID); UNUSED_PARAMETER(ioAVCCommand); DLogFunc(@"self=%p", self); return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_DeviceProcessRS422Command(CMIOHardwarePlugInRef self, CMIODeviceID deviceID, CMIODeviceRS422Command *ioRS422Command) { UNUSED_PARAMETER(deviceID); UNUSED_PARAMETER(ioRS422Command); DLogFunc(@"self=%p", self); return kCMIOHardwareNoError; } OSStatus HardwarePlugIn_StreamDeckPlay(CMIOHardwarePlugInRef self, CMIOStreamID streamID) { UNUSED_PARAMETER(streamID); DLogFunc(@"self=%p", self); return kCMIOHardwareIllegalOperationError; } OSStatus HardwarePlugIn_StreamDeckStop(CMIOHardwarePlugInRef self, CMIOStreamID streamID) { UNUSED_PARAMETER(streamID); DLogFunc(@"self=%p", self); return kCMIOHardwareIllegalOperationError; } OSStatus HardwarePlugIn_StreamDeckJog(CMIOHardwarePlugInRef self, CMIOStreamID streamID, SInt32 speed) { UNUSED_PARAMETER(streamID); UNUSED_PARAMETER(speed); DLogFunc(@"self=%p", self); return kCMIOHardwareIllegalOperationError; } OSStatus HardwarePlugIn_StreamDeckCueTo(CMIOHardwarePlugInRef self, CMIOStreamID streamID, Float64 requestedTimecode, Boolean playOnCue) { UNUSED_PARAMETER(streamID); UNUSED_PARAMETER(requestedTimecode); UNUSED_PARAMETER(playOnCue); DLogFunc(@"self=%p", self); return kCMIOHardwareIllegalOperationError; } static CMIOHardwarePlugInInterface sInterface = { // Padding for COM NULL, // IUnknown Routines (HRESULT (*)(void *, CFUUIDBytes, void **))HardwarePlugIn_QueryInterface, (ULONG(*)(void *))HardwarePlugIn_AddRef, (ULONG(*)(void *))HardwarePlugIn_Release, // DAL Plug-In Routines HardwarePlugIn_Initialize, HardwarePlugIn_InitializeWithObjectID, HardwarePlugIn_Teardown, HardwarePlugIn_ObjectShow, HardwarePlugIn_ObjectHasProperty, HardwarePlugIn_ObjectIsPropertySettable, HardwarePlugIn_ObjectGetPropertyDataSize, HardwarePlugIn_ObjectGetPropertyData, HardwarePlugIn_ObjectSetPropertyData, HardwarePlugIn_DeviceSuspend, HardwarePlugIn_DeviceResume, HardwarePlugIn_DeviceStartStream, HardwarePlugIn_DeviceStopStream, HardwarePlugIn_DeviceProcessAVCCommand, HardwarePlugIn_DeviceProcessRS422Command, HardwarePlugIn_StreamCopyBufferQueue, HardwarePlugIn_StreamDeckPlay, HardwarePlugIn_StreamDeckStop, HardwarePlugIn_StreamDeckJog, HardwarePlugIn_StreamDeckCueTo}; static CMIOHardwarePlugInInterface *sInterfacePtr = &sInterface; static CMIOHardwarePlugInRef sPlugInRef = &sInterfacePtr; CMIOHardwarePlugInRef OBSDALPlugInRef() { return sPlugInRef; }