ONVIF协议网络摄像机(IPC)客户端程序开发(13):图像抓拍
来源:互联网 发布:加厚行美工刀架 编辑:程序博客网 时间:2024/05/29 18:04
1 专栏导读
本专栏第一篇文章「专栏开篇」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解,专栏前面文章讲过的知识点(或代码段),后面文章不会赘述。为了节省篇幅,突出重点,在文章中展示的示例代码仅仅是关键代码,你可以在「专栏开篇」中获取完整代码。
如有错误,欢迎你的留言纠正!让我们共同成长!你的「点赞」或「打赏」是对我最大的支持和鼓励!
2 原理简介
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用户认证。
3 编码流程
通过「设备发现」,得到 「设备服务地址」。
使用「设备服务地址」调用GetCapabilities接口,得到「媒体服务地址」。
使用「媒体服务地址」调用GetProfiles接口,得到主次码流的「媒体配置信息」,其中包含ProfileToken。
使用ProfileToken 调用GetSnapshotUri接口,得到主次码图像抓拍的URI地址。
根据URI地址,使用HTTP的GET方式获取图片。
4 示例代码
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'
以下的示例代码就是使用wget实现的图像抓拍功能。
/**************************************************************************函数: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(char *src_uri, char *username, char *password, char *dest_uri, unsigned int size_dest_uri){ int result = 0; unsigned int needBufSize = 0; SOAP_ASSERT(NULL != src_uri); SOAP_ASSERT(NULL != username); SOAP_ASSERT(NULL != password); SOAP_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){ int result = 0; struct soap *soap = NULL; struct _trt__GetSnapshotUri req; struct _trt__GetSnapshotUriResponse rep; SOAP_ASSERT(NULL != MediaXAddr); SOAP_ASSERT(NULL != uri); memset(uri, 0x00, sizeuri); SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT))); 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){ 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(capa.MediaXAddr, &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); } if (NULL != profiles) { free(profiles); profiles = NULL; }}int main(int argc, char **argv){ ONVIF_DetectDevice(cb_discovery); return 0;}
- ONVIF协议网络摄像机(IPC)客户端程序开发(13):图像抓拍
- ONVIF协议网络摄像机(IPC)客户端程序开发(13):图像抓拍
- ONVIF协议网络摄像机(IPC)客户端程序开发(5):门外汉理解ONVIF协议
- ONVIF协议网络摄像机(IPC)客户端程序开发(5):门外汉理解ONVIF协议
- ONVIF协议网络摄像机(IPC)客户端程序开发(2):第一次使用IPC摄像头
- ONVIF协议网络摄像机(IPC)客户端程序开发(2):第一次使用IPC摄像头
- ONVIF协议网络摄像机(IPC)客户端程序开发(9):鉴权(认证)
- ONVIF协议网络摄像机(IPC)客户端程序开发(9):鉴权(认证)
- ONVIF协议网络摄像机(IPC)客户端程序开发(1):专栏开篇
- ONVIF协议网络摄像机(IPC)客户端程序开发(3):理解什么是Web Services
- ONVIF协议网络摄像机(IPC)客户端程序开发(7):设备搜索
- ONVIF协议网络摄像机(IPC)客户端程序开发(8):获取设备基本信息
- ONVIF协议网络摄像机(IPC)客户端程序开发(10):设备校时
- ONVIF协议网络摄像机(IPC)客户端程序开发(11):设备能力
- ONVIF协议网络摄像机(IPC)客户端程序开发(12):读取音视频流
- ONVIF协议网络摄像机(IPC)客户端程序开发(14):修改分辨率
- ONVIF协议网络摄像机(IPC)客户端程序开发(15):遮挡报警
- ONVIF协议网络摄像机(IPC)客户端程序开发(1)
- Effective Java笔记-序列化
- JS获取表单的值并输出
- fastdfs-client-java工具类的使用
- 终于还是在上电时序问题上给自己挖了个坑
- 面试小题
- ONVIF协议网络摄像机(IPC)客户端程序开发(13):图像抓拍
- 异常
- Django初探(一) 分类:网络编程Django
- 0、AD系列教程之索引篇文档
- 转载36大数据(36dsj.com):36大数据»大数据等最核心的关键技术:32个算法
- Codeblocks使用说明
- P1171 售货员的难题
- java基础语法循环语句
- 常见算法(logistic回归,随机森林,GBDT和xgboost)