• 以http协议实现onvif协议并完成对IPC摄像头的监控



    前言

    因为工作上的原因,需要接入IPC摄像头,实现监控功能,因而开始了对于IPC摄像头的学习之路。因为要做到通用,所以目光直接锁定了onvif协议,因为这是一个通用的协议,各大摄像头厂家都适配于这个协议。在网上搜索后,发现有关onvif协议的实现大部分博主都是通过gsoap这个工具来实现的,但经过查看后,发现gsoap的框架在编译后生成的可执行文件动不动就是10几M,而我工作所用到的板子flash才16M,实际可用的也就12 13M,因而走这条路肯定是行不通了,于是开始研究其他方向,最终找到可以利用http协议实现onvif协议,因为onvif协议在与以IPC摄像头为服务器,自身终端为客户端,在两者进行通信时,使用的是http协议,只不过其命令比较复杂,不像gsoap工具已经集成好了,但为了节省内存空间,只能使用这种方式来实现一些控制。

    1实现http连接

            因为我实现监控功能的前提是能获得该IPC摄像头的IP地址,因而设备查找这一块就省略了(其实是比较简单的)。首先我们基于已经知道的IPC摄像头的ip地址来实现http连接,以下是连接时的关键函数:

          

    1. int SendHttpCmdAndReceiveMessage(char*IpAdress,char*sndBuf,int sndLen,char*recvBuf,int recvLen)
    2. {
    3. int sock;
    4. struct sockaddr_in remote;
    5. if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0){
    6. perror("Can't create TCP socket!\n");
    7. }
    8. bzero(&remote,sizeof(remote));
    9. remote.sin_family = AF_INET;
    10. if(inet_aton(IpAdress,&remote.sin_addr) == 0) //服务器的IP地址来自程序的参数
    11. {
    12. printf("Server IP Address Error!\n");
    13. return 0;
    14. }
    15. remote.sin_port = htons(80);
    16. int addr_len = sizeof(remote);
    17. //向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
    18. if(connect(sock,(struct sockaddr*)&remote, addr_len) < 0)
    19. {
    20. printf("Can Not Connect To %s!\n",IpAdress);
    21. return 0;
    22. }
    23. send(sock,sndBuf,sndLen,0);
    24. usleep(500);
    25. recvLen = recv(sock,recvBuf,recvLen,0);
    26. close(sock);
    27. return 1;
    28. }

      该函数就实现了与IPC的http连接,IP地址是摄像头的地址,端口默认是80,并且该函数是发送与接收同步的。有这个基础,我们就能通过这个函数,发出一些指令,从而控制摄像头。

    2 获取设备编码参数

            在这里,我们需要获取摄像头的一些参数,因为我们最终是要将视频播放出来,因而我们要知道摄像头的分辨率,帧率等信息,下面是发送获取编码命令的相关代码片段:

    1. /*获取设备编码*/
    2. int ONVIF_IPC_GetVideoEnc_API()
    3. {
    4. int DataStrlen = 0;
    5. int ReturnData = 0;
    6. char cfgStr[2048] = { 0 };
    7. //char * rcvData = (char *)malloc(RCV_BUFF_SIZE);
    8. char rcvData[20480];
    9. assert(rcvData);
    10. DataStrlen = sprintf(cfgStr,"%s","POST /onvif/Media HTTP/1.1\r\n");
    11. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host: ",gHttpMessage.IPAdress,"\r\n");
    12. DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
    13. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s", "Content-Length: ",289,"\r\n\r\n");
    14. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","");
    15. DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "\r\n");
    16. SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
    17. ReturnData = GetRealm(rcvData);
    18. GetNonce(rcvData);
    19. ProduceResponceHttp();
    20. DataStrlen = 0;
    21. DataStrlen = sprintf(cfgStr,"%s", "POST /onvif/Media HTTP/1.1\r\n");
    22. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host:",gHttpMessage.IPAdress,"\r\n");
    23. DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
    24. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\",","Authorization: Digest username=","admin");
    25. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," realm=",gHttpMessage.realm);
    26. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," qop=","auth");
    27. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," algorithm=","MD5");
    28. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," uri=","/onvif/Media");
    29. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," nonce=",gHttpMessage.nonce);
    30. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s"," nc=","00000001",",");
    31. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," cnonce=","9C6FD4D2CE6E99424E1DDD37312C0A24");
    32. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"\r\n"," response=",gHttpMessage.responce);
    33. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","Content-Length: ",289,"\r\n\r\n");
    34. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","");
    35. DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "\r\n");
    36. SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
    37. printf("The rcv data =%s\r\n",rcvData);
    38. GetResolution(rcvData);
    39. GetToken(rcvData);
    40. GetVideoCoderName(rcvData);
    41. GetTtlAndUserConutAndPort(rcvData);
    42. GetH264ProfileAndSessionTimeout(rcvData);
    43. GetVideoCoderToken(rcvData);
    44. // if(rcvData != NULL)
    45. // {
    46. // free(rcvData);
    47. // }
    48. printf("This is test 2\r\n");
    49. return 0;
    50. }

    在这里就可以看出,用http协议的不方便了,因为我们需要发送非常多的东西,而且摄像头需要鉴权,也就等同于我们在网页上登录。因为用的是摘要认证方式,所以同样的命令我们要发送两次,第二次才能获得摄像头真正的设备信息。并且我们得到的消息也是一堆复杂的xml文本内容,还需要在这里面去获得我们想要的东西。

    1. HTTP/1.1 200 OK
    2. Date: Thu, 28 Jul 2022 17:54:42 GMT
    3. Server: webserver
    4. X-Frame-Options: SAMEORIGIN
    5. Content-Length: 3319
    6. Connection: close
    7. Content-Type: application/soap+xml; charset=utf-8
    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>
    9. <tt:Max>5tt:Max>
    10. tt:QualityRange>
    11. <tt:H264><tt:ResolutionsAvailable><tt:Width>1280tt:Width>
    12. <tt:Height>720tt:Height>
    13. tt:ResolutionsAvailable>
    14. <tt:GovLengthRange><tt:Min>1tt:Min>
    15. <tt:Max>400tt:Max>
    16. tt:GovLengthRange>
    17. <tt:FrameRateRange><tt:Min>1tt:Min>
    18. <tt:Max>25tt:Max>
    19. tt:FrameRateRange>
    20. <tt:EncodingIntervalRange><tt:Min>1tt:Min>
    21. <tt:Max>1tt:Max>
    22. tt:EncodingIntervalRange>
    23. <tt:H264ProfilesSupported>Baselinett:H264ProfilesSupported>
    24. <tt:H264ProfilesSupported>Maintt:H264ProfilesSupported>
    25. tt:H264>
    26. <tt:Extension><tt:H264><tt:ResolutionsAvailable><tt:Width>1280tt:Width>
    27. <tt:Height>720tt:Height>
    28. tt:ResolutionsAvailable>
    29. <tt:GovLengthRange><tt:Min>1tt:Min>
    30. <tt:Max>400tt:Max>
    31. tt:GovLengthRange>
    32. <tt:FrameRateRange><tt:Min>1tt:Min>
    33. <tt:Max>25tt:Max>
    34. tt:FrameRateRange>
    35. <tt:EncodingIntervalRange><tt:Min>1tt:Min>
    36. <tt:Max>1tt:Max>
    37. tt:EncodingIntervalRange>
    38. <tt:H264ProfilesSupported>Baselinett:H264ProfilesSupported>
    39. <tt:H264ProfilesSupported>Maintt:H264ProfilesSupported>
    40. <tt:BitrateRange><tt:Min>32tt:Min>
    41. <tt:Max>2048tt:Max>
    42. tt:BitrateRange>
    43. tt:H264>
    44. tt:Extension>
    45. trt:Options>
    46. trt:GetVideoEncoderConfigurationOptionsResponse>
    47. env:Body>
    48. env:Envelope>

    上面就是我们得到的编码信息,通过字符串相关操作函数我们可以得到该摄像头的编码方式为h264,分辨率为1280x720,帧率为25帧。

    3.设置摄像头相关参数

    当我们能获得摄像头相关信息后,并不意味之就能实现显示了,有时候受解码器的限制,我们还需要去修改摄像头的参数,这样才能满足我们某个时刻的需求:

    1. void ONVIF_IPC_SetVideoEnc_API()
    2. {
    3. int DataStrlen = 0;
    4. char cfgStr[2048] = { 0 };
    5. char * rcvData = (char *)malloc(RCV_BUFF_SIZE);
    6. DataStrlen = 0;
    7. DataStrlen = sprintf(cfgStr,"%s","POST /onvif/Media HTTP/1.1\r\n");
    8. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host: ",gHttpMessage.IPAdress,"\r\n");
    9. DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
    10. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s", "Content-Length: ",1180,"\r\n\r\n");
    11. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    12. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    13. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    14. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    15. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"%s","">\r\n");
    16. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","",gHttpMessage.VideoEncoderName,"\r\n");
    17. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","",gHttpMessage.UserConut,"\r\n");
    18. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","H264\r\n");
    19. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    20. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","1280\r\n");
    21. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","720\r\n");
    22. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    23. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","2\r\n");
    24. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    25. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","20\r\n");
    26. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","1\r\n");
    27. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","1024\r\n");
    28. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    29. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    30. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","30\r\n");
    31. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","",gHttpMessage.H264Profile,"\r\n");
    32. DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
    33. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    34. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    35. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","IPv4\r\n");
    36. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","0.0.0.0\r\n");;
    37. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    38. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","",gHttpMessage.port,"\r\n");
    39. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","",gHttpMessage.TTL,"\r\n");
    40. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","false\r\n");
    41. DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
    42. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","",gHttpMessage.SessionTimeout,"\r\n");
    43. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    44. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","true\r\n");
    45. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    46. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    47. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    48. //printf("The digest send data =%s\r\n",cfgStr);
    49. SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
    50. //printf("The digest recv data =%s\r\n",rcvData);
    51. GetRealm(rcvData);
    52. GetNonce(rcvData);
    53. ProduceResponceHttp();
    54. DataStrlen = 0;
    55. DataStrlen = sprintf(cfgStr,"%s", "POST /onvif/Media HTTP/1.1\r\n");
    56. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s", "Host:",gHttpMessage.IPAdress,"\r\n");
    57. DataStrlen += sprintf(cfgStr+DataStrlen,"%s", "Content-Type: application/soap+xml; charset=utf-8\r\n");
    58. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\",","Authorization: Digest username=","admin");
    59. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," realm=",gHttpMessage.realm);
    60. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," qop=","auth");
    61. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," algorithm=","MD5");
    62. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," uri=","/onvif/Media");
    63. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," nonce=",gHttpMessage.nonce);
    64. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s"," nc=","00000001",",");
    65. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\","," cnonce=","9C6FD4D2CE6E99424E1DDD37312C0A24");
    66. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"\r\n"," response=",gHttpMessage.responce);
    67. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s", "Content-Length: ",1180,"\r\n\r\n");
    68. printf("len1=%d\r\n",DataStrlen);
    69. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    70. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    71. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    72. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    73. DataStrlen += sprintf(cfgStr+DataStrlen,"%s\"%s\"%s","">\r\n");
    74. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","",gHttpMessage.VideoEncoderName,"\r\n");
    75. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","",gHttpMessage.UserConut,"\r\n");
    76. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","H264\r\n");
    77. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    78. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","1280\r\n");
    79. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","720\r\n");
    80. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    81. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","2\r\n");
    82. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    83. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","15\r\n");
    84. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","1\r\n");
    85. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","1024\r\n");
    86. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    87. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    88. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","30\r\n");
    89. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","",gHttpMessage.H264Profile,"\r\n");
    90. DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
    91. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    92. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    93. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","IPv4\r\n");
    94. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","0.0.0.0\r\n");;
    95. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    96. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","",gHttpMessage.port,"\r\n");
    97. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%d%s","",gHttpMessage.TTL,"\r\n");
    98. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","false\r\n");
    99. DataStrlen += sprintf(cfgStr+DataStrlen,"%s"," \r\n");
    100. DataStrlen += sprintf(cfgStr+DataStrlen,"%s%s%s","",gHttpMessage.SessionTimeout,"\r\n");
    101. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    102. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","true\r\n");
    103. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    104. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    105. DataStrlen += sprintf(cfgStr+DataStrlen,"%s","\r\n");
    106. //printf("len2=%d\r\n",DataStrlen);
    107. //printf("The digest send data =%s\r\n",cfgStr);
    108. SendHttpCmdAndReceiveMessage(gHttpMessage.IPAdress,cfgStr,strlen(cfgStr),rcvData,RCV_BUFF_SIZE);
    109. //printf("The recv data =%s\r\n",rcvData);
    110. free(rcvData);
    111. }

    通过该函数我们能设置摄像头的参数。


    总结

    这期就写到这里了,后面再更新如何通过rtsp接收IPC摄像头的视频数据,将其写入文件,或者播放到屏幕上。


  • 相关阅读:
    ChatGPT 之 PPT 大师
    SpringBoot-19-模块开发-员工修改/删除
    ram和rom的区别
    Python爬虫爬取某会计师协会网站的指定文章(文末送书)
    手把手教你使用云产品实现抖音同款微信早安推送(腾讯云Python版本)
    黑五电子邮件营销最全指南!
    Shiro-SpringBoot (二)
    基于MATLAB的函数拟合
    day57 集合 List Set Map
    lxm not found:Mac Python 安装 lxml包
  • 原文地址:https://blog.csdn.net/weixin_48264057/article/details/126020320