diff --git a/README.md b/README.md index 3b15935..fce92af 100644 --- a/README.md +++ b/README.md @@ -65,10 +65,13 @@ code into a separate DLL, and now I present to you the ESCAPI: - setupESCAPI - Initialize the whole library. (in escapi.cpp) - countCaptureDevices - Request number of capture devices available. - getCaptureDeviceName - Request the printable name of a capture device. +- getCaptureDeviceUniqueName - Request the unique name of a capture device. - initCapture - Tries to open the video capture device. Returns 0 on failure, 1 on success. - doCapture - Requests a video frame to be captured. - isCaptureDone - Returns 1 when the requested frame has been captured. - deinitCapture - Closes the video capture device. +- registerForDeviceNotification - Start handle camera connect/disconnect and calling callback for this events. +- unregisterForDeviceNotification - Stop handle camera connect/disconnect. So basically, you call setup to initialize the library, call init to start the capture device, and call doCapture to diff --git a/common/escapi.cpp b/common/escapi.cpp index 898fd2b..0b561b8 100644 --- a/common/escapi.cpp +++ b/common/escapi.cpp @@ -7,6 +7,7 @@ deinitCaptureProc deinitCapture; doCaptureProc doCapture; isCaptureDoneProc isCaptureDone; getCaptureDeviceNameProc getCaptureDeviceName; +getCaptureDeviceUniqueNameProc getCaptureDeviceUniqueName; ESCAPIVersionProc ESCAPIVersion; getCapturePropertyValueProc getCapturePropertyValue; getCapturePropertyAutoProc getCapturePropertyAuto; @@ -14,6 +15,8 @@ setCapturePropertyProc setCaptureProperty; getCaptureErrorLineProc getCaptureErrorLine; getCaptureErrorCodeProc getCaptureErrorCode; initCaptureWithOptionsProc initCaptureWithOptions; +registerForDeviceNotificationProc registerForDeviceNotification; +unregisterForDeviceNotificationProc unregisterForDeviceNotification; /* Internal: initialize COM */ @@ -35,6 +38,7 @@ int setupESCAPI() isCaptureDone = (isCaptureDoneProc)GetProcAddress(capdll, "isCaptureDone"); initCOM = (initCOMProc)GetProcAddress(capdll, "initCOM"); getCaptureDeviceName = (getCaptureDeviceNameProc)GetProcAddress(capdll, "getCaptureDeviceName"); + getCaptureDeviceUniqueName = (getCaptureDeviceUniqueNameProc)GetProcAddress(capdll, "getCaptureDeviceUniqueName"); ESCAPIVersion = (ESCAPIVersionProc)GetProcAddress(capdll, "ESCAPIVersion"); getCapturePropertyValue = (getCapturePropertyValueProc)GetProcAddress(capdll, "getCapturePropertyValue"); getCapturePropertyAuto = (getCapturePropertyAutoProc)GetProcAddress(capdll, "getCapturePropertyAuto"); @@ -42,12 +46,15 @@ int setupESCAPI() getCaptureErrorLine = (getCaptureErrorLineProc)GetProcAddress(capdll, "getCaptureErrorLine"); getCaptureErrorCode = (getCaptureErrorCodeProc)GetProcAddress(capdll, "getCaptureErrorCode"); initCaptureWithOptions = (initCaptureWithOptionsProc)GetProcAddress(capdll, "initCaptureWithOptions"); + registerForDeviceNotification = (registerForDeviceNotificationProc)GetProcAddress(capdll, "registerForDeviceNotification"); + unregisterForDeviceNotification = (unregisterForDeviceNotificationProc)GetProcAddress(capdll, "unregisterForDeviceNotification"); /* Check that we got all the entry points */ if (initCOM == NULL || ESCAPIVersion == NULL || getCaptureDeviceName == NULL || + getCaptureDeviceUniqueName == NULL || countCaptureDevices == NULL || initCapture == NULL || deinitCapture == NULL || @@ -58,7 +65,9 @@ int setupESCAPI() setCaptureProperty == NULL || getCaptureErrorLine == NULL || getCaptureErrorCode == NULL || - initCaptureWithOptions == NULL) + initCaptureWithOptions == NULL || + registerForDeviceNotification == NULL || + unregisterForDeviceNotification == NULL) return 0; /* Verify DLL version is at least what we want */ diff --git a/common/escapi.h b/common/escapi.h index 5e4a909..cfabc84 100644 --- a/common/escapi.h +++ b/common/escapi.h @@ -1,4 +1,5 @@ /* Extremely Simple Capture API */ +#include struct SimpleCapParams { @@ -62,6 +63,9 @@ typedef int (*isCaptureDoneProc)(unsigned int deviceno); /* Get the user-friendly name of a capture device. */ typedef void (*getCaptureDeviceNameProc)(unsigned int deviceno, char *namebuffer, int bufferlength); +/* Get the unique name of a capture device. */ +typedef void(*getCaptureDeviceUniqueNameProc)(unsigned int deviceno, char *namebuffer, int bufferlength); + /* Returns the ESCAPI DLL version. 0x200 for 2.0 */ typedef int (*ESCAPIVersionProc)(); @@ -92,10 +96,14 @@ typedef int (*getCaptureErrorLineProc)(unsigned int deviceno); /* Return HRESULT of the catastrophic error, or 0 if none. */ typedef int (*getCaptureErrorCodeProc)(unsigned int deviceno); -/* initCaptureWithOptions allows additional options to be given. Otherwise it's identical with initCapture -*/ +/* initCaptureWithOptions allows additional options to be given. Otherwise it's identical with initCapture */ typedef int (*initCaptureWithOptionsProc)(unsigned int deviceno, struct SimpleCapParams *aParams, unsigned int aOptions); +/* Start handle camera connect/disconnect and calling callback for this events. */ +typedef void(*registerForDeviceNotificationProc)(std::function callback); +/* Stop handle camera connect/disconnect. */ +typedef void(*unregisterForDeviceNotificationProc)(); + // Options accepted by above: // Return raw data instead of converted rgb. Using this option assumes you know what you're doing. #define CAPTURE_OPTION_RAWDATA 1 @@ -110,6 +118,7 @@ extern deinitCaptureProc deinitCapture; extern doCaptureProc doCapture; extern isCaptureDoneProc isCaptureDone; extern getCaptureDeviceNameProc getCaptureDeviceName; +extern getCaptureDeviceUniqueNameProc getCaptureDeviceUniqueName; extern ESCAPIVersionProc ESCAPIVersion; extern getCapturePropertyValueProc getCapturePropertyValue; extern getCapturePropertyAutoProc getCapturePropertyAuto; @@ -117,4 +126,6 @@ extern setCapturePropertyProc setCaptureProperty; extern getCaptureErrorLineProc getCaptureErrorLine; extern getCaptureErrorCodeProc getCaptureErrorCode; extern initCaptureWithOptionsProc initCaptureWithOptions; +extern registerForDeviceNotificationProc registerForDeviceNotification; +extern unregisterForDeviceNotificationProc unregisterForDeviceNotification; #endif \ No newline at end of file diff --git a/escapi_dll/cameraeventshandler.cpp b/escapi_dll/cameraeventshandler.cpp new file mode 100644 index 0000000..09f7ad3 --- /dev/null +++ b/escapi_dll/cameraeventshandler.cpp @@ -0,0 +1,114 @@ +#include "cameraeventshandler.h" +#include + +HDEVNOTIFY gDeviceNotify = NULL; +std::thread gMsgLoop; +BOOL gIsWorking; +HWND gHwnd; +bool gIsAccept; +std::function gCallback; + +void CreateMessageOnlyWindow() +{ + static LPCWSTR class_name = (LPCWSTR)"HANDLER_CLASS"; + WNDCLASSEX wx = {}; + wx.cbSize = sizeof(WNDCLASSEX); + wx.lpfnWndProc = WndProc; + wx.hInstance = NULL; + wx.lpszClassName = class_name; + + if (RegisterClassEx(&wx)) + { + gHwnd = CreateWindowEx(0, class_name, (LPCWSTR)"handler", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + } + + DEV_BROADCAST_DEVICEINTERFACE di = { 0 }; + di.dbcc_size = sizeof(di); + di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + di.dbcc_classguid = KSCATEGORY_CAPTURE; + + gDeviceNotify = RegisterDeviceNotification( + gHwnd, + &di, + DEVICE_NOTIFY_WINDOW_HANDLE + ); + + MSG msg; + BOOL bRet; + + while (gIsWorking) + { + bRet = GetMessage(&msg, NULL, 0, 0); + if (bRet == -1) + { + PostQuitMessage(0); + break; + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_DEVICECHANGE: + { + if (lParam != 0) + { + // call only once for device connection / disconnection event + // https://stackoverflow.com/questions/44183743/wm-devicechange-comes-twice-when-disconnecting-connecting-device + gIsAccept = !gIsAccept; + if (gIsAccept) + { + switch (wParam) + { + case DBT_DEVICEARRIVAL: + gCallback(true); + break; + case DBT_DEVICEREMOVECOMPLETE: + gCallback(false); + break; + default: + // don't need to handle other wParams + break; + } + } + } + break; + } + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + return 0; +} + +void RegisterForDeviceNotificationFromInterface(const std::function& callback) +{ + gCallback = callback; + gIsWorking = true; + gMsgLoop = std::thread(&CreateMessageOnlyWindow); +} + +void UnregisterForDeviceNotificationFromInterface() +{ + if (gDeviceNotify) + UnregisterDeviceNotification(gDeviceNotify); + + gIsWorking = false; + PostMessage(gHwnd, WM_CLOSE, 0, 0); + + if (gMsgLoop.joinable()) + gMsgLoop.join(); +} diff --git a/escapi_dll/cameraeventshandler.h b/escapi_dll/cameraeventshandler.h new file mode 100644 index 0000000..a08fb53 --- /dev/null +++ b/escapi_dll/cameraeventshandler.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); +void RegisterForDeviceNotificationFromInterface(const std::function& callback); +void UnregisterForDeviceNotificationFromInterface(); \ No newline at end of file diff --git a/escapi_dll/escapi_dll.cpp b/escapi_dll/escapi_dll.cpp index d4cc124..98c4cd5 100644 --- a/escapi_dll/escapi_dll.cpp +++ b/escapi_dll/escapi_dll.cpp @@ -2,6 +2,9 @@ #define ESCAPI_DEFINITIONS_ONLY #include "escapi.h" +#include +#include +#include #define MAXDEVICES 16 @@ -13,12 +16,15 @@ extern HRESULT InitDevice(int device); extern void CleanupDevice(int device); extern int CountCaptureDevices(); extern void GetCaptureDeviceName(int deviceno, char * namebuffer, int bufferlength); +extern void GetCaptureDeviceSymbolicLink(int deviceno, char * namebuffer, int bufferlength); extern void CheckForFail(int device); extern int GetErrorCode(int device); extern int GetErrorLine(int device); extern float GetProperty(int device, int prop); extern int GetPropertyAuto(int device, int prop); extern int SetProperty(int device, int prop, float value, int autoval); +extern void RegisterForDeviceNotification(const std::function& callback); +extern void UnregisterForDeviceNotification(); BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, @@ -37,6 +43,14 @@ extern "C" void __declspec(dllexport) getCaptureDeviceName(unsigned int deviceno GetCaptureDeviceName(deviceno, namebuffer, bufferlength); } +extern "C" void __declspec(dllexport) getCaptureDeviceUniqueName(unsigned int deviceno, char *namebuffer, int bufferlength) +{ + if (deviceno > MAXDEVICES) + return; + + GetCaptureDeviceSymbolicLink(deviceno, namebuffer, bufferlength); +} + extern "C" int __declspec(dllexport) ESCAPIDLLVersion() { return 0x200; // due to mess up, earlier programs check for exact version; this needs to stay constant @@ -78,6 +92,16 @@ extern "C" void __declspec(dllexport) deinitCapture(unsigned int deviceno) CleanupDevice(deviceno); } +extern "C" void __declspec(dllexport) registerForDeviceNotification(std::function callback) +{ + RegisterForDeviceNotification(callback); +} + +extern "C" void __declspec(dllexport) unregisterForDeviceNotification() +{ + UnregisterForDeviceNotification(); +} + extern "C" void __declspec(dllexport) doCapture(unsigned int deviceno) { if (deviceno > MAXDEVICES) diff --git a/escapi_dll/escapi_dll.vcxproj b/escapi_dll/escapi_dll.vcxproj index 279b3e9..ce349c8 100644 --- a/escapi_dll/escapi_dll.vcxproj +++ b/escapi_dll/escapi_dll.vcxproj @@ -158,6 +158,7 @@ + diff --git a/escapi_dll/interface.cpp b/escapi_dll/interface.cpp index f2e4243..9b11b86 100644 --- a/escapi_dll/interface.cpp +++ b/escapi_dll/interface.cpp @@ -9,6 +9,7 @@ #include "capture.h" #include "scopedrelease.h" #include "choosedeviceparam.h" +#include "cameraeventshandler.h" #define MAXDEVICES 16 @@ -27,6 +28,7 @@ void CleanupDevice(int aDevice) gDevice[aDevice] = 0; } } + HRESULT InitDevice(int aDevice) { if (gDevice[aDevice]) @@ -43,8 +45,6 @@ HRESULT InitDevice(int aDevice) return hr; } - - int CountCaptureDevices() { HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); @@ -76,7 +76,7 @@ int CountCaptureDevices() return param.mCount; } -void GetCaptureDeviceName(int aDevice, char * aNamebuffer, int aBufferlength) +void GetCaptureName(int aDevice, char * aNamebuffer, int aBufferlength, REFGUID guidKey) { int i; if (!aNamebuffer || aBufferlength <= 0) @@ -116,7 +116,7 @@ void GetCaptureDeviceName(int aDevice, char * aNamebuffer, int aBufferlength) WCHAR *name = 0; UINT32 namelen = 255; hr = param.mDevices[aDevice]->GetAllocatedString( - MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, + guidKey, &name, &namelen ); @@ -135,6 +135,16 @@ void GetCaptureDeviceName(int aDevice, char * aNamebuffer, int aBufferlength) } } +void GetCaptureDeviceName(int aDevice, char * aNamebuffer, int aBufferlength) +{ + GetCaptureName(aDevice, aNamebuffer, aBufferlength, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME); +} + +void GetCaptureDeviceSymbolicLink(int aDevice, char * aNamebuffer, int aBufferlength) +{ + GetCaptureName(aDevice, aNamebuffer, aBufferlength, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK); +} + void CheckForFail(int aDevice) { if (!gDevice[aDevice]) @@ -198,3 +208,13 @@ int SetProperty(int aDevice, int aProp, float aValue, int aAutoval) return 0; return gDevice[aDevice]->setProperty(aProp, aValue, aAutoval); } + +void RegisterForDeviceNotification(const std::function& callback) +{ + RegisterForDeviceNotificationFromInterface(callback); +} + +void UnregisterForDeviceNotification() +{ + UnregisterForDeviceNotificationFromInterface(); +} \ No newline at end of file