基于gsoap的onvif抓图

1 前言

现在网上一些关于gsoap的文章有部分过时了,博主参考起来做的时候遇到了挺多问题,所以想着自己梳理一遍,于是有了这片文章。
因为写这篇文章的时候比较忙,写的时候断断续续,所以内容可能会有所疏漏,麻烦见谅和指出。

2 前置文件下载以及安装

1.依赖的库文件以及程序

首先,要用gsoap框架实现抓图需要一系列的库文件和程序,这里直接给出安装的命令

sudo apt install flex bison libssl-dev unzip zlib1g-dev ffmpeg

当然也可以自己下载源码包自己编译安装,好处是使用的版本可以自己控制,具体的下载方式可以自己百度。

2.下载gsoap

gSOAP官方网址:http://www.cs.fsu.edu/~engelen/soap.html

gSOAP开源版下载网址(最新版本):http://sourceforge.net/projects/gsoap2
gSOAP开源版下载网址(历史版本):https://sourceforge.net/projects/gsoap2/files/gSOAP/

在这里插入图片描述

3.下载wsdl文件(可选)

​ 说明一下,这一步如果你需要在没网的环境下生产gsoap框架或者说生成的gsoap框架过大需要裁剪的话就需要下载所需要的wsdl文件。

​ wsdl相关文件的下载:Network Interface Specifications - ONVIF

​ 进去网页后右键所需的文件的超链接另存为下载到了,如下图

​ 以本篇博文演示的抓图功能所需的wsdl文件和xsd文件需要以下几种:devicemgmt.wsdl 、media.wsdl、remotediscovery.wsdl 、onvif.xsd
在这里插入图片描述

3 编译gsoap

​ 这一步如果是自己下载了wsdl文件的话

1.解压gsoap压缩包

​ 将下载到的gsoap压缩包放到你的工程文件夹内,然后解压,解压方式可以自己搜索,博主这里用的zip解压,命令如下:

unzip gsoap_2.8.131.zip

在这里插入图片描述
得到文件夹在这里插入图片描述,cd进去
在这里插入图片描述

2.编译gsoap

​ 在gsoap的configure可执行文件所在的文件夹下进行编译配置,所需的命令:

./configure --with-openssl=/usr/lib/x86_64-linux-gnu/ --prefix=/root/Project/Onvif_snapshot/gsoap-2.8/new

​ 这里说明一下:1.–with-openssl= 后面跟的是你自己系统上libssl库所在的路径,如果不知道库文件的位置的话,可以用如下命令:

sudo find / -name libssl.so 或者 sudo find / -name libssl.a

2、–prefix=后面跟的是你想要编译出来的文件所在的文件夹的路径,这个按自己的喜好填就行,博主这里是直接在gsoap的文件夹下面创建了一个new文件夹

3、在gsoap的configure可执行文件所在的文件夹下进行编译,命令如下:

sudo make && sudo make install

没问题的话界面如图:

在这里插入图片描述

4、然后新建一个文件夹,将new中的对应文件复制出来

在这里插入图片描述

其中typemap.dat 在 share/WS下,stdsoap2.h在include下,custom和import在share下

5、在typemap.dat文件的最后新增一行

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

在这里插入图片描述

6、使用wsdl2h工具生成onvif.h文件

./bin/wsdl2h -o onvif.h -c -s -t ./typemap.dat  \
 https://www.onvif.org/ver10/network/wsdl/remotediscovery.wsdl \
https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl \
https://www.onvif.org/ver10/media/wsdl/media.wsdl

如果使用本地文件,就把里面./typemap.dat \后面的路径改为本地文件的路径,例如:

./bin/wsdl2h -o onvif.h -c -s -t ./typemap.dat  \
devicemgmt.wsdl  \
media.wsdl \
remotediscovery.wsdl \
onvif.xsd 

生成过程如下:

root@iZbp1bslec5v3fdrobnq0qZ:~/Project/Onvif_snapshot/onvif_gsoap# ./bin/wsdl2h -o onvif.h -c -s -t ./typemap.dat  \
 https://www.onvif.org/ver10/network/wsdl/remotediscovery.wsdl \
https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl \
https://www.onvif.org/ver10/media/wsdl/media.wsdl
Saving onvif.h


**  The gSOAP WSDL/WADL/XSD processor for C and C++, wsdl2h release 2.8.131
**  Copyright (C) 2000-2023 Genivia Inc. All Rights Reserved.
**  The wsdl2h tool and its generated software are released under the GPL.
**  ----------------------------------------------------------------------------
**  A commercial use license is available from Genivia Inc., contact@genivia.com
**  ----------------------------------------------------------------------------

Reading type definitions from type map "./typemap.dat"
Connecting to 'https://www.onvif.org/ver10/network/wsdl/remotediscovery.wsdl' to retrieve WSDL/WADL or XSD... connected, receiving...
Done reading 'https://www.onvif.org/ver10/network/wsdl/remotediscovery.wsdl'
Connecting to 'https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl' to retrieve WSDL/WADL or XSD... connected, receiving...
  Connecting to 'https://www.onvif.org/ver10/schema/onvif.xsd' to retrieve schema... connected, receiving...
    Connecting to 'http://docs.oasis-open.org/wsn/b-2.xsd' to retrieve schema... connected, receiving...
      Connecting to 'http://docs.oasis-open.org/wsrf/bf-2.xsd' to retrieve schema... connected, receiving...
      Done reading 'http://docs.oasis-open.org/wsrf/bf-2.xsd'
      Connecting to 'http://docs.oasis-open.org/wsn/t-1.xsd' to retrieve schema... connected, receiving...
      Done reading 'http://docs.oasis-open.org/wsn/t-1.xsd'
    Done reading 'http://docs.oasis-open.org/wsn/b-2.xsd'
    Connecting to 'https://www.onvif.org/ver10/schema/common.xsd' to retrieve schema... connected, receiving...
    Done reading 'https://www.onvif.org/ver10/schema/common.xsd'
  Done reading 'https://www.onvif.org/ver10/schema/onvif.xsd'
Done reading 'https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl'
Connecting to 'https://www.onvif.org/ver10/media/wsdl/media.wsdl' to retrieve WSDL/WADL or XSD... connected, receiving...
Done reading 'https://www.onvif.org/ver10/media/wsdl/media.wsdl'

Warning: ignoring type inheritance by default for C, use option -F to generate struct declarations with simulated inheritance using transient pointer members pointing to derived types to serialize derived types as elements annotated by xsi:type attributes in XML.

Warning: 2 service bindings found, but collected as one service (use option -Nname to produce a separate service for each binding)

To finalize code generation, execute:
> soapcpp2 onvif.h

7、如果要鉴权功能的话,需要使用soap_wsse_add_UsernameTokenDigest函数,所以要在onvif.h头文件开头加入

#import "wsse.h"

8、因为onvif.h中同时包含了

#import "wsdd10.h" // wsdd10.h中又#import "wsa.h"
#import "wsa5.h"   // wsa.h和wsa5.h两个文件重复定义了int SOAP_ENV__Fault

所以修改import\wsa5.h文件

int SOAP_ENV__Fault修改为int SOAP_ENV__Fault_alex

9、使用soapcpp2工具生成onvif框架

./bin/soapcpp2 -2 -x -c onvif.h  -Iimport .

生成过程如下:

**  The gSOAP code generator for C and C++, soapcpp2 release 2.8.131
**  Copyright (C) 2000-2023 Genivia Inc. All Rights Reserved.
**  The soapcpp2 tool and its generated software are released under the GPL.
**  ----------------------------------------------------------------------------
**  A commercial use license is available from Genivia Inc., contact@genivia.com
**  ----------------------------------------------------------------------------

Saving soapStub.h annotated copy of the source interface header file
Saving soapH.h serialization functions to #include in projects
Saving soap.nsmap namespace mapping table
Saving soapC.c serialization functions

Compilation successful 

10、修改stdsoap2.c文件,在开头加入:

#include "wsdd.nsmap"

4 功能实现

1、文件整合

从编译目录下的custom、import、plugin、WS文件夹内复制文件到本地文件夹,删去多余文件,最后结果如下:

在这里插入图片描述

将这些文件都归为一个文件夹,我命名为onvif_frame

2、样例代码

新建文件夹src作为功能实现文件夹,抓图功能的样例代码如下:
在这里插入图片描述

onvif_comm.c

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <netinet/in.h>
#include "wsseapi.h"
#include "onvif_comm.h"

void soap_perror(struct soap *soap, const char *str)
{
    ERROR_DBLOG();
    if (NULL == str) {
        SOAP_DBGERR("[soap] error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    } else {
        SOAP_DBGERR("[soap] %s error: %d, %s, %s\n", str, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    }
    return;
}

void* ONVIF_soap_malloc(struct soap *soap, unsigned int n)
{
    ERROR_DBLOG();
    void *p = NULL;

    if (n > 0) {
        p = soap_malloc(soap, n);
        assert(NULL != p);
        memset(p, 0x00 ,n);
    }
    return p;
}

struct soap *ONVIF_soap_new(int timeout)
{
    ERROR_DBLOG();
    struct soap *soap = NULL;                                                   // soap环境变量
    soap = soap_new();
    assert(NULL != soap);
    soap_set_namespaces(soap, namespaces);                                      // 设置soap的namespaces
    soap->recv_timeout    = timeout;                                            // 设置超时(超过指定时间没有数据就退出)
    soap->send_timeout    = timeout;
    soap->connect_timeout = timeout;
#if defined(__linux__) || defined(__linux)                                      // 参考https://www.genivia.com/dev.html#client-c的修改:
    soap->socket_flags = MSG_NOSIGNAL;                                          // To prevent connection reset errors
#endif

    soap_set_mode(soap, SOAP_C_UTFSTRING);                                      // 设置为UTF-8编码,否则叠加中文OSD会乱码

    return soap;
}

void ONVIF_soap_delete(struct soap *soap)
{
    ERROR_DBLOG();
    soap_destroy(soap);                                                         // remove deserialized class instances (C++ only)
    soap_end(soap);                                                             // Clean up deserialized data (except class instances) and temporary data
    soap_done(soap);                                                            // Reset, close communications, and remove callbacks
    soap_free(soap);                                                            // Reset and deallocate the context created with soap_new or soap_copy
}

/************************************************************************
**函数:ONVIF_SetAuthInfo
**功能:设置认证信息
**参数:
        [in] soap     - soap环境变量
        [in] username - 用户名
        [in] password - 密码
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password)
{
    ERROR_DBLOG();
    int result = 0;

    assert(NULL != username);
    assert(NULL != password);

    result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);
    SOAP_CHECK_ERROR(result, soap, "add_UsernameTokenDigest");

EXIT:

    return result;
}

/************************************************************************
**函数:ONVIF_init_header
**功能:初始化soap描述消息头
**参数:
        [in] soap - soap环境变量
**返回:无
**备注:
    1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放
************************************************************************/
void ONVIF_init_header(struct soap *soap)
{
    ERROR_DBLOG();
    struct SOAP_ENV__Header *header = NULL;
    assert(NULL != soap);
    header = (struct SOAP_ENV__Header *)ONVIF_soap_malloc(soap, sizeof(struct SOAP_ENV__Header));
    soap_default_SOAP_ENV__Header(soap, header);
    header->wsa__MessageID = (char*)soap_wsa_rand_uuid(soap);
    header->wsa__To        = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TO) + 1);
    header->wsa__Action    = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ACTION) + 1);
    strcpy(header->wsa__To, SOAP_TO);
    strcpy(header->wsa__Action, SOAP_ACTION);
    soap->header = header;
    return;
}

/************************************************************************
**函数:ONVIF_init_ProbeType
**功能:初始化探测设备的范围和类型
**参数:
        [in]  soap  - soap环境变量
        [out] probe - 填充要探测的设备范围和类型
**返回:
        0表明探测到,非0表明未探测到
**备注:
    1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放
************************************************************************/
void ONVIF_init_ProbeType(struct soap *soap, struct wsdd__ProbeType *probe)
{
    ERROR_DBLOG();
    struct wsdd__ScopesType *scope = NULL;                                      // 用于描述查找哪类的Web服务

    assert(NULL != soap);
    assert(NULL != probe);

    scope = (struct wsdd__ScopesType *)ONVIF_soap_malloc(soap, sizeof(struct wsdd__ScopesType));
    soap_default_wsdd__ScopesType(soap, scope);                                 // 设置寻找设备的范围
    scope->__item = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ITEM) + 1);
    strcpy(scope->__item, SOAP_ITEM);

    memset(probe, 0x00, sizeof(struct wsdd__ProbeType));
    soap_default_wsdd__ProbeType(soap, probe);
    probe->Scopes = scope;
    probe->Types  = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TYPES) + 1);     // 设置寻找设备的类型
    strcpy(probe->Types, SOAP_TYPES);

    return;
}

void ONVIF_DetectDevice(void (*cb)(char *DeviceXAddr))
{
    ERROR_DBLOG();
    int i;
    int result = 0;
    unsigned int count = 0;                                                     // 搜索到的设备个数
    struct soap *soap = NULL;                                                   // soap环境变量
    struct wsdd__ProbeType      req;                                            // 用于发送Probe消息
    struct __wsdd__ProbeMatches rep;                                            // 用于接收Probe应答
    struct wsdd__ProbeMatchType *probeMatch;
    soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);
    assert(NULL != soap);
    ONVIF_init_header(soap);                                                    // 设置消息头描述
    ONVIF_init_ProbeType(soap, &req);                                           // 设置寻找的设备的范围和类型

    struct in_addr if_req;
	if_req.s_addr = inet_addr(NET_CARD);  // 想绑定的IP地址
	soap->ipv4_multicast_if = (char*)soap_malloc(soap, sizeof(struct in_addr));
	memset(soap->ipv4_multicast_if, 0, sizeof(struct in_addr));
	memcpy(soap->ipv4_multicast_if, (char*)&if_req, sizeof(if_req));

    result = soap_send___wsdd__Probe(soap, SOAP_MCAST_ADDR, NULL, &req);        // 向组播地址广播Probe消息
    while (SOAP_OK == result)                                                   // 开始循环接收设备发送过来的消息
    {
        memset(&rep, 0x00, sizeof(rep));
        result = soap_recv___wsdd__ProbeMatches(soap, &rep);
        if (SOAP_OK == result) 
        {
            if (soap->error) 
            {
                soap_perror(soap, "ProbeMatches");
            } 
            else 
            {                                                            // 成功接收到设备的应答消息
                if (NULL != rep.wsdd__ProbeMatches) 
                {
                    count += rep.wsdd__ProbeMatches->__sizeProbeMatch;
                    for(i = 0; i < rep.wsdd__ProbeMatches->__sizeProbeMatch; i++) 
                    {
                        probeMatch = rep.wsdd__ProbeMatches->ProbeMatch + i;
                        if (NULL != cb) 
                        {
                            cb(probeMatch->XAddrs);                             // 使用设备服务地址执行函数回调
                        }
                    }
                }
            }
        } 
        else if (soap->error) 
        {
            break;
        }
    }
    SOAP_DBGLOG("\ndetect end! It has detected %d devices!\n", count);

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }
    return ;
}


/************************************************************************
**函数:ONVIF_GetCapabilities
**功能:获取设备能力信息
**参数:
        [in] DeviceXAddr - 设备服务地址
        [out] capa       - 返回设备能力信息信息
**返回:
        0表明成功,非0表明失败
**备注:
    1). 其中最主要的参数之一是媒体服务地址
************************************************************************/
int ONVIF_GetCapabilities(const char *DeviceXAddr, struct tagCapabilities *capa)
{
    ERROR_DBLOG();
    int result = 0;
    struct soap *soap = NULL;
    struct _tds__GetCapabilities            req;
    struct _tds__GetCapabilitiesResponse    rep;

    assert(NULL != DeviceXAddr);
    assert(NULL != capa);
    soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);
    assert(NULL != soap);

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    memset(&req, 0x00, sizeof(req));
    memset(&rep, 0x00, sizeof(rep));
    result = soap_call___tds__GetCapabilities(soap, DeviceXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "GetCapabilities");

    //dump_tds__GetCapabilitiesResponse(&rep);

    memset(capa, 0x00, sizeof(struct tagCapabilities));
    if (NULL != rep.Capabilities) {
        if (NULL != rep.Capabilities->Media) {
            if (NULL != rep.Capabilities->Media->XAddr) {
                strncpy(capa->MediaXAddr, rep.Capabilities->Media->XAddr, sizeof(capa->MediaXAddr) - 1);
            }
        }
        if (NULL != rep.Capabilities->Events) {
            if (NULL != rep.Capabilities->Events->XAddr) {
                strncpy(capa->EventXAddr, rep.Capabilities->Events->XAddr, sizeof(capa->EventXAddr) - 1);
            }
        }
    }

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

/************************************************************************
**函数:ONVIF_GetProfiles
**功能:获取设备的音视频码流配置信息
**参数:
        [in] MediaXAddr - 媒体服务地址
        [out] profiles  - 返回的设备音视频码流配置信息列表,调用者有责任使用free释放该缓存
**返回:
        返回设备可支持的码流数量(通常是主/辅码流),即使profiles列表个数
**备注:
        1). 注意:一个码流(如主码流)可以包含视频和音频数据,也可以仅仅包含视频数据。
************************************************************************/
int ONVIF_GetProfiles(const char *MediaXAddr, struct tagProfile **profiles)
{
    ERROR_DBLOG();
    int i = 0;
    int result = 0;
    struct soap *soap = NULL;
    struct _trt__GetProfiles            req;
    struct _trt__GetProfilesResponse    rep;

    assert(NULL != MediaXAddr);
    soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);
    assert(NULL != soap);

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    memset(&req, 0x00, sizeof(req));
    memset(&rep, 0x00, sizeof(rep));
    result = soap_call___trt__GetProfiles(soap, MediaXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "GetProfiles");

    //dump_trt__GetProfilesResponse(&rep);

    if (rep.__sizeProfiles > 0) {                                               // 分配缓存
        (*profiles) = (struct tagProfile *)malloc(rep.__sizeProfiles * sizeof(struct tagProfile));
        assert(NULL != (*profiles));
        memset((*profiles), 0x00, rep.__sizeProfiles * sizeof(struct tagProfile));
    }

    for(i = 0; i < rep.__sizeProfiles; i++) {                                   // 提取所有配置文件信息(我们所关心的)
        struct tt__Profile *ttProfile = &rep.Profiles[i];
        struct tagProfile *plst = &(*profiles)[i];

        if (NULL != ttProfile->token) {                                         // 配置文件Token
            strncpy(plst->token, ttProfile->token, sizeof(plst->token) - 1);
        }

        if (NULL != ttProfile->VideoEncoderConfiguration) {                     // 视频编码器配置信息
            if (NULL != ttProfile->VideoEncoderConfiguration->token) {          // 视频编码器Token
                strncpy(plst->venc.token, ttProfile->VideoEncoderConfiguration->token, sizeof(plst->venc.token) - 1);
            }
            if (NULL != ttProfile->VideoEncoderConfiguration->Resolution) {     // 视频编码器分辨率
                plst->venc.Width  = ttProfile->VideoEncoderConfiguration->Resolution->Width;
                plst->venc.Height = ttProfile->VideoEncoderConfiguration->Resolution->Height;
            }
        }
    }

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return rep.__sizeProfiles;
}


/************************************************************************
**函数:make_uri_withauth
**功能:构造带有认证信息的URI地址
**参数:
        [in]  src_uri       - 未带认证信息的URI地址
        [in]  username      - 用户名
        [in]  password      - 密码
        [out] dest_uri      - 返回的带认证信息的URI地址
        [in]  size_dest_uri - dest_uri缓存大小
**返回:
        0成功,非0失败
**备注:
    1). 例子:
    无认证信息的uri:rtsp://100.100.100.140:554/av0_0
    带认证信息的uri:rtsp://username:password@100.100.100.140:554/av0_0
************************************************************************/
int make_uri_withauth(char *src_uri, char *username, char *password, char *dest_uri, unsigned int size_dest_uri)
{
    ERROR_DBLOG();
    int result = 0;
    unsigned int needBufSize = 0;

    assert(NULL != src_uri);
    assert(NULL != username);
    assert(NULL != password);
    assert(NULL != dest_uri);
    memset(dest_uri, 0x00, size_dest_uri);

    needBufSize = strlen(src_uri) + strlen(username) + strlen(password) + 3;    // 检查缓存是否足够,包括‘:’和‘@’和字符串结束符
    if (size_dest_uri < needBufSize) {
        SOAP_DBGERR("dest uri buf size is not enough.\n");
        result = -1;
        goto EXIT;
    }

    if (0 == strlen(username) && 0 == strlen(password)) {                       // 生成新的uri地址
        strcpy(dest_uri, src_uri);
    } else {
        char *p = strstr(src_uri, "//");
        if (NULL == p) {
            SOAP_DBGERR("can't found '//', src uri is: %s.\n", src_uri);
            result = -1;
            goto EXIT;
        }
        p += 2;

        memcpy(dest_uri, src_uri, p - src_uri);
        sprintf(dest_uri + strlen(dest_uri), "%s:%s@", username, password);
        strcat(dest_uri, p);
    }

EXIT:

    return result;
}

/************************************************************************
**函数:ONVIF_GetSnapshotUri
**功能:获取设备图像抓拍地址(HTTP)
**参数:
        [in]  MediaXAddr    - 媒体服务地址
        [in]  ProfileToken  - the media profile token
        [out] uri           - 返回的地址
        [in]  sizeuri       - 地址缓存大小
**返回:
        0表明成功,非0表明失败
**备注:
    1). 并非所有的ProfileToken都支持图像抓拍地址。举例:XXX品牌的IPC有如下三个配置
    profile0/profile1/TestMediaProfile,其中TestMediaProfile返回的图像抓拍地址就是
    空指针。
************************************************************************/
int ONVIF_GetSnapshotUri(const char *MediaXAddr, char *ProfileToken, char *uri, unsigned int sizeuri)
{
    ERROR_DBLOG();
    int result = 0;
    struct soap *soap = NULL;
    struct _trt__GetSnapshotUri         req;
    struct _trt__GetSnapshotUriResponse rep;

    assert(NULL != MediaXAddr);
    assert(NULL != uri);
    memset(uri, 0x00, sizeuri);
    soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);
    assert(NULL != soap);

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    memset(&req, 0x00, sizeof(req));
    memset(&rep, 0x00, sizeof(rep));
    req.ProfileToken = ProfileToken;
    result = soap_call___trt__GetSnapshotUri(soap, MediaXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "GetSnapshotUri");

    //dump_trt__GetSnapshotUriResponse(&rep);

    result = -1;
    if (NULL != rep.MediaUri) {
        if (NULL != rep.MediaUri->Uri) {
            if (sizeuri > strlen(rep.MediaUri->Uri)) {
                strcpy(uri, rep.MediaUri->Uri);
                result = 0;
            } else {
                SOAP_DBGERR("Not enough cache!\n");
            }
        }
    }

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

void cb_discovery(char *DeviceXAddr)
{
    ERROR_DBLOG();
    int stmno = 0;                                                              // 码流序号,0为主码流,1为辅码流
    int profile_cnt = 0;                                                        // 设备配置文件个数
    struct tagProfile *profiles = NULL;                                         // 设备配置文件列表
    struct tagCapabilities capa;                                                // 设备能力信息

    char cmd[256];
    char uri[ONVIF_ADDRESS_SIZE] = {0};                                         // 不带认证信息的URI地址
    char uri_auth[ONVIF_ADDRESS_SIZE + 50] = {0};                               // 带有认证信息的URI地址

    ONVIF_GetCapabilities(DeviceXAddr, &capa);                                  // 获取设备能力信息(获取媒体服务地址)
    
    profile_cnt = ONVIF_GetProfiles(DeviceXAddr, &profiles);                    // 获取媒体配置信息(主/辅码流配置信息)

    if (profile_cnt > stmno) {
        ONVIF_GetSnapshotUri(capa.MediaXAddr, profiles[stmno].token, uri, sizeof(uri)); // 获取图像抓拍URI

        make_uri_withauth(uri, USERNAME, PASSWORD, uri_auth, sizeof(uri_auth)); // 生成带认证信息的URI(有的IPC要求认证)

        sprintf(cmd, "wget -O out.jpeg '%s'", uri_auth);                        // 使用wget下载图片
        system(cmd);
        printf("抓图uri: %s\n", uri_auth);
    }

    if (NULL != profiles) {
        free(profiles);
        profiles = NULL;
    }
}


onvif_comm.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "soapH.h"
#include "wsaapi.h"

#ifndef __ONVIF_COMM_H__
#define __ONVIF_COMM_H__

#ifdef __cplusplus
extern "C" {
#endif

#ifndef DIM
#define DIM(array)  (sizeof(array) / sizeof(array[0]))
#endif

#ifndef max
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#endif

#ifndef min
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#endif

#define ERROR_DBLOG()           //printf(">> File:%s  Line:%d  Function:%s\n", __FILE__, __LINE__, __FUNCTION__)
#define SOAP_DBGLOG             printf
#define SOAP_DBGERR             printf

#define USERNAME        "admin"                                                 
#define PASSWORD        "Xinzailing_Web"

#define SOAP_TO         "urn:schemas-xmlsoap-org:ws:2005:04:discovery"
#define SOAP_ACTION     "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe"

#define SOAP_MCAST_ADDR "soap.udp://239.255.255.250:3702"                      

#define SOAP_ITEM       ""                                                     
#define SOAP_TYPES      "dn:NetworkVideoTransmitter"                        

#define SOAP_SOCK_TIMEOUT    (10)                                            

#define ONVIF_ADDRESS_SIZE   (128)                                             
#define ONVIF_TOKEN_SIZE     (65)                                             

#define NET_CARD   "172.18.0.1"

struct tagVideoEncoderConfiguration
{
    char token[ONVIF_TOKEN_SIZE];                                             
    int Width;                                                             
    int Height;
};

struct tagProfile {
    char token[ONVIF_TOKEN_SIZE];                                          

    struct tagVideoEncoderConfiguration venc;                                   
};


struct tagCapabilities {
    char MediaXAddr[ONVIF_ADDRESS_SIZE];                                       
    char EventXAddr[ONVIF_ADDRESS_SIZE];                                      
                                                                               
};

#define SOAP_CHECK_ERROR(result, soap, str) \
    do { \
        if (SOAP_OK != (result) || SOAP_OK != (soap)->error) { \
            soap_perror((soap), (str)); \
            if (SOAP_OK == (result)) { \
                (result) = (soap)->error; \
            } \
            goto EXIT; \
        } \
    } while (0)


void            soap_perror(struct soap *soap, const char *str);
void *          ONVIF_soap_malloc(struct soap *soap, unsigned int n);
struct soap *   ONVIF_soap_new(int timeout);
void            ONVIF_soap_delete(struct soap *soap);

int             ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password);
void            ONVIF_init_header(struct soap *soap);
void            ONVIF_init_ProbeType(struct soap *soap, struct wsdd__ProbeType *probe);
void            ONVIF_DetectDevice(void (*cb)(char *DeviceXAddr));

int             ONVIF_GetCapabilities(const char *DeviceXAddr, struct tagCapabilities *capa);
int             ONVIF_GetProfiles(const char *MediaXAddr, struct tagProfile **profiles);
int             ONVIF_GetSnapshotUri(const char *MediaXAddr, char *ProfileToken, char *uri, unsigned int sizeuri);
int             make_uri_withauth(char *src_uri, char *username, char *password, char *dest_uri, unsigned int size_dest_uri);
void            cb_discovery(char *DeviceXAddr);
#ifdef __cplusplus
}
#endif

#endif

main.c

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "onvif_comm.h"


int main(int argc, char **argv)
{
   ONVIF_DetectDevice(cb_discovery);
   return 0;
}

博主是用cmake编译的,顺便贴一下CMakeLists.txt的代码,这样写是为了保证编译出的文件比较小

# CMake 最低版本号要求
cmake_minimum_required(VERSION 2.6)

# 项目信息
project (onvif)

# 取消默认链接参数
string(REGEX REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}")
message("CMAKE_SHARED_LIBRARY_LINK_C_FLAGS = " ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS})

# 编译器参数
add_compile_options(-DWITH_OPENSSL -DWITH_DOM -DWITH_ZLIB -DWITH_NOIDREF -Os -ffunction-sections -fdata-sections)

# 链接器参数 
set(LINK_FLAGS  "-Wl,-exclude-libs=ALL,-gc-sections -s")
set(CMAKE_SHARED_LINKER_FLAGS "${LINK_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS    "${LINK_FLAGS}")

# 指定生成目标
add_executable(${PROJECT_NAME})

# 添加头文件路径
INCLUDE_DIRECTORIES(./onvif_frame)
INCLUDE_DIRECTORIES(./src)

# 添加所有源文件源文件路径
aux_source_directory(./src sources)
aux_source_directory(./onvif_frame sources)
target_sources(${PROJECT_NAME} PUBLIC ${sources})

# 指定要链接的动态库
TARGET_LINK_LIBRARIES(${PROJECT_NAME} z ssl crypto ${LIBS})

# 设置安装路径
INSTALL(TARGETS ${PROJECT_NAME}
                ARCHIVE DESTINATION lib
                LIBRARY DESTINATION lib
                RUNTIME DESTINATION sbin
        )


5 最后的话

博主最近比较忙,可能写的没那么详细,有什么问题可以问,后续可能会更新。

除了本篇博客外和onvif相关的后面可能会出裁剪wsdl文件的方法,以及不使用wget用curl库进行图片下载、不使用gsoap框架实现抓图等内容(有空的话)。

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值