因为工作上的原因,需要接入IPC摄像头,实现监控功能,因而开始了对于IPC摄像头的学习之路。因为要做到通用,所以目光直接锁定了onvif协议,因为这是一个通用的协议,各大摄像头厂家都适配于这个协议。在网上搜索后,发现有关onvif协议的实现大部分博主都是通过gsoap这个工具来实现的,但经过查看后,发现gsoap的框架在编译后生成的可执行文件动不动就是10几M,而我工作所用到的板子flash才16M,实际可用的也就12 13M,因而走这条路肯定是行不通了,于是开始研究其他方向,最终找到可以利用http协议实现onvif协议,因为onvif协议在与以IPC摄像头为服务器,自身终端为客户端,在两者进行通信时,使用的是http协议,只不过其命令比较复杂,不像gsoap工具已经集成好了,但为了节省内存空间,只能使用这种方式来实现一些控制。
因为我实现监控功能的前提是能获得该IPC摄像头的IP地址,因而设备查找这一块就省略了(其实是比较简单的)。首先我们基于已经知道的IPC摄像头的ip地址来实现http连接,以下是连接时的关键函数:
- int SendHttpCmdAndReceiveMessage(char*IpAdress,char*sndBuf,int sndLen,char*recvBuf,int recvLen)
- {
- int sock;
- struct sockaddr_in remote;
- if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0){
- perror("Can't create TCP socket!\n");
- }
- bzero(&remote,sizeof(remote));
- remote.sin_family = AF_INET;
- if(inet_aton(IpAdress,&remote.sin_addr) == 0) //服务器的IP地址来自程序的参数
- {
- printf("Server IP Address Error!\n");
- return 0;
- }
- remote.sin_port = htons(80);
- int addr_len = sizeof(remote);
- //向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
- if(connect(sock,(struct sockaddr*)&remote, addr_len) < 0)
- {
- printf("Can Not Connect To %s!\n",IpAdress);
- return 0;
- }
- send(sock,sndBuf,sndLen,0);
- usleep(500);
- recvLen = recv(sock,recvBuf,recvLen,0);
- close(sock);
- return 1;
- }
该函数就实现了与IPC的http连接,IP地址是摄像头的地址,端口默认是80,并且该函数是发送与接收同步的。有这个基础,我们就能通过这个函数,发出一些指令,从而控制摄像头。
在这里,我们需要获取摄像头的一些参数,因为我们最终是要将视频播放出来,因而我们要知道摄像头的分辨率,帧率等信息,下面是发送获取编码命令的相关代码片段:
- /*获取设备编码*/
- int ONVIF_IPC_GetVideoEnc_API()
- {
- int DataStrlen = 0;
- int ReturnData = 0;
- char cfgStr[2048] = { 0 };
- //char * rcvData = (char *)malloc(RCV_BUFF_SIZE);
- char rcvData[20480];
- assert(rcvData);
-
- DataStrlen = sprintf(cfgStr,"%s","POST /onvif/Media HTTP/1.1\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host: ",gHttpMessage.IPAdress,"\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s", "Content-Length: ",289,"\r\n\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "
\r\n"); -
- SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
- ReturnData = GetRealm(rcvData);
- GetNonce(rcvData);
- ProduceResponceHttp();
- DataStrlen = 0;
- DataStrlen = sprintf(cfgStr,"%s", "POST /onvif/Media HTTP/1.1\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host:",gHttpMessage.IPAdress,"\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\",","Authorization: Digest username=","admin");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," realm=",gHttpMessage.realm);
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," qop=","auth");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," algorithm=","MD5");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," uri=","/onvif/Media");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," nonce=",gHttpMessage.nonce);
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s"," nc=","00000001",",");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," cnonce=","9C6FD4D2CE6E99424E1DDD37312C0A24");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"\r\n"," response=",gHttpMessage.responce);
-
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","Content-Length: ",289,"\r\n\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "
\r\n"); -
- SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
-
- printf("The rcv data =%s\r\n",rcvData);
- GetResolution(rcvData);
- GetToken(rcvData);
-
-
- GetVideoCoderName(rcvData);
- GetTtlAndUserConutAndPort(rcvData);
- GetH264ProfileAndSessionTimeout(rcvData);
- GetVideoCoderToken(rcvData);
-
- // if(rcvData != NULL)
- // {
- // free(rcvData);
- // }
- printf("This is test 2\r\n");
- return 0;
- }
在这里就可以看出,用http协议的不方便了,因为我们需要发送非常多的东西,而且摄像头需要鉴权,也就等同于我们在网页上登录。因为用的是摘要认证方式,所以同样的命令我们要发送两次,第二次才能获得摄像头真正的设备信息。并且我们得到的消息也是一堆复杂的xml文本内容,还需要在这里面去获得我们想要的东西。
- HTTP/1.1 200 OK
- Date: Thu, 28 Jul 2022 17:54:42 GMT
- Server: webserver
- X-Frame-Options: SAMEORIGIN
- Content-Length: 3319
- Connection: close
- Content-Type: application/soap+xml; charset=utf-8
-
- <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:soapenc="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tst="http://www.onvif.org/ver10/storage/wsdl" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:dn="http://www.onvif.org/ver10/network/wsdl" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl" xmlns:wsoap12="http://schemas.xmlsoap.org/wsdl/soap12" xmlns:http="http://schemas.xmlsoap.org/wsdl/http" xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:wsadis="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:wsrf-bf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:wsntw="http://docs.oasis-open.org/wsn/bw-2" xmlns:wsrf-rw="http://docs.oasis-open.org/wsrf/rw-2" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsrf-r="http://docs.oasis-open.org/wsrf/r-2" xmlns:trc="http://www.onvif.org/ver10/recording/wsdl" xmlns:tse="http://www.onvif.org/ver10/search/wsdl" xmlns:trp="http://www.onvif.org/ver10/replay/wsdl" xmlns:tnshik="http://www.hikvision.com/2011/event/topics" xmlns:hikwsd="http://www.onvifext.com/onvif/ext/ver10/wsdl" xmlns:hikxsd="http://www.onvifext.com/onvif/ext/ver10/schema" xmlns:tas="http://www.onvif.org/ver10/advancedsecurity/wsdl"><env:Body><trt:GetVideoEncoderConfigurationOptionsResponse><trt:Options><tt:QualityRange><tt:Min>0tt:Min>
- <tt:Max>5tt:Max>
- tt:QualityRange>
- <tt:H264><tt:ResolutionsAvailable><tt:Width>1280tt:Width>
- <tt:Height>720tt:Height>
- tt:ResolutionsAvailable>
- <tt:GovLengthRange><tt:Min>1tt:Min>
- <tt:Max>400tt:Max>
- tt:GovLengthRange>
- <tt:FrameRateRange><tt:Min>1tt:Min>
- <tt:Max>25tt:Max>
- tt:FrameRateRange>
- <tt:EncodingIntervalRange><tt:Min>1tt:Min>
- <tt:Max>1tt:Max>
- tt:EncodingIntervalRange>
- <tt:H264ProfilesSupported>Baselinett:H264ProfilesSupported>
- <tt:H264ProfilesSupported>Maintt:H264ProfilesSupported>
- tt:H264>
- <tt:Extension><tt:H264><tt:ResolutionsAvailable><tt:Width>1280tt:Width>
- <tt:Height>720tt:Height>
- tt:ResolutionsAvailable>
- <tt:GovLengthRange><tt:Min>1tt:Min>
- <tt:Max>400tt:Max>
- tt:GovLengthRange>
- <tt:FrameRateRange><tt:Min>1tt:Min>
- <tt:Max>25tt:Max>
- tt:FrameRateRange>
- <tt:EncodingIntervalRange><tt:Min>1tt:Min>
- <tt:Max>1tt:Max>
- tt:EncodingIntervalRange>
- <tt:H264ProfilesSupported>Baselinett:H264ProfilesSupported>
- <tt:H264ProfilesSupported>Maintt:H264ProfilesSupported>
- <tt:BitrateRange><tt:Min>32tt:Min>
- <tt:Max>2048tt:Max>
- tt:BitrateRange>
- tt:H264>
- tt:Extension>
- trt:Options>
- trt:GetVideoEncoderConfigurationOptionsResponse>
- env:Body>
- env:Envelope>
上面就是我们得到的编码信息,通过字符串相关操作函数我们可以得到该摄像头的编码方式为h264,分辨率为1280x720,帧率为25帧。
当我们能获得摄像头相关信息后,并不意味之就能实现显示了,有时候受解码器的限制,我们还需要去修改摄像头的参数,这样才能满足我们某个时刻的需求:
- void ONVIF_IPC_SetVideoEnc_API()
- {
- int DataStrlen = 0;
- char cfgStr[2048] = { 0 };
- char * rcvData = (char *)malloc(RCV_BUFF_SIZE);
-
- DataStrlen = 0;
- DataStrlen = sprintf(cfgStr,"%s","POST /onvif/Media HTTP/1.1\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host: ",gHttpMessage.IPAdress,"\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s", "Content-Length: ",1180,"\r\n\r\n");
-
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"%s","
">\r\n" ); -
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","
" ,gHttpMessage.VideoEncoderName,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","
" ,gHttpMessage.UserConut,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
H264 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
1280 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
720 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
2 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
20 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
1 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
1024 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
30 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","
" ,gHttpMessage.H264Profile,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
IPv4 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
0.0.0.0 \r\n");; - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","
" ,gHttpMessage.port,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","
" ,gHttpMessage.TTL,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
false \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","
" ,gHttpMessage.SessionTimeout,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
true \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
-
-
- //printf("The digest send data =%s\r\n",cfgStr);
- SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
- //printf("The digest recv data =%s\r\n",rcvData);
-
- GetRealm(rcvData);
- GetNonce(rcvData);
- ProduceResponceHttp();
-
- DataStrlen = 0;
- DataStrlen = sprintf(cfgStr,"%s", "POST /onvif/Media HTTP/1.1\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host:",gHttpMessage.IPAdress,"\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\",","Authorization: Digest username=","admin");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," realm=",gHttpMessage.realm);
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," qop=","auth");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," algorithm=","MD5");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," uri=","/onvif/Media");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," nonce=",gHttpMessage.nonce);
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s"," nc=","00000001",",");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," cnonce=","9C6FD4D2CE6E99424E1DDD37312C0A24");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"\r\n"," response=",gHttpMessage.responce);
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s", "Content-Length: ",1180,"\r\n\r\n");
- printf("len1=%d\r\n",DataStrlen);
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"%s","
">\r\n" ); -
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","
" ,gHttpMessage.VideoEncoderName,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","
" ,gHttpMessage.UserConut,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
H264 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
1280 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
720 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
2 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
15 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
1 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
1024 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
30 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","
" ,gHttpMessage.H264Profile,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
\r\n" ); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
IPv4 \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
0.0.0.0 \r\n");; - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","
" ,gHttpMessage.port,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","
" ,gHttpMessage.TTL,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
false \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","
" ,gHttpMessage.SessionTimeout,"\r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","
true \r\n"); - DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
- DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
-
- //printf("len2=%d\r\n",DataStrlen);
- //printf("The digest send data =%s\r\n",cfgStr);
- SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
- //printf("The recv data =%s\r\n",rcvData);
-
- free(rcvData);
- }
通过该函数我们能设置摄像头的参数。
这期就写到这里了,后面再更新如何通过rtsp接收IPC摄像头的视频数据,将其写入文件,或者播放到屏幕上。