ONVIF 獲取設備基本信息(網絡攝像頭)——實例筆記

相關配置

ONVIF官網:http://www.onvif.org/
gSOAP安裝配置:gSOAP安裝配置+使用案例參考+參考鏈接
操作系統:CentOS7

資料參考

許振坪的ONVIF專欄:傳送門
大佬寫的 678三篇文章(主要針對)。
設備發現參考:ONVIF 設備發現(網絡攝像頭)——實例筆記

代碼實戰

完整源碼下載:GitHub碼雲
如何生成ONVIF框架參考:ONVIF協議網絡攝像機(IPC)客戶端程序開發(6):使用gSOAP生成ONVIF框架代碼
因爲大佬沒有在第8篇文章中給出完整代碼,所以直接編譯肯定過不了,再此進行補充
要做的是 將第7篇中的代碼和第8篇中的代碼連起來,去掉大佬沒有提供的頭文件和函數,改爲自己的。相關修改過程已經在大佬的文章下面評論了,大家可以自行參考。

以下是我整理好後的代碼:
main.c

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "soapH.h"
#include "wsaapi.h"
#include "wsdd.nsmap"
//#include "onvif_dump.h"

#define SOAP_ASSERT     assert
#define SOAP_DBGLOG     printf
#define SOAP_DBGERR     printf

#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"                       // onvif規定的組播地址

#define SOAP_ITEM       ""                                                      // 尋找的設備範圍
#define SOAP_TYPES      "dn:NetworkVideoTransmitter"                            // 尋找的設備類型

#define SOAP_SOCK_TIMEOUT    (10)               // socket超時時間(單秒秒)

#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)
{
    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)
{
    void *p = NULL;

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

struct soap *ONVIF_soap_new(int timeout)
{
    struct soap *soap = NULL;                                                   // soap環境變量

    SOAP_ASSERT(NULL != (soap = soap_new()));

    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)
{
    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_init_header
**功能:初始化soap描述消息頭
**參數:
        [in] soap - soap環境變量
**返回:無
**備註:
    1). 在本函數內部通過ONVIF_soap_malloc分配的內存,將在ONVIF_soap_delete中被釋放
************************************************************************/
void ONVIF_init_header(struct soap *soap)
{
    struct SOAP_ENV__Header *header = NULL;

    SOAP_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)
{
    struct wsdd__ScopesType *scope = NULL;                                      // 用於描述查找哪類的Web服務

    SOAP_ASSERT(NULL != soap);
    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))
{
    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_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    ONVIF_init_header(soap);                                                    // 設置消息頭描述
    ONVIF_init_ProbeType(soap, &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 {                                                            // 成功接收到設備的應答消息
                printf("__sizeProbeMatch:%d\n",rep.wsdd__ProbeMatches->__sizeProbeMatch);
                //dump__wsdd__ProbeMatches(&rep);

                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_GetDeviceInformation
**功能:獲取設備基本信息
**參數:
        [in] DeviceXAddr - 設備服務地址
**返回:
        0表明成功,非0表明失敗
**備註:
************************************************************************/
int ONVIF_GetDeviceInformation(const char *DeviceXAddr)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _tds__GetDeviceInformation           devinfo_req;
    struct _tds__GetDeviceInformationResponse   devinfo_resp;

    SOAP_ASSERT(NULL != DeviceXAddr);
SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    memset(&devinfo_req, 0x00, sizeof(devinfo_req));
    memset(&devinfo_resp, 0x00, sizeof(devinfo_resp));
    result = soap_call___tds__GetDeviceInformation(soap, DeviceXAddr, NULL, &devinfo_req, &devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "GetDeviceInformation");

    //dump_tds__GetDeviceInformationResponse(&devinfo_resp);
    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);

EXIT:

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

void cb_discovery(char *DeviceXAddr)
{
    ONVIF_GetDeviceInformation(DeviceXAddr);
}

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

終端輸入 gcc -o main main.c stdsoap2.c soapC.c soapClient.c wsaapi.c duration.c 進行編譯
運行效果如下:
一個設備獲取不到,另一個已經成功打印相關信息。
在這裏插入圖片描述

補充

我提供的打印信息還不夠完整。
//dump__wsdd__ProbeMatches(&rep); 166行左右
//dump_tds__GetDeviceInformationResponse(&devinfo_resp); 217行左右

__wsdd__ProbeMatches 和 _tds__GetDeviceInformationResponse
搜索這2個變量的類型
設計到相關結構體,可到 soapStub.h 中搜索
在這裏插入圖片描述

以下提供下我搜到的一些內容,結構體__wsdd__ProbeMatches下一層套一層,我受不了了。

struct __wsdd__ProbeMatches {
        /** Optional element 'wsdd:ProbeMatches' of XML schema type 'wsdd:ProbeMatchesType' */
        struct wsdd__ProbeMatchesType *wsdd__ProbeMatches;
};
struct wsdd__ProbeMatchesType {
        /** Sequence of elements 'wsdd:ProbeMatch' of XML schema type 'wsdd:ProbeMatchType' stored in dynamic array ProbeMatch of length __sizeProbeMatch */
        int __sizeProbeMatch;
        struct wsdd__ProbeMatchType *ProbeMatch;
};
struct wsdd__ProbeMatchType {
        /** Required element 'wsa:EndpointReference' of XML schema type 'wsa:EndpointReference' */
        struct wsa__EndpointReferenceType wsa__EndpointReference;
        /** Optional element 'wsdd:Types' of XML schema type 'xsd:QName' */
        char *Types;
        /** Optional element 'wsdd:Scopes' of XML schema type 'wsdd:ScopesType' */
        struct wsdd__ScopesType *Scopes;
        /** Optional element 'wsdd:XAddrs' of XML schema type 'wsdd:UriListType' */
        char *XAddrs;
        /** Required element 'wsdd:MetadataVersion' of XML schema type 'xsd:unsignedInt' */
        unsigned int MetadataVersion;
};
struct _tds__GetDeviceInformationResponse {
        /** Required element 'tds:Manufacturer' of XML schema type 'xsd:string' */
        char *Manufacturer;
        /** Required element 'tds:Model' of XML schema type 'xsd:string' */
        char *Model;
        /** Required element 'tds:FirmwareVersion' of XML schema type 'xsd:string' */
        char *FirmwareVersion;
        /** Required element 'tds:SerialNumber' of XML schema type 'xsd:string' */
        char *SerialNumber;
        /** Required element 'tds:HardwareId' of XML schema type 'xsd:string' */
        char *HardwareId;
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章