I'm trying to write a simple helper application that is used to prompt the user to turn on a camcorder if no signal is detected, which in this case would mean the camcorder is off and/or the HDMI cable is not plugged into the PCMCIA capture card. If the signal is present, then I launch the respective recording application, in this case Wirecast.
How could I perhaps go about creating this using C# in VisualStudio?
Update
I think I'm alot closer now by trying a suggestion based in one of the comments suggesting to use GraphEdit and seeing what is available on the hardware. I was able to find within the properties of the capture device, a 'Signal Detected' flag that changes from 0 to 1 if the camcorder is on/off or the HDMI cable is unplugged, which is what I want.
Now, How would I go about accessing this flag through code? I think I'm really close, but don't know how to access the structure of cElems
and pElems
from the caGUID
. cElems
returns a value of 3, which is the same number of tabs displayed in the GraphEdit property window shown below in a screenshot. pElems
returns a different value every time I run the app, so I'm not sure what's going on in that structure. I would think the flag I'm looking for lies somewhere within those structures.
Code:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using DirectShowLib; namespace Test { static class Program { [STAThread] static void Main() { using (System.Threading.Mutex mutex = new System.Threading.Mutex(false, "Global\\" + appGuid)) { if (!mutex.WaitOne(0, false)) { return; } DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); foreach (var dev in capDevices) { if (dev.DevicePath == @"@device:pnp:\\?\pci#ven_1131&dev_7160&subsys_12abf50a&rev_03#6&37bccbbe&0&000800e1#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\{6f814be9-9af6-43cf-9249-c0340100021c}") { IFilterGraph2 m_FilterGraph = (IFilterGraph2)new FilterGraph(); IBaseFilter capFilter = null; ICaptureGraphBuilder2 capGraph = null; capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2(); int hr; hr = capGraph.SetFiltergraph(m_FilterGraph); hr = m_FilterGraph.AddSourceFilterForMoniker(dev.Mon, null, dev.Name, out capFilter); ISpecifyPropertyPages pProp = capFilter as ISpecifyPropertyPages; FilterInfo filterInfo; hr = capFilter.QueryFilterInfo(out filterInfo); DsCAUUID caGUID; hr = pProp.GetPages(out caGUID); Console.WriteLine(caGUID.cElems); Console.WriteLine(caGUID.pElems); // caGUID.cElems returns '3', which is the correct number of tabs in the property pages shown in GraphEdit. // caGUID.pElems returns a different value every time break; } } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } private static string appGuid = "z0a76b5a-02cd-15c5-b9d9-d303zcdde7b9"; } }
3 Answers
Answers 1
I can't translate to C# because I no longer use Windows really, but if you are fine with translating the following C++ to C# then you can use it..
There is this WinAPI called RegisterDeviceNotification
that lets you know when a device is plugged in or its state changed via a WinProc-Callback..
Taken from my Github: https://github.com/Brandon-T/HDMI
See also: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa363431(v=vs.85).aspx
List of GUID's that I've used in my own projects:
GUID devices[] = { {0x4D36E96E, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}, //PlugNPlay Display {0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}, //GUID_DEVINTERFACE_USB_DEVICE {0x0850302A, 0xB344, 0x4FDA, 0x9B, 0xE9, 0x90, 0x57, 0x6B, 0x8D, 0x46, 0xF0}, //GUID_BTHPORT_DEVICE_INTERFACE {0xE6F07B5F, 0xEE97, 0x4a90, 0xB0, 0x76, 0x33, 0xF5, 0x7B, 0xF4, 0xEA, 0xA7}, //GUID_DEVINTERFACE_MONITOR {0x1CA05180, 0xA699, 0x450A, 0x9A, 0x0C, 0xDE, 0x4F, 0xBE, 0x3D, 0xDD, 0x89}, //GUID_DISPLAY_DEVICE_ARRIVAL {0x5B45201D, 0xF2F2, 0x4F3B, 0x85, 0xBB, 0x30, 0xFF, 0x1F, 0x95, 0x35, 0x99}, //GUID_DEVINTERFACE_DISPLAY_ADAPTER {0x1AD9E4F0, 0xF88D, 0x4360, 0xBA, 0xB9, 0x4C, 0x2D, 0x55, 0xE5, 0x64, 0xCD}, //GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL };
Then I create a class to monitor a specific device:
#include <windows.h> #include <dbt.h> #include <algorithm> class Device { private: HDEVNOTIFY hNotify; public: Device() : hNotify(NULL) {} Device(HWND window, GUID dev_guid); Device(Device&& dev) : hNotify(NULL) {std::swap(hNotify, dev.hNotify);} ~Device() {UnregisterDeviceNotification(hNotify);} Device(const Device& dev) = delete; Device& operator = (const Device& dev) = delete; Device& operator = (Device&& dev) {std::swap(hNotify, dev.hNotify);return *this;} }; Device::Device(HWND window, GUID dev_guid) : hNotify(NULL) { if (window) { DEV_BROADCAST_DEVICEINTERFACE filter; memset(&filter, 0, sizeof(filter)); filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; filter.dbcc_classguid = dev_guid; hNotify = RegisterDeviceNotification(window, &filter, DEVICE_NOTIFY_WINDOW_HANDLE); //DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES } }
Finally, I create a window/message-window to monitor the devices:
int Create() { WNDCLASSEX wx = {0}; wx.cbSize = sizeof(WNDCLASSEX); wx.lpfnWndProc = WndProc; wx.hInstance = GetModuleHandle(NULL); wx.lpszClassName = "HDMI_MONITOR"; if (RegisterClassEx(&wx)) { MSG msg = {0}; CreateWindowEx(0, "HDMI_MONITOR", "HDMI_MONITOR", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); while(GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } return 0; } //The callback function: LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static std::unique_ptr<Device> dev; switch(msg) { case WM_CREATE: { dev.reset(new Device(hwnd, devices[0])); //GUID for plug-n-play devices.. } break; case WM_DEVICECHANGE: { DEV_BROADCAST_DEVICEINTERFACE* info = (DEV_BROADCAST_DEVICEINTERFACE*) lParam; switch(wParam) { case DBT_DEVICEARRIVAL: std::cout<<"Device was plugged in\n"; break; case DBT_DEVICEREMOVECOMPLETE: std::cout<<"Device was un-plugged in\n"; break; default: std::cout<<"wParam: "<<(void*)wParam<<"\n"; break; } } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; }
Answers 2
This really may depend on the vendor, but testing with a similar device, the vendor is writing to a custom registry key when the device is plugged in and removed.
- To detect registry changes before and after an event I use RegShot, there is a handy tutorial over here that describes the process.
- From this, once you have hopefully determined what key they are updating, you can subscribe via WMI to the Registry. Check out this answer for how to do this
Answers 3
I believe I just figured it out! I stumbled upon the IAMAnalogVideoDecoder
method: get_HorizontalLocked
.
This method returns true or false for me when the camcorder is off and/or the HDMI cable is unplugged, which is perfect for my needs.
DirectShowLib
can be found here: https://sourceforge.net/projects/directshownet/files/
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using DirectShowLib; namespace Test { static class Program { [STAThread] static void Main() { using (System.Threading.Mutex mutex = new System.Threading.Mutex(false, "Global\\" + appGuid)) { if (!mutex.WaitOne(0, false)) { return; } DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); foreach (var device in capDevices) { if (device.DevicePath == @"@device:pnp:\\?\pci#ven_1131&dev_7160 &subsys_12abf50a&rev_03#6&37bccbbe&0&000800e1#{65e8773d-8f56 -11d0-a3b9-00a0c9223196}\{6f814be9-9af6-43cf -9249-c0340100021c}") { IFilterGraph2 m_FilterGraph = (IFilterGraph2)new FilterGraph(); IBaseFilter capFilter = null; ICaptureGraphBuilder2 capGraph = null; capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2(); int hr; hr = capGraph.SetFiltergraph(m_FilterGraph); hr = m_FilterGraph.AddSourceFilterForMoniker(device.Mon, null, device.Name, out capFilter); IAMAnalogVideoDecoder videoDec = capFilter as IAMAnalogVideoDecoder; bool signalDetected = false; hr = videoDec.get_HorizontalLocked(out signalDetected); if (signalDetected == true) { System.Diagnostics.Process.Start( @"C:\Users\PC\Documents\HIDDEN_FOLDER\WirecastLaunch.wcst"); return; } else { // Poll for 'signal' change } break; } } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } private static string appGuid = "z0a76b5a-02cd-15c5-b9d9-d303zcdde7b9"; } }
0 comments:
Post a Comment