cgo+gSoap+onvif学习总结:9、go和c进行socket通信进行onvif协议处理

26 篇文章 5 订阅

cgo+gSoap+onvif学习总结:9、go和c进行socket通信进行onvif协议处理


1. 前言

cgo目前看仍然存在一些问题,虽然我在amd64和x86上使用正常了,1.18版本没有出现内存分割违例问题,但是arm64上运行时仍存在分割违例问题,所以打算使用一个比较稳妥的方式进行c和go的通信,那就是socket通信。

至于c++方式实现的话可以使用grpc+pb的方式来和go通信,或者数据复杂一些也可以使用mongos和nanomsg来进行,这些可以留着扩展研究,由兴趣的可以尝试一下,这些处理方式比起原始的socket虽然更复杂但更具扩展性,可以结合项目实际使用情况来定。

2. 思路

我这里由于是c代码,grpc方式不太方便,而nanomsg用在一两个接口而且就两个进程通信显得比较浪费,所以综合考虑下来使用简单的socket传递json数据足以(如果你的数据未来要提供给多个进程使用可以考虑nanomsg,如果你使用了c++而且可能给予多个进程甚至在分布式环境使用可以考虑grpc)。

利用socket传递json数据来进行onvif协议处理,onvif部分完全由gSoap框架的c代码实现,然后创建tcp server,go端通过tcp client传递一些必要的地址、用户名、密码等信息,之后C端tcp server调用gSoap生成的onvif框架代码实现即可。

思路清晰之后,开发起来就比较快了,实际操作过程再调试一些细节基本上就完工了(这些细节很重要,架构设计时可能没有考虑到,但是实际开发时一定要在项目初期进行澄清时注意这些细节并且根据对这些细节的熟悉程度来评估自己的工作量)

3. c代码

3.1 注意事项

c代码主体包括两部分:gsoap实现的onvif部分以及tcp server部分,如果再细一些可以拆分json数据处理这一业务部分,这里由于业务部分较少且之后只会有很小的改动,所以将数据业务处理部分和tcp server部分放到了一起(json打包和解析使用cJSON即可)。

c的tcp部分可以参考这里:https://blog.csdn.net/weixin_39510813/article/details/75647645

onvif部分参考我们之前的这个系列文章即可,基本没有变动,最后再修改一下cmake即可。

需要注意线程安全问题,由于线程共用变量,所以soap的创建和释放一定要线程内部处理,在外部处理可能由于soap的抢占引起段错误。

3.2 代码

代码函数写的比较臃肿,没有做优化,需要对函数做拆分优化。

tcp_server.c:

//
// Created by admin on 2022/6/21.
//

/*
* 文件:tcp_server.c
* 内容:利用TCP实现客户端和服务器的实时聊天。
* 注  :服务器的端口号及IP,客户端的端口号及IP的输入通过main函数的argc和argv来实现。
* 未输入端口号的话使用默认端口号,服务器为1111,客户端为2222。
* 编译:gcc tcp_server.c -o ser -lpthread
* 运行:./ser 127.0.0.1 7788 5
*/

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <cJSON.h>
#include "onvif_cgo_client.h"

#define SPORT 1111
#define IP "127.0.0.1"
#define MAXLEN 1024

struct network_info
{
    struct sockaddr addr;
    int addr_len;
    int sock;
}target = {0,0,0};

enum action {
    GET_DEV_INFO = 1,
    GET_CAPABILITIES = 2,
    GET_PROFILES = 3,
    GET_RTSP_URI = 4,
    GET_SNAPSHOT_URI = 5,
    GET_VIDEO_SOURCE = 6,
    PTZ = 7,
    PRESET = 8
};

int send_message(struct network_info netinfo, char buf[200]) {
    int len = sendto(netinfo.sock, buf, strlen(buf), 0, (struct sockaddr*)(&(netinfo.addr)), netinfo.addr_len);
    if(len < 0)
    {
        printf("msg is:%s,send failer,errno is %d,errno message is:%s\n",buf,errno,strerror(errno));
        return len;
    }

    return 0;
}

int parse_recv_message(struct network_info networkInfo, char *buf) {
    cJSON *root = NULL;
    cJSON *action = NULL;
    const cJSON *userNameObj = NULL;
    const cJSON *passwordObj = NULL;
    const cJSON *serviceAddrObj = NULL;
    const cJSON *direction = NULL;
    const cJSON *speed = NULL;
    const cJSON *presetAction = NULL;
    const cJSON *presetToken = NULL;
    const cJSON *presetName = NULL;
    char res[200] = {'\0'};
    struct soap *soap = NULL;
    char username[200] = {'\0'};
    char password[200] = {'\0'};
    char serviceAddr[200] = {'\0'};
    char mediaAddr[200] = {'\0'};
    char profileToken[200] = {'\0'};
    char rtspUri[200] = {'\0'};
    char videoSourceToken[200] = {'\0'};

    root = cJSON_Parse(buf);
    if (NULL == root) {
        printf("file:%s,line:%d.Parse root failed.\n", __FILE__, __LINE__);
        return -1;
    }

    action = cJSON_GetObjectItem(root, "action");
    if (NULL == action) {
        cJSON_Delete(root);
        printf("file:%s,line:%d.Get action failed.\n", __FILE__, __LINE__);
        return -2;
    }

    userNameObj = cJSON_GetObjectItem(root, "username");
    if (NULL == userNameObj) {
        cJSON_Delete(root);
        printf("file:%s,line:%d.Get username failed.\n", __FILE__, __LINE__);
        return -3;
    }
    strcpy(username, userNameObj->valuestring);

    passwordObj = cJSON_GetObjectItem(root, "password");
    if (NULL == passwordObj) {
        cJSON_Delete(root);
        printf("file:%s,line:%d.Get password failed.\n", __FILE__, __LINE__);
        return -4;
    }
    strcpy(password, passwordObj->valuestring);

    serviceAddrObj = cJSON_GetObjectItem(root, "serviceAddr");
    if (NULL == serviceAddrObj) {
        cJSON_Delete(root);
        printf("file:%s,line:%d.Get service addr failed.\n", __FILE__, __LINE__);
        return -5;
    }
    strcpy(serviceAddr, serviceAddrObj->valuestring);

    switch (action->valueint) {
        case GET_DEV_INFO:
            break;
        case GET_CAPABILITIES:
            break;
        case GET_PROFILES:
            break;
        case GET_RTSP_URI:
            soap = new_soap(soap);
            get_device_info(soap, username, password, serviceAddr);
            get_capabilities(soap, username, password, serviceAddr, mediaAddr);
            get_profiles(soap, username, password, profileToken, mediaAddr);
            if (0 == get_rtsp_uri(soap, username, password, profileToken, mediaAddr, rtspUri)) {
                strcpy(res, "get rtsp success");
            } else {
                strcpy(res, "get rtsp failed");
            }
            send_message(networkInfo, res);
            del_soap(soap);
            break;
        case GET_SNAPSHOT_URI:
            break;
        case GET_VIDEO_SOURCE:
            break;
        case PTZ:
            soap = new_soap(soap);
            get_device_info(soap, username, password, serviceAddr);
            get_capabilities(soap, username, password, serviceAddr, mediaAddr);
            get_profiles(soap, username, password, profileToken, mediaAddr);
            get_video_source(soap, username, password, videoSourceToken, mediaAddr);

            int dir = 0;
            float ptzSpeed = 0;
            direction = cJSON_GetObjectItem(root, "direction");
            if (direction != NULL) {
                dir = direction->valueint;
            }
            speed = cJSON_GetObjectItem(root, "speed");
            if (speed != NULL) {
                ptzSpeed = speed->valuedouble;
            }
            printf("1-14分别代表上、下、左、右、左上、左下、右上、右下、停止、缩、放、调焦加、调焦减、调焦停止,dir=%d,speed=%f\n", dir, ptzSpeed);
            if ((dir >= 1) && (dir <= 11)) {
                if (0 == ptz(soap, username, password, dir, ptzSpeed, profileToken, mediaAddr)) {
                    strcpy(res, "ptz success");
                } else {
                    strcpy(res, "ptz failed");
                }
            } else if (dir >= 12 && dir <= 14) {
                if (0 == focus(soap, username, password, dir, ptzSpeed, videoSourceToken, mediaAddr)) {
                    strcpy(res, "focus success");
                } else {
                    strcpy(res, "focus failed");
                }
            }
            send_message(networkInfo, res);
            del_soap(soap);
            break;
        case PRESET:
            break;
        default:
            break;
    }
    cJSON_Delete(root);

    return 0;
}

void* myrecv(void* arg)
{
    int new_fd = 0,len = 0;
    char buf[MAXLEN] = {'\0'};
    time_t t;
    struct network_info netinfo;
    netinfo = *((struct network_info*)arg);
    new_fd = netinfo.sock;

    while(1)
    {
        memset(buf, '\0', sizeof(buf)-1);
        len = recv(new_fd, buf, sizeof(buf) - 1, 0);
        if(len > 0)
        {
            t = time(NULL);
            printf("&client:%s    @%s\n", buf, ctime(&t));
            parse_recv_message(netinfo, buf);
        }
        else if(len < 0)
        {
            printf("recv msg error,error code = %d,error msg = %s\n",errno, strerror(errno));
            break;
        }
        else
        {
            printf("client is quit\n");
            close(new_fd);
            break;
        }
    }
}

int main(int argc,char* argv[])
{
    int pid = 0,sock_fd = 0,new_fd = 0;
    socklen_t len;
    struct sockaddr_in self_addr,their_addr;
    unsigned int myport,lisnum;
    char ip[17] = {'\0'};
    time_t t;
    pthread_t pthread_recv;

    if(argv[2] && argc >=3)
        myport = atoi(argv[2]);
    else
        myport = SPORT;

    if(argv[3] && argc >= 4)
        lisnum = atoi(argv[3]);
    else
        lisnum = 3;

    //创建套接字
    sock_fd = socket(AF_INET,SOCK_STREAM,0);
    if(sock_fd < 0)
    {
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    //绑定自己的端口号
    memset(&self_addr, 0, sizeof(self_addr));
    self_addr.sin_family = AF_INET;
    self_addr.sin_port = htons(myport);
    if(argv[1])
        self_addr.sin_addr.s_addr = inet_addr(argv[1]);
    else
        self_addr.sin_addr.s_addr = INADDR_ANY;

    if(bind(sock_fd, (struct sockaddr*)&self_addr, sizeof(self_addr)) == -1)
    {
        perror("bind error");
        exit(EXIT_FAILURE);
    }

    //监听
    if(listen(sock_fd,lisnum) == -1)
    {
        perror("listen error");
        exit(EXIT_FAILURE);
    }

    //接受客户端的连接请求
    memset(&their_addr, 0, sizeof(their_addr));
    len = sizeof(their_addr);
    printf("wait for connect\n");
    while(1)
    {
        if((new_fd = accept(sock_fd, (struct sockaddr*)&their_addr, &len)) == -1)
        {
            printf("len=%d,new_fd=%d\n", len, new_fd);
            perror("accept error");
            exit(EXIT_FAILURE);
        }
        printf("=====================================================================\n");
        printf("client connect ok\n");
        printf("=====================================================================\n");
        memcpy(&(target.addr), &(their_addr), len);
        target.addr_len = len;
        target.sock = new_fd;

        //创建线程用于进程间通信
        pthread_create(&pthread_recv, NULL, myrecv, (void*)&target);
    }

    pthread_join(pthread_recv, NULL);
    close(new_fd);
    close(sock_fd); //关闭套接字

    return 0;
}

onvif_cgo_client.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 "onvif_cgo_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, char *rtsp_uri) {
    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);
    strcpy(rtsp_uri, streamUriResponse.MediaUri->Uri);
    return res;
}

int get_snapshot(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr, char *snapshot_uri) {
    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);
    strcpy(snapshot_uri, 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));
    continuousMove.Timeout = NULL;
    continuousMove.Velocity->Zoom = NULL;

    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(int argc, void *argv[]) {
    struct soap *soap = NULL;
    soap = new_soap(soap);
    const char username[] = "admin";
    const char password[] = "@";
    char serviceAddr[] = "http://1.1.1.1: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);

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

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

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

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

onvif_cgo_client.h

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

#ifndef ONVIF_CGO_CLIENT_H
#define ONVIF_CGO_CLIENT_H

struct soap *new_soap(struct soap *soap);

void del_soap(struct soap *soap);

int get_device_info(struct soap *soap, const char *username, const char *password, char *xAddr);

int get_capabilities(struct soap *soap, const char *username, const char *password, char *xAddr, char *mediaAddr);

int get_profiles(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr);

int get_rtsp_uri(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr,
        char *rtsp_uri);

int get_snapshot(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr,
        char *snapshot_uri);

int get_video_source(struct soap *soap, const char *username, const char *password, char *videoSource, char *xAddr);

int ptz(struct soap *soap, const char *username, const char *password, int direction, float speed, char *profileToken,
        char *xAddr);

int
focus(struct soap *soap, const char *username, const char *password, int direction, float speed, char *videoSourceToken,
      char *xAddr);

#endif //ONVIF_CGO_CLIENT_H

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(onvif-cgo)

set(CMAKE_C_COMPILER /home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/bin/aarch64-openwrt-linux-musl-gcc)
set(CMAKE_C_FLAGS "-DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACES")
aux_source_directory(./soap/ SRC_LIST)
include_directories(./)
add_executable(gsoap-onvif ${SRC_LIST} onvif_cgo_client.c onvif_cgo_client.h tcp_server.c cJSON.c ./soap/custom)
target_link_libraries(gsoap-onvif -lpthread -ldl -lssl -lcrypto)

3.3 tcp测试工具测试

sscom的串口工具也可以用来测试tcp:

在这里插入图片描述

sscom的官方地址:http://www.daxia.com/

4. go代码

go代码相对就比较简单了,创建tcp客户端,然后需要的时候发送json并接收返回值即可。

package main

import (
	"encoding/json"
	"net"
	"time"

	"github.com/wonderivan/logger"
)

var tcpConn *net.TCPConn

func tcpClient() bool {
	server := "127.0.0.1:1111"
	tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
	if err != nil {
		logger.Warn(err)
		return false
	}
	tcpConn, err = net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		logger.Warn(err)
		return false
	}

	logger.Debug("connection tcp success")
	return true
}

func tcpSender(conn net.Conn, words string) {
	conn.Write([]byte(words))

	//接收服务端反馈
	buffer := make([]byte, 2048)

	conn.SetReadDeadline(time.Now().Add(3 * time.Second))
	n, err := conn.Read(buffer)
	if err != nil {
		logger.Warn(err)
		return
	}
	logger.Debug(string(buffer[:n]))
	return
}

func main() {
	if tcpClient() {
		type message struct {
			Action      int
			Username    string
			Password    string
			ServiceAddr string
		}
		m := message{
			Action:      4,
			Username:    "admin",
			Password:    "@",
			ServiceAddr: "http://40.40.40.101:80/onvif/device_service",
		}
		b, err := json.Marshal(m)
		if err != nil {
			logger.Error(err)
			return
		}
		tcpSender(tcpConn, string(b))
	}
}

5. 结果

在这里插入图片描述

其它的ptz、preset等的处理都是类似的,只要约定要json发送解析即可。

6. 最后

本来是想要开发一些app之类的程序来利用onvif协议搜索控制摄像头,但市面上家用的比较便宜的家用摄像头基本都不支持onvif协议(可能是为了控制成本吧),所以暂时就这样吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

昵称系统有问题

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

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

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

打赏作者

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

抵扣说明:

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

余额充值