本专栏第一篇文章「专栏开篇」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解,专栏前面文章讲过的知识点(或代码段),后面文章不会赘述。为了节省篇幅,突出重点,在文章中展示的示例代码仅仅是关键代码,你可以在「专栏开篇」中获取完整代码。
如有错误,欢迎你的留言纠正!让我们共同成长!你的「点赞」或「打赏」是对我最大的支持和鼓励!
IPC图像抓拍有两种方法:
第一种方法我还没试过,没有发言权,以下介绍第二种方法。
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用户认证。
通过「设备发现」,得到 「设备服务地址」。
使用「设备服务地址」调用GetCapabilities接口,得到「媒体服务地址」。
使用「媒体服务地址」调用GetProfiles接口,得到主次码流的「媒体配置信息」,其中包含ProfileToken。
使用ProfileToken 调用GetSnapshotUri接口,得到主次码图像抓拍的URI地址。
根据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:[email protected]: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:[email protected]: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;
}