20

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

klcjr89
  • 5,662
  • 9
  • 54
  • 91
  • 1
    If your capture program uses DirectShow, there's probably a COM interface that exposes information on physical ports available on the input hardware. I suggest looking at the DirectShow "GraphEdit" program to build a graph for reading multimedia streams from your hardware and seeing what's exposed. – Dai Apr 09 '16 at 01:08
  • 1
    http://www.codeproject.com/Articles/21503/Hardware-Helper-Library-for-C – Mikes3ds Apr 09 '16 at 01:15
  • @Mikes3ds , I dont think that library would help? The PCI card I'm using always remains attached, but I need to know if the HDMI cable is plugged into this card and if it has a signal present. It looks like that library would only be for detecting if the card itself was added or removed. – klcjr89 Apr 09 '16 at 10:10
  • The lib that @Mikes3ds might work, if you check the contents of m.Msg in WndProc when you pull the cable – Rots Apr 11 '16 at 04:08
  • @Rots , even if the cable is plugged in doesn't mean the camcorder is powered on however. – klcjr89 Apr 11 '16 at 05:14
  • Take a look here: http://www.drdobbs.com/embedded-systems/writing-pcmcia-software/184409254 – Maciej Los Apr 11 '16 at 17:59
  • You might be interested in this too: https://courses.engr.illinois.edu/ece412/References/pcmcia/PCMCIA-Programming.pdf – Maciej Los Apr 11 '16 at 18:07
  • 1
    @MaciejLos thanks for the links, even though I don't think those help me out. – klcjr89 Apr 11 '16 at 18:50
  • 1
    I don't think there's a general mechanism for getting information available in the filter properties. In cases like this your best bet would be to use the SDK from the card vendor. At least contact them, if you haven't yet. But here's some other wild ideas: try referencing the COM component in VS to get an RCW assembly; display the filter's info panel offscreen, then hack through the controls to extract the info you need; decompile the vendor-supplied software to see how it's using the dshow COM interfaces offered by the vendor's drivers. – zeromus Apr 14 '16 at 04:36
  • The vendor doesn't have an SDK unfortunately, StarTech is the supplier of the capture card. Right now I'm trying to see if I can find a change in the Windows registry when I unplug the camcorder. – klcjr89 Apr 14 '16 at 05:01
  • I'm pretty certain it has to be somewhere in the registry, if I change the ':Video Standard' dropdown option, it persists even without prompting to re-save the filter. This makes me think the 'Signal Detected' flag is somewhere in the registry. – klcjr89 Apr 14 '16 at 06:41
  • Maybe check [this answer](http://stackoverflow.com/questions/10822226/how-do-i-check-if-my-directshow-renderer-filter-is-being-used) and [msdn on intelligent connect](https://msdn.microsoft.com/en-us/library/windows/desktop/dd390342(v=vs.85).aspx) – Reinstate Monica Apr 14 '16 at 10:28
  • The capture card conforms to kernel streaming proxy (KSProxy), wouldn't I be able to get events for it? – klcjr89 Apr 14 '16 at 16:24

3 Answers3

5

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;
}
Brandon
  • 20,445
  • 9
  • 73
  • 162
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";
    }
}
klcjr89
  • 5,662
  • 9
  • 54
  • 91
0

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
Community
  • 1
  • 1
BMac
  • 2,026
  • 3
  • 20
  • 29