如何检测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
访问 cElems
和 pElems
的结构。 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).aspx
和
https://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;
}
我正在尝试编写一个简单的辅助应用程序,用于在未检测到信号时提示用户打开摄像机,在这种情况下这意味着摄像机已关闭 and/or HDMI电缆未插入 PCMCIA 采集卡。如果信号存在,那么我将启动相应的录音应用程序,在本例中为 Wirecast。
我怎样才能在 VisualStudio 中使用 C# 创建它?
更新
我想我现在更接近了,通过尝试基于建议使用 GraphEdit 的评论之一的建议并查看硬件上可用的内容。我能够在捕获设备的属性中找到一个 'Signal Detected' 标志,如果摄像机是 on/off 或 HDMI 电缆被拔掉,它会从 0 变为 1,这就是我想要的。
现在,我将如何通过代码访问这个标志?我想我真的很接近,但不知道如何从 caGUID
访问 cElems
和 pElems
的结构。 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).aspx 和 https://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;
}