Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add callback handle for camera connection / disconnection and unique name getter #8

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion common/escapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ deinitCaptureProc deinitCapture;
doCaptureProc doCapture;
isCaptureDoneProc isCaptureDone;
getCaptureDeviceNameProc getCaptureDeviceName;
getCaptureDeviceUniqueNameProc getCaptureDeviceUniqueName;
ESCAPIVersionProc ESCAPIVersion;
getCapturePropertyValueProc getCapturePropertyValue;
getCapturePropertyAutoProc getCapturePropertyAuto;
setCapturePropertyProc setCaptureProperty;
getCaptureErrorLineProc getCaptureErrorLine;
getCaptureErrorCodeProc getCaptureErrorCode;
initCaptureWithOptionsProc initCaptureWithOptions;
registerForDeviceNotificationProc registerForDeviceNotification;
unregisterForDeviceNotificationProc unregisterForDeviceNotification;


/* Internal: initialize COM */
Expand All @@ -35,19 +38,23 @@ 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");
setCaptureProperty = (setCapturePropertyProc)GetProcAddress(capdll, "setCaptureProperty");
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 ||
Expand All @@ -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 */
Expand Down
15 changes: 13 additions & 2 deletions common/escapi.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Extremely Simple Capture API */
#include <functional>

struct SimpleCapParams
{
Expand Down Expand Up @@ -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)();

Expand Down Expand Up @@ -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<void(bool isArrival)> 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
Expand All @@ -110,11 +118,14 @@ extern deinitCaptureProc deinitCapture;
extern doCaptureProc doCapture;
extern isCaptureDoneProc isCaptureDone;
extern getCaptureDeviceNameProc getCaptureDeviceName;
extern getCaptureDeviceUniqueNameProc getCaptureDeviceUniqueName;
extern ESCAPIVersionProc ESCAPIVersion;
extern getCapturePropertyValueProc getCapturePropertyValue;
extern getCapturePropertyAutoProc getCapturePropertyAuto;
extern setCapturePropertyProc setCaptureProperty;
extern getCaptureErrorLineProc getCaptureErrorLine;
extern getCaptureErrorCodeProc getCaptureErrorCode;
extern initCaptureWithOptionsProc initCaptureWithOptions;
extern registerForDeviceNotificationProc registerForDeviceNotification;
extern unregisterForDeviceNotificationProc unregisterForDeviceNotification;
#endif
114 changes: 114 additions & 0 deletions escapi_dll/cameraeventshandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include "cameraeventshandler.h"
#include <thread>

HDEVNOTIFY gDeviceNotify = NULL;
std::thread gMsgLoop;
BOOL gIsWorking;
HWND gHwnd;
bool gIsAccept;
std::function<void(bool isArrival)> 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<void(bool isArrival)>& 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();
}
13 changes: 13 additions & 0 deletions escapi_dll/cameraeventshandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <Windows.h>
#include <Dbt.h>
#include <ks.h>
#include <ksmedia.h>
#include <functional>

#include <Mfidl.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void RegisterForDeviceNotificationFromInterface(const std::function<void(bool isArrival)>& callback);
void UnregisterForDeviceNotificationFromInterface();
24 changes: 24 additions & 0 deletions escapi_dll/escapi_dll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#define ESCAPI_DEFINITIONS_ONLY
#include "escapi.h"

#include <Dbt.h>
#include <ks.h>
#include <ksmedia.h>

#define MAXDEVICES 16

Expand All @@ -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<void(bool isArrival)>& callback);
extern void UnregisterForDeviceNotification();

BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
Expand All @@ -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
Expand Down Expand Up @@ -78,6 +92,16 @@ extern "C" void __declspec(dllexport) deinitCapture(unsigned int deviceno)
CleanupDevice(deviceno);
}

extern "C" void __declspec(dllexport) registerForDeviceNotification(std::function<void(bool isArrival)> callback)
{
RegisterForDeviceNotification(callback);
}

extern "C" void __declspec(dllexport) unregisterForDeviceNotification()
{
UnregisterForDeviceNotification();
}

extern "C" void __declspec(dllexport) doCapture(unsigned int deviceno)
{
if (deviceno > MAXDEVICES)
Expand Down
1 change: 1 addition & 0 deletions escapi_dll/escapi_dll.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="cameraeventshandler.cpp" />
<ClCompile Include="capture.cpp" />
<ClCompile Include="conversion.cpp" />
<ClCompile Include="escapi_dll.cpp" />
Expand Down
28 changes: 24 additions & 4 deletions escapi_dll/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "capture.h"
#include "scopedrelease.h"
#include "choosedeviceparam.h"
#include "cameraeventshandler.h"

#define MAXDEVICES 16

Expand All @@ -27,6 +28,7 @@ void CleanupDevice(int aDevice)
gDevice[aDevice] = 0;
}
}

HRESULT InitDevice(int aDevice)
{
if (gDevice[aDevice])
Expand All @@ -43,8 +45,6 @@ HRESULT InitDevice(int aDevice)
return hr;
}



int CountCaptureDevices()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
);
Expand All @@ -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])
Expand Down Expand Up @@ -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<void(bool isArrival)>& callback)
{
RegisterForDeviceNotificationFromInterface(callback);
}

void UnregisterForDeviceNotification()
{
UnregisterForDeviceNotificationFromInterface();
}