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、进行镜头调焦、聚焦和预置点的增删改查 的相关文章

  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • C# 和 Javascript SHA256 哈希的代码示例

    我有一个在服务器端运行的 C 算法 它对 Base64 编码的字符串进行哈希处理 byte salt Convert FromBase64String serverSalt Step 1 SHA256Managed sha256 new S
  • 将数组向左或向右旋转一定数量的位置,复杂度为 o(n)

    我想编写一个程序 根据用户的输入 正 gt 负 include
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • C - 找到极限之间的所有友好数字

    首先是定义 一对友好的数字由两个不同的整数组成 其中 第一个整数的除数之和等于第二个整数 并且 第二个整数的除数之和等于第一个整数 完美数是等于其自身约数之和的数 我想做的是制作一个程序 询问用户一个下限和一个上限 然后向他 她提供这两个限
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • Cython 和类的构造函数

    我对 Cython 使用默认构造函数有疑问 我的 C 类 Node 如下 Node h class Node public Node std cerr lt lt calling no arg constructor lt lt std e
  • 在 ASP.NET Core 3.1 中使用包含“System.Web.HttpContext”的旧项目

    我们有一些用 Net Framework编写的遗留项目 应该由由ASP NET Core3 1编写的API项目使用 问题是这些遗留项目正在使用 System Web HttpContext 您知道它不再存在于 net core 中 现在我们
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • 在数据库中搜索时忽略空文本框

    此代码能够搜索数据并将其加载到DataGridView基于搜索表单文本框中提供的值 如果我将任何文本框留空 则不会有搜索结果 因为 SQL 查询是用 AND 组合的 如何在搜索 从 SQL 查询或 C 代码 时忽略空文本框 private
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • clang 实例化后静态成员初始化

    这样的代码可以用 GCC 编译 但 clang 3 5 失败 include
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • WCF:将随机数添加到 UsernameToken

    我正在尝试连接到用 Java 编写的 Web 服务 但有些东西我无法弄清楚 使用 WCF 和 customBinding 几乎一切似乎都很好 除了 SOAP 消息的一部分 因为它缺少 Nonce 和 Created 部分节点 显然我错过了一
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj

随机推荐

  • 支持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代码实例并测试