ONVIF协议网络摄像机(IPC)客户端程序开发(13):图像抓拍

来源:互联网 发布:加厚行美工刀架 编辑:程序博客网 时间:2024/05/29 18:04

1 专栏导读

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

如有错误,欢迎你的留言纠正!让我们共同成长!你的「点赞」「打赏」是对我最大的支持和鼓励!

2 原理简介

IPC图像抓拍有两种方法:

  1. 对RTSP视频流进行视频截图;
  2. 使用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 编码流程

  1. 通过「设备发现」,得到 「设备服务地址」。

  2. 使用「设备服务地址」调用GetCapabilities接口,得到「媒体服务地址」。

  3. 使用「媒体服务地址」调用GetProfiles接口,得到主次码流的「媒体配置信息」,其中包含ProfileToken。

  4. 使用ProfileToken 调用GetSnapshotUri接口,得到主次码图像抓拍的URI地址。

  5. 根据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;}
阅读全文
1 0