原理简介

IPC图像抓拍有两种方法:

  • 对RTSP视频流进行视频截图;
  • 使用HTTP的GET方式获取图片。

以下介绍第二种方法。

ONVIF协议除了提供RTSP的URL外,其实也给出了抓拍的URL,使用Media模块的GetSnapshotUri接口可获取图像抓拍的URL。

比如,我从IPC获得的抓拍URL为:http://100.100.100.160/onvifsnapshot/media_service/snapshot?channel=1&subtype=0。

那如何通过这个地址获得图片呢?其实在media.wsdl中,该接口的函数功能说明中已经描述的很清楚了:「The URI can be used for acquiring a JPEG image through a HTTP GET operation」,也就是通过HTTP的GET方式获得JPEG图片。

在浏览器上输入抓拍的URL,在浏览器中就会显示出图片,刷新,图片会变化,对于需要验证的IPC,会要求我们输入用户名密码进行HTTP用户认证。

编码流程

  1. 通过「设备发现」,得到 「设备服务地址」。
  2. 使用「设备服务地址」调用GetCapabilities接口,得到「媒体服务地址」。
  3. 使用「媒体服务地址」调用GetProfiles接口,得到主次码流的「媒体配置信息」,其中包含ProfileToken。
  4. 使用ProfileToken 调用GetSnapshotUri接口,得到主次码图像抓拍的URI地址。
  5. 根据URI地址,使用HTTP的GET方式获取图片。
    • Windows的MFC里有CInternetSession,CHttpConnection,CHttpFile这些类提供通过HTTP获得图像数据。
    • Linux可以使用很多开源方案,甚至可以直接使用shell命令wget来下载图像即可,简单高效。比如:
    wget -O out.jpeg 'http://100.100.100.5:80/capture/webCapture.jpg?channel=1&FTpsend=0&checkinfo=0'
    

    如果需要带认证信息,可以使用:

    wget -O out.jpeg 'http://username:password@100.100.100.5:80/capture/webCapture.jpg?channel=1&FTpsend=0&checkinfo=0'
    

示例代码

具体请参见

除了onvif_head.sh修改为:

#!/bin/bash
mkdir onvif_head
cd onvif_head
../bin/wsdl2h -o  onvif.h  -s -d -x -t ../gsoap/WS/typemap.dat \
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl \
https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl

main.cpp修改为

#include <assert.h>
#include "soapH.h"
#include "wsdd.nsmap"
#include "soapStub.h"
#include "wsseapi.h"
#include "wsaapi.h"
#include <map>#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超时时间(单秒秒)void soap_perror(struct soap *soap, const char *str)
{if (nullptr == 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));}
}void* ONVIF_soap_malloc(struct soap *soap, unsigned int n)
{void *p = nullptr;if (n > 0) {p = soap_malloc(soap, n);SOAP_ASSERT(nullptr != p);memset(p, 0x00 ,n);}return p;
}struct soap *ONVIF_soap_new(int timeout)
{struct soap *soap = nullptr;                                                   // soap环境变量SOAP_ASSERT(nullptr != (soap = soap_new()));soap_set_namespaces(soap, namespaces);                                      // 设置soap的namespacessoap->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
#endifsoap_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 datasoap_done(soap);                                                            // Reset, close communications, and remove callbackssoap_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 = nullptr;SOAP_ASSERT(nullptr != 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;
}/************************************************************************
**函数: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 = nullptr;                                      // 用于描述查找哪类的Web服务SOAP_ASSERT(nullptr != soap);SOAP_ASSERT(nullptr != 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);
}void ONVIF_DetectDevice(void (*cb)(char *DeviceXAddr))
{int i;int result = 0;unsigned int count = 0;                                                     // 搜索到的设备个数struct soap *soap = nullptr;                                                   // soap环境变量struct wsdd__ProbeType      req;                                            // 用于发送Probe消息struct __wsdd__ProbeMatches rep;                                            // 用于接收Probe应答struct wsdd__ProbeMatchType *probeMatch;SOAP_ASSERT(nullptr != (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, nullptr, &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);if (nullptr != rep.wsdd__ProbeMatches) {count += rep.wsdd__ProbeMatches->__sizeProbeMatch;for(i = 0; i < rep.wsdd__ProbeMatches->__sizeProbeMatch; i++) {probeMatch = rep.wsdd__ProbeMatches->ProbeMatch + i;if (nullptr != cb ) {std::string url = probeMatch->XAddrs;// if(url == "http://192.168.0.116/onvif/device_service"){cb(probeMatch->XAddrs);                             // 使用设备服务地址执行函数回调//  }}}}}} else if (soap->error) {break;}}SOAP_DBGLOG("\ndetect end! It has detected %d devices!\n", count);if (nullptr != soap) {ONVIF_soap_delete(soap);}}#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)/************************************************************************
**函数:ONVIF_SetAuthInfo
**功能:设置认证信息
**参数:[in] soap     - soap环境变量[in] username - 用户名[in] password - 密码
**返回:0表明成功,非0表明失败
**备注:
************************************************************************/
static int ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password)
{int result = 0;SOAP_ASSERT(nullptr != username);SOAP_ASSERT(nullptr != password);result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);SOAP_CHECK_ERROR(result, soap, "add_UsernameTokenDigest");EXIT:return result;
}#define USERNAME    "admin"
#define PASSWORD    "hik12345"
/************************************************************************
**函数:ONVIF_GetDeviceInformation
**功能:获取设备基本信息
**参数:[in] DeviceXAddr - 设备服务地址
**返回:0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_GetDeviceInformation(const char *DeviceXAddr)
{int result = 0;struct soap *soap = nullptr;_tds__GetDeviceInformation           devinfo_req;_tds__GetDeviceInformationResponse   devinfo_resp;SOAP_ASSERT(nullptr != DeviceXAddr);SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);result = soap_call___tds__GetDeviceInformation(soap, DeviceXAddr, nullptr, &devinfo_req, devinfo_resp);SOAP_CHECK_ERROR(result, soap, "GetDeviceInformation");std::cout << "      Manufacturer:\t" << devinfo_resp.Manufacturer << "\n";std::cout << "      Model:\t" << devinfo_resp.Model << "\n";std::cout << "      FirmwareVersion:\t" << devinfo_resp.FirmwareVersion << "\n";std::cout << "      SerialNumber:\t" << devinfo_resp.SerialNumber << "\n";std::cout << "      HardwareId:\t" << devinfo_resp.HardwareId << "\n";EXIT:if (nullptr != soap) {ONVIF_soap_delete(soap);}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 std::string& MediaXAddr, const std::string& ProfileToken, std::string * snapUri)
{int result = 0;struct soap *soap = nullptr;_trt__GetSnapshotUri         req;_trt__GetSnapshotUriResponse rep;SOAP_ASSERT(!MediaXAddr.empty() && !ProfileToken.empty());SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);req.ProfileToken = const_cast<char *>(ProfileToken.c_str());result = soap_call___trt__GetSnapshotUri(soap, MediaXAddr.c_str(), NULL, &req, rep);SOAP_CHECK_ERROR(result, soap, "GetSnapshotUri");if (nullptr != rep.MediaUri && nullptr != rep.MediaUri->Uri) {*snapUri = rep.MediaUri->Uri;}EXIT:if (NULL != soap) {ONVIF_soap_delete(soap);}return result;
}bool ONVIF_GetProfiles(const std::string& mediaXAddr, std::string  * profilesToken)
{int result = 0;struct soap *soap = nullptr;_trt__GetProfiles           devinfo_req;_trt__GetProfilesResponse   devinfo_resp;SOAP_ASSERT(!mediaXAddr.empty());SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);result = soap_call___trt__GetProfiles(soap, mediaXAddr.c_str(), nullptr, &devinfo_req, devinfo_resp);SOAP_CHECK_ERROR(result, soap, "ONVIF_GetProfiles");SOAP_ASSERT(devinfo_resp.__sizeProfiles > 0);*profilesToken = (*devinfo_resp.Profiles)->token;EXIT:if (nullptr != soap) {ONVIF_soap_delete(soap);}return result;
}/************************************************************************
**函数:ONVIF_GetCapabilities
**功能:获取设备能力信息
**参数:[in] DeviceXAddr - 设备服务地址[in]
**返回:0表明成功,非0表明失败
**备注:1). 其中最主要的参数之一是媒体服务地址
************************************************************************/
int ONVIF_GetCapabilities(const std::string& deviceXAddr, std::string * mediaXAddr)
{int result = 0;struct soap *soap = nullptr;_tds__GetCapabilities            devinfo_req;_tds__GetCapabilitiesResponse    devinfo_resp;SOAP_ASSERT(!deviceXAddr.empty());SOAP_ASSERT(nullptr != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));result = soap_call___tds__GetCapabilities(soap, deviceXAddr.c_str(), NULL, &devinfo_req, devinfo_resp);SOAP_CHECK_ERROR(result, soap, "GetCapabilities");if(devinfo_resp.Capabilities->Media != nullptr){*mediaXAddr = devinfo_resp.Capabilities->Media->XAddr;}EXIT:if (nullptr != soap) {ONVIF_soap_delete(soap);}return result;
}/************************************************************************
**函数: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
************************************************************************/
static int make_uri_withauth(const std::string& src_uri, const std::string&username, const std::string&password, std::string *dest_uri)
{int result = 0;SOAP_ASSERT(!src_uri.empty());if (username.empty() &&password.empty()) {                       // 生成新的uri地址*dest_uri = src_uri;} else {std::string::size_type position = src_uri.find("//");if (std::string::npos == position) {SOAP_DBGERR("can't found '//', src uri is: %s.\n", src_uri.c_str());result = -1;return result;}position += 2;dest_uri->append(src_uri,0,   position) ;dest_uri->append(username + ":" + password + "@");dest_uri->append(src_uri,position, std::string::npos) ;}return result;
}void cb_discovery(char *deviceXAddr)
{std::string  mediaXAddr, profilesToken, snapUri, snapAuthUri;ONVIF_GetCapabilities(deviceXAddr, &mediaXAddr);ONVIF_GetProfiles(mediaXAddr, &profilesToken);ONVIF_GetSnapshotUri(mediaXAddr, profilesToken, &snapUri);make_uri_withauth(snapUri, USERNAME, PASSWORD, &snapAuthUri);char cmd[256];sprintf(cmd, "wget -O %s '%s'",   "out.jpeg", snapAuthUri.c_str());                        // 使用wget下载图片system(cmd);
}int main(int argc, char **argv)
{ONVIF_DetectDevice(cb_discovery);return 0;
}

其他文章

  • Onvif协议:理解什么是Web Services
  • Onvif协议:使用gSOAP创建SOAP调用实例
  • Onvif协议:门外汉理解ONVIF协议
  • Onvif协议:到底什么是ONVIF协议
  • Onvif协议:实现Probe命令来进行设备发现(discover)
  • Onvif协议:IPC客户端开发之获取设备基本信息
  • Onvif协议:IPC客户端开发之鉴权
  • Onvif协议:IPC客户端开发之获取设备能力
  • Onvif协议:IPC客户端开发之PTZ控制
  • Onvif协议:IPC客户端开发之获取实时预览的Url地址
  • Onvif协议:IPC客户端开发之图像抓拍
  • ONVIF Device Test Tool测试工具使用方法

Onvif协议:IPC客户端开发之图像抓拍相关推荐

  1. Onvif协议:IPC客户端开发之获取设备能力

    原理简介 ONVIF协议接口由多个模块组成,每个模块分别对应着不同的WSDL文档,在ONVIF官网中能查看到这些模块,以及每个模块中的接口函数,这里列举几个模块: DeviceMgmt(设备管理) 使 ...

  2. ONVIF网络摄像头(IPC)客户端开发—ONVIF介绍

    1.前言: 网上已经有很多关于ONVIF开发的资料,这里概括介绍一下ONVIF协议以及介绍一下我自己在开发ONVIF网络摄像头的一些流程和经验,做个开发记录和经验总结,以备将来查看,也可供他人参考.如 ...

  3. 基于ONVIF协议的摄像头开发总结

    原文:http://www.cnblogs.com/big-devil/p/7625752.html 最近在做onvif协议的相关工作,看到一篇介绍onvif协议很好的文章,遂转载过来,以作记录 在查 ...

  4. 小米摄像头有onvif协议_监控摄像头完好但图像不行,肯定逃不过这10个问题

    摄像头仅仅是视频监控系统的一部分,即使摄像头完好无损,监控画面也可能会出现不显示.卡顿.丢失等情况.想要彻底解决网络监控的问题,往往需要排查各个连接设备才可以判断.下面我们就总结了10个问题,彻底解决 ...

  5. ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载H264视频流

    写得很详细 https://caibiao-lee.blog.csdn.net/article/details/102131820?utm_medium=distribute.wap_relevant ...

  6. SOAP学习之二:全网最简单的ONVIF协议IPC云台PTZ控制VC++代码--不使用gsoap!!

    接触soap和xml第三天,稍有一点认识,看了很多文章,都不适合我的项目.目前项目中用到三台不同品牌的IPC,虽说厂家都用SDK,但在项目中引入三套SDK有点臃肿,何况目前已实现了使用VLC的拉流播放 ...

  7. Onvif协议:IPC客户端开发之PTZ控制

    介绍 在安防摄像头中,不仅仅涉及到固定摄像头的枪击,同样还包含可以360°转动的球机.因此对球机的云台方向控制是Onvif协议开发过程中必不可少的过程 球机的云台控制主要包含:八个方向(上.下.左.右 ...

  8. Onvif协议:理解什么是Web Services

    ONVIF规范中设备管理和控制部分所定义的接口均以Web Services的形式提供.要理解什么是ONVIF,就必须先知道什么是Web Services.所以,开始介绍ONVIF之前,我单独整理了一篇 ...

  9. ONVIF协议网络摄像机(IPC)客户端程序开发(5):门外汉理解ONVIF协议

    1. 专栏导读 本专栏第一篇文章「专栏开篇」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解,专栏前面文章讲过的知识点(或代码段),后面文章不会赘述.为了节省篇幅,突出重点,在文章中展示的示例代码 ...

  10. ONVIF协议开发之网络摄像头云台控制(C版)

    在之前的文章中(<python-onvif实现客户端控制相机云台>),介绍过用python实现基于onvif协议的相机云台控制,考虑到嵌入式端的执行效率问题,还是需要实现C/C++版本的接 ...

最新文章

  1. 评估报告有效期过期了怎么办_T4学生签证过期了,怎么申请Vignette Transfer?
  2. 简单的社交网络分析(基于R)
  3. Spring Sleuth和Zipkin跟踪微服务
  4. vc6怎么看错误在哪_周杰伦超话第一!微博超话在哪进入签到?怎么看排名?不会来看!...
  5. 【概率论与数理统计】如何理解自由度n?
  6. 论文浅尝 | Doc2EDAG:一种针对中文金融事件抽取的端到端文档级框架
  7. Java模板引擎 FreeMarker介绍1
  8. 20分钟理解React Native For Android原理
  9. 网友神总结:我们继续用 XP 的十大理由
  10. 在线式极限学习机OS-ELM
  11. PPT模板 | 红色学术风论文答辩PPT模板
  12. 简单编程(五)编程验证一下“角谷猜想”
  13. Hi3516EV200设置手动曝光时间
  14. 初学python------写一个心理测试
  15. Linux系统下安装screen
  16. NTL密码算法开源库——大整数ZZ类(二)
  17. VR分享会邀请函 | 如何利用VR影像创造商业应用新价值?
  18. 利用传感器实现微信的摇一摇功能
  19. java web分层和层间数据传递 vo bo po (转载)
  20. 阿里Java程序员分享自己的职业规划,希望对你有所启发

热门文章

  1. STM8L驱动I2C类型的12864
  2. python使用gps设备
  3. 如何建立强有力的人脉关系
  4. 免费织梦CMS文章采集器之采集聚合
  5. python socket编程实例 带图形界面_python的socket编程实例
  6. unable to find setter method for attribute:[commandName]
  7. php搞笑图片合成,PS教你怎么把照片做成搞笑的qq表情
  8. HTML5期末大作业:我的家乡网站设计——我的家乡
  9. 通过身份证号查出所在籍贯以及性别。
  10. 基于UDP的网络群聊系统