如何通过DirectShow用C++实现PTZ相机的控制?

1.初始化相机

cpp 复制代码
#include<Windows.h>
#include<dshow.h> // DirectShow 头文件
#include<iostream>

#pragma comment(lib, "strmiids.lib") // 链接 DirectShow 系统库

struct PTZCam {
	IAMCameraControl* pCamCtrl; // IAMCameraControl 是 Windows 系统提供的相机控制接口

	PTZCam(): 
		pCamCtrl(nullptr) {}
};

#pragma region 函数声明
bool PTZ_InitCam(PTZCam* cam); // 初始化相机
void PTZ_Release(PTZCam* cam); // 释放相机资源
#pragma endregion


#pragma region 函数实现
bool PTZ_InitCam(PTZCam* cam) {
	if (!cam) return false;

	// HRESULT 是 Windows API 的返回值类型用来表示 成功 / 失败
	// CoInitializeEx 是扩展版的 COM 初始化函数,COM 是 Component Object Model
	HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

	if (FAILED(hr)) return false;

	ICreateDevEnum* pDevEnum = nullptr; // 创建"设备扫描器",用来扫描电脑里的摄像头
	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
		(void**)&pDevEnum);

	if (FAILED(hr))
	{
		CoUninitialize();
		return false;
	}

	IEnumMoniker* pEnum = nullptr;
	// 把扫描到的相机列表存到 pEnum 里
	hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
	if (FAILED(hr) || !pEnum)
	{
		pDevEnum->Release();
		CoUninitialize();
		return false;
	}

	IMoniker* pMoniker = nullptr; // 设备条目指针,相机的"名字/路径/信息包"
	if (pEnum->Next(1, &pMoniker, NULL) == S_OK) // 从相机列表里取出第1个相机
	{
		IBaseFilter* pFilter = nullptr; // 设备过滤器指针,相机的"驱动层接口"
		hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
		if (SUCCEEDED(hr))
		{
			// 向相机请求获取 PTZ 遥控器
			hr = pFilter->QueryInterface(IID_IAMCameraControl, (void**)&cam->pCamCtrl);
			pFilter->Release();
		}
		pMoniker->Release();
	}

	pEnum->Release();
	pDevEnum->Release();

	return cam->pCamCtrl != nullptr;
}
void PTZ_Release(PTZCam* cam) {
	if (!cam) return;

	if (cam->pCamCtrl) {
		cam->pCamCtrl->Release();
		cam->pCamCtrl = nullptr;
	}
	CoUninitialize();
}
#pragma endregion


int main(){
	PTZCam cam;

	if (!PTZ_InitCam(&cam))
	{
		std::cout << "Camera init failed!" << std::endl;
		system("pause");
		PTZ_Release(&cam);
		return 1;
	}

	bool running = true; // 是否正在运行
	while (running)
	{

	}

	return 0;
}

2.获取硬件信息并打印

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;

struct PTZCam {
	IAMCameraControl* pCamCtrl; // IAMCameraControl 是 Windows 系统提供的相机控制接口

	wchar_t camName[256];

	long minPan;
	long maxPan;
	long currPan;
	long minTilt;
	long maxTilt;
	long currTilt;
	long minZoom;
	long maxZoom;
	long currZoom;

	bool zoomSupported;

	PTZCam(): 
		pCamCtrl(nullptr),
		minPan(0), 
		maxPan(0),
		currPan(0),
		minTilt(0),
		maxTilt(0),
		currTilt(0),
		minZoom(0),
		maxZoom(0),
		currZoom(0),
		zoomSupported(false)
	{
		camName[0] = 0;
	}
};

#pragma region 函数声明
bool PTZ_InitCam(PTZCam* cam); // 初始化相机
void PTZ_Release(PTZCam* cam); // 释放相机资源

bool PTZ_QueryCapabilities(PTZCam* cam); // 获取硬件信息
#pragma endregion


#pragma region 函数实现
bool PTZ_InitCam(PTZCam* cam) {
	if (!cam) return false;

	// HRESULT 是 Windows API 的返回值类型用来表示 成功 / 失败
	// CoInitializeEx 是扩展版的 COM 初始化函数,COM 是 Component Object Model
	HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

	if (FAILED(hr)) return false;

	ICreateDevEnum* pDevEnum = nullptr; // 创建"设备扫描器",用来扫描电脑里的摄像头
	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
		(void**)&pDevEnum);

	if (FAILED(hr))
	{
		CoUninitialize();
		return false;
	}

	IEnumMoniker* pEnum = nullptr;
	// 把扫描到的相机列表存到 pEnum 里
	hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
	if (FAILED(hr) || !pEnum)
	{
		pDevEnum->Release();
		CoUninitialize();
		return false;
	}

	IMoniker* pMoniker = nullptr; // 设备条目指针,相机的"名字/路径/信息包"
	if (pEnum->Next(1, &pMoniker, NULL) == S_OK) // 从相机列表里取出第1个相机
	{
		// 获取相机名称
		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);
			pFilter->Release();
		}
		pMoniker->Release();
	}

	pEnum->Release();
	pDevEnum->Release();

	return cam->pCamCtrl != nullptr;
}
void PTZ_Release(PTZCam* cam) {
	if (!cam) return;

	if (cam->pCamCtrl) {
		cam->pCamCtrl->Release();
		cam->pCamCtrl = nullptr;
	}
	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;
}
#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) 
	{

	}

	return 0;
}

3.实现PTZ对应功能函数

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;

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;

	PTZCam(): 
		pCamCtrl(nullptr),
		minPan(0), 
		maxPan(0),
		currPan(0),
		minTilt(0),
		maxTilt(0),
		currTilt(0),
		minZoom(0),
		maxZoom(0),
		currZoom(0),
		zoomSupported(false)
	{
		camName[0] = 0;
	}
};

#pragma region 函数声明
bool PTZ_InitCam(PTZCam* cam); // 初始化相机
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


#pragma region 函数实现
bool PTZ_InitCam(PTZCam* cam) {
	if (!cam) return false;

	// HRESULT 是 Windows API 的返回值类型用来表示 成功 / 失败
	// CoInitializeEx 是扩展版的 COM 初始化函数,COM 是 Component Object Model
	HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

	if (FAILED(hr)) return false;

	ICreateDevEnum* pDevEnum = nullptr; // 创建"设备扫描器",用来扫描电脑里的摄像头
	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
		(void**)&pDevEnum);

	if (FAILED(hr))
	{
		CoUninitialize();
		return false;
	}

	IEnumMoniker* pEnum = nullptr;
	// 把扫描到的相机列表存到 pEnum 里
	hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
	if (FAILED(hr) || !pEnum)
	{
		pDevEnum->Release();
		CoUninitialize();
		return false;
	}

	IMoniker* pMoniker = nullptr; // 设备条目指针,相机的"名字/路径/信息包"
	if (pEnum->Next(1, &pMoniker, NULL) == S_OK) // 从相机列表里取出第1个相机
	{
		// 获取相机名称
		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);
			pFilter->Release();
		}
		pMoniker->Release();
	}

	pEnum->Release();
	pDevEnum->Release();

	return cam->pCamCtrl != nullptr;
}
void PTZ_Release(PTZCam* cam) {
	if (!cam) return;

	if (cam->pCamCtrl) {
		cam->pCamCtrl->Release();
		cam->pCamCtrl = nullptr;
	}
	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->minPan) tiltVal = cam->minPan;
	if (tiltVal > cam->maxPan) tiltVal = cam->maxPan;
	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 (zoomVal < cam->minPan) zoomVal = cam->minPan;
	if (zoomVal > cam->maxPan) zoomVal = cam->maxPan;
	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;

	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


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) 
	{

	}

	return 0;
}

4.监听按键并执行操作

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;

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;

	PTZCam(): 
		pCamCtrl(nullptr),
		minPan(0), 
		maxPan(0),
		currPan(0),
		minTilt(0),
		maxTilt(0),
		currTilt(0),
		minZoom(0),
		maxZoom(0),
		currZoom(0),
		zoomSupported(false)
	{
		camName[0] = 0;
	}
};

#pragma region 函数声明
bool PTZ_InitCam(PTZCam* cam); // 初始化相机
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


#pragma region 函数实现
bool PTZ_InitCam(PTZCam* cam) {
	if (!cam) return false;

	// HRESULT 是 Windows API 的返回值类型用来表示 成功 / 失败
	// CoInitializeEx 是扩展版的 COM 初始化函数,COM 是 Component Object Model
	HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

	if (FAILED(hr)) return false;

	ICreateDevEnum* pDevEnum = nullptr; // 创建"设备扫描器",用来扫描电脑里的摄像头
	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
		(void**)&pDevEnum);

	if (FAILED(hr))
	{
		CoUninitialize();
		return false;
	}

	IEnumMoniker* pEnum = nullptr;
	// 把扫描到的相机列表存到 pEnum 里
	hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
	if (FAILED(hr) || !pEnum)
	{
		pDevEnum->Release();
		CoUninitialize();
		return false;
	}

	IMoniker* pMoniker = nullptr; // 设备条目指针,相机的"名字/路径/信息包"
	if (pEnum->Next(1, &pMoniker, NULL) == S_OK) // 从相机列表里取出第1个相机
	{
		// 获取相机名称
		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);
			pFilter->Release();
		}
		pMoniker->Release();
	}

	pEnum->Release();
	pDevEnum->Release();

	return cam->pCamCtrl != nullptr;
}
void PTZ_Release(PTZCam* cam) {
	if (!cam) return;

	if (cam->pCamCtrl) {
		cam->pCamCtrl->Release();
		cam->pCamCtrl = nullptr;
	}
	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->minPan) tiltVal = cam->minPan;
	if (tiltVal > cam->maxPan) tiltVal = cam->maxPan;
	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 (zoomVal < cam->minPan) zoomVal = cam->minPan;
	if (zoomVal > cam->maxPan) zoomVal = cam->maxPan;
	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


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;
}
相关推荐
实心儿儿2 小时前
C++ —— C++11
开发语言·c++
chushiyunen2 小时前
python web框架streamlit
开发语言·前端·python
纯爱掌门人2 小时前
鸿蒙跨设备互通:让你的应用“借用“另一台设备的相机和图库
数码相机·华为·harmonyos
小辉同志2 小时前
17. 电话号码的字母组合
c++·算法·leetcode·深度优先
思小瓜……。2 小时前
校园邮箱过期如何激活MATLAB R2022a
开发语言·matlab·激活软件
眷蓝天2 小时前
python基础
开发语言·python
迷糊小鬼2 小时前
Button matrix(矩阵按钮) (lv_buttonmatrix)
c语言·开发语言·前端·ui·矩阵
你撅嘴真丑2 小时前
和为给定数 与 最匹配的矩阵
c++·算法·矩阵
南境十里·墨染春水2 小时前
C++ 笔记:std::bind 函数模板详解
前端·c++·笔记