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

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 结果展示

缩放和聚焦:

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

预设检查、添加、跳转、删除:

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

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的开发总结说实话我还没想好啥时候继续,最近有个嵌入式中级考试,可能会先总结这块,估计也得个大半年甚至一年)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2022年3月16日
下一篇 2022年3月16日

相关推荐