基于"如何通过DirectShow用C++实现PTZ相机的控制?"的脚本,我们来实现一些优化和更新。
1.增加三个函数

PTZ_GetCameraCount() 获取相机的数量
PTZ_GetCameraName(int cameraIndex) 通过相机的索引获取相机的名字
PTZ_InitByIndex(void* handle, int cameraIndex) 通过相机索引获取相机的句柄(实际上是一个结构体 我要把c++文件做成dll导入unity当中 通过类型强转可以实现c++数据和c++++数据的转换)

2.增加了对虚拟相机设备的跳过

3.源代码
cpp
#include<Windows.h>
#include<dshow.h> // DirectShow 头文件
#include<iostream>
#pragma comment(lib, "strmiids.lib") // 链接 DirectShow 系统库
// Movement step size
const int PAN_STEP = 10;
const int TILT_STEP = 10;
const int ZOOM_STEP = 10;
// Safety limit: prevent going too low
const long TILT_DOWN_LIMIT = -60;
// Maximum number of cameras we can list
const int MAX_CAMERA_COUNT = 32;
// -----------------------------------------------------------------------------
// Check if camera should be filtered out (skip it)
// Add your own filter keywords here
// -----------------------------------------------------------------------------
inline bool ShouldSkipCamera(const wchar_t* camName) // 内联函数
{
if (!camName || !camName[0]) return false;
// Add keywords to skip:
if (wcsstr(camName, L"Virtual Camera") != nullptr) return true;
if (wcsstr(camName, L"STUDIO CAMERA") != nullptr) return true;
if (wcsstr(camName, L"virtual") != nullptr) return true;
if (wcsstr(camName, L"OBS") != nullptr) return true;
return false; // Keep this camera
}
struct PTZCam {
IAMCameraControl* pCamCtrl; // IAMCameraControl 是 Windows 系统提供的相机控制接口
wchar_t camName[256]; // Windows 系统底层(COM、DirectShow、设备名)全部用 Unicode 宽字符串
long minPan;
long maxPan;
long currPan;
long minTilt;
long maxTilt;
long currTilt;
long minZoom;
long maxZoom;
long currZoom;
bool zoomSupported;
bool comInitialized; // 是否由我们初始化了 COM
PTZCam() :
pCamCtrl(nullptr),
minPan(0),
maxPan(0),
currPan(0),
minTilt(0),
maxTilt(0),
currTilt(0),
minZoom(0),
maxZoom(0),
currZoom(0),
zoomSupported(false),
comInitialized(false)
{
camName[0] = 0;
}
};
#pragma region 原始函数声明(内部用)
bool PTZ_InitCam(PTZCam* cam, int cameraIndex); // 初始化指定索引的相机
void PTZ_Release(PTZCam* cam); // 释放相机资源
bool PTZ_QueryCapabilities(PTZCam* cam); // 获取硬件信息
bool PTZ_SetPan(PTZCam* cam, long panVal);
bool PTZ_SetTilt(PTZCam* cam, long tiltVal);
bool PTZ_SetZoom(PTZCam* cam, long zoomVal);
void PTZ_PanLeft(PTZCam* cam);
void PTZ_PanRight(PTZCam* cam);
void PTZ_TiltUp(PTZCam* cam);
void PTZ_TiltDown(PTZCam* cam);
void PTZ_ZoomIn(PTZCam* cam);
void PTZ_ZoomOut(PTZCam* cam);
#pragma endregion
// DLL export interface for Unity C#
extern "C" {
__declspec(dllexport) void* PTZ_Create();
__declspec(dllexport) void PTZ_Destroy(void* handle);
// === Multiple camera support ===
__declspec(dllexport) int PTZ_GetCameraCount();
__declspec(dllexport) const wchar_t* PTZ_GetCameraName(int cameraIndex);
__declspec(dllexport) bool PTZ_InitByIndex(void* handle, int cameraIndex);
__declspec(dllexport) bool PTZ_Init(void* handle); // Legacy: initialize first camera (index 0)
__declspec(dllexport) void PTZ_Release(void* handle);
__declspec(dllexport) bool PTZ_QueryCapabilities(void* handle);
__declspec(dllexport) bool PTZ_SetPan(void* handle, long panVal);
__declspec(dllexport) bool PTZ_SetTilt(void* handle, long tiltVal);
__declspec(dllexport) bool PTZ_SetZoom(void* handle, long zoomVal);
__declspec(dllexport) void PTZ_PanLeft(void* handle);
__declspec(dllexport) void PTZ_PanRight(void* handle);
__declspec(dllexport) void PTZ_TiltUp(void* handle);
__declspec(dllexport) void PTZ_TiltDown(void* handle);
__declspec(dllexport) void PTZ_ZoomIn(void* handle);
__declspec(dllexport) void PTZ_ZoomOut(void* handle);
__declspec(dllexport) long PTZ_GetMinPan(void* handle);
__declspec(dllexport) long PTZ_GetMaxPan(void* handle);
__declspec(dllexport) long PTZ_GetCurrentPan(void* handle);
__declspec(dllexport) long PTZ_GetMinTilt(void* handle);
__declspec(dllexport) long PTZ_GetMaxTilt(void* handle);
__declspec(dllexport) long PTZ_GetCurrentTilt(void* handle);
__declspec(dllexport) long PTZ_GetMinZoom(void* handle);
__declspec(dllexport) long PTZ_GetMaxZoom(void* handle);
__declspec(dllexport) long PTZ_GetCurrentZoom(void* handle);
__declspec(dllexport) bool PTZ_IsZoomSupported(void* handle);
} // extern "C"
// DLL 入口
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
#pragma region 函数实现
bool PTZ_InitCam(PTZCam* cam, int cameraIndex) {
if (!cam) {
std::cout << "PTZ_InitCam: cam is null" << std::endl;
return false;
}
if (cameraIndex < 0) {
std::cout << "PTZ_InitCam: cameraIndex " << cameraIndex << " invalid" << std::endl;
return false;
}
// HRESULT 是 Windows API 的返回值类型用来表示 成功 / 失败
// CoInitializeEx 是扩展版的 COM 初始化函数,COM 是 Component Object Model
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (SUCCEEDED(hr)) {
cam->comInitialized = true; // 是我们初始化的,释放时我们来卸载
}
else if (hr == RPC_E_CHANGED_MODE || hr == S_FALSE) {
// COM 已经被初始化了(Unity 已经初始化了),不用我们管
cam->comInitialized = false;
}
else {
std::cout << "PTZ_InitCam: CoInitializeEx failed, hr=0x" << std::hex << hr << std::endl;
return false;
}
ICreateDevEnum* pDevEnum = nullptr; // 创建"设备扫描器",用来扫描电脑里的摄像头
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
(void**)&pDevEnum);
if (FAILED(hr))
{
std::cout << "PTZ_InitCam: CoCreateInstance failed, hr=0x" << std::hex << hr << std::endl;
if (cam->comInitialized) CoUninitialize();
return false;
}
IEnumMoniker* pEnum = nullptr;
// 把扫描到的相机列表存到 pEnum 里
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
if (FAILED(hr) || !pEnum)
{
std::cout << "PTZ_InitCam: CreateClassEnumerator failed, hr=0x" << std::hex << hr << std::endl;
pDevEnum->Release();
if (cam->comInitialized) CoUninitialize();
return false;
}
IMoniker* pMoniker = nullptr; // 设备条目指针,相机的"名字/路径/信息包"
// Skip to the requested camera index (also skip filtered cameras)
int currentValidIndex = 0;
bool found = false;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
{
bool shouldSkip = false;
IPropertyBag* pPropBag = nullptr;
HRESULT hrName = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
if (SUCCEEDED(hrName)) {
VARIANT var;
VariantInit(&var);
hrName = pPropBag->Read(L"FriendlyName", &var, 0);
if (SUCCEEDED(hrName)) {
if (ShouldSkipCamera(var.bstrVal)) {
shouldSkip = true;
}
VariantClear(&var);
}
pPropBag->Release();
}
if (!shouldSkip) {
if (currentValidIndex == cameraIndex) {
found = true; // Found target camera
break;
}
currentValidIndex++;
pMoniker->Release();
pMoniker = nullptr;
}
else {
pMoniker->Release();
pMoniker = nullptr;
}
}
if (!found)
{
std::cout << "PTZ_InitCam: Camera index " << cameraIndex << " not found after filtering" << std::endl;
pEnum->Release();
pDevEnum->Release();
if (cam->comInitialized) CoUninitialize();
return false;
}
// 获取相机名称
IPropertyBag* pPropBag = nullptr;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
if (SUCCEEDED(hr))
{
VARIANT var;
VariantInit(&var);
hr = pPropBag->Read(L"FriendlyName", &var, 0);
if (SUCCEEDED(hr))
{
// 把相机名字存到结构体里
wcscpy_s(cam->camName, var.bstrVal);
VariantClear(&var);
}
pPropBag->Release();
}
IBaseFilter* pFilter = nullptr; // 设备过滤器指针,相机的"驱动层接口"
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
if (SUCCEEDED(hr))
{
// 向相机请求获取 PTZ 遥控器
hr = pFilter->QueryInterface(IID_IAMCameraControl, (void**)&cam->pCamCtrl);
if (FAILED(hr)) {
std::cout << "PTZ_InitCam: QueryInterface IAMCameraControl failed, hr=0x" << std::hex << hr << std::endl;
}
pFilter->Release();
}
else {
std::cout << "PTZ_InitCam: BindToObject failed, hr=0x" << std::hex << hr << std::endl;
}
pMoniker->Release();
pEnum->Release();
pDevEnum->Release();
if (cam->pCamCtrl == nullptr) {
std::cout << "PTZ_InitCam: pCamCtrl is null - camera does not support PTZ control" << std::endl;
}
return cam->pCamCtrl != nullptr;
}
void PTZ_Release(PTZCam* cam) {
if (!cam) return;
if (cam->pCamCtrl) {
cam->pCamCtrl->Release();
cam->pCamCtrl = nullptr;
}
// 只有我们自己初始化的 COM,才需要卸载
if (cam->comInitialized) {
CoUninitialize();
}
}
bool PTZ_QueryCapabilities(PTZCam* cam) {
if (!cam || !cam->pCamCtrl) return false;
long stepPan, defPan, flagsPan; // 补足参数
HRESULT hrPan = cam->pCamCtrl->GetRange(0, &cam->minPan, &cam->maxPan, &stepPan, &defPan, &flagsPan);
if (FAILED(hrPan)) return false;
long currValPan, currFlagsPan;
// 获取Pan当前值 0表示Pan
cam->pCamCtrl->Get(0, &currValPan, &currFlagsPan);
cam->currPan = currValPan;
cam->pCamCtrl->Set(0, cam->currPan, 1); // 0表示Pan 1表示手动控制模式
long stepTilt, defTilt, flagsTilt;
HRESULT hrTilt = cam->pCamCtrl->GetRange(1, &cam->minTilt, &cam->maxTilt, &stepTilt, &defTilt, &flagsTilt);
if (FAILED(hrTilt)) return false;
long currValTilt, currFlagsTilt;
cam->pCamCtrl->Get(1, &currValTilt, &currFlagsTilt);
cam->currTilt = currValTilt;
cam->pCamCtrl->Set(1, cam->currTilt, 1);// 1表示Tilt 1表示手动控制模式
long stepZoom, defZoom, flagsZoom;
HRESULT hrZoom = cam->pCamCtrl->GetRange(3, &cam->minZoom, &cam->maxZoom, &stepZoom, &defZoom, &flagsZoom);
cam->zoomSupported = SUCCEEDED(hrZoom);
if (cam->zoomSupported)
{
long currValZoom, currFlagsZoom;
cam->pCamCtrl->Get(3, &currValZoom, &currFlagsZoom);
cam->currZoom = currValZoom;
cam->pCamCtrl->Set(3, cam->currZoom, 1);// 3表示Zoom 1表示手动控制模式
}
return true;
}
bool PTZ_SetPan(PTZCam* cam, long panVal) {
if (!cam || !cam->pCamCtrl) return false;
if (panVal < cam->minPan) panVal = cam->minPan;
if (panVal > cam->maxPan) panVal = cam->maxPan;
cam->currPan = panVal;
HRESULT hr = cam->pCamCtrl->Set(0, panVal, 1);
return SUCCEEDED(hr);
}
bool PTZ_SetTilt(PTZCam* cam, long tiltVal) {
if (!cam || !cam->pCamCtrl) return false;
if (tiltVal < cam->minTilt) tiltVal = cam->minTilt;
if (tiltVal > cam->maxTilt) tiltVal = cam->maxTilt;
cam->currTilt = tiltVal;
HRESULT hr = cam->pCamCtrl->Set(1, tiltVal, 1);
return SUCCEEDED(hr);
}
bool PTZ_SetZoom(PTZCam* cam, long zoomVal) {
if (!cam || !cam->pCamCtrl) return false;
if (!cam->zoomSupported) return false;
if (zoomVal < cam->minZoom) zoomVal = cam->minZoom;
if (zoomVal > cam->maxZoom) zoomVal = cam->maxZoom;
cam->currZoom = zoomVal;
HRESULT hr = cam->pCamCtrl->Set(3, zoomVal, 1);
return SUCCEEDED(hr);
}
void PTZ_PanLeft(PTZCam* cam)
{
if (!cam || !cam->pCamCtrl) return;
cam->currPan -= PAN_STEP;
if (cam->currPan < cam->minPan)
cam->currPan = cam->minPan;
PTZ_SetPan(cam, cam->currPan);
std::cout << "← Pan Left (pan=" << cam->currPan << ")" << std::endl;
}
void PTZ_PanRight(PTZCam* cam)
{
if (!cam || !cam->pCamCtrl) return;
cam->currPan += PAN_STEP;
if (cam->currPan > cam->maxPan)
cam->currPan = cam->maxPan;
PTZ_SetPan(cam, cam->currPan);
std::cout << "→ Pan Right (pan=" << cam->currPan << ")" << std::endl;
}
void PTZ_TiltUp(PTZCam* cam)
{
if (!cam || !cam->pCamCtrl) return;
cam->currTilt += TILT_STEP;
if (cam->currTilt > cam->maxTilt)
cam->currTilt = cam->maxTilt;
PTZ_SetTilt(cam, cam->currTilt);
std::cout << "↑ Tilt Up (tilt=" << cam->currTilt << ")" << std::endl;
}
void PTZ_TiltDown(PTZCam* cam)
{
if (!cam || !cam->pCamCtrl) return;
cam->currTilt -= TILT_STEP;
if (cam->currTilt < cam->minTilt)
cam->currTilt = cam->minTilt;
if (cam->currTilt < TILT_DOWN_LIMIT) // 限制向下
cam->currTilt = TILT_DOWN_LIMIT;
PTZ_SetTilt(cam, cam->currTilt);
std::cout << "↓ Tilt Down (tilt=" << cam->currTilt << ")" << std::endl;
}
void PTZ_ZoomIn(PTZCam* cam)
{
if (!cam || !cam->pCamCtrl || !cam->zoomSupported) return;
cam->currZoom += ZOOM_STEP;
if (cam->currZoom > cam->maxZoom)
cam->currZoom = cam->maxZoom;
PTZ_SetZoom(cam, cam->currZoom);
std::cout << "+ Zoom In (zoom=" << cam->currZoom << ")" << std::endl;
}
void PTZ_ZoomOut(PTZCam* cam)
{
if (!cam || !cam->pCamCtrl || !cam->zoomSupported) return;
cam->currZoom -= ZOOM_STEP;
if (cam->currZoom < cam->minZoom)
cam->currZoom = cam->minZoom;
PTZ_SetZoom(cam, cam->currZoom);
std::cout << "- Zoom Out (zoom=" << cam->currZoom << ")" << std::endl;
}
#pragma endregion
#pragma region DLL 导出接口实现
extern "C" void* PTZ_Create() {
return new PTZCam();
}
extern "C" void PTZ_Destroy(void* handle) {
if (handle) {
delete (PTZCam*)handle;
}
}
extern "C" int PTZ_GetCameraCount() {
// Count how many video input devices are there (filtered)
HRESULT hr;
ICreateDevEnum* pDevEnum = nullptr;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum);
if (FAILED(hr)) return 0;
IEnumMoniker* pEnum = nullptr;
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
if (FAILED(hr) || !pEnum) {
pDevEnum->Release();
return 0;
}
int count = 0;
IMoniker* pMoniker = nullptr;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
bool shouldSkip = false;
IPropertyBag* pPropBag = nullptr;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
if (SUCCEEDED(hr)) {
VARIANT var;
VariantInit(&var);
hr = pPropBag->Read(L"FriendlyName", &var, 0);
if (SUCCEEDED(hr)) {
if (ShouldSkipCamera(var.bstrVal)) {
shouldSkip = true;
}
VariantClear(&var);
}
pPropBag->Release();
}
if (!shouldSkip) {
count++;
}
pMoniker->Release();
}
pEnum->Release();
pDevEnum->Release();
return count;
}
// -----------------------------------------------------------------------------
// 获取指定索引相机的设备名称 (skips filtered cameras)
// 参数: cameraIndex - 相机索引,从 0 开始
// 返回: 宽字符串指针,C# 用 Marshal.PtrToStringUni() 转成 string
// 失败返回空字符串
// -----------------------------------------------------------------------------
extern "C" const wchar_t* PTZ_GetCameraName(int cameraIndex) {
// 参数有效性检查
if (cameraIndex < 0) return L"";
// 静态缓冲区存储结果,生命周期由DLL维护
// 单次调用足够存放相机名称,多线程不影响(Unity主线程调用)
static wchar_t nameBuffer[256];
nameBuffer[0] = 0;
HRESULT hr;
ICreateDevEnum* pDevEnum = nullptr;
// 创建设备枚举器
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pDevEnum);
if (FAILED(hr)) return L"";
IEnumMoniker* pEnum = nullptr;
// 枚举视频输入设备类别(摄像头)
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
if (FAILED(hr) || !pEnum) {
pDevEnum->Release();
return L"";
}
IMoniker* pMoniker = nullptr;
bool found = false;
int currentValidIndex = 0;
// 遍历所有设备,跳过被过滤的,直到找到目标索引
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
bool shouldSkip = false;
IPropertyBag* pPropBag = nullptr;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
if (SUCCEEDED(hr)) {
VARIANT var;
VariantInit(&var);
hr = pPropBag->Read(L"FriendlyName", &var, 0);
if (SUCCEEDED(hr)) {
if (ShouldSkipCamera(var.bstrVal)) {
shouldSkip = true;
}
else {
// Not skipped, check if this is our target
if (currentValidIndex == cameraIndex) {
// Found it!
wcsncpy_s(nameBuffer, 256, var.bstrVal, _TRUNCATE);
found = true;
}
currentValidIndex++;
}
VariantClear(&var);
}
pPropBag->Release();
}
if (!found) {
pMoniker->Release();
}
if (found) break;
}
// 释放COM资源
if (!found && pMoniker) {
pMoniker->Release();
}
pEnum->Release();
pDevEnum->Release();
return nameBuffer;
}
extern "C" bool PTZ_InitByIndex(void* handle, int cameraIndex) {
return PTZ_InitCam((PTZCam*)handle, cameraIndex);
}
extern "C" bool PTZ_Init(void* handle) {
return PTZ_InitCam((PTZCam*)handle, 0); // Legacy: init first camera (index 0)
}
extern "C" void PTZ_Release(void* handle) {
PTZ_Release((PTZCam*)handle);
}
extern "C" bool PTZ_QueryCapabilities(void* handle) {
return PTZ_QueryCapabilities((PTZCam*)handle);
}
extern "C" bool PTZ_SetPan(void* handle, long panVal) {
return PTZ_SetPan((PTZCam*)handle, panVal);
}
extern "C" bool PTZ_SetTilt(void* handle, long tiltVal) {
return PTZ_SetTilt((PTZCam*)handle, tiltVal);
}
extern "C" bool PTZ_SetZoom(void* handle, long zoomVal) {
return PTZ_SetZoom((PTZCam*)handle, zoomVal);
}
extern "C" void PTZ_PanLeft(void* handle) {
PTZ_PanLeft((PTZCam*)handle);
}
extern "C" void PTZ_PanRight(void* handle) {
PTZ_PanRight((PTZCam*)handle);
}
extern "C" void PTZ_TiltUp(void* handle) {
PTZ_TiltUp((PTZCam*)handle);
}
extern "C" void PTZ_TiltDown(void* handle) {
PTZ_TiltDown((PTZCam*)handle);
}
extern "C" void PTZ_ZoomIn(void* handle) {
PTZ_ZoomIn((PTZCam*)handle);
}
extern "C" void PTZ_ZoomOut(void* handle) {
PTZ_ZoomOut((PTZCam*)handle);
}
extern "C" long PTZ_GetMinPan(void* handle) {
return ((PTZCam*)handle)->minPan;
}
extern "C" long PTZ_GetMaxPan(void* handle) {
return ((PTZCam*)handle)->maxPan;
}
extern "C" long PTZ_GetCurrentPan(void* handle) {
return ((PTZCam*)handle)->currPan;
}
extern "C" long PTZ_GetMinTilt(void* handle) {
return ((PTZCam*)handle)->minTilt;
}
extern "C" long PTZ_GetMaxTilt(void* handle) {
return ((PTZCam*)handle)->maxTilt;
}
extern "C" long PTZ_GetCurrentTilt(void* handle) {
return ((PTZCam*)handle)->currTilt;
}
extern "C" long PTZ_GetMinZoom(void* handle) {
return ((PTZCam*)handle)->minZoom;
}
extern "C" long PTZ_GetMaxZoom(void* handle) {
return ((PTZCam*)handle)->maxZoom;
}
extern "C" long PTZ_GetCurrentZoom(void* handle) {
return ((PTZCam*)handle)->currZoom;
}
extern "C" bool PTZ_IsZoomSupported(void* handle) {
return ((PTZCam*)handle)->zoomSupported;
}
#pragma endregion
//int main() {
// PTZCam cam;
//
// if (!PTZ_InitCam(&cam))
// {
// std::cout << "Camera init failed!" << std::endl;
// system("pause");
// PTZ_Release(&cam);
// return 1;
// }
//
// std::cout << "Camera initialized!" << std::endl;
// std::wcout << L"Camera Name: " << cam.camName << std::endl << std::endl; // 输出名字
//
// if (!PTZ_QueryCapabilities(&cam))
// {
// std::cout << "ERROR: Failed to get camera capabilities!" << std::endl;
// system("pause");
// PTZ_Release(&cam);
// return 1;
// }
//
// // Print capabilities
// std::cout << "Pan: range=" << cam.minPan << " ~ " << cam.maxPan << ", current=" << cam.currPan << ", step=" << PAN_STEP << std::endl;
// std::cout << "Tilt: range=" << cam.minTilt << " ~ " << cam.maxTilt << ", current=" << cam.currTilt << ", step=" << TILT_STEP << std::endl;
// std::cout << "Tilt down safety limit: " << TILT_DOWN_LIMIT << std::endl;
// if (cam.zoomSupported)
// {
// std::cout << "zoomSupported = true" << std::endl;
// std::cout << "Zoom: range=" << cam.minZoom << " ~ " << cam.maxZoom << ", current=" << cam.currZoom << ", step=" << ZOOM_STEP << std::endl;
// }
// else
// {
// std::cout << "WARNING: Zoom not supported by this camera!" << std::endl;
// }
// std::cout << std::endl;
//
// // Print control instructions
// std::cout << "========== Control Instructions ==========" << std::endl;
// std::cout << " W - Tilt Up" << std::endl;
// std::cout << " S - Tilt Down" << std::endl;
// std::cout << " A - Pan Left" << std::endl;
// std::cout << " D - Pan Right" << std::endl;
// std::cout << " Q - Zoom Out (Wide)" << std::endl;
// std::cout << " E - Zoom In (Tele)" << std::endl;
// std::cout << " ESC - Exit" << std::endl << std::endl;
// std::cout << "Ready..." << std::endl;
//
// bool running = true; // 是否正在运行
// while (running)
// {
// if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) // 检测【ESC键】是否被按下
// {
// running = false;
// }
// else if (GetAsyncKeyState('A') & 0x8000)
// {
// PTZ_PanLeft(&cam);
// Sleep(100); // Debounce
// }
// else if (GetAsyncKeyState('D') & 0x8000)
// {
// PTZ_PanRight(&cam);
// Sleep(100); // Debounce
// }
// else if (GetAsyncKeyState('W') & 0x8000)
// {
// PTZ_TiltUp(&cam);
// Sleep(100); // Debounce
// }
// else if (GetAsyncKeyState('S') & 0x8000)
// {
// PTZ_TiltDown(&cam);
// Sleep(100); // Debounce
// }
// else if (GetAsyncKeyState('Q') & 0x8000)
// {
// PTZ_ZoomOut(&cam);
// Sleep(100); // Debounce
// }
// else if (GetAsyncKeyState('E') & 0x8000)
// {
// PTZ_ZoomIn(&cam);
// Sleep(100); // Debounce
// }
//
// Sleep(10);
// }
//
// PTZ_Release(&cam);
//
// return 0;
//}