cgo+gSoap+onvif学习总结:4、实现设备鉴权并获取设备信息

26 篇文章 5 订阅

cgo+gSoap+onvif学习总结:4、实现设备鉴权并获取设备信息


1. 前言

这节我们实现一下客户端用户鉴权并获取设备信息,我们沿用之前的流程:1.gSoap生成c/c++框架代码;2、实现c实例代码并测试;3、完成cgo代码并编译测试。

2. gSoap生成满足鉴权和获取设备信息的c代码框架

鉴权和获取设备信息需要的wsdl为核心的devicemgmt.wsdl和event.wsdl,鉴权还需要单独修改onvif.h添加wsse.h依赖ssl。

2.1 生成c框架代码

cd ./soap/
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

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

上述依赖的文件我们可以通过find命令查找位置后拷贝:

find gSoap源码位置比如~ -name xxx.c或者xxx.h

2.2 可能的错误及解决方法

以下记录的问题是不好一眼看出来的,有的错误根据提示直接拷贝源码中文件出来就可以解决的就没有罗列出来。

2.2.1 问题1:wsa5.h报错

在这里插入图片描述

wsa5.h(290): **ERROR**: service operation name clash: struct/class 'SOAP_ENV__Fault' already declared at wsa.h:278

解决方法:

https://sourceforge.net/p/gsoap2/bugs/1283/

wsa5.h问题,修改报错结构体的名字即可,可能需要重新编译gSoap。

2.2.2 问题2:soap_serialize_xsd__dateTime等未定义
soapC.c:(.text+0x6bd6): undefined reference to `soap_in_xsd__dateTime'
/usr/bin/ld: soapC.c:(.text+0x1e4fb): undefined reference to `soap_in_xsd__dateTime'
/usr/bin/ld: CMakeFiles/gsoap-onvif.dir/soap/soapC.c.o: in function `soap_putelement':
soapC.c:(.text+0x32fc2): undefined reference to `soap_out_xsd__dateTime'
/usr/bin/ld: CMakeFiles/gsoap-onvif.dir/soap/soapC.c.o: in function `soap_markelement':
soapC.c:(.text+0x42437): undefined reference to `soap_serialize_xsd__dateTime'
...

解决方法:

https://sourceforge.net/p/gsoap2/bugs/904/

缺少duration.c和duration.h

2.2.3 问题3:duration.c编译报错
/mnt/d/code/onvif_cgo/soap/duration.c:61:81: error: expected ‘)’ before ‘*’ token
   61 | SOAP_FMAC3 void SOAP_FMAC4 soap_default_xsd__duration(struct soap *soap, LONG64 *a)
      |                                                                                 ^
/mnt/d/code/onvif_cgo/soap/soapH.h:69499:48: note: in definition of macro ‘soap_default_xsd__duration’
69499 | #define soap_default_xsd__duration(soap, a) (*(a) = (char *)0)
      |                                                ^
/mnt/d/code/onvif_cgo/soap/soapH.h:69499:51: error: expected ‘)’ before ‘=’ token
69499 | #define soap_default_xsd__duration(soap, a) (*(a) = (char *)0)
      |                                                   ^
/mnt/d/code/onvif_cgo/soap/duration.c:61:28: note: in expansion of macro ‘soap_default_xsd__duration’
   61 | SOAP_FMAC3 void SOAP_FMAC4 soap_default_xsd__duration(struct soap *soap, LONG64 *a)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/duration.c:67:83: error: expected ‘)’ before ‘a’
   67 | SOAP_FMAC3 const char * SOAP_FMAC4 soap_xsd__duration2s(struct soap *soap, LONG64 a)
      |                                                                                   ^
/mnt/d/code/onvif_cgo/soap/soapH.h:69503:40: note: in definition of macro ‘soap_xsd__duration2s’
69503 | #define soap_xsd__duration2s(soap, a) (a)
      |                                        ^
/mnt/d/code/onvif_cgo/soap/duration.c:106:27: error: conflicting types for ‘soap_out_xsd__duration’
  106 | SOAP_FMAC3 int SOAP_FMAC4 soap_out_xsd__duration(struct soap *soap, const char *tag, int id, const LONG64 *a, const char *type)
      |                           ^~~~~~~~~~~~~~~~~~~~~~
In file included from /mnt/d/code/onvif_cgo/soap/duration.c:58:
/mnt/d/code/onvif_cgo/soap/soapH.h:69504:27: note: previous declaration of ‘soap_out_xsd__duration’ was here
69504 | SOAP_FMAC3 int SOAP_FMAC4 soap_out_xsd__duration(struct soap*, const char*, int, char*const*, const char*);
      |                           ^~~~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/duration.c: In function ‘soap_out_xsd__duration’:
/mnt/d/code/onvif_cgo/soap/duration.c:109:56: warning: passing argument 2 of ‘soap_string_out’ makes pointer from integer without a cast [-Wint-conver
sion]
  109 |    || soap_string_out(soap, soap_xsd__duration2s(soap, *a), 0))
/mnt/d/code/onvif_cgo/soap/soapH.h:69503:40: note: in definition of macro ‘soap_xsd__duration2s’
69503 | #define soap_xsd__duration2s(soap, a) (a)
      |                                        ^
In file included from /mnt/d/code/onvif_cgo/soap/soapStub.h:39,
                 from /mnt/d/code/onvif_cgo/soap/soapH.h:16,
                 from /mnt/d/code/onvif_cgo/soap/duration.c:58:
/mnt/d/code/onvif_cgo/soap/stdsoap2.h:3390:69: note: expected ‘const char *’ but argument is of type ‘int64_t’ {aka ‘const long int’}
 3390 | SOAP_FMAC1 int SOAP_FMAC2 soap_string_out(struct soap*, const char *s, int flag);
      |                                                         ~~~~~~~~~~~~^
In file included from /mnt/d/code/onvif_cgo/soap/duration.c:58:
/mnt/d/code/onvif_cgo/soap/duration.c: At top level:
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:54: error: expected declaration specifiers or ‘...’ before ‘(’ token
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                      ^
/mnt/d/code/onvif_cgo/soap/duration.c:114:27: note: in expansion of macro ‘soap_s2xsd__duration’
  114 | SOAP_FMAC3 int SOAP_FMAC4 soap_s2xsd__duration(struct soap *soap, const char *s, LONG64 *a)
      |                           ^~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:62: error: expected declaration specifiers or ‘...’ before ‘(’ token
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                              ^
/mnt/d/code/onvif_cgo/soap/duration.c:114:27: note: in expansion of macro ‘soap_s2xsd__duration’
  114 | SOAP_FMAC3 int SOAP_FMAC4 soap_s2xsd__duration(struct soap *soap, const char *s, LONG64 *a)
      |                           ^~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:67: error: expected declaration specifiers or ‘...’ before ‘(’ token
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                                   ^
/mnt/d/code/onvif_cgo/soap/duration.c:114:27: note: in expansion of macro ‘soap_s2xsd__duration’
  114 | SOAP_FMAC3 int SOAP_FMAC4 soap_s2xsd__duration(struct soap *soap, const char *s, LONG64 *a)
      |                           ^~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:80: error: expected declaration specifiers or ‘...’ before numeric constant
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                                                ^
/mnt/d/code/onvif_cgo/soap/duration.c:114:27: note: in expansion of macro ‘soap_s2xsd__duration’
  114 | SOAP_FMAC3 int SOAP_FMAC4 soap_s2xsd__duration(struct soap *soap, const char *s, LONG64 *a)
      |                           ^~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:83: error: expected declaration specifiers or ‘...’ before numeric constant
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                                                   ^
/mnt/d/code/onvif_cgo/soap/duration.c:114:27: note: in expansion of macro ‘soap_s2xsd__duration’
  114 | SOAP_FMAC3 int SOAP_FMAC4 soap_s2xsd__duration(struct soap *soap, const char *s, LONG64 *a)
      |                           ^~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:86: error: expected declaration specifiers or ‘...’ before ‘-’ token
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                                                      ^
/mnt/d/code/onvif_cgo/soap/duration.c:114:27: note: in expansion of macro ‘soap_s2xsd__duration’
  114 | SOAP_FMAC3 int SOAP_FMAC4 soap_s2xsd__duration(struct soap *soap, const char *s, LONG64 *a)
      |                           ^~~~~~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:90: error: expected declaration specifiers or ‘...’ before string constant
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/soapH.h:69506:90: note: in definition of macro ‘soap_s2xsd__duration’
69506 | #define soap_s2xsd__duration(soap, s, a) soap_s2char((soap), (s), (char**)(a), 5, 0, -1, "[-+]?P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(
\\.\\d*)?S)?)?")
      |                                                                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~
/mnt/d/code/onvif_cgo/soap/duration.c:216:32: error: conflicting types for ‘soap_in_xsd__duration’
  216 | SOAP_FMAC3 LONG64 * SOAP_FMAC4 soap_in_xsd__duration(struct soap *soap, const char *tag, LONG64 *a, const char *type)
      |                                ^~~~~~~~~~~~~~~~~~~~~
In file included from /mnt/d/code/onvif_cgo/soap/duration.c:58:
/mnt/d/code/onvif_cgo/soap/soapH.h:69507:32: note: previous declaration of ‘soap_in_xsd__duration’ was here
69507 | SOAP_FMAC3 char * * SOAP_FMAC4 soap_in_xsd__duration(struct soap*, const char*, char **, const char*);
      |                                ^~~~~~~~~~~~~~~~~~~~~
make[2]: *** [CMakeFiles/gsoap-onvif.dir/build.make:76: CMakeFiles/gsoap-onvif.dir/soap/duration.c.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/gsoap-onvif.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

解决方法:

https://blog.csdn.net/wxliang1023/article/details/83788731

我们将源码下的typemap.dat拷贝出来,在最后修改添加如下内容:

xsd__duration = #import "custom/duration.h" | xsd__duration

之后重新生成onvif.h的时候指定修改后的typemap.dat即可,这个文件是用来映射xml和c/c++代码结构体的。

3. 实现c代码实例并测试运行

3.1 c代码如下

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

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

static void del_soap(struct soap *soap) {
    //清除soap
    // clean up and remove deserialized data
    soap_end(soap);
    //detach and free runtime context
    soap_free(soap);
}

int discovery() {
//soap环境变量
    struct soap *soap;

    //发送消息描述
    struct wsdd__ProbeType req;
    struct wsdd__ProbeType wsdd__Probe;

    struct __wsdd__ProbeMatches resp;

    //描述查找那类的Web消息
    struct wsdd__ScopesType sScope;

    //soap消息头消息
    struct SOAP_ENV__Header header;

    //获得的设备信息个数
    int count = 0;

    //返回值
    int result = 0;

    //存放uuid 格式(8-4-4-4-12)
    char uuid_string[64];

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

    //soap初始化,申请空间
    soap = soap_new();
    if (soap == NULL) {
        printf("malloc soap error \n");
        return -1;
    }

    soap_set_namespaces(soap, namespaces);  //设置命名空间,就是xml文件的头
    soap->recv_timeout = 5;  //超出5s没数据就推出,超时时间

    //将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";
//    req.Types = "dn:NetworkVideoTransmitter";

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

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

            //接收 ProbeMatches,成功返回0,错误返回-1
            result = soap_recv___wsdd__ProbeMatches(soap, &resp);
            printf(" --soap_recv___wsdd__ProbeMatches() result=%d \n", result);
            if (result == -1) {
                printf("Find %d devices!\n", count);
                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);
    }

    del_soap(soap);
    return result;
}

int set_auth_info(struct soap *soap, const char *username, const char *password) {
    int result = 0;

    if (NULL == username) {
        printf("username is null.\n");
        return -1;
    }
    if (NULL == password) {
        printf("password is nil.\n");
        return -2;
    }

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

    return result;
}

int get_device_info(const char *username, const char *password, const char *dev_addr) {
    int result = 0;
    struct soap *soap = NULL;
    struct _tds__GetDeviceInformation devinfo_req;
    struct _tds__GetDeviceInformationResponse devinfo_resp;

    if (NULL == dev_addr) {
        printf("dev addr is nil.\n");
        return -1;
    }
    soap = soap_new();
    if (soap == NULL) {
        printf("malloc soap error \n");
        return -2;
    }
    soap_set_namespaces(soap, namespaces);  //设置命名空间,就是xml文件的头
    soap->recv_timeout = 5;  //超出5s没数据就推出,超时时间

//    set_auth_info(soap, username, password);
    result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);
    printf("result:%d\n", result);

    memset(&devinfo_req, 0x00, sizeof(devinfo_req));
    memset(&devinfo_resp, 0x00, sizeof(devinfo_resp));
    result = soap_call___tds__GetDeviceInformation(soap, dev_addr, NULL, &devinfo_req, &devinfo_resp);

    if (NULL != soap) {
        printf("Manufacturer:%s\n", devinfo_resp.Manufacturer);
        printf("Model:%s\n", devinfo_resp.Model);
        printf("FirmwareVersion:%s\n", devinfo_resp.FirmwareVersion);
        printf("SerialNumber:%s\n", devinfo_resp.SerialNumber);
        printf("HardwareId:%s\n", devinfo_resp.HardwareId);
        del_soap(soap);
    }
    return result;
}

int main() {
    discovery();
    get_device_info("admin", "admin", "http://40.40.40.101:80/onvif/device_service");
}

3.2 注意事项

注意:地址不是单纯的ip地址,而是设备服务地址,可以通过onvif device test tool查看复制:

在这里插入图片描述

3.3 cmake

cmake内容,可以再优化以下通过外部传参生成静态库和可执行程序,测试c代码的可执行程序运行正常后,注释掉c代码中的main函数,然后将c代码编译成静态库用于cgo程序即可(不建议使用动态库,静态库虽然会造成程序稍微大一些但是更适合go):

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)
#add_executable(gsoap-onvif ${SRC_LIST} ./soap/custom device.c)
#add_executable(discovery_onvif ${SRC_LIST} main.c ./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)

结果(由于我的网络存在多个虚拟网络,所以没有搜索到,但是我们直连进行鉴权获取设备信息是成功了):

在这里插入图片描述

4. 实现cgo代码实例并测试运行

4.1 cmake

我们暂时注释生成c可执行程序的cmake部分并注释掉c中的main函数部分,然后用于生成静态库,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)
#add_executable(gsoap-onvif ${SRC_LIST} ./soap/custom device.c)
#add_executable(discovery_onvif ${SRC_LIST} main.c ./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)

4.2 cgo代码

go代码(注意cgo依赖ssl等,即设置的LDFLAGS部分,否则这里编译还是会报错找不到ssl相关的接口):

package main

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

func main() {
    C.discovery();
    C.get_device_info(C.CString("admin"), C.CString("admin"), C.CString("http://40.40.40.101:80/onvif/device_service"));
}

编译:

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

4.3 运行结果

在这里插入图片描述

5. 整体项目结构

zy@LS2-R910CQQT:/mnt/d/code/onvif_cgo$ tree -a -I ".git|build|.idea"
.
├── CMakeLists.txt
├── client.c
├── client.h
├── device.c
├── discovery.c
├── main.go
├── onvif_cgo
└── soap
    ├── DeviceBinding.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, 68 files

device.c和discovery.c是我用来测试的,和项目无关。

6. 最后

只要顺利编译好c代码,那么cgo部分就比较简单了。ok,过了鉴权这一关,接下来ptz、获取rtsp等信息相对就比较简单了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称系统有问题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值