ONVIF PTZ控制海康云台相机

之前帮一个算法朋友弄的 ONVIF PTZ控制海康云台相机代码在无人车上的应用.

主要是鉴权没弄过,折腾onvif的人肯定会碰到,所以特别记录一下.

开始本来是在X64的WIN32或ubuntu工控机平台弄海康摄像头控制的,因为有SDK的直接支持.

结果无人车主控选的是NVIDIA XAIVER arm下控制云台,这就想起了用ONVIF了.

ONVIF无非就是用gsoap+rtsp来折腾的 就把gsoap库先下载和onvif官方下载对应的文件wsdl接口.如果是搞过webservice的活应该有所了解, gsoap 我不多说 百度一下应该好多讲的只管下载. 

版本没有太大的要求, 下面文件有些我可能删除了. 我保留主要文件 .

ONVIF PTZ控制海康云台相机_第1张图片

onvif标准接口也是下载 一堆wsdl文件  PTZ在下面

ONVIF PTZ控制海康云台相机_第2张图片

主要作用类的封装:

class OnvifSDK
{
public:
    std::string MediaAddr;
    std::string PTZAddr;
public:
    static struct soap* NewSoap(const char* ip);
    OnvifSDK(struct soap* soap_, const char* username, const char* password);
    virtual ~OnvifSDK();
    bool GetDeviceInfo(char* url);
    bool GetCapabilities(char* url);
    bool GetProfiles();
    std::string GetUri(enum tt__TransportProtocol transtype);
    bool GetStstus(float* pvalue, float* tvalue, float* zvalue);
    //连续控制
    void ControlPTZ(int control, bool control_type, int speed);
    //绝对控制
    void SetPTZ(float pvalue, float tvalue, float zvalue);
    //相对控制
    void SetMoveStep(ptz_common commond, float move_step);
protected:
#ifdef WITH_OPENSSL
#else
    void calc_nonce(struct soap* soap, char nonce[SOAP_WSSE_NONCELEN]);
    struct _wsse__Security* soap_wsse_add_Security(struct soap* soap);
    int soap_wsse_add_UsernameTokenText(struct soap* soap, const char* id, const char* username, const char* password);
    int soap_wsse_add_UsernameTokenDigest(struct soap* soap, const char* id, const char* username, const char* password);
    void calc_digest(struct soap* soap, const char* created,const char* nonce, int noncelen, const char* password, char hash[SOAP_SMD_SHA1_SIZE]);
    void TokenTimestmap(struct soap* pSoap, time_t lifetime = TOKEN_TIMESTAMP);
    int soap_wsse_add_Timestamp(struct soap* soap, const char* id, time_t lifetime);
#endif
private:
    std::string UserName;
    std::string PassWord;
    std::string Band;
    enum xsd__boolean TrueFlag;
    struct tt__PTZSpeed PTZSpeed;
    struct tt__Vector2D PTZVectorPT;
    struct tt__Vector1D PTZVectorZ;
    struct tt__PTZVector VectorABSPTZ;
    struct soap* soap;
    struct _tds__GetCapabilities capa_req;
    struct _tds__GetCapabilitiesResponse capa_resp;
    struct _trt__GetProfiles trt__GetProfiles;
    struct _trt__GetProfilesResponse trt__GetProfilesResponse;
    struct _trt__GetStreamUri trt__GetStreamUri;
    struct _trt__GetStreamUriResponse trt__GetStreamUriResponse;
    struct _tptz__GetStatus tptz__GetStatus;
    struct _tptz__GetStatusResponse tptz__GetStatusResponse;
    struct _tptz__ContinuousMove tptz__ContinuousMove;
    struct _tptz__ContinuousMoveResponse tptz__ContinuousMoveResponse;
    struct _tptz__Stop tptz__Stop;
    struct _tptz__StopResponse tptz__StopResponse;
    struct _tds__GetDeviceInformation tds__GetDeviceInformation;
    struct _tds__GetDeviceInformationResponse tds__GetDeviceInformationResponse;
    struct _tptz__AbsoluteMove tptz__AbsoluteMove;
    struct _tptz__AbsoluteMoveResponse tptz__AbsoluteMoveResponse;
    struct _tptz__RelativeMove tptz__RelativeMove;
    struct _tptz__RelativeMoveResponse tptz__RelativeMoveResponse;
};

实现部分(ptz控制方法):

bool OnvifSDK::GetDeviceInfo(char* url)
{
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    int ret = soap_call___tds__GetDeviceInformation(soap, url, NULL, &tds__GetDeviceInformation, &tds__GetDeviceInformationResponse);
    if (ret != SOAP_OK)
        return false;
    if (soap->error)
        return false;
    else
    {
        Band = std::string(tds__GetDeviceInformationResponse.Manufacturer);
        return true;
    }
}

bool OnvifSDK::GetCapabilities(char* url)
{
    capa_req.__sizeCategory = 1;
    capa_req.Category =  (enum tt__CapabilityCategory*)malloc(sizeof(enum tt__CapabilityCategory));
    enum tt__CapabilityCategory temp_category = tt__CapabilityCategory__All;
    capa_req.Category = &temp_category;
    capa_resp.Capabilities =  (struct tt__Capabilities*)malloc(sizeof(struct tt__Capabilities)) ;
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    int result = soap_call___tds__GetCapabilities(soap, url, NULL, &capa_req, &capa_resp);
    if (soap->error)
    {
        return false;
    }
    else
    {
        if (capa_resp.Capabilities == NULL)
        {
            printf(" GetCapabilities  failed!  result=%d \n", result);
            return false;
        }
        else
        {
            printf(" Media->XAddr=%s \n", capa_resp.Capabilities->Media->XAddr);
            printf(" Media->PTZ=%s \n", capa_resp.Capabilities->PTZ->XAddr);
            return true;
        }
    }
}

bool OnvifSDK::GetProfiles()
{
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    if (soap_call___trt__GetProfiles(soap, capa_resp.Capabilities->Media->XAddr, NULL, &trt__GetProfiles, &trt__GetProfilesResponse) == -1)
    {
        return false;
    }
    else
    {
        if (trt__GetProfilesResponse.Profiles != NULL)
        {
            if(trt__GetProfilesResponse.Profiles->Name!=NULL)
            {
                printf("Profiles Name:%s  \n", trt__GetProfilesResponse.Profiles->Name);
            }
            if (trt__GetProfilesResponse.Profiles->token != NULL)
            {
                trt__GetStreamUri.ProfileToken = trt__GetProfilesResponse.Profiles->token;
                tptz__GetStatus.ProfileToken = trt__GetProfilesResponse.Profiles->token;
                tptz__ContinuousMove.ProfileToken = trt__GetProfilesResponse.Profiles->token;
                tptz__Stop.ProfileToken = trt__GetProfilesResponse.Profiles->token;
                tptz__AbsoluteMove.ProfileToken  = trt__GetProfilesResponse.Profiles->token;
                tptz__RelativeMove.ProfileToken = trt__GetProfilesResponse.Profiles->token;
                return true;
            }
            else
                return false;
        }
        else
            return false;
    }
}

std::string OnvifSDK::GetUri(enum tt__TransportProtocol transtype)
{
    trt__GetStreamUri.StreamSetup = (struct tt__StreamSetup*)soap_malloc(soap, sizeof(struct tt__StreamSetup));
    trt__GetStreamUri.StreamSetup->Stream =  tt__StreamType__RTP_Unicast;//stream type
    trt__GetStreamUri.StreamSetup->Transport = (struct tt__Transport*)soap_malloc(soap, sizeof(struct tt__Transport));
    trt__GetStreamUri.StreamSetup->Transport->Protocol = transtype;
    trt__GetStreamUri.StreamSetup->Transport->Tunnel = 0;
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    soap_call___trt__GetStreamUri(soap, capa_resp.Capabilities->Media->XAddr, NULL, &trt__GetStreamUri, &trt__GetStreamUriResponse);
    if (soap->error)
        return "";
    else
        return std::string(trt__GetStreamUriResponse.MediaUri->Uri);
}

bool OnvifSDK::GetStstus(float* pvalue, float* tvalue, float* zvalue)
{
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    soap_call___tptz__GetStatus(soap
                                , capa_resp.Capabilities->PTZ->XAddr
                                , NULL
                                , &tptz__GetStatus
                                , &tptz__GetStatusResponse);
    if (soap->error)
        return false;
    else
    {
        *pvalue = tptz__GetStatusResponse.PTZStatus->Position->PanTilt->x;
        *tvalue = tptz__GetStatusResponse.PTZStatus->Position->PanTilt->y;
        *zvalue = tptz__GetStatusResponse.PTZStatus->Position->Zoom->x;
        return true;
    }
}

void OnvifSDK::ControlPTZ(int control, bool control_type, int speed)
{
    memset(&tptz__ContinuousMove, 0x0, sizeof(tptz__ContinuousMove));
    memset(&tptz__ContinuousMoveResponse, 0x0, sizeof(tptz__ContinuousMoveResponse));
    tptz__ContinuousMove.ProfileToken = trt__GetProfilesResponse.Profiles->token;
    tptz__ContinuousMove.Velocity = (struct tt__PTZSpeed*)soap_malloc(soap, sizeof(tt__PTZSpeed));
    memset(tptz__ContinuousMove.Velocity, 0x0, sizeof(tt__PTZSpeed));
    tptz__ContinuousMove.Velocity->PanTilt = (struct tt__Vector2D*)soap_malloc(soap, sizeof(tt__Vector2D));
    memset(tptz__ContinuousMove.Velocity->PanTilt, 0x0, sizeof(tt__Vector2D));
    tptz__ContinuousMove.Velocity->Zoom = (struct tt__Vector1D*)soap_malloc(soap, sizeof(tt__Vector1D));
    memset(tptz__ContinuousMove.Velocity->Zoom, 0x0, sizeof(tt__Vector1D));
    tptz__ContinuousMove.Velocity->PanTilt->x = 0;
    tptz__ContinuousMove.Velocity->PanTilt->y = 0;
    tptz__ContinuousMove.Velocity->Zoom->x = 0;
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    if (control_type)
    {
        switch (control)
        {
        case 0:
            tptz__ContinuousMove.Velocity->PanTilt->y = (float)speed / 10;
            break;
        case 1:
            tptz__ContinuousMove.Velocity->PanTilt->y = (float)speed / 10 * -1.0;
            break;
        case 2:
            tptz__ContinuousMove.Velocity->PanTilt->x = (float)speed / 10 * -1.0;
            break;
        case 3:
            tptz__ContinuousMove.Velocity->PanTilt->x = (float)speed / 10;
            break;
        case 4:
            tptz__ContinuousMove.Velocity->PanTilt->y = (float)speed / 10;
            tptz__ContinuousMove.Velocity->PanTilt->x = (float)speed / 10 * -1.0;
            break;
        case 5:
            tptz__ContinuousMove.Velocity->PanTilt->y = (float)speed / 10;
            tptz__ContinuousMove.Velocity->PanTilt->x = (float)speed / 10;
            break;
        case 6:
            tptz__ContinuousMove.Velocity->PanTilt->y = (float)speed / 10 * -1.0;
            tptz__ContinuousMove.Velocity->PanTilt->x = (float)speed / 10 * -1.0;
            break;
        case 7:
            tptz__ContinuousMove.Velocity->PanTilt->y = (float)speed / 10 * -1.0;
            tptz__ContinuousMove.Velocity->PanTilt->x = (float)speed / 10;
            break;
        case 8:
            tptz__ContinuousMove.Velocity->Zoom->x = (float)speed / 10;
            break;
        case 9:
            tptz__ContinuousMove.Velocity->Zoom->x = (float)speed / 10 * -1.0;
            break;
        default:
            return;
        }
        int ret = soap_call___tptz__ContinuousMove(soap, capa_resp.Capabilities->PTZ->XAddr, NULL, &tptz__ContinuousMove, &tptz__ContinuousMoveResponse);
        if (SOAP_OK == ret)
        {
            printf("RelativeMove----OK\n");
        }
    }
    else
    {
        TrueFlag = xsd__boolean__true_;
        tptz__Stop.PanTilt = &TrueFlag;
        tptz__Stop.Zoom = &TrueFlag;
        soap_call___tptz__Stop(soap, capa_resp.Capabilities->PTZ->XAddr, NULL, &tptz__Stop, &tptz__StopResponse);
    }
}

void OnvifSDK::SetPTZ(float pvalue, float tvalue, float zvalue)
{
    tptz__AbsoluteMove.Position->PanTilt->x = pvalue;
    tptz__AbsoluteMove.Position->PanTilt->y = tvalue;
    tptz__AbsoluteMove.Position->Zoom->x = zvalue;
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    int ret = soap_call___tptz__AbsoluteMove(soap, capa_resp.Capabilities->PTZ->XAddr, NULL, &tptz__AbsoluteMove, &tptz__AbsoluteMoveResponse);
}

void OnvifSDK::SetMoveStep(ptz_common commond, float move_step)
{
    memset(&tptz__RelativeMove, 0x0, sizeof(tptz__RelativeMove));
    memset(&tptz__RelativeMoveResponse, 0x0, sizeof(tptz__RelativeMoveResponse));
    //    printf("__sizeProfiles: %d\n", reponse_pro.__sizeProfiles);
    //    printf("token: %s\n", reponse_pro.Profiles->token);
    tptz__RelativeMove.ProfileToken = trt__GetProfilesResponse.Profiles->token;
    tptz__RelativeMove.Translation = (struct tt__PTZVector*)soap_malloc(soap, sizeof(tt__PTZVector));
    memset(tptz__RelativeMove.Translation, 0x0, sizeof(tt__PTZVector));
    tptz__RelativeMove.Translation->PanTilt = (struct tt__Vector2D*)soap_malloc(soap, sizeof(tt__Vector2D));
    memset(tptz__RelativeMove.Translation->PanTilt, 0x0, sizeof(tt__Vector2D));
    tptz__RelativeMove.Translation->Zoom = (struct tt__Vector1D*)soap_malloc(soap, sizeof(tt__Vector1D));
    memset(tptz__RelativeMove.Translation->Zoom, 0x0, sizeof(tt__Vector1D));
    if (move_step >= 1)
        move_step = 0.99999;
    switch (commond)
    {
    case PTZ_LEFT:
        tptz__RelativeMove.Translation->PanTilt->x = move_step;
        break;
    case PTZ_RIGHT:
        tptz__RelativeMove.Translation->PanTilt->x = -move_step;
        break;
    case PTZ_UP:
        tptz__RelativeMove.Translation->PanTilt->y = -move_step;
        break;
    case PTZ_DOWN:
        tptz__RelativeMove.Translation->PanTilt->y = move_step;
        break;
    case PTZ_LEFTUP:
        tptz__RelativeMove.Translation->PanTilt->x = move_step;
        tptz__RelativeMove.Translation->PanTilt->y = -move_step;
        break;
    case PTZ_LEFTDOWN:
        tptz__RelativeMove.Translation->PanTilt->x = move_step;
        tptz__RelativeMove.Translation->PanTilt->y = move_step;
        break;
    case PTZ_RIGHTUP:
        tptz__RelativeMove.Translation->PanTilt->x = -move_step;
        tptz__RelativeMove.Translation->PanTilt->y = -move_step;
        break;
    case PTZ_RIGHTDOWN:
        tptz__RelativeMove.Translation->PanTilt->x = -move_step;
        tptz__RelativeMove.Translation->PanTilt->y = move_step;
        break;
    case PTZ_ZOOMIN:
        tptz__RelativeMove.Translation->Zoom->x = move_step;
        break;
    case PTZ_ZOOMOUT:
        tptz__RelativeMove.Translation->Zoom->x = -move_step;
        break;
    default:
        soap_destroy(soap);
        soap_end(soap);
        soap_free(soap);
        return ;
        break;
    }
    soap_wsse_add_UsernameTokenDigest(soap, "", UserName.c_str(), PassWord.c_str());
    int result = soap_call___tptz__RelativeMove(soap, capa_resp.Capabilities->PTZ->XAddr, NULL, &tptz__RelativeMove, &tptz__RelativeMoveResponse);
    if (SOAP_OK == result)
        printf("RelativeMove----OK\n");
    else
    {
        printf("RelativeMove----faild\n");
        //printf("soap error: %d--%d, %s, %s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    }
}

鉴权是要用到openssl库的,不过嵌入式下集成一个大库你可能不太乐意,也可以用下面sha1文件中的实现 是等效的啊! sha1.c代码见工程

#include "soapH.h"
#include "soapStub.h"
#include "PTZBinding.nsmap"

#ifdef WITH_OPENSSL
#include "dom.c"
#include "mecevp.c"
#include "smdevp.c"
#include "threads.c"
#include "wsaapi.c"
#include "wsseapi.c"
#else
#include "sha1.c"
#endif

实现中鉴权部分涉及使用:

#ifdef WITH_OPENSSL
#else
void OnvifSDK::calc_nonce(struct soap* soap, char nonce[SOAP_WSSE_NONCELEN])
{
    int i;
    time_t r = time(NULL);
    memcpy(nonce, &r, 4);
    for (i = 4; i < SOAP_WSSE_NONCELEN; i += 4)
    {
        r = soap_random;
        memcpy(nonce + i, &r, 4);
    }
}

_wsse__Security* OnvifSDK::soap_wsse_add_Security(struct soap* soap)
{
    /* if we don't have a SOAP Header, create one */
    //?? soap_header(soap);  此处无法编译 到源码去拷贝
    if (soap->header == NULL)
    {
        if ((soap->header = (struct SOAP_ENV__Header*)soap_malloc(soap, sizeof(struct SOAP_ENV__Header))))
            soap_default_SOAP_ENV__Header(soap, soap->header);
    }

    /* if we don't have a wsse:Security element in the SOAP Header, create one */
    if (!soap->header->wsse__Security)
    {
        soap->header->wsse__Security = (_wsse__Security*)soap_malloc(soap, sizeof(_wsse__Security));
        soap_default__wsse__Security(soap, soap->header->wsse__Security);
    }
    return soap->header->wsse__Security;
}

int OnvifSDK::soap_wsse_add_UsernameTokenText(struct soap* soap, const char* id,const char* username, const char* password)
{
    _wsse__Security* security = soap_wsse_add_Security(soap);
    /* allocate a UsernameToken if we don't have one already */
    if (!security->UsernameToken)
        security->UsernameToken = (_wsse__UsernameToken*)soap_malloc(soap, sizeof(_wsse__UsernameToken));
    soap_default__wsse__UsernameToken(soap, security->UsernameToken);
    /* populate the UsernameToken */
    security->UsernameToken->wsu__Id = soap_strdup(soap, id);
    security->UsernameToken->Username = soap_strdup(soap, username);
    /* allocate and populate the Password */
    if (password)
    {
        security->UsernameToken->Password = (_wsse__Password*)soap_malloc(soap, sizeof(_wsse__Password));
        soap_default__wsse__Password(soap, security->UsernameToken->Password);
        security->UsernameToken->Password->Type = (char*)wsse_PasswordTextURI;
        security->UsernameToken->Password->__item = soap_strdup(soap, password);
    }
    return SOAP_OK;
}

int OnvifSDK::soap_wsse_add_UsernameTokenDigest(struct soap* soap, const char* id,const char* username, const char* password)
{
    _wsse__Security* security = soap_wsse_add_Security(soap);
    time_t now = time(NULL);
    const char* created = soap_dateTime2s(soap, now);
    char HA[SOAP_SMD_SHA1_SIZE], HABase64[29];
    char nonce[SOAP_WSSE_NONCELEN], *nonceBase64;
    /* generate a nonce */
    calc_nonce(soap, nonce);
    nonceBase64 = soap_s2base64(soap, (unsigned char*)nonce, NULL, SOAP_WSSE_NONCELEN);
    /* The specs are not clear: compute digest over binary nonce or base64 nonce? */
    /* compute SHA1(created, nonce, password) */
    /*  // boost计算结果不对,应该是我的使用方法不对
        unsigned int Digest[5] = {0};
        boost::uuids::detail::sha1  sha;
        sha.process_bytes(nonce, strlen(nonce));
        sha.process_bytes(created, strlen(created));
        sha.process_bytes(password, strlen(password));
        sha.get_digest(Digest);

        for (int n=0,i=0; n> 24) & 0xFF;
            HA[n++] = (Digest[i] >> 16) & 0xFF;
            HA[n++] = (Digest[i] >> 8) & 0xFF;
            HA[n++] = Digest[i] & 0xFF;
            i++;
        }*/
    calc_digest(soap, created, nonce, SOAP_WSSE_NONCELEN, password, HA);
    // calc_digest(soap, created, nonce, SOAP_WSSE_NONCELEN, password, HA);
    /*
        calc_digest(soap, created, nonceBase64, strlen(nonceBase64), password, HA);
    */
    soap_s2base64(soap, (unsigned char*)HA, HABase64, SOAP_SMD_SHA1_SIZE);
    /* populate the UsernameToken with digest */
    soap_wsse_add_UsernameTokenText(soap, id, username, HABase64);
    /* populate the remainder of the password, nonce, and created */
    security->UsernameToken->Password->Type = (char*)wsse_PasswordDigestURI;
    security->UsernameToken->Nonce = (struct wsse__EncodedString*)soap_malloc(soap, sizeof(struct wsse__EncodedString));
    if (!security->UsernameToken->Nonce)
        return soap->error = SOAP_EOM;
    soap_default_wsse__EncodedString(soap, security->UsernameToken->Nonce);
    security->UsernameToken->Nonce->__item = nonceBase64;
    security->UsernameToken->Nonce->EncodingType = (char*)wsse_Base64BinaryURI;
    security->UsernameToken->wsu__Created = soap_strdup(soap, created);
    return SOAP_OK;
}

void OnvifSDK::calc_digest(struct soap* soap, const char* created, const char* nonce, int noncelen, const char* password, char hash[SOAP_SMD_SHA1_SIZE])
{
    SHA1Context sha;
    SHA1Reset(&sha);
    SHA1Input(&sha, (unsigned char*)nonce, noncelen);
    SHA1Input(&sha, (unsigned char*)created, strlen(created));
    SHA1Input(&sha, (unsigned char*)password, strlen(password));
    if (!SHA1Result(&sha))
        fprintf(stderr, "ERROR-- could not compute message digest\n");
    else
    {
        int j = 0;
        for (int i = 0; i < 5; i++)
        {
            hash[j++] = sha.Message_Digest[i] >> 24;
            hash[j++] = sha.Message_Digest[i] >> 16;
            hash[j++] = sha.Message_Digest[i] >> 8;
            hash[j++] = sha.Message_Digest[i] >> 0;
        }
    }
}

void OnvifSDK::TokenTimestmap(struct soap* pSoap, time_t lifetime /*= TOKEN_TIMESTAMP*/)
{
    soap_wsse_add_Timestamp(pSoap, "Time", lifetime);
}

int OnvifSDK::soap_wsse_add_Timestamp(struct soap* soap, const char* id, time_t lifetime)
{
    _wsse__Security* security = soap_wsse_add_Security(soap);
    time_t now = time(NULL);
    char* created = soap_strdup(soap, soap_dateTime2s(soap, now));
    char* expired = lifetime ? soap_strdup(soap, soap_dateTime2s(soap, now + lifetime)) : NULL;
    /* allocate a Timestamp if we don't have one already */
    if (!security->wsu__Timestamp)
        security->wsu__Timestamp = (_wsu__Timestamp*)soap_malloc(soap, sizeof(_wsu__Timestamp));
    soap_default__wsu__Timestamp(soap, security->wsu__Timestamp);
    /* populate the wsu:Timestamp element */
    security->wsu__Timestamp->wsu__Id = soap_strdup(soap, id);
    security->wsu__Timestamp->Created = created;
    security->wsu__Timestamp->Expires = expired;
    return SOAP_OK;
}

#endif

完整工程下载

你可能感兴趣的:(C++,ffmpeg,流媒体,webservice,onvif,ffmpeg,rtsp,自动驾驶)