ONVIF协议网络摄像机客户端使用gsoap获取RTSP流地址GStreamer拉流播放

什么是ONVIF协议


        
ONVIF(开放式网络视频接口论坛)是一个全球性的开放式行业论坛,旨在促进开发和使用基于物理IP的安全产品接口的全球开放标准。

        ONVIF规范的目标是建立一个网络视频框架协议,使不同厂商生产的网络视频产品完全互通。

        1:设备管控和控制:通过Web Services提供接口,使用SOAP协议进行数据交互。

        2:音视频流‌:通过RTP/RTSP进行传输。

        3:‌统一接口‌:抽象了功能的接口,统一了对设备配置和操作的方式,使得控制端不再关心设备的型号,而是关注设备提供的Web Service‌23。

        学习并使用ONVIF协议我个人觉得要理清楚角色,就网络摄像机而言,对于应用场景可分为客户端和服务端。使用的网络摄像机就是服务端,也就是了解ONVIF出现最多的web services服务端,想要使用这个对接网络摄像机就是从这里面获取相关信息。    

        开发客户端通过ONVIF规范接口和网络摄像机进行通讯,其中常见的功能有:

        1:获取网络相机的基本信息

        2:修改网络的系统日期、时间

        3:修改网络摄像机的网络配置(IP、子网掩码等)

        4:获取、修改网络摄像头的各种参数(视频分辨率、码率、帧率、OSD(叠加信息)、云平台控制)

        在onvif协议中,有一些列profile的技术规格。引入这些规格是使得终端用户能够更容易区分各个profile所支持的特性,而无需确定各个版本间的兼容性。

        其中应用于视频流的主要是使用profile S 技术规格。

        ONVIF官方profile地址

在学习ONVIF协议时会出现很多名词:Web Service、WSDL、XML、SOAP。刚接触的初学者会一脸懵逼,这些都是什么玩意有什么用,以我对他们的了解而言,这些也就了解一下皮毛就行了,不用纯手搓从底层开始搓。因为有一个很强大的工具来简化这个过程那即是gsoap

gsoap官网:http://www.cs.fsu.edu/~engelen/soap.html

需要什么功能在使用这个工具的时候直接连接。

就像这样的链接:

http://www.onvif.org/onvif/ver20/util/operationIndex.html

http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl

http://www.onvif.org/onvif/ver10/deviceio.wsdl

http://www.onvif.org/onvif/ver10/event/wsdl/event.wsdl

http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl

http://www.onvif.org/onvif/ver10/analyticsdevice.wsdl

http://www.onvif.org/onvif/ver10/display.wsdl

http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl

http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl

http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl

http://www.onvif.org/onvif/ver10/Receiver.wsdl

http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl

http://www.onvif.org/onvif/ver10/Recording.wsdl

http://www.onvif.org/onvif/ver10/Replay.wsdl

http://www.onvif.org/onvif/ver10/Search.wsdl

需要什么链接什么

2:怎么使用

我是基于linux平台来进行相关的开发,那就需要自己去gsoap官网下载对应的工具库然后编译,很简单这个。

1:去官网下载相关的文件http://sourceforge.net/projects/gsoap2

2:解压压缩包然后进行配置

./configure --prefix=/usr/local/gSOAP

3:编译 make

4:安装:sudo make install

然后生成的编译好的库就在/usr/local/gSOAP这个路径下面

然后去文件夹bin里面找到

这两个工具程序,这俩是用来生成头文件框架的直接用就可以了

先把gsoap库里面的文件拷贝到自己的工程中

然后需要什么链接什么:

./wsdl2h -P -x -c -s -t ./typemap.dat -o  onvif.h https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl https://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl https://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl

./wsdl2h -P -x -c -s -t ./typemap.dat -o  onvif.h https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl https://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl https://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl

根据上面的介绍,需要什么功能后面连接什么wsdl。

然后运行

soapcpp2 -C -c -x -I import -I custom xxx.h

然后就会生成这些文件了

那么基本框架就OK了

然后就是自己写程序了

我的程序

#include <gst/gst.h>
#include "soapH.h"char* onvif() {struct soap *soap;struct _trt__GetProfilesResponse profilesResp;struct _trt__GetStreamUriResponse streamUriResp;char *rtspUri = NULL; // 用于存储RTSP URI的动态分配字符串// 初始化gSOAP运行时上下文soap = soap_new();if (soap == NULL) {fprintf(stderr, "soap_new failed\n");return NULL;}// 设置设备的端点URL和认证信息const char *device_url = "http://xxx.xxx.20.10:8000/onvif/device_service";soap->userid = "admin";soap->passwd = "admin";// 调用Web服务获取配置文件struct _trt__GetProfiles getProfiles;soap_call___trt__GetProfiles(soap, device_url, NULL, &getProfiles, &profilesResp);if (soap->error == SOAP_OK && profilesResp.Profiles) {// 假设我们使用第一个配置文件struct tt__Profile *profile = profilesResp.Profiles;// 获取每个配置文件的RTSP地址struct _trt__GetStreamUri getStreamUri;getStreamUri.ProfileToken = profile->token;// 分配并初始化Transport结构体struct tt__Transport *transport = (struct tt__Transport*)soap_malloc(soap, sizeof(struct tt__Transport));transport->Protocol = tt__TransportProtocol__RTSP; // 确保枚举值正确// 初始化StreamSetup结构体struct tt__StreamSetup streamSetup;streamSetup.Stream = tt__StreamType__RTP_Unicast; // 确保枚举值正确streamSetup.Transport = transport;// 赋值给getStreamUri.StreamSetupgetStreamUri.StreamSetup = &streamSetup;soap_call___trt__GetStreamUri(soap, device_url, NULL, &getStreamUri, &streamUriResp);if (soap->error == SOAP_OK && streamUriResp.MediaUri) {// 动态分配字符串以存储RTSP URIrtspUri = malloc(strlen("rtsp://") + strlen(streamUriResp.MediaUri->Uri) + 1);if (rtspUri != NULL) {sprintf(rtspUri, "%s", streamUriResp.MediaUri->Uri);} else {fprintf(stderr, "Memory allocation failed for RTSP URI\n");}} else {soap_print_fault(soap, stderr);}} else {soap_print_fault(soap, stderr);}// 清理资源soap_destroy(soap);soap_end(soap);soap_free(soap);return rtspUri; // 返回动态分配的RTSP URI字符串
}/* 回调函数 */
static void on_pad_added(GstElement *element, GstPad *pad, gpointer data);int main(int argc, char *argv[]) {GstElement *pipeline, *source, *depay, *parse, *decoder, *convert, *sink;GstBus *bus;GstMessage *msg;GstStateChangeReturn ret;char *rtspUri = onvif();/* 初始化 GStreamer库 */gst_init(&argc, &argv);/* 创建各个元素 */source = gst_element_factory_make("rtspsrc", "source");// RTSP 源depay = gst_element_factory_make("rtph264depay", "depay");//H.264 RTP 解封装parse = gst_element_factory_make("h264parse", "parse");// H.264 解析器decoder = gst_element_factory_make("mppvideodec", "decoder");// MPP 视频解码器convert = gst_element_factory_make("videoconvert", "convert");// 视频格式转换sink = gst_element_factory_make("autovideosink", "sink");// 自动选择视频输出if (!source || !depay || !parse || !decoder || !convert || !sink) {g_printerr("Not all elements could be created.\n");return -1;}/* Create the empty pipeline */pipeline = gst_pipeline_new("test-pipeline");if (!pipeline) {g_printerr("无法创建管道\n");gst_object_unref(source);gst_object_unref(depay);gst_object_unref(parse);gst_object_unref(decoder);gst_object_unref(convert);gst_object_unref(sink);return -1;}/* 设置源的属性 */g_object_set(G_OBJECT(source), "location", rtspUri, NULL);/* 构建管道 */gst_bin_add_many(GST_BIN(pipeline), source, depay, parse, decoder, convert, sink, NULL);/* 手动连接元素 */if (gst_element_link(depay, parse) != TRUE ||gst_element_link(parse, decoder) != TRUE ||gst_element_link(decoder, convert) != TRUE ||gst_element_link(convert, sink) != TRUE) {g_printerr("Elements could not be linked.\n");gst_object_unref(pipeline);return -1;}/* 动态连接 rtspsrc 到 rtph264depay */g_signal_connect(source, "pad-added", G_CALLBACK(on_pad_added), depay);/* 开始播放 */ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);if (ret == GST_STATE_CHANGE_FAILURE) {g_printerr("Unable to set the pipeline to the playing state.\n");gst_object_unref(pipeline);return -1;}/* 等待错误或 EOS(End-Of-Stream)消息 */bus = gst_element_get_bus(pipeline);msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);/* 解析消息 */if (msg != NULL) {GError *err;gchar *debug_info;switch (GST_MESSAGE_TYPE(msg)) {case GST_MESSAGE_ERROR:gst_message_parse_error(msg, &err, &debug_info);g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME(msg->src), err->message);g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");g_clear_error(&err);g_free(debug_info);break;case GST_MESSAGE_EOS:g_print("达到流末尾.\n");break;default:g_printerr("收到意外的消息.\n");break;}gst_message_unref(msg);}/* Free resources */gst_object_unref(bus);gst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(pipeline);return 0;
}
/* 回调函数,用于动态链接 rtspsrc 到 rtph264depay */
static void on_pad_added(GstElement *element, GstPad *pad, gpointer data) {GstPad *sinkpad;GstElement *depay = (GstElement *)data;g_print("Received new pad '%s' from '%s':\n", GST_PAD_NAME(pad), GST_ELEMENT_NAME(element));/* If our depay is already linked, we have nothing to do here */sinkpad = gst_element_get_static_pad(depay, "sink");if (sinkpad && gst_pad_is_linked(sinkpad)) {g_print("We are already linked. Ignoring.\n");gst_object_unref(sinkpad);return;}/* 尝试链接 */if (sinkpad && gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK) {g_print("Type of the pad does not match type of sink pad.\n");} else {g_print("Link succeeded.\n");}/* 释放 sink pad */if (sinkpad) {gst_object_unref(sinkpad);}
}

我看好多文章都没说怎么编译的,直接用gcc就可以

就像这样,也可以把这些.c文件编译库连接,这样好一点。calcclient.是自己的程序,写好编译通过即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/483227.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javaweb_Day05

1.请求响应 1.1 概述 1.2 请求 1.2.1 请求参数 1.2.2 响应 2.分层解耦 2.1 三层架构 &#xff08;1&#xff09;代码分层 2.2 分层解耦 2.3 IOC&DI入门 &#xff08;1&#xff09;控制反转IOC &#xff08;2&#xff09;依赖注入DI &#xff08;3&#xff09;汇总 …

Stable Diffusion 3详解

&#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&…

[VUE]框架网页开发02-如何打包Vue.js框架网页并在服务器中通过Tomcat启动

在现代Web开发中&#xff0c;Vue.js已经成为前端开发的热门选择之一。然而&#xff0c;将Vue.js项目打包并部署到生产环境可能会让一些开发者感到困惑。本文将详细介绍如何将Vue.js项目打包&#xff0c;并通过Tomcat服务器启动运行。 1. 准备工作 确保你的项目能够正常运行,项…

网络分层模型( OSI、TCP/IP、五层协议)

1、网络分层模型 计算机网络是一个极其复杂的系统。想象一下最简单的情况&#xff1a;两台连接在网络上的计算机需要相互传输文件。不仅需要确保存在一条传输数据的通路&#xff0c;还需要完成以下几项工作&#xff1a; 发起通信的计算机必须激活数据通路&#xff0c;这包括发…

采药 刷题笔记 (动态规划)0/1背包

P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 动态规划 0/1背包 的本质在于继承 一行一行更新 上一行是考虑前i个物品的最优情况 当前行是考虑第i1个物品的情况 当前行的最优解 来自上一行和前i个物品的最优解进行比较 如果当前装了当前物品&#xff…

汽车操作系统详解

目录 1. 车控汽车操作系统 2. 车载汽车操作系统 3. OEM定制操作系统 刚开始工作的时候&#xff0c;接触的是汽车控制相关的开发工作&#xff0c;天真地以为汽车操作系统就是指实时操作系统&#xff0c;例如FreeRTOS、OSEK OS、AUTOSAR OS等等&#xff1b;然而&#xff0c;随…

Shire 1.1 发布:更强大的交互支持,升级 AI 智能体与 IDE 的整合体验

在经过多个项目上的试用后&#xff0c;我们进入了持续的修修补补&#xff0c;以及功能的增强阶段。终于&#xff0c;我们发布了 Shire 1.1 版本&#xff0c;这个版本带来了更强大的交互支持&#xff0c; 多功能升级 AI 与 IDE 的整合体验。 交互&#xff1a;丰富与大量 IDE 插件…

Springboot(四十九)SpringBoot3整合jetcache缓存

上文中我们学习了springboot中缓存的基本使用。缓存分为本地caffeine缓存和远程redis缓存。现在有一个小小的问题,我想使用本地caffeine缓存和远程redis缓存组成二级缓存。还想保证他们的一致性,这个事情该怎么办呢? Jetcache框架为我们解决了这个问题。 ‌JetCache‌是一个…

学习笔记052——Spring Boot 自定义 Starter

文章目录 Spring Boot 自定义 Starter1、自定义一个要装载的项目2、创建属性读取类 ServiceProperties3、创建 Service4、创建自动配置类 AutoConfigration5、创建 spring 工程文件6、将项目打成 jar 包7、jar 打包到本地仓库8、配置application.yml Spring Boot 自定义 Starte…

专业清洁艺术,还原生活本色——友嘉高效除菌洗碗机

生活中&#xff0c;每个人都渴望拥有一份洁净的生活环境。而家&#xff0c;作为我们最温馨的港湾&#xff0c;对洁净的追求更是无时无刻不在进行。每当饭后的欢声笑语过后&#xff0c;面对一堆沾满油渍、藏匿着细菌的餐具&#xff0c;我们不禁感到一丝烦忧。然而&#xff0c;有…

C++类与对象(二)

一、默认成员函数 class A{}; 像上面一样&#xff0c;一个什么都没有的类叫做空类&#xff0c;但是这个什么都没有并不是真正的什么都没有&#xff0c;只是我们看不见&#xff0c;空类里面其实是有6个默认成员函数的&#xff0c;当我们在类里面什么都不写的时候&#xff0c;编译…

PHP RabbitMQ连接超时问题

问题背景 Error: The connection timed out after 3 sec while awaiting incoming data 看到这个报错&#xff0c;我不以为意&#xff0c;认为是我设置的超时时间不够导致的&#xff0c;那就设置长一点 Error: The connection timed out after 300 sec while awaiting incom…

在21世纪的我用C语言探寻世界本质——字符函数和字符串函数(2)

人无完人&#xff0c;持之以恒&#xff0c;方能见真我&#xff01;&#xff01;&#xff01; 共同进步&#xff01;&#xff01; 文章目录 一、strncpy函数的使用二、strncat函数的使用三、strncmp函数的使用四、strstr的使用和模拟实现五、strtok函数的使用六、strerror和perr…

使用CertD全自动申请和部署SSL证书至服务器

1. Certd简介 Certd是一个开源的证书生命周期管理系统&#xff0c;专注于帮助开发者和组织更加便捷、安全地管理他们的数字证书。无论是在小型个人项目中还是大型企业环境中&#xff0c;Certd都能提供强大的功能&#xff0c;确保您的HTTPS服务始终处于安全状态。 1.1. 技术分…

uniapp实现加密Token并在每次请求前动态更新(vue、微信小程序、原生js也通用!)

导语&#xff1a;在Web开发中&#xff0c;Token作为一种身份验证的机制&#xff0c;被广泛应用于前后端交互过程中。本文将为大家介绍如何在每次请求前动态设置加密的Token&#xff0c;并在请求一次后使Token值加1&#xff08;或其他动态改变的逻辑&#xff09;&#xff0c;从而…

idea打jar包或引入包

一&#xff0c;通过Maven的方式打jar包 将相要打包的依赖写入到pom.xml文件中&#xff0c;如下所示&#xff1a; 然后使用打包命令&#xff1a; maven package 就能按照pom.xml中设置的打包了。 二&#xff0c;通过idea打包 前段时间遇到一个情况是使用Maven打包的时候src主程…

uniapp在App端引用echarts组件,解决无法渲染formatter问题

在App端option里面直接写上formatter&#xff0c;是无法执行方法的。 解决办法&#xff1a; 需要在echarts组件里面给options再重新赋值 效果图

pytest(二)excel数据驱动

一、excel数据驱动 excel文件内容 excel数据驱动使用方法 import openpyxl import pytestdef get_excel():excel_obj openpyxl.load_workbook("../pytest结合数据驱动-excel/data.xlsx")sheet_obj excel_obj["Sheet1"]values sheet_obj.valuescase_li…

ubuntu 和windows时区设置和时间修改

windows 时区设置 查看当前时区 tzutil /g 列出可选的时区&#xff0c;参考 时区列表备份 tzutil /l 设置时区 tzutil /s "China Standard Time" 修改日期和时间&#xff0c;直接输入date或者time修改 ubuntu 时区设置 timedatectl指令列表&#xff1a;list-timez…

卷积神经网络(CNN)的层次结构

卷积神经网络&#xff08;CNN&#xff09;是一种以其处理图像和视频数据的能力而闻名的深度学习模型&#xff0c;其基本结构通常包括以下几个层次&#xff0c;每个层次都有其特定的功能和作用&#xff1a; 1. 输入层&#xff08;Input Layer&#xff09;&#xff1a; 卷积神经网…