go+gSoap+onvif学习总结:7、进行镜头调焦、聚焦和预置点的增删改查

2023-10-28

cgo+gSoap+onvif学习总结:7、进行镜头调焦、聚焦和预置点的增删改查


1. 前言

镜头调焦和聚焦之前我们说过,一个使用的ptz能力,一个使用的imaging能力,而预置点使用的还是使用的ptz能力。

2. gSoap生成c代码框架

网络接口规范地址:https://www.onvif.org/profiles/specifications/

cd ./soap/
//注意这里的typemap.dat是上节我们修改过的,不然还会出现duration.c编译报错
wsdl2h -c -t ./typemap.dat -o onvif.h http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl https://www.onvif.org/ver10/events/wsdl/event.wsdl https://www.onvif.org/ver10/media/wsdl/media.wsdl https://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl https://www.onvif.org/ver20/imaging/wsdl/imaging.wsdl

vim onvif.h
添加 #import "wsse.h"

//需要依赖如下文件,从gSoap源码拷贝到我们的工程中(拷贝一次后续就可以一直用了)
dom.c、dom.h、wsseapi.c、wsseapi.h、smdevp.c、smdevp.h、mecevp.c、mecevp.h、threads.c、threads.h、wsaapi.c、wsaapi.h
//注意下面的文件需要custom文件夹,否则路径不对
struct_timeval.h、struct_timeval.c
//duration.c以及duration.h对于生成框架代码和编译c代码路径可能会有差异,根据实际情况可能需要两个位置都有

//只生成客户端源文件,我们只实现客户端,不添加-C会多生成服务端代码
soapcpp2 -x -L -C onvif.h

整个过程截图:

在这里插入图片描述

3. 完成c代码实例并测试

由于缩放也是在ptz的,所以我们在上节的ptz基础上增加两个值来进行缩、放控制指令zoom下发。

focus单独使用img能力调用接口进行move和stop,此外,focus还需要videoSourceToken,这个token不同于profileToken,但是获取方式类似,所以还需要增加videoSourceToken获取的实现。

预置点的接口虽然也是使用的ptz能力,但是接口和ptz控制的接口不同,所以我们也单独再封装接口做增删改查及预置点跳转。

3.1 c代码

//
// Created by admin on 2022/3/3.
//

#include <string.h>
#include "soap/soapStub.h"
#include "soap/wsdd.nsmap"
#include "soap/soapH.h"
#include "soap/wsseapi.h"
#include "client.h"

struct soap *new_soap(struct soap *soap) {
    //soap初始化,申请空间
    soap = soap_new();
    if (soap == NULL) {
        printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
        return NULL;
    }

    soap_set_namespaces(soap, namespaces);
    soap->recv_timeout = 3;
    printf("func:%s,line:%d.new soap success!\n", __FUNCTION__, __LINE__);
    return soap;
}

void del_soap(struct soap *soap) {
    //清除soap
    soap_end(soap);
    soap_free(soap);
}

int discovery(struct soap *soap) {
    //发送消息描述
    struct wsdd__ProbeType req;
    struct __wsdd__ProbeMatches resp;
    //描述查找那类的Web消息
    struct wsdd__ScopesType sScope;
    //soap消息头消息
    struct SOAP_ENV__Header header;
    //获得的设备信息个数
    int count = 0;
    //返回值
    int res;
    //存放uuid 格式(8-4-4-4-12)
    char uuid_string[64];

    printf("func:%s,line:%d.discovery dev!\n", __FUNCTION__, __LINE__);
    if (soap == NULL) {
        printf("func:%s,line:%d.soap is nil.\n", __FUNCTION__, __LINE__);
        return -1;
    }

    sprintf(uuid_string, "464A4854-4656-5242-4530-110000000000");
    printf("func:%s,line:%d.uuid = %s\n", __FUNCTION__, __LINE__, uuid_string);

    //将header设置为soap消息,头属性,暂且认为是soap和header绑定
    soap_default_SOAP_ENV__Header(soap, &header);
    header.wsa5__MessageID = uuid_string;
    header.wsa5__To = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";
    header.wsa5__Action = "http://schemas.xmllocal_soap.org/ws/2005/04/discovery/Probe";
    //设置soap头消息的ID
    soap->header = &header;

    /* 设置所需寻找设备的类型和范围,二者至少设置一个
        否则可能收到非ONVIF设备,出现异常
     */
    //设置soap消息的请求服务属性
    soap_default_wsdd__ScopesType(soap, &sScope);
    sScope.__item = "onvif://www.onvif.org";
    soap_default_wsdd__ProbeType(soap, &req);
    req.Scopes = &sScope;

    /* 设置所需设备的类型,ns1为命名空间前缀,在wsdd.nsmap 文件中
       {"tdn","http://www.onvif.org/ver10/network/wsdl"}的tdn,如果不是tdn,而是其它,
       例如ns1这里也要随之改为ns1
    */
    req.Types = "ns1:NetworkVideoTransmitter";

    //调用gSoap接口 向 239.255.255.250:3702 发送udp消息
    res = soap_send___wsdd__Probe(soap, "soap.udp://239.255.255.250:3702/", NULL, &req);

    if (res == -1) {
        printf("func:%s,line:%d.soap error: %d, %s, %s \n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
               *soap_faultstring(soap));
        res = soap->error;
    } else {
        do {
            printf("func:%s,line:%d.begin receive probe match, find dev count:%d.... \n", __FUNCTION__, __LINE__,
                   count);

            //接收 ProbeMatches,成功返回0,错误返回-1
            res = soap_recv___wsdd__ProbeMatches(soap, &resp);
            printf("func:%s,line:%d.result=%d \n", __FUNCTION__, __LINE__, res);
            if (res == -1) {
                break;
            } else {
                //读取服务器回应的Probematch消息
                printf("soap_recv___wsdd__Probe: __sizeProbeMatch = %d \n", resp.wsdd__ProbeMatches->__sizeProbeMatch);
                printf("Target EP Address : %s \n",
                       resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference.Address);
                printf("Target Type : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Types);
                printf("Target Service Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->XAddrs);
                printf("Target Metadata Version: %d \n", resp.wsdd__ProbeMatches->ProbeMatch->MetadataVersion);
                printf("Target Scope Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Scopes->__item);
                count++;
            }
        } while (1);
    }

    return res;
}

int set_auth_info(struct soap *soap, const char *username, const char *password) {
    if (NULL == username) {
        printf("func:%s,line:%d.username is null.\n", __FUNCTION__, __LINE__);
        return -1;
    }
    if (NULL == password) {
        printf("func:%s,line:%d.password is nil.\n", __FUNCTION__, __LINE__);
        return -2;
    }

    int result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);

    return result;
}

int get_device_info(struct soap *soap, const char *username, const char *password, char *xAddr) {
    if (NULL == xAddr) {
        printf("func:%s,line:%d.dev addr is nil.\n", __FUNCTION__, __LINE__);
        return -1;
    }
    if (soap == NULL) {
        printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
        return -2;
    }

    struct _tds__GetDeviceInformation deviceInformation;
    struct _tds__GetDeviceInformationResponse deviceInformationResponse;

    set_auth_info(soap, username, password);

    int res = soap_call___tds__GetDeviceInformation(soap, xAddr, NULL, &deviceInformation, &deviceInformationResponse);

    if (NULL != soap) {
        printf("Manufacturer:%s\n", deviceInformationResponse.Manufacturer);
        printf("Model:%s\n", deviceInformationResponse.Model);
        printf("FirmwareVersion:%s\n", deviceInformationResponse.FirmwareVersion);
        printf("SerialNumber:%s\n", deviceInformationResponse.SerialNumber);
        printf("HardwareId:%s\n", deviceInformationResponse.HardwareId);
        soap_default__tds__GetDeviceInformation(soap, &deviceInformation);
        soap_default__tds__GetDeviceInformationResponse(soap, &deviceInformationResponse);
    }
    return res;
}

int get_capabilities(struct soap *soap, const char *username, const char *password, char *xAddr, char *mediaAddr) {
    struct _tds__GetCapabilities capabilities;
    struct _tds__GetCapabilitiesResponse capabilitiesResponse;

    set_auth_info(soap, username, password);
    int res = soap_call___tds__GetCapabilities(soap, xAddr, NULL, &capabilities, &capabilitiesResponse);
    if (soap->error) {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
               *soap_faultstring(soap));
        return soap->error;
    }
    if (capabilitiesResponse.Capabilities == NULL) {
        printf("func:%s,line:%d.GetCapabilities  failed!  result=%d \n", __FUNCTION__, __LINE__, res);
    } else {
        printf("func:%s,line:%d.Media->XAddr=%s \n", __FUNCTION__, __LINE__,
               capabilitiesResponse.Capabilities->Media->XAddr);
        strcpy(mediaAddr, capabilitiesResponse.Capabilities->Media->XAddr);
    }
    return res;
}

int get_profiles(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
    struct _trt__GetProfiles profiles;
    struct _trt__GetProfilesResponse profilesResponse;
    set_auth_info(soap, username, password);
    int res = soap_call___trt__GetProfiles(soap, xAddr, NULL, &profiles, &profilesResponse);
    if (res == -1)
        //NOTE: it may be regular if result isn't SOAP_OK.Because some attributes aren't supported by server.
        //any question email leoluopy@gmail.com
    {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
               *soap_faultstring(soap));
        return soap->error;
    }

    if (profilesResponse.__sizeProfiles <= 0) {
        printf("func:%s,line:%d.Profiles Get Error\n", __FUNCTION__, __LINE__);
        return res;
    }

    for (int i = 0; i < profilesResponse.__sizeProfiles; i++) {
        if (profilesResponse.Profiles[i].token != NULL) {
            printf("func:%s,line:%d.Profiles token:%s\n", __FUNCTION__, __LINE__, profilesResponse.Profiles->Name);

            //默认我们取第一个即可,可以优化使用字符串数组存储多个
            if (i == 0) {
                strcpy(profileToken, profilesResponse.Profiles[i].token);
            }
        }
    }
    return res;
}

int get_rtsp_uri(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
    struct _trt__GetStreamUri streamUri;
    struct _trt__GetStreamUriResponse streamUriResponse;
    streamUri.StreamSetup = (struct tt__StreamSetup *) soap_malloc(soap, sizeof(struct tt__StreamSetup));
    streamUri.StreamSetup->Stream = 0;
    streamUri.StreamSetup->Transport = (struct tt__Transport *) soap_malloc(soap, sizeof(struct tt__Transport));
    streamUri.StreamSetup->Transport->Protocol = 0;
    streamUri.StreamSetup->Transport->Tunnel = 0;
    streamUri.StreamSetup->__size = 1;
    streamUri.StreamSetup->__any = NULL;
    streamUri.StreamSetup->__anyAttribute = NULL;
    streamUri.ProfileToken = profileToken;
    set_auth_info(soap, username, password);
    int res = soap_call___trt__GetStreamUri(soap, xAddr, NULL, &streamUri, &streamUriResponse);
    if (soap->error) {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
               *soap_faultstring(soap));
        return soap->error;
    }
    printf("func:%s,line:%d.RTSP uri is :%s \n", __FUNCTION__, __LINE__, streamUriResponse.MediaUri->Uri);
    return res;
}

int get_snapshot(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
    struct _trt__GetSnapshotUri snapshotUri;
    struct _trt__GetSnapshotUriResponse snapshotUriResponse;

    set_auth_info(soap, username, password);
    snapshotUri.ProfileToken = profileToken;
    int res = soap_call___trt__GetSnapshotUri(soap, xAddr, NULL, &snapshotUri, &snapshotUriResponse);
    if (soap->error) {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
               *soap_faultstring(soap));
        return soap->error;
    }
    printf("func:%s,line:%d.Snapshot uri is :%s \n", __FUNCTION__, __LINE__,
           snapshotUriResponse.MediaUri->Uri);
    return res;
}

int get_video_source(struct soap *soap, const char *username, const char *password, char *videoSource, char *xAddr) {
    struct _trt__GetVideoSources getVideoSources;
    struct _trt__GetVideoSourcesResponse getVideoSourcesResponse;

    set_auth_info(soap, username, password);

    int res = soap_call___trt__GetVideoSources(soap, xAddr, NULL, &getVideoSources, &getVideoSourcesResponse);
    if (soap->error) {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
               *soap_faultstring(soap));
        return soap->error;
    }
    if (getVideoSourcesResponse.__sizeVideoSources <= 0) {
        printf("func:%s,line:%d.get video sources failed.\n", __FUNCTION__, __LINE__);
        return res;
    } else {
        for (int i = 0; i < getVideoSourcesResponse.__sizeVideoSources; i++) {
            printf("func:%s,line:%d.get video source token:%s\n", __FUNCTION__, __LINE__,
                   getVideoSourcesResponse.VideoSources[i].token);

            //我们暂时只获取第一个videoSourceToken
            if (i == 0) {
                strcpy(videoSource, getVideoSourcesResponse.VideoSources[i].token);
            }
        }
    }
    return res;
}

int ptz(struct soap *soap, const char *username, const char *password, int direction, float speed, char *profileToken,
        char *xAddr) {
    struct _tptz__ContinuousMove continuousMove;
    struct _tptz__ContinuousMoveResponse continuousMoveResponse;
    struct _tptz__Stop stop;
    struct _tptz__StopResponse stopResponse;
    int res;

    set_auth_info(soap, username, password);
    continuousMove.ProfileToken = profileToken;
    continuousMove.Velocity = (struct tt__PTZSpeed *) soap_malloc(soap, sizeof(struct tt__PTZSpeed));
    continuousMove.Velocity->PanTilt = (struct tt__Vector2D *) soap_malloc(soap, sizeof(struct tt__Vector2D));

    switch (direction) {
        case 1:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = speed;
            break;
        case 2:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = -speed;
            break;
        case 3:
            continuousMove.Velocity->PanTilt->x = -speed;
            continuousMove.Velocity->PanTilt->y = 0;
            break;
        case 4:
            continuousMove.Velocity->PanTilt->x = speed;
            continuousMove.Velocity->PanTilt->y = 0;
            break;
        case 5:
            continuousMove.Velocity->PanTilt->x = -speed;
            continuousMove.Velocity->PanTilt->y = speed;
            break;
        case 6:
            continuousMove.Velocity->PanTilt->x = -speed;
            continuousMove.Velocity->PanTilt->y = -speed;
            break;
        case 7:
            continuousMove.Velocity->PanTilt->x = speed;
            continuousMove.Velocity->PanTilt->y = speed;
            break;
        case 8:
            continuousMove.Velocity->PanTilt->x = speed;
            continuousMove.Velocity->PanTilt->y = -speed;
            break;
        case 9:
            stop.ProfileToken = profileToken;
            stop.Zoom = NULL;
            stop.PanTilt = NULL;
            res = soap_call___tptz__Stop(soap, xAddr, NULL, &stop, &stopResponse);
            if (soap->error) {
                printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
                       *soap_faultcode(soap),
                       *soap_faultstring(soap));
                return soap->error;
            }
            return res;
        case 10:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = 0;
            continuousMove.Velocity->Zoom = (struct tt__Vector1D *) soap_malloc(soap, sizeof(struct tt__Vector1D));
            continuousMove.Velocity->Zoom->x = speed;
            break;
        case 11:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = 0;
            continuousMove.Velocity->Zoom = (struct tt__Vector1D *) soap_malloc(soap, sizeof(struct tt__Vector1D));
            continuousMove.Velocity->Zoom->x = -speed;
            break;
        default:
            printf("func:%s,line:%d.Ptz direction unknown.\n", __FUNCTION__, __LINE__);
            return -1;
    }
    res = soap_call___tptz__ContinuousMove(soap, xAddr, NULL, &continuousMove, &continuousMoveResponse);
    if (soap->error) {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
               *soap_faultstring(soap));
        return soap->error;
    }

    return res;
}

int
focus(struct soap *soap, const char *username, const char *password, int direction, float speed, char *videoSourceToken,
      char *xAddr) {
    struct _timg__Move timgMove;
    struct _timg__MoveResponse timgMoveResponse;
    struct _timg__Stop timgStop;
    struct _timg__StopResponse timgStopResponse;
    int res;

    set_auth_info(soap, username, password);
    timgMove.Focus = (struct tt__FocusMove *) soap_malloc(soap, sizeof(struct tt__FocusMove));
    timgMove.Focus->Continuous = (struct tt__ContinuousFocus *) soap_malloc(soap, sizeof(struct tt__ContinuousFocus));
    timgMove.VideoSourceToken = videoSourceToken;
    switch (direction) {
        case 12:
            timgMove.Focus->Continuous->Speed = speed;
            break;
        case 13:
            timgMove.Focus->Continuous->Speed = -speed;
            break;
        case 14:
            timgStop.VideoSourceToken = videoSourceToken;
            res = soap_call___timg__Stop(soap, xAddr, NULL, &timgStop, &timgStopResponse);
            if (soap->error) {
                printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
                       *soap_faultcode(soap), *soap_faultstring(soap));
                return soap->error;
            }
            return res;
        default:
            printf("unknown direction");
            return -1;
    }
    res = soap_call___timg__Move(soap, xAddr, NULL, &timgMove, &timgMoveResponse);
    if (soap->error) {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
               *soap_faultcode(soap), *soap_faultstring(soap));
        return soap->error;
    }
    return res;
}

int preset(struct soap *soap, const char *username, const char *password, int presetAction, char *presetToken,
           char *presetName, char *profileToken, char *xAddr) {
    int res;
    set_auth_info(soap, username, password);
    switch (presetAction) {
        case 1: {
            struct _tptz__GetPresets getPresets;
            struct _tptz__GetPresetsResponse getPresetsResponse;
            getPresets.ProfileToken = profileToken;
            res = soap_call___tptz__GetPresets(soap, xAddr, NULL, &getPresets, &getPresetsResponse);
            if (soap->error) {
                printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
                       *soap_faultcode(soap), *soap_faultstring(soap));
                return soap->error;
            }
            if (getPresetsResponse.__sizePreset <= 0) {
                printf("func:%s,line:%d.get presets failed.\n", __FUNCTION__, __LINE__);
                return res;
            }
            for (int i = 0; i < getPresetsResponse.__sizePreset; i++) {
                printf("func:%s,line:%d.preset token:%s,preset name:%s\n", __FUNCTION__, __LINE__,
                       getPresetsResponse.Preset[i].token, getPresetsResponse.Preset[i].Name);
            }
            return res;
        }
        case 2: {
            struct _tptz__SetPreset setPreset;
            struct _tptz__SetPresetResponse setPresetResponse;
            setPreset.ProfileToken = profileToken;
            setPreset.PresetName = presetName;
            setPreset.PresetToken = presetToken;
            res = soap_call___tptz__SetPreset(soap, xAddr, NULL, &setPreset, &setPresetResponse);
            break;
        }
        case 3: {
            struct _tptz__GotoPreset gotoPreset;
            struct _tptz__GotoPresetResponse gotoPresetsResponse;
            gotoPreset.ProfileToken = profileToken;
            gotoPreset.PresetToken = presetToken;
            gotoPreset.Speed = NULL;
            res = soap_call___tptz__GotoPreset(soap, xAddr, NULL, &gotoPreset, &gotoPresetsResponse);
            break;
        }
        case 4: {
            struct _tptz__RemovePreset removePreset;
            struct _tptz__RemovePresetResponse removePresetResponse;
            removePreset.PresetToken = presetToken;
            removePreset.ProfileToken = profileToken;
            res = soap_call___tptz__RemovePreset(soap, xAddr, NULL, &removePreset, &removePresetResponse);
            break;
        }
        default:
            printf("func:%s,line:%d.Unknown preset action.\n", __FUNCTION__, __LINE__);
    }

    if (soap->error) {
        printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
               *soap_faultcode(soap), *soap_faultstring(soap));
        return soap->error;
    }

    return res;
}

int main() {
    struct soap *soap = NULL;
    soap = new_soap(soap);
    const char username[] = "admin";
    const char password[] = "admin";
    char serviceAddr[] = "http://40.40.40.101:80/onvif/device_service";

    discovery(soap);

    get_device_info(soap, username, password, serviceAddr);

    char mediaAddr[200] = {'\0'};
    get_capabilities(soap, username, password, serviceAddr, mediaAddr);

    char profileToken[200] = {'\0'};
    get_profiles(soap, username, password, profileToken, mediaAddr);

    get_rtsp_uri(soap, username, password, profileToken, mediaAddr);

    get_snapshot(soap, username, password, profileToken, mediaAddr);

    char videoSourceToken[200] = {'\0'};
    get_video_source(soap, username, password, videoSourceToken, mediaAddr);

    int direction = -1;
    while (direction != 0) {
        printf("请输入数字进行ptz,1-14分别代表上、下、左、右、左上、左下、右上、右下、停止、缩、放、调焦加、调焦减、调焦停止;退出请输入0:");
        scanf("%d", &direction);
        if ((direction >= 1) && (direction <= 11)) {
            ptz(soap, username, password, direction, 0.5f, profileToken, mediaAddr);
        } else if (direction >= 12 && direction <= 14) {
            focus(soap, username, password, direction, 0.5f, videoSourceToken, mediaAddr);
        }
    }

    int presetAction = -1;
    while (presetAction != 0) {
        printf("请输入数字进行preset,1-4分别代表查询、设置、跳转、删除预置点;退出输入0:\n");
        scanf("%d", &presetAction);
        if (1 == presetAction) {
            preset(soap, username, password, presetAction, NULL, NULL, profileToken, mediaAddr);
        } else if (2 == presetAction) {
            printf("请输入要设置的预置点token信息:\n");
            char presentToken[10];
            scanf("%s", presentToken);
            printf("请输入要设置的预置点name信息()长度不超过200:\n");
            char presentName[201];
            scanf("%s", presentName);
            preset(soap, username, password, presetAction, presentToken, presentName, profileToken, mediaAddr);
        } else if (3 == presetAction) {
            printf("请输入要跳转的预置点token信息:\n");
            char presentToken[10];
            scanf("%s", presentToken);
            preset(soap, username, password, presetAction, presentToken, NULL, profileToken, mediaAddr);
        } else if (4 == presetAction) {
            printf("请输入要删除的预置点token信息:\n");
            char presentToken[10];
            scanf("%s", presentToken);
            preset(soap, username, password, presetAction, presentToken, NULL, profileToken, mediaAddr);
        }
    }

    del_soap(soap);
}

3.2 cmake

cmake_minimum_required(VERSION 3.0)
project(onvif-cgo)

set(CMAKE_C_FLAGS "-DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACES")
aux_source_directory(./soap/ SRC_LIST)
include_directories(./ /usr/include/ ./soap/custom)
link_directories(~/ /usr/local/ /usr/lib/)
add_executable(gsoap-onvif ${SRC_LIST} client.c client.h ./soap/custom)
target_link_libraries(gsoap-onvif -lpthread -ldl -lssl -lcrypto)
#ADD_LIBRARY(c_onvif SHARED ${SRC_LIST} client.c)
#ADD_LIBRARY(c_onvif_static STATIC ${SRC_LIST} client.c client.h ./soap/custom)

3.3 结果展示

缩放和调焦:

在这里插入图片描述

预置点查、增、跳转、删除:

在这里插入图片描述

4. 完成cgo代码示例并测试

和之前一样,c代码去除main函数,cmake编译静态库,然后写好头文件中的外部调用接口即可用于go调用。

4.1 cgo代码及编译

package main

/*
#cgo CFLAGS: -I ./ -I /usr/local/
#cgo LDFLAGS: -L ./build -lc_onvif_static -lpthread -ldl -lssl -lcrypto
#include "client.h"
#include "malloc.h"
*/
import "C"

import (
    "fmt"
    "unsafe"
)

func main() {
    var soap C.P_Soap
    soap = C.new_soap(soap)
    username := C.CString("admin")
    password := C.CString("admin")
    serviceAddr := C.CString("http://40.40.40.101:80/onvif/device_service")

    C.discovery(soap)

    C.get_device_info(soap, username, password, serviceAddr)

    mediaAddr := [200]C.char{}
    C.get_capabilities(soap, username, password, serviceAddr, &mediaAddr[0])

    profileToken := [200]C.char{}
    C.get_profiles(soap, username, password, &profileToken[0], &mediaAddr[0])

    C.get_rtsp_uri(soap, username, password, &profileToken[0], &mediaAddr[0])

    C.get_snapshot(soap, username, password, &profileToken[0], &mediaAddr[0])

    videoSourceToken := [200]C.char{}
    C.get_video_source(soap, username, password, &videoSourceToken[0], &mediaAddr[0])

    PTZ:for {
        direction := uint(0)
        fmt.Println("请输入数字进行ptz,1-14分别代表上、下、左、右、左上、左下、右上、右下、停止、缩、放、调焦加、调焦减、调焦停止;退出请输入0:");
        fmt.Scanln(&direction)
        switch (direction) {
        case 0:
            break PTZ
        case 1,2,3,4,5,6,7,8,9,10,11:
            C.ptz(soap, username, password, C.int(direction), C.float(0.5), &profileToken[0], &mediaAddr[0])
            continue
        case 12,13,14:
            C.focus(soap, username, password, C.int(direction), C.float(0.5), &videoSourceToken[0], &mediaAddr[0]);
            continue
        default:
            fmt.Println("Unknown direction.")
        }
    }

    Preset:for {
        presetAction := uint(0)
        fmt.Println("请输入数字进行preset,1-4分别代表查询、设置、跳转、删除预置点;退出输入0:")
        fmt.Scanln(&presetAction)
        switch(presetAction) {
        case 0:
            break Preset
        case 1:
            C.preset(soap, username, password, C.int(presetAction), nil, nil, &profileToken[0], &mediaAddr[0])
        case 2:
            fmt.Println("请输入要设置的预置点token信息:")
            presentToken := ""
            fmt.Scanln(&presentToken)
            fmt.Println("请输入要设置的预置点name信息长度不超过200:")
            presentName := ""
            fmt.Scanln(&presentName)
            C.preset(soap, username, password, C.int(presetAction), C.CString(presentToken), C.CString(presentName), &profileToken[0], &mediaAddr[0])
        case 3:
            fmt.Println("请输入要跳转的预置点token信息:")
            presentToken := ""
            fmt.Scanln(&presentToken)
            C.preset(soap, username, password, C.int(presetAction), C.CString(presentToken), nil, &profileToken[0], &mediaAddr[0])
        case 4:
            fmt.Println("请输入要删除的预置点token信息:")
            presentToken := ""
            fmt.Scanln(&presentToken)
            C.preset(soap, username, password, C.int(presetAction), C.CString(presentToken), nil, &profileToken[0], &mediaAddr[0])
        default:
            fmt.Println("unknown present action.")
            break
        }
    }

    C.del_soap(soap)

    C.free(unsafe.Pointer(username))
    C.free(unsafe.Pointer(password))
    C.free(unsafe.Pointer(serviceAddr))

    return
}

编译:

GOOS=linux GOARCH=amd64 CGO_ENABLE=1 go build -o onvif_cgo main.go

4.4 结果展示

在这里插入图片描述

5. 整体项目结构

zy@LS2-R910CQQT:/mnt/d/code/onvif_cgo$ tree -a -I ".idea|.git|build|cmake-build-debug"
.
├── CMakeLists.txt
├── client.c
├── client.h
├── libc_onvif.so
├── main.go
├── onvif_cgo
└── soap
    ├── DeviceBinding.nsmap
    ├── ImagingBinding.nsmap
    ├── MediaBinding.nsmap
    ├── PTZBinding.nsmap
    ├── PullPointSubscriptionBinding.nsmap
    ├── RemoteDiscoveryBinding.nsmap
    ├── custom
    │   ├── README.txt
    │   ├── chrono_duration.cpp
    │   ├── chrono_duration.h
    │   ├── chrono_time_point.cpp
    │   ├── chrono_time_point.h
    │   ├── duration.c
    │   ├── duration.h
    │   ├── float128.c
    │   ├── float128.h
    │   ├── int128.c
    │   ├── int128.h
    │   ├── long_double.c
    │   ├── long_double.h
    │   ├── long_time.c
    │   ├── long_time.h
    │   ├── qbytearray_base64.cpp
    │   ├── qbytearray_base64.h
    │   ├── qbytearray_hex.cpp
    │   ├── qbytearray_hex.h
    │   ├── qdate.cpp
    │   ├── qdate.h
    │   ├── qdatetime.cpp
    │   ├── qdatetime.h
    │   ├── qstring.cpp
    │   ├── qstring.h
    │   ├── qtime.cpp
    │   ├── qtime.h
    │   ├── struct_timeval.c
    │   ├── struct_timeval.h
    │   ├── struct_tm.c
    │   ├── struct_tm.h
    │   ├── struct_tm_date.c
    │   └── struct_tm_date.h
    ├── dom.c
    ├── dom.h
    ├── duration.c
    ├── duration.h
    ├── mecevp.c
    ├── mecevp.h
    ├── onvif.h
    ├── smdevp.c
    ├── smdevp.h
    ├── soapC.c
    ├── soapClient.c
    ├── soapH.h
    ├── soapStub.h
    ├── stdsoap2.h
    ├── stdsoap2_ssl.c
    ├── struct_timeval.c
    ├── struct_timeval.h
    ├── threads.c
    ├── threads.h
    ├── typemap.dat
    ├── wsaapi.c
    ├── wsaapi.h
    ├── wsdd.nsmap
    ├── wsseapi.c
    └── wsseapi.h

2 directories, 70 files

6. 最后

目前来说,暂时用到的onvif客户端相关的内容就这些了,其它的可能是在此基础上扩展音视频编解码或者CV算法调用等,接下来我可能会将这部分内容借助H5做一个简单的摄像头控制页面,和Go做简单的前后端调用,看下h5如何通过rtsp流来实时播放视频,并看下如何在一定程度上降低延迟,再之后我们可能会去总结一些CV的东西,比如OpenCV如何使用,如何和onvif结合使用,如何在手机上、PC上结合Qt等来开发软件使用,对于一些没有人脸识别的手机,我们如何自己做一个人脸识别的软件来做简单的代替或者使用人脸登录配合传统的用户名密码登录等等(ps:前面鸽的stm32的开发总结说实话我还没想好啥时候继续,最近有个嵌入式中级考试,可能会先总结这块,估计也得个大半年甚至一年)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go+gSoap+onvif学习总结:7、进行镜头调焦、聚焦和预置点的增删改查 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 重新插入通道导致死锁

    我有稳定的入站 作业 流 将其输入到无缓冲通道中 我有一个for range循环来迭代项目并处理它们 如果处理该项目失败 我会将项目重新插入通道中 以便稍后重试 问题是当我将项目重新插入通道时 它陷入僵局 我明白为什么会发生这种情况 处理器
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么

随机推荐

  • 支持Vulkan的移动GPU

    去年差不多这个时候 Vulkan标准发布 NVIDIA和AMD随之发布了显卡的Vulkan驱动 虽然都是实验版本 但是毕竟能够工作的 Intel的速度就慢了不少 时隔一年 Intel终于推出了Vulkan认证的驱动 虽然之前就有了实验性质的
  • 最通俗易懂的多线程面试60题

    多线程面试60题 1 多线程有什么用 2 线程和进程的区别是什么 3 Java 实现线程有哪几种方式 4 启动线程方法 start 和 run 有什么区别 5 怎么终止一个线程 6 一个线程的生命周期有哪几种状态 它们之间如何流转的 7 线
  • 希沃展台如何使用_峄城区教体局率团指导希沃教学一体机培训

    秋风起兮白云飞 草木黄兮雁南归 历经漫长的暑假 学生返回校园 城郊中学迎来了一批新装备 希沃教学一体机 为了让老师们更快更好的掌握使用新设备 提高课堂教学效率 9月16日城郊中学启动希沃教学一体机培训工程 峄城区教体局教研室 政工股 安全办
  • 【MATLAB第22期】基于MATLAB的xgboost算法多输入多输出回归模型 已购用户可在之前下载链接免费获取

    MATLAB第22期 基于MATLAB的xgboost算法多输入多输出回归模型 已购用户可在之前下载链接免费获取 往期文章 xgboost安装教程 最近有很多小伙伴私信我有关xgboost预测的问题 被问到最多的问题总结如下 1 xgboo
  • Laravel报错:ErrorException (E_ERROR) Route [*] not defined.

    问题介绍 使用环境 laravel58 我想使用资源管理器进行跳转 路由代码如下 Route group namespace gt Admin prefix gt admin middleware gt adminLogin functio
  • Android 减包 - 减少APK大小

    用户经常会避免下载看起来体积较大的应用 特别是在不稳定的2G 3G网络或者在以字节付费的网络 这篇文章描述了怎样减少你的APK大小 这会让更多的用户愿意下载你的应用 理解APK的结构 在讨论怎样减少应用大小之前 先了解APK的结构是有用的
  • ETH标准合约

    pragma solidity 0 4 16 contract owned address public owner function owned public owner msg sender modifier onlyOwner req
  • Sass、LESS 和 Stylus区别总结

    CSS 预处理器技术已经非常的成熟了 而且也涌现出了越来越多的 CSS 的预处理器框架 本文便总结下 Sass Less CSS Stylus这三个预处理器的区别和各自的基本语法 1 什么是 CSS 预处理器 CSS 预处理器是一种语言用来
  • 从编程小白到架构总监:大型网站系统架构演化之路

    前言 一个成熟的大型网站 如淘宝 京东等 的系统架构并不是开始设计就具备完整的高性能 高可用 安全等特性 它总是随着用户量的增加 业务功能的扩展逐渐演变完善的 在这个过程中 开发模式 技术架构 设计思想也发生了很大的变化 就连技术人员也从几
  • mysql使用IP地址访问失败

    mysql使用IP地址访问失败 ERROR 1045 28000 Access denied for user root WIN JATIACT91UR localdomain using password YES mysql h loca
  • 小熊派学习1

    1 写代码时xxx c为业务代码 xxx gn为编译脚本 2 头文件ohos init h 提供用于在服务开发期间初始化服务和功能的条目 不必深入理解 3 截图代码中最后一句 必须有APP FEATURE INIT Hello World
  • Mysql 统计最近七天内的数据并分组

    己做项目 想要做有关管理页面的相关报表 其中有一张图表 采用折线图的方式 表示用户增减趋势 显示最近七天内 每天的用户新增数量 第一步 查询一定范围内的数据 数量 查询最近一天的数据 select from table where to d
  • 智能合约部署Error: exceeds block gas limit undefined

    在学习区块链时 我们按照某些文章的教程 使用 Browser solidity 在 Go Ethereum上进行智能合约部署时 可能会出现Error exceeds block gas limit undefined的报错信息 表示当前合约
  • GitLab服务器修改管理员用户root密码

    我们搭建好GitLab服务 打开页面后 需要输入用户名密码 但它们是什么呢 初始管理员用户为root 密码在安装过程中已随机生成并保存在 etc gitlab initial root password中 有效期24小时 我们可以自己去查找
  • 【知网研学】使用方法

    目录 前言 一 下载知网研学途径 二 使用步骤 1 导入文档 2 论文内笔记标记 3 对论文内容复制 说明 前言 注 本文对 知网研学 该软件的使用方法包括下载 导入文档 编辑论文等的详细解说 一 下载知网研学途径 1 浏览器搜索进行下载
  • 入门FFmpeg编程 --Android

    1 前言 FFmpeg是一个强大的音视频处理库 但是通常接触时以命令形式较多 本篇文章讲了FFmpeg相关api的使用 尤其是它强大的过滤器filter库的使用 1 1 能学到什么 Android下集成FFmpeg 使用avcodec解码库
  • 如何辨别ChatGPT是不是真的

    随着ChatGPT爆红 国内陆续出现了几个所谓的 ChatGPT 反向代理站点 乍一试回答似乎还挺靠谱 但它们真的是ChatGPT吗 本文以其中一个站点为例 对其真伪进行辨别 其实最多只需要问两个问题 基本上就可以做出判断了 1 你是谁 2
  • 为什么说区块链共享的不仅仅是数据?

    数据共享是人与生俱来的需求 比如 在咖啡馆谈人生理想 执笔书写文字等等 这些都是普通人用来和他人交流信息的重要方式 互联网的出现 打破了数据共享在地域和时间方面的限制 它可以让不同人在地球的不同位置进行即时交流 电子邮件 网上即时通讯等技术
  • 单片机及C语言入门

    一 什么是单片机 将CPU芯片 存储器芯片 I O接口芯片和简单的I O设备 小键盘 LED显示器 等装配在一块印刷电路板上 再配上监控程序 固化在ROM中 就构成了一台单片微型计算机 简称单片机 由于单片机在使用时 通常处于测控系统的核心
  • go+gSoap+onvif学习总结:7、进行镜头调焦、聚焦和预置点的增删改查

    cgo gSoap onvif学习总结 7 进行镜头调焦 聚焦和预置点的增删改查 文章目录 cgo gSoap onvif学习总结 7 进行镜头调焦 聚焦和预置点的增删改查 1 前言 2 gSoap生成c代码框架 3 完成c代码实例并测试