Onvif协议:IPC客户端开发之PTZ控制

1060 篇文章 297 订阅

介绍

在安防摄像头中,不仅仅涉及到固定摄像头的枪击,同样还包含可以360°转动的球机。因此对球机的云台方向控制是Onvif协议开发过程中必不可少的过程

球机的云台控制主要包含:八个方向(上、下、左、右、左上、左下、右上、右下),放大、缩小等,这在个过程中还包含对转动速度的控制或者放大缩小的速度控制。对应的方向及正负值如下图:

在这里插入图片描述

编码流程

1、通过设备服务地址(形如http://xx/onvif/device_service),调用GetCapabilities函数接口,获取到Ptz的URL;

2、通过Ptz的URL,调用GetProfiles函数接口,获取到ProfileToken;

3、对_tptz__AbsoluteMove结构体进行填充;

4、调用soap_call___tptz__AbsoluteMove函数接口实现摄像头转动功能;

实践

具体请参见

除了onvif_head.sh修改为:

#!/bin/bash
mkdir onvif_head
cd onvif_head
../bin/wsdl2h -o  onvif.h  -s -d -x -t ../gsoap/WS/typemap.dat \
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl \
https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl \
http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl \
http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl 

main.cpp修改为:

#include <assert.h>
#include "soapH.h"
#include "wsdd.nsmap"
#include "soapStub.h"
#include "wsseapi.h"
#include "wsaapi.h"
#include <map>




#define SOAP_ASSERT     assert
#define SOAP_DBGLOG     printf
#define SOAP_DBGERR     printf

#define SOAP_TO         "urn:schemas-xmlsoap-org:ws:2005:04:discovery"
#define SOAP_ACTION     "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe"

#define SOAP_MCAST_ADDR "soap.udp://239.255.255.250:3702"                       // onvif规定的组播地址

#define SOAP_ITEM       ""                                                      // 寻找的设备范围
#define SOAP_TYPES      "dn:NetworkVideoTransmitter"                            // 寻找的设备类型

#define SOAP_SOCK_TIMEOUT    (10)               // socket超时时间(单秒秒)




void soap_perror(struct soap *soap, const char *str)
{
    if (nullptr == str) {
        SOAP_DBGERR("[soap] error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    } else {
        SOAP_DBGERR("[soap] %s error: %d, %s, %s\n", str, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    }
}

void* ONVIF_soap_malloc(struct soap *soap, unsigned int n)
{
    void *p = nullptr;

    if (n > 0) {
        p = soap_malloc(soap, n);
                SOAP_ASSERT(nullptr != p);
        memset(p, 0x00 ,n);
    }
    return p;
}

struct soap *ONVIF_soap_new(int timeout)
{
    struct soap *soap = nullptr;                                                   // soap环境变量

            SOAP_ASSERT(nullptr != (soap = soap_new()));

    soap_set_namespaces(soap, namespaces);                                      // 设置soap的namespaces
    soap->recv_timeout    = timeout;                                            // 设置超时(超过指定时间没有数据就退出)
    soap->send_timeout    = timeout;
    soap->connect_timeout = timeout;

#if defined(__linux__) || defined(__linux)                                      // 参考https://www.genivia.com/dev.html#client-c的修改:
    soap->socket_flags = MSG_NOSIGNAL;                                          // To prevent connection reset errors
#endif

    soap_set_mode(soap, SOAP_C_UTFSTRING);                                      // 设置为UTF-8编码,否则叠加中文OSD会乱码

    return soap;
}

void ONVIF_soap_delete(struct soap *soap)
{
    soap_destroy(soap);                                                         // remove deserialized class instances (C++ only)
    soap_end(soap);                                                             // Clean up deserialized data (except class instances) and temporary data
    soap_done(soap);                                                            // Reset, close communications, and remove callbacks
    soap_free(soap);                                                            // Reset and deallocate the context created with soap_new or soap_copy
}

/************************************************************************
**函数:ONVIF_init_header
**功能:初始化soap描述消息头
**参数:
        [in] soap - soap环境变量
**返回:无
**备注:
    1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放
************************************************************************/
void ONVIF_init_header(struct soap *soap)
{
    struct SOAP_ENV__Header *header = nullptr;

            SOAP_ASSERT(nullptr != soap);

    header = (struct SOAP_ENV__Header *)ONVIF_soap_malloc(soap, sizeof(struct SOAP_ENV__Header));
    soap_default_SOAP_ENV__Header(soap, header);
    header->wsa__MessageID =  (char*)soap_wsa_rand_uuid(soap);
    header->wsa__To        = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TO) + 1);
    header->wsa__Action    = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ACTION) + 1);
    strcpy(header->wsa__To, SOAP_TO);
    strcpy(header->wsa__Action, SOAP_ACTION);
    soap->header = header;
}

/************************************************************************
**函数:ONVIF_init_ProbeType
**功能:初始化探测设备的范围和类型
**参数:
        [in]  soap  - soap环境变量
        [out] probe - 填充要探测的设备范围和类型
**返回:
        0表明探测到,非0表明未探测到
**备注:
    1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放
************************************************************************/
void ONVIF_init_ProbeType(struct soap *soap, struct wsdd__ProbeType *probe)
{
    struct wsdd__ScopesType *scope = nullptr;                                      // 用于描述查找哪类的Web服务

            SOAP_ASSERT(nullptr != soap);
            SOAP_ASSERT(nullptr != probe);

    scope = (struct wsdd__ScopesType *)ONVIF_soap_malloc(soap, sizeof(struct wsdd__ScopesType));
    soap_default_wsdd__ScopesType(soap, scope);                                 // 设置寻找设备的范围
    scope->__item = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ITEM) + 1);
    strcpy(scope->__item, SOAP_ITEM);

    memset(probe, 0x00, sizeof(struct wsdd__ProbeType));
    soap_default_wsdd__ProbeType(soap, probe);
    probe->Scopes = scope;
    probe->Types  = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TYPES) + 1);     // 设置寻找设备的类型
    strcpy(probe->Types, SOAP_TYPES);
}

void ONVIF_DetectDevice(void (*cb)(char *DeviceXAddr))
{
    int i;
    int result = 0;
    unsigned int count = 0;                                                     // 搜索到的设备个数
    struct soap *soap = nullptr;                                                   // soap环境变量
    struct wsdd__ProbeType      req;                                            // 用于发送Probe消息
    struct __wsdd__ProbeMatches rep;                                            // 用于接收Probe应答
    struct wsdd__ProbeMatchType *probeMatch;

    SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    ONVIF_init_header(soap);                                                    // 设置消息头描述
    ONVIF_init_ProbeType(soap, &req);                                           // 设置寻找的设备的范围和类型
    result = soap_send___wsdd__Probe(soap, SOAP_MCAST_ADDR, nullptr, &req);        // 向组播地址广播Probe消息
    while (SOAP_OK == result)                                                   // 开始循环接收设备发送过来的消息
    {
        memset(&rep, 0x00, sizeof(rep));
        result = soap_recv___wsdd__ProbeMatches(soap, &rep);
        if (SOAP_OK == result) {
            if (soap->error) {
                soap_perror(soap, "ProbeMatches");
            } else {                                                            // 成功接收到设备的应答消息
                if (nullptr != rep.wsdd__ProbeMatches) {
                    count += rep.wsdd__ProbeMatches->__sizeProbeMatch;
                    for(i = 0; i < rep.wsdd__ProbeMatches->__sizeProbeMatch; i++) {
                        probeMatch = rep.wsdd__ProbeMatches->ProbeMatch + i;
                        if (nullptr != cb ) {
                            std::string url = probeMatch->XAddrs;
                            if(url == "http://192.168.0.116/onvif/device_service"){
                                cb(probeMatch->XAddrs);                             // 使用设备服务地址执行函数回调
                            }
                        }
                    }
                }
            }
        } else if (soap->error) {
            break;
        }
    }

    SOAP_DBGLOG("\ndetect end! It has detected %d devices!\n", count);

    if (nullptr != soap) {
        ONVIF_soap_delete(soap);
    }

}


#define SOAP_CHECK_ERROR(result, soap, str) \
    do { \
        if (SOAP_OK != (result) || SOAP_OK != (soap)->error) { \
            soap_perror((soap), (str)); \
            if (SOAP_OK == (result)) { \
                (result) = (soap)->error; \
            } \
            goto EXIT; \
        } \
} while (0)



/************************************************************************
**函数:ONVIF_SetAuthInfo
**功能:设置认证信息
**参数:
        [in] soap     - soap环境变量
        [in] username - 用户名
        [in] password - 密码
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
static int ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password)
{
    int result = 0;

    SOAP_ASSERT(nullptr != username);
    SOAP_ASSERT(nullptr != password);

    result = soap_wsse_add_UsernameTokenDigest(soap, nullptr, username, password);
    SOAP_CHECK_ERROR(result, soap, "add_UsernameTokenDigest");

    EXIT:

    return result;
}
/************************************************************************
**函数:make_uri_withauth
**功能:构造带有认证信息的URI地址
**参数:
        [in]  src_uri       - 未带认证信息的URI地址
        [in]  username      - 用户名
        [in]  password      - 密码
        [out] dest_uri      - 返回的带认证信息的URI地址
        [in]  size_dest_uri - dest_uri缓存大小
**返回:
        0成功,非0失败
**备注:
    1). 例子:
    无认证信息的uri:rtsp://100.100.100.140:554/av0_0
    带认证信息的uri:rtsp://username:password@100.100.100.140:554/av0_0
************************************************************************/
static int make_uri_withauth(const std::string& src_uri, const std::string&username, const std::string&password, std::string *dest_uri)
{
    int result = 0;


            SOAP_ASSERT(!src_uri.empty());

    if (username.empty() &&password.empty()) {                       // 生成新的uri地址
        *dest_uri = src_uri;
    } else {
        std::string::size_type position = src_uri.find("//");
        if (std::string::npos == position) {
            SOAP_DBGERR("can't found '//', src uri is: %s.\n", src_uri.c_str());
            result = -1;
            return result;
        }

        position += 2;
        dest_uri->append(src_uri,0,   position) ;
        dest_uri->append(username + ":" + password + "@");
        dest_uri->append(src_uri,position, std::string::npos) ;
    }


    return result;
}

#define USERNAME    "admin"
#define PASSWORD    "hik12345"
/************************************************************************
**函数:ONVIF_GetDeviceInformation
**功能:获取设备基本信息
**参数:
        [in] DeviceXAddr - 设备服务地址
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_GetDeviceInformation(const char *DeviceXAddr)
{
    int result = 0;
    struct soap *soap = nullptr;
    _tds__GetDeviceInformation           devinfo_req;
    _tds__GetDeviceInformationResponse   devinfo_resp;

    SOAP_ASSERT(nullptr != DeviceXAddr);
    SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));



    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    result = soap_call___tds__GetDeviceInformation(soap, DeviceXAddr, nullptr, &devinfo_req, devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "GetDeviceInformation");
    std::cout << "      Manufacturer:\t" << devinfo_resp.Manufacturer << "\n";
    std::cout << "      Model:\t" << devinfo_resp.Model << "\n";
    std::cout << "      FirmwareVersion:\t" << devinfo_resp.FirmwareVersion << "\n";
    std::cout << "      SerialNumber:\t" << devinfo_resp.SerialNumber << "\n";
    std::cout << "      HardwareId:\t" << devinfo_resp.HardwareId << "\n";



    EXIT:

    if (nullptr != soap) {
        ONVIF_soap_delete(soap);
    }
    return result;
}

/************************************************************************
**函数:ONVIF_GetCapabilities
**功能:获取设备能力信息
**参数:
        [in] DeviceXAddr - 设备服务地址
        [in]
**返回:
        0表明成功,非0表明失败
**备注:
    1). 其中最主要的参数之一是媒体服务地址
************************************************************************/
int ONVIF_GetCapabilities(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(), nullptr, &devinfo_req, devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "GetCapabilities");

    if(devinfo_resp.Capabilities->PTZ != nullptr){
        *ptzXAddr = devinfo_resp.Capabilities->PTZ->XAddr;
    }

    EXIT:

    if (nullptr != soap) {
        ONVIF_soap_delete(soap);
    }
    return result;
}

int ONVIF_GetProfiles(const std::string& ptzXAddr, std::string  * profilesToken)
{
    int result = 0;
    struct soap *soap = nullptr;
    _trt__GetProfiles           devinfo_req;
    _trt__GetProfilesResponse   devinfo_resp;

            SOAP_ASSERT(!ptzXAddr.empty());
            SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

 
 ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___trt__GetProfiles(soap, ptzXAddr.c_str(), nullptr, &devinfo_req, devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "ONVIF_GetProfiles");


            SOAP_ASSERT(devinfo_resp.__sizeProfiles > 0);


    *profilesToken = devinfo_resp.Profiles[0]->token;

    EXIT:
    if (nullptr != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}


/************************************************************************
**函数:ONVIF_GetSnapshotUri
**功能:获取设备图像抓拍地址(HTTP)
**参数:
        [in]  MediaXAddr    - 媒体服务地址
        [in]  ProfileToken  - the media profile token
        [out] uri           - 返回的地址
        [in]  sizeuri       - 地址缓存大小
**返回:
        0表明成功,非0表明失败
**备注:
    1). 并非所有的ProfileToken都支持图像抓拍地址。举例:XXX品牌的IPC有如下三个配置profile0/profile1/TestMediaProfile,其中TestMediaProfile返回的图像抓拍地址就是空指针。
************************************************************************/
int ONVIF_GetSnapshotUri(const std::string& MediaXAddr, const std::string& ProfileToken, std::string * snapUri)
{
    int result = 0;
    struct soap *soap = nullptr;
     _trt__GetSnapshotUri         req;
     _trt__GetSnapshotUriResponse rep;

     SOAP_ASSERT(!MediaXAddr.empty() && !ProfileToken.empty());
     SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    req.ProfileToken = const_cast<char *>(ProfileToken.c_str());
    result = soap_call___trt__GetSnapshotUri(soap, MediaXAddr.c_str(), nullptr, &req, rep);
    SOAP_CHECK_ERROR(result, soap, "GetSnapshotUri");


    if (nullptr != rep.MediaUri && nullptr != rep.MediaUri->Uri) {
        *snapUri = rep.MediaUri->Uri;
    }

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}
// 获取当前ptz的位置以及状态
int ONVIF_PTZ_GetStatus(const std::string& ptzXAddr, const std::string& ProfileToken){
    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){
        std::cout << " 空闲 ... " << std::endl;
    }else if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__MOVING){
        std::cout << " 移动中 ... " << std::endl;
    }else if(*getStatusResponse.PTZStatus->MoveStatus->PanTilt == tt__MoveStatus__UNKNOWN){
        std::cout << " 未知 ... " << std::endl;
    }

    std::cout << "当前p: "  <<getStatusResponse.PTZStatus->Position->PanTilt->x  << "\n";
    std::cout << "当前t: "  << getStatusResponse.PTZStatus->Position->PanTilt->y  << "\n";
    std::cout << "当前z: "  << getStatusResponse.PTZStatus->Position->Zoom->x  << "\n";
    EXIT:
    if (nullptr != soap) {
        ONVIF_soap_delete(soap);
    }

    return 0;
}

// 以指定速度移动到指定位置的ptz
// p :  -1  ~ 1   []
// t :  -1  ~ 1
// z :   0  ~ 1
int ONVIF_PTZAbsoluteMove(const std::string& ptzXAddr, const std::string& ProfileToken){
    int result = 0;
    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)));


    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    absoluteMove.ProfileToken = const_cast<char *>(ProfileToken.c_str());

    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 = 0.440833;  // p
    absoluteMove.Position->PanTilt->y = 0.583455;   // t
    absoluteMove.Position->Zoom->x = 0.0333333;   // z

     // x 和y的绝对值越接近1,表示云台的速度越快
    absoluteMove.Speed->PanTilt->x = 0.5;
    absoluteMove.Speed->PanTilt->y = 0.5;
    absoluteMove.Speed->Zoom->x = 0.5;
    result = soap_call___tptz__AbsoluteMove(soap,ptzXAddr.c_str(), nullptr,&absoluteMove,absoluteMoveResponse);
    SOAP_CHECK_ERROR(result, soap, "ONVIF_PTZAbsoluteMove");


EXIT:
    if (nullptr != soap) {
        ONVIF_soap_delete(soap);
    }
    return 0;
}


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

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,
};
// speed --> (0, 1]
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");
/*    sleep(1); //如果当前soap被删除(或者发送stop指令),就会停止移动
    ONVIF_PTZStopMove(ptzXAddr, ProfileToken);*/
 EXIT:
    if (nullptr != soap) {
        ONVIF_soap_delete(soap);
    }
    return result;
}




void cb_discovery(char *deviceXAddr)
{
    std::string  ptzXAddr, profilesToken, snapUri, snapAuthUri;
    ONVIF_GetCapabilities(deviceXAddr, &ptzXAddr);
    ONVIF_GetProfiles(ptzXAddr, &profilesToken);
   // ONVIF_PTZ_GetStatus(ptzXAddr, profilesToken);
   // ONVIF_PTZAbsoluteMove(ptzXAddr, profilesToken);
    ONVIF_PTZContinuousMove(ptzXAddr, profilesToken, PTZ_CMD_LEFTUP, 0.3);
}



int main(int argc, char **argv)
{
    ONVIF_DetectDevice(cb_discovery);
    return 0;
}


参考

Onvif协议客户端开发(8)–球机云台的控制
ONVIF PTZ云台控制–RelativeMove
初学小结使用Onvif协议进行PTZ控制
Onvif PTZ简介
使用Onvif协议进行设备PTZ云台控制
ONVIF PTZ控制

其他文章

ONVIF协议是一种开放的网络视频接口标准,旨在实现网络摄像机、网络视频录像机和其他相关设备的互通互联。在开发ONVIF协议网络摄像机客户端程序时,我们需要遵循以下步骤: 首先,为了开发ONVIF协议网络摄像机客户端程序,我们需要对ONVIF协议进行深入了解。ONVIF协议定义了网络摄像机和客户端之间的通信规则和接口,包括设备发现、视频流传输、设备控制等。我们需要熟悉协议的各个方面,包括消息结构、命令和响应等。 其次,我们需要选择合适的开发平台和工具。ONVIF协议可以在不同平台上运行,如Windows、Linux等。我们可以选择适合我们的开发环境的编程语言和工具,并研究它们的ONVIF协议支持。 接下来,我们需要编写客户端程序的代码。首先,我们需要实现设备发现功能,通过发送协议指定的消息,搜索网络中的ONVIF设备。一旦找到设备,我们就可以获取设备的信息,如设备地址、设备型号等。然后,我们可以向设备发送命令,如实时视频流请求、设备时间同步等。我们需要处理设备返回的响应消息,并根据协议规范处理各种结果。 最后,我们需要对开发客户端程序进行测试和调试。我们可以创建模拟的ONVIF设备,模拟设备的各种行为,并验证客户端程序的功能和性能。我们还可以使用一些专业的测试工具来检查客户端程序的兼容性和稳定性。 总而言之,在开发ONVIF协议网络摄像机客户端程序时,我们需要深入了解ONVIF协议,选择适合的开发平台和工具,编写代码实现协议的各种功能,并进行测试和调试。这个过程中需要耐心和细心,以确保开发出高质量的客户端程序。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值