7-Onvif协议:IPC客户端之PTZ控制
在安防摄像头中,不仅仅涉及到固定摄像头的枪击,同样还包含可以360°转动的球机。因此对球机的云台方向控制是Onvif协议开发过程中必不可少的过程。
参考文章
https://blog.csdn.net/zhizhengguan/article/details/109379719?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171765035116800184113873%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=171765035116800184113873&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-1-109379719-null-null.142
https://blog.csdn.net/chi_qing/article/details/89319101
一、PTZ控制简介
在安防摄像头中,不仅仅涉及到固定摄像头的枪击,同样还包含可以360°转动的球机。因此对球机的云台方向控制是Onvif协议开发过程中必不可少的过程。
二、PTZ摄像机中PTZ分别代表什么?
- P: pan,表示相机水平方向上移动,即相机的旋转
- T: Tilt, 表示垂直方向上的移动,即相机镜头的高低俯仰
- Z: Zoom,表示变焦,即调整相机的焦距
三、客户端实现PTZ控制的流程
- 通过设备服务地址(形如http://192.168.1.137:81/onvif/device_service),调用**ONVIF_GetPTZCapabilities()**函数接口,获取到PTZ的URL(形如http://192.168.1.137:81/onvif/ptz);
- 通过PTZ的URL,调用**ONVIF_GetPTZProfilesToken()**函数接口,获取到ProfileToken;
- 对数据类型进行填充;
- 调用函数接口实现摄像头转动功能。
四、相关数据结构与函数
所以函数与数据结构定义在client\onvif_head\onvif.h下
调用ptz.wsdl的以下接口,文件路径https://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl
1.函数
函数原型在client\application\soapClient.cpp
1.AbsoluteMove
(绝对移动)
int soap_call___tptz__AbsoluteMove(
struct soap *soap,
NULL, // char *endpoint = NULL selects default endpoint for this operation
NULL, // char *action = NULL selects default action for this operation
// input parameters:
_tptz__AbsoluteMove* tptz__AbsoluteMove,
// output parameters:
_tptz__AbsoluteMoveResponse &tptz__AbsoluteMoveResponse
);
2.ContinuousMove
(沿某方向持续移动)
int soap_call___tptz__ContinuousMove(
struct soap *soap,
NULL, // char *endpoint = NULL selects default endpoint for this operation
NULL, // char *action = NULL selects default action for this operation
// input parameters:
_tptz__ContinuousMove* tptz__ContinuousMove,
// output parameters:
_tptz__ContinuousMoveResponse &tptz__ContinuousMoveResponse
);
3.RelativeMove
(相对移动)
int soap_call___tptz__RelativeMove(
struct soap *soap,
NULL, // char *endpoint = NULL selects default endpoint for this operation
NULL, // char *action = NULL selects default action for this operation
// input parameters:
_tptz__RelativeMove* tptz__RelativeMove,
// output parameters:
_tptz__RelativeMoveResponse &tptz__RelativeMoveResponse
);
2.数据结构
绝对位置移动的数据结构为 _tptz__AbsoluteMove
,class类型的数据结构定义在client\soap\soapStub.h下
其中ProfileToken
是一个标识。可以用获取配置文件函数soap_call___trt__GetProfiles
获得,函数定义:
soap_call___trt__GetProfiles(struct soap *soap, const char *soap_endpoint, const char
**tt__PTZVector
**为转动位置结构体,class类型的数据结构也定义在client\soap\soapStub.h下
struct tt__PTZVector {
/** Optional element 'tt:PanTilt' of XSD type 'tt:Vector2D' */
struct tt__Vector2D *PanTilt;//2维坐标结构体
/** Optional element 'tt:Zoom' of XSD type 'tt:Vector1D' */
struct tt__Vector1D *Zoom;//1维坐标结构体
};
tt__Vector2D
2维坐标,class类型的数据结构也定义在client\soap\soapStub.h下
# 图片中为C++版本,下述代码为c版本
struct tt__Vector2D {
/** Required attribute 'x' of XSD type 'xsd:float' */
float x; //Pan
/** Required attribute 'y' of XSD type 'xsd:float' */
float y; //Tilt
/** Optional attribute 'space' of XSD type 'xsd:anyURI' */
//URI:一般填入"http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace"即可
char *space;
};
tt__Vector1D
1维坐标,class类型的数据结构也定义在client\soap\soapStub.h下
struct tt__Vector1D {
/** Required attribute 'x' of XSD type 'xsd:float' */
float x;
/** Optional attribute 'space' of XSD type 'xsd:anyURI' */
char *space;
};
相对位置移动函数和连续移动函数的参数可参考绝对位置移动函数的参数查找方法,这些class类型的数据结构都定义在client\soap\soapStub.h下
五、功能实现(c++)
函数实现说明具体参考ONVIF_PTZAbsoluteMove()的代码注释
头文件:
struct OnvifPTZInfo
{
int status;
float p;
float t;
float z;
};
struct ONVIFPTZAbsoluteMoveInfo
{
float absoluteP;
float absoluteT;
float absoluteZ;
float vp;
float vt;
float vz;
};
struct ONVIFPTZRelativeMoveInfo
{
float relativeP;
float relativeT;
float relativeZ;
float vp;
float vt;
float vz;
};
enum PTZCMD
{
PTZ_CMD_LEFT,
PTZ_CMD_RIGHT,
PTZ_CMD_UP,
PTZ_CMD_DOWN,
PTZ_CMD_LEFTUP,
PTZ_CMD_LEFTDOWN,
PTZ_CMD_RIGHTUP,
PTZ_CMD_RIGHTDOWN,
PTZ_CMD_ZOOM_IN,
PTZ_CMD_ZOOM_OUT,
};
int ONVIF_GetPTZCapabilities(const std::string& deviceXAddr, std::string *ptzXAddr);
bool ONVIF_GetPTZProfilesToken(const std::string& mediaXAddr, std::string *ptzprofilesToken);
int ONVIF_PTZ_GetStatus(const std::string& ptzXAddr, const std::string& ProfileToken, struct OnvifPTZInfo * ptzInfo);
int ONVIF_PTZStopMove(const std::string& ptzXAddr, const std::string& ProfileToken);
int ONVIF_PTZAbsoluteMove(const std::string& ptzXAddr, const std::string& ProfileToken, struct ONVIFPTZAbsoluteMoveInfo *ptzAbsoluteMoveInfo);
int ONVIF_PTZRelativeMove(const std::string& ptzXAddr, const std::string& ProfileToken, struct ONVIFPTZRelativeMoveInfo *ptzRelativeMoveInfo);
int ONVIF_PTZContinuousMove(const std::string& ptzXAddr, const std::string& ProfileToken, enum PTZCMD cmd, float speed);
函数的实现:
所有移动命令应以非阻塞方式执行,表示不应等到请求的移动操作完成。新的移动请求可以覆盖最后的移动操作。
1.ONVIF_GetPTZCapabilities()
int ONVIF_GetPTZCapabilities(const std::string& deviceXAddr, std::string *ptzXAddr)
{
int result = 0;
struct soap *soap = nullptr;
_tds__GetCapabilities devinfo_req;
_tds__GetCapabilitiesResponse devinfo_resp;
SOAP_ASSERT(!deviceXAddr.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
result = soap_call___tds__GetCapabilities(soap, deviceXAddr.c_str(), NULL, &devinfo_req, devinfo_resp);
SOAP_CHECK_ERROR(result, soap, "GetCapabilities");
if(devinfo_resp.Capabilities->PTZ != nullptr)
{
*ptzXAddr=devinfo_resp.Capabilities->PTZ->XAddr;
}
else
{
*ptzXAddr="null";
}
#if DEBUG
std::cout << "---------------------ONVIF_GetPTZCapabilities------------------" << "\n";
std::cout << "PTZ:" << *ptzXAddr << "\n";
std::cout << "-------------------------------------------------------------" << "\n";
#endif
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return result;
}
2.ONVIF_GetPTZProfilesToken()
bool ONVIF_GetPTZProfilesToken(const std::string& mediaXAddr, std::string *ptzprofilesToken)
{
int result = 0;
struct soap *soap = nullptr;
_trt__GetProfiles devinfo_req;
_trt__GetProfilesResponse devinfo_resp;
SOAP_ASSERT(!mediaXAddr.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
result = soap_call___trt__GetProfiles(soap, mediaXAddr.c_str(), nullptr, &devinfo_req, devinfo_resp);
SOAP_CHECK_ERROR(result, soap, "ONVIF_GetProfiles");
SOAP_ASSERT(devinfo_resp.__sizeProfiles > 0);
*ptzprofilesToken = devinfo_resp.Profiles[0]->token;
#if DEBUG
std::cout << "-----------------ONVIF_GetPTZProfilesToken-------------------" << "\n";
std::cout << *ptzprofilesToken << "\n";
std::cout << "-------------------------------------------------------------" << "\n";
#endif
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return result;
}
3.ONVIF_PTZ_GetStatus()
// 获取当前ptz的位置以及状态
int ONVIF_PTZ_GetStatus(const std::string& ptzXAddr, const std::string& ProfileToken, struct OnvifPTZInfo * ptzInfo){
int result = 0;
struct soap *soap = nullptr;
_tptz__GetStatus getStatus;
_tptz__GetStatusResponse getStatusResponse;
SOAP_ASSERT(!ptzXAddr.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
getStatus.ProfileToken = const_cast<char *>(ProfileToken.c_str());
result = soap_call___tptz__GetStatus(soap, ptzXAddr.c_str(), nullptr, &getStatus, getStatusResponse);
SOAP_CHECK_ERROR(result, soap, "ONVIF_PTZ_GetStatus");
if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__IDLE){
ptzInfo->status = 0;
}else if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__MOVING){
ptzInfo->status = 1;
}else if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__UNKNOWN){
ptzInfo->status = 2;
}
ptzInfo->p = getStatusResponse.PTZStatus->Position->PanTilt->x;
ptzInfo->t = getStatusResponse.PTZStatus->Position->PanTilt->y;
ptzInfo->z = getStatusResponse.PTZStatus->Position->Zoom->x;
#if DEBUG
std::cout << "----------------------ONVIF_PTZ_GetStatus--------------------" << "\n";
if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__IDLE){
std::cout << "MoveStatus:IDLE " << std::endl;
}else if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__MOVING){
std::cout << "MoveStatus:MOVING " << std::endl;
}else if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__UNKNOWN){
std::cout << "MoveStatus:UNKNOWN " << std::endl;
}
std::cout << "当前p: " << ptzInfo->p << "\n";
std::cout << "当前t: " << ptzInfo->t << "\n";
std::cout << "当前z: " << ptzInfo->z << "\n";
std::cout << "-------------------------------------------------------------" << "\n";
#endif
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return 0;
}
4.ONVIF_PTZStopMove()
int ONVIF_PTZStopMove(const std::string& ptzXAddr, const std::string& ProfileToken){
int result = 0;
struct soap *soap = nullptr;
_tptz__Stop tptzStop;
_tptz__StopResponse tptzStopResponse;
SOAP_ASSERT(!ptzXAddr.empty() && !ProfileToken.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
tptzStop.ProfileToken = const_cast<char *>(ProfileToken.c_str());
result = soap_call___tptz__Stop(soap, ptzXAddr.c_str(), nullptr, &tptzStop, tptzStopResponse);
SOAP_CHECK_ERROR(result, soap, "ONVIF_PTZStopMove");
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return result;
}
5.ONVIF_PTZAbsoluteMove()
int ONVIF_PTZAbsoluteMove(const std::string& ptzXAddr, const std::string& ProfileToken, struct ONVIFPTZAbsoluteMoveInfo *ptzAbsoluteMoveInfo){
int result = 0;
//1、soap结构体初始化
struct soap *soap = nullptr;
_tptz__AbsoluteMove absoluteMove;
_tptz__AbsoluteMoveResponse absoluteMoveResponse;
SOAP_ASSERT(!ptzXAddr.empty() && !ProfileToken.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
//2、鉴权(用户认证)
ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
//3、配置文件获取(profiletoken),作为函数参数
absoluteMove.ProfileToken = const_cast<char *>(ProfileToken.c_str());
//4、为结构体分配内存并填充结构体
absoluteMove.Position = soap_new_tt__PTZVector(soap);
absoluteMove.Position->PanTilt = soap_new_tt__Vector2D(soap);
absoluteMove.Position->Zoom = soap_new_tt__Vector1D(soap);
absoluteMove.Speed = soap_new_tt__PTZSpeed(soap);
absoluteMove.Speed->PanTilt = soap_new_tt__Vector2D(soap);
absoluteMove.Speed->Zoom = soap_new_tt__Vector1D(soap);
absoluteMove.Position->PanTilt->x = ptzAbsoluteMoveInfo->absoluteP; // p
absoluteMove.Position->PanTilt->y = ptzAbsoluteMoveInfo->absoluteT; // t
absoluteMove.Position->Zoom->x = ptzAbsoluteMoveInfo->absoluteZ; // z
// x 和y的绝对值越接近1,表示云台的速度越快
absoluteMove.Speed->PanTilt->x = ptzAbsoluteMoveInfo->vp;
absoluteMove.Speed->PanTilt->y = ptzAbsoluteMoveInfo->vt;
absoluteMove.Speed->Zoom->x = ptzAbsoluteMoveInfo->vz;
//5、发送调用命令
result = soap_call___tptz__AbsoluteMove(soap,ptzXAddr.c_str(), nullptr,&absoluteMove,absoluteMoveResponse);
SOAP_CHECK_ERROR(result, soap, "ONVIF_PTZAbsoluteMove");
//6、soap结构体清理
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return 0;
}
6.ONVIF_PTZRelativeMove()
int ONVIF_PTZRelativeMove(const std::string& ptzXAddr, const std::string& ProfileToken, struct ONVIFPTZRelativeMoveInfo *ptzRelativeMoveInfo)
{
int result = 0;
struct OnvifPTZInfo ptzInfo;
struct ONVIFPTZAbsoluteMoveInfo ptzAbsoluteMoveInfo;
ONVIF_PTZ_GetStatus(ptzXAddr, ProfileToken, &ptzInfo);
ptzAbsoluteMoveInfo.absoluteP = ptzInfo.p + ptzRelativeMoveInfo->relativeP;
ptzAbsoluteMoveInfo.absoluteT = ptzInfo.t + ptzRelativeMoveInfo->relativeT;
ptzAbsoluteMoveInfo.absoluteZ = ptzInfo.z + ptzRelativeMoveInfo->relativeZ;
ptzAbsoluteMoveInfo.vp = ptzRelativeMoveInfo->vp;
ptzAbsoluteMoveInfo.vt = ptzRelativeMoveInfo->vt;
ptzAbsoluteMoveInfo.vz = ptzRelativeMoveInfo->vz;
result = ONVIF_PTZAbsoluteMove(ptzXAddr, ProfileToken, &ptzAbsoluteMoveInfo);
return result;
}
7.ONVIF_PTZContinuousMove()
int ONVIF_PTZContinuousMove(const std::string& ptzXAddr, const std::string& ProfileToken, enum PTZCMD cmd, float speed){
int result = 0;
struct soap *soap = nullptr;
_tptz__ContinuousMove continuousMove;
_tptz__ContinuousMoveResponse continuousMoveResponse;
SOAP_ASSERT(!ptzXAddr.empty() && !ProfileToken.empty());
SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
continuousMove.ProfileToken = const_cast<char *>(ProfileToken.c_str());
continuousMove.Velocity = soap_new_tt__PTZSpeed(soap);
continuousMove.Velocity->PanTilt = soap_new_tt__Vector2D(soap);
continuousMove.Velocity->Zoom = soap_new_tt__Vector1D(soap);
switch (cmd)
{
case PTZ_CMD_LEFT:
continuousMove.Velocity->PanTilt->x = -speed;
continuousMove.Velocity->PanTilt->y = 0;
break;
case PTZ_CMD_RIGHT:
continuousMove.Velocity->PanTilt->x = speed;
continuousMove.Velocity->PanTilt->y = 0;
break;
case PTZ_CMD_UP:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = speed;
break;
case PTZ_CMD_DOWN:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = -speed;
break;
case PTZ_CMD_LEFTUP:
continuousMove.Velocity->PanTilt->x = -speed;
continuousMove.Velocity->PanTilt->y = speed;
break;
case PTZ_CMD_LEFTDOWN:
continuousMove.Velocity->PanTilt->x = -speed;
continuousMove.Velocity->PanTilt->y = -speed;
break;
case PTZ_CMD_RIGHTUP:
continuousMove.Velocity->PanTilt->x = speed;
continuousMove.Velocity->PanTilt->y = speed;
break;
case PTZ_CMD_RIGHTDOWN:
continuousMove.Velocity->PanTilt->x = speed;
continuousMove.Velocity->PanTilt->y = -speed;
break;
case PTZ_CMD_ZOOM_IN:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = 0;
continuousMove.Velocity->Zoom->x = speed;
break;
case PTZ_CMD_ZOOM_OUT:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = 0;
continuousMove.Velocity->Zoom->x = -speed;
break;
default:
break;
}
// 也可以使用soap_call___tptz__RelativeMove实现
result = soap_call___tptz__ContinuousMove(soap,ptzXAddr.c_str(), nullptr,&continuousMove,continuousMoveResponse);
SOAP_CHECK_ERROR(result, soap, "ONVIF_PTZAbsoluteMove");
EXIT:
if (nullptr != soap) {
ONVIF_soap_delete(soap);
}
return result;
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)