如何检测HDMI线是否插入PCMCIA卡/无信号?

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

我正在尝试编写一个简单的辅助应用程序,用于在未检测到信号时提示用户打开摄像机,在这种情况下这意味着摄像机已关闭 and/or HDMI电缆未插入 PCMCIA 采集卡。如果信号存在,那么我将启动相应的录音应用程序,在本例中为 Wirecast。

我怎样才能在 VisualStudio 中使用 C# 创建它?

更新

我想我现在更接近了,通过尝试基于建议使用 GraphEdit 的评论之一的建议并查看硬件上可用的内容。我能够在捕获设备的属性中找到一个 'Signal Detected' 标志,如果摄像机是 on/off 或 HDMI 电缆被拔掉,它会从 0 变为 1,这就是我想要的。

现在,我将如何通过代码访问这个标志?我想我真的很接近,但不知道如何从 caGUID 访问 cElemspElems 的结构。 cElems returns 值为 3,这与下面屏幕截图中显示的 GraphEdit 属性 window 中显示的选项卡数量相同。 pElems returns 每次我 运行 应用程序时都有不同的值,所以我不确定该结构中发生了什么。我认为我正在寻找的标志位于这些结构中的某个地方。

代码:

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

这确实可能取决于供应商,但在使用类似设备进行测试时,供应商会在插入和移除设备时写入自定义注册表项。

  • 为了检测事件前后的注册表更改,我使用 RegShot,here 上有一个描述该过程的便捷教程。
  • 据此,一旦您确定了他们正在更新的密钥,您就可以通过 WMI 订阅注册表。查看 this answer for how to do this

我相信我刚刚弄明白了!我偶然发现了 IAMAnalogVideoDecoder 方法:get_HorizontalLocked.

此方法returns摄像机关闭时对我来说是对还是错and/orHDMI 电缆已拔出,非常适合我的需要。

DirectShowLib 可以在这里找到: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";
    }
}

我无法转换为 C#,因为我不再真正使用 Windows,但如果您可以将以下 C++ 转换为 C#,那么您可以使用它..

有一个名为 RegisterDeviceNotification 的 WinAPI,它可以让您知道设备何时插入或通过 WinProc-Callback 更改其状态..

摘自我的Github:https://github.com/Brandon-T/HDMI

另请参阅:https://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspxhttps://msdn.microsoft.com/en-us/library/windows/desktop/aa363431(v=vs.85).aspx

我在自己的项目中使用的 GUID 列表:

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

然后我创建一个class来监控特定的设备:

#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
    }
}

最后,我创建了一个 window/message-window 来监控设备:

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