Sunday, April 17, 2016

How to detect if HDMI cable is plugged into PCMCIA card / no signal?

Leave a Comment

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";     } } 

enter image description hereenter image description here

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";     } } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment