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

接触soap和xml第三天,稍有一点认识,看了很多文章,都不适合我的项目。目前项目中用到三台不同品牌的IPC,虽说厂家都用SDK,但在项目中引入三套SDK有点臃肿,何况目前已实现了使用VLC的拉流播放,就差云台控制了。只有一条路了就是使用ONVIF,C#中简单,C++都是使用gsoap,走了一遍流程,太麻烦,太复杂,使用WSDL文件生的代码好几兆不说,需要引用的源文件一大堆!现在就是要简单的实现云台控制PTZ,没必要搞这么复杂,研究了一天,不按寻常路也是挺香!

一、获取PTZ控制XML消息

需要的工具:WireShark、XMLspy、ONVIF Device Test Tool。

具体思路:控制PTZ使用的是soap协议,soap协议可以通过微软的Soap ToolKit3.0来实现,现在就差具体的控制协议了,也就是XML消息,获取XML消息的流程:

1、使用ONVIF Device Test Tool连接IPC

首先用ONVIF Device Test Tool连接IPC,这一块文章很多,就不多说了,然后进行云台控制,测试没有问题后进行下一步。

SOAP学习之二:全网最简单的ONVIF协议IPC云台PTZ控制VC++代码--不使用gsoap!!_第1张图片

 

2、使用WireShark抓包查看XML

第一步实现后就可以使用WireShark抓包,启动本地连接的捕获,然后用ONVIF Device Test Tool控制PTZ,根据IPC的IP地址查看HTTP包,得到控制连续移动的XML如下:

SOAP学习之二:全网最简单的ONVIF协议IPC云台PTZ控制VC++代码--不使用gsoap!!_第2张图片

 拷贝出XML后的消息如下:

   
    
        
            
                
                    
                        admin
                        
                    
                        6UOJDvX8SFsITkYmBA/wltXMC2A=
                        
                    
                        i+bhTDOL/vrj6s6Tox5TxQ==
                        
                    
                        2022-11-13T06:36:19Z
                        
                    
                
            
        
            
                
                    Profile_1
                    
                
                    
                    
                
            
        

3、使用XMLspy测试xml消息(本步骤为可选项)

使用XMLspy,在 SOAP Debugger中SOAP菜单中建立一个新的Request,可以随便用一个WSDL文件建立,然后使用SOAP菜单下的Change SOAP Request Parameters修改Connection Endpoint为IPC的PTZ控制地址就好,这个地址在第一步PTZ URL中可以看到。然后使用Send Request to Server测试,IPC正常工作,这个xml消息就OK了。

SOAP学习之二:全网最简单的ONVIF协议IPC云台PTZ控制VC++代码--不使用gsoap!!_第3张图片

 其实,如果对1、2步骤操作没问题的话,XML消息也不需要测试,也就是说即使不适用XMLspy也不影响结果。

同样的方法可获得控制PTZ的三个参数的控制XML消息。

如果感觉上述步骤麻烦,也可以使用gsoap来生产XML消息文件,实际上就是仅仅使用gsoap的wsdl2h和soapccp2这两个可执行文件,具体操作文章很多,我参考的是流浪的Coder博客园的文章使用gSOAP工具生成onvif框架代码,这篇写的比较细,有一个问题就是我使用下载的PTZ.wsdl文件生成.h文件出错,使用在线的可以正常生成,生成C代码后,在目录中会有很多xml文件,找到相应的文件就可以了。

SOAP学习之二:全网最简单的ONVIF协议IPC云台PTZ控制VC++代码--不使用gsoap!!_第4张图片

 二、VC++代码实现

建立一个控制台VC++程序,引入Soap ToolKit3.0,具体操作见我上一篇文章:SOAP学习之一:Visual C++创建简单的客户端--使用soap toolkit 3.0获取UTC服务器时间。

这里面主要有两个问题,费了不少周折:

1、使用WriteXml方法

大多数介绍Soap ToolKit的文章都是使用的StartElement方法创建消息元素,但这做只能限定到固定的格式,根据PTZ的控制协议,无法使用该方法创建消息,找了好久,最后还是看了soap toolkit 3.0自带的帮助文件,看到了WirteXml方法,这下好了,可以为所欲为了:

SOAP学习之二:全网最简单的ONVIF协议IPC云台PTZ控制VC++代码--不使用gsoap!!_第5张图片

 2、鉴权问题

常规的鉴权是通过HTTP连接时鉴权,看到soap toolkit 3.0的HttpConnector30有两个属性:AuthUser和AuthPassword,想着这下没有问题了,但是测试中出现未鉴权的错误,搜来搜去,还是看了帮助文件,发现鉴权方案属性:

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

 默认的不是摘要认证( Digest authentication),添加该属性赋值0x08搞定!

还有一种方法是通过XML消息里的 Header鉴权,这里面涉及密文传输,懒得去研究,直接抓包后把相应消息放入Header中也能实现,这种方法较笨!

解决了以上连个问题,剩下的就简单了,全部代码如下,实现了海康IPC的PTZ控制(PTZ通过IPC的485输出到云台),其中,两种鉴权方式都有使用,实现了连续移动和停止,在控制台窗口输入m移动,输入s停止,输入a是使用Header鉴权的停止,代码尽量使用了标准 XML元素的创建,只有实在不行才使用WriteXml方法:

#include "stdafx.h"
#import "msxml4.dll"
//using namespace MSXML2; 
#import "C:\Program Files (x86)\Common Files\MSSoap\Binaries\mssoap30.dll" \
                  exclude("IStream", "IErrorInfo", "ISequentialStream", "_LARGE_INTEGER", \
                              "_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib30;

void Move(void)    
 {  
    ISoapSerializerPtr   Serializer; 
    ISoapConnectorPtr   Connector; 
 //   Connect  to   the   service. 
    if(FAILED(Connector.CreateInstance(__uuidof(HttpConnector30))))    //创建对象
    {
        wprintf(_T("failed"));
    }

  // Connect to the service
  Connector->Property["EndPointURL"] ="http://192.168.0.64:/onvif/PTZ";
  Connector->Connect();
  // Begin message
  Connector->Property["SoapAction"] = "";
  Connector->Property["AuthUser"] = "admin";
  Connector->Property["AuthPassword"] = "abc12345";
  Connector->Property["WinHTTPAuthScheme"] = 0x08;
  Connector->BeginMessage();
  // Create the SoapSerializer
  Serializer.CreateInstance(__uuidof(SoapSerializer30));
  // Connect the serializer to the input stream of the connector
  Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
  // Build the SOAP Message
  Serializer->StartEnvelope("","","");
  Serializer->StartBody("");
  Serializer->StartElement("ContinuousMove","","","tptz");
  Serializer->StartElement("ProfileToken","","","tptz");
  Serializer->WriteString("Profile_1");
  Serializer->EndElement(); 
  Serializer->StartElement("Velocity","","","tptz");
  Serializer->WriteXml(""); //X轴移动,参数为移动速度,其他轴参考这个修改
  Serializer->WriteXml("");
  Serializer->WriteXml("");
  Serializer->WriteXml("");
  Serializer->EndElement();
  Serializer->WriteXml("0");
  Serializer->EndElement();
  Serializer->EndBody();
  Serializer->EndEnvelope();
  // Send the message to the web service
  Connector->EndMessage();
}

void StopA(void)    
 
{  

    ISoapSerializerPtr   Serializer; 
    ISoapConnectorPtr   Connector; 
    if(FAILED(Connector.CreateInstance(__uuidof(HttpConnector30))))    //创建对象
    {
         wprintf(_T("failed"));
     }

  // Connect to the service
  Connector->Property["EndPointURL"] ="http://192.168.0.64:/onvif/PTZ";
  Connector->Connect();
  // Begin message
  Connector->Property["SoapAction"] = "";
  Connector->BeginMessage();
  // Create the SoapSerializer
  Serializer.CreateInstance(__uuidof(SoapSerializer30));
  // Connect the serializer to the input stream of the connector
  Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));

  // Build the SOAP Message
  Serializer->StartEnvelope("","","");
  Serializer->StartHeader("");
  Serializer->WriteXml("");
  Serializer->WriteXml("admin");
  Serializer->WriteXml("jBnekXflgIuMFm4EPFJpKZ38Mvk=");
  Serializer->WriteXml("L5gyUZm2OWJmkX+MRNWDOA==2022-11-13T02:54:41Z");
  Serializer->WriteXml("");
  Serializer->EndHeader();
  Serializer->StartBody("");
  Serializer->StartElement("Stop","","","tptz");
  Serializer->StartElement("ProfileToken","","","tptz");
  Serializer->WriteString("Profile_1");
  Serializer->EndElement(); 
  Serializer->StartElement("Velocity","","","tptz");
  Serializer->WriteString("true");
  Serializer->EndElement();
  Serializer->StartElement("Timeout","","","tptz");
  Serializer->WriteString("0");
  Serializer->EndElement();
  Serializer->EndElement();
  Serializer->EndBody();
  Serializer->EndEnvelope();
  // Send the message to the web service
  Connector->EndMessage();
}

void Stop(void)    
 
{  

    ISoapSerializerPtr   Serializer; 
    ISoapConnectorPtr   Connector; 
    if(FAILED(Connector.CreateInstance(__uuidof(HttpConnector30))))    //创建对象
    {
        wprintf(_T("failed"));
    }

  // Connect to the service
  Connector->Property["EndPointURL"] ="http://192.168.0.64:/onvif/PTZ";
  Connector->Connect();
  // Begin message
  Connector->Property["SoapAction"] = "";
  Connector->Property["AuthUser"] = "admin";
  Connector->Property["AuthPassword"] = "abc12345";
  Connector->Property["WinHTTPAuthScheme"] = 0x08;
  Connector->BeginMessage();
  // Create the SoapSerializer
  Serializer.CreateInstance(__uuidof(SoapSerializer30));
  // Connect the serializer to the input stream of the connector
  Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
  // Build the SOAP Message
  Serializer->StartEnvelope("","","");
  Serializer->StartBody("");
  Serializer->StartElement("Stop","","","tptz");
  Serializer->StartElement("ProfileToken","","","tptz");
  Serializer->WriteString("Profile_1");
  Serializer->EndElement(); 
  Serializer->StartElement("Velocity","","","tptz");
  Serializer->WriteString("true");
  Serializer->EndElement();
  Serializer->StartElement("Timeout","","","tptz");
  Serializer->WriteString("0");
  Serializer->EndElement();
  Serializer->EndElement();
  Serializer->EndBody();
  Serializer->EndEnvelope();
  // Send the message to the web service
  Connector->EndMessage();
}

int _tmain(int argc, _TCHAR* argv[])
{
	char str;
	CoInitialize(NULL);  
	while(1)
	{
		str=getchar();
		if(str=='m')
		{
			Move();
			printf("X Axis Continous Move\n");
		}
		else if(str=='s')
		{
			Stop();
			printf("Stop by Header Authentication\n");
		}
		else if(str=='a')
		{
			StopA();
			printf("Stop by HttpConnect Authentication\n");
		}
		else if(str=='q')
			break;
	}
    CoUninitialize();  
	return 0;
}

代码目前没有返回数据,以后完善,不影响控制,发送命令后可以看到IPC返回OK消息:

SOAP学习之二:全网最简单的ONVIF协议IPC云台PTZ控制VC++代码--不使用gsoap!!_第7张图片

 三、总结

程序实现了单独的PTZ控制,代码量非常少,个人认为是目前我能看到的唯一使用 soap toolkit实现的PTZ控制代码。

另外搜索资料时个人心得首先 使用非某度搜索首推某歌,不行的话使用必应,这样少看广告,效率高,特别是搜外文资料。其次搜索顺序先国内不行外文,搜索结果两页之内找不到有价值的,要么关键词有问题,要么不要再搜了,自己研究吧!!大多数文章就是抄来抄去,看的实在太累!!

你可能感兴趣的:(VC++编程技巧,SOAP,PTZ,学习,c++,实时音视频)