ESP32通过onvif获取的HTTP读海康摄像头截图,arduino代码实现digest认证
ESP32开发,所以不能用海康SDK,之前用QT开发时也不想用SDK,发现有这种通过HTTP直接获取最新截图的方式,直接一个QNetworkRequest,不要太简单。URL是这样的:http://user:password@192.168.1.64/onvif-http/snapshot?Profile_2
本来还用onvif去获取URL,后发现是固定的,也不用每次去获取XML了。
话不多说,贴代码:
String md5(String str)
{
MD5Builder md;
md.begin();
md.add(str);
md.calculate();
return md.toString();
}
String extractParam(String& authHeader, String param) {
int start = authHeader.indexOf(param + \"=\\\"\") + param.length() + 2;
int end = authHeader.indexOf(\"\\\"\", start);
return authHeader.substring(start, end);
}
int digestAuthRequest(HTTPClient &http) {
const char* keys[] = {\"WWW-Authenticate\"};
http.collectHeaders(keys, 1);
int httpCode = http.GET();
if(httpCode == HTTP_CODE_UNAUTHORIZED) {
String authHeader = http.header(\"WWW-Authenticate\");
// 解析realm, nonce等参数
String realm = extractParam(authHeader, \"realm\");
String nonce = extractParam(authHeader, \"nonce\");
//String opaque = extractParam(authHeader, \"opaque\");
// 计算响应值
String ha1 = md5(\"admin:\" + realm + \":password\");//我这里user是admin直接写死了
String ha2 = md5(\"GET:/onvif-http/snapshot?Profile_2\");
String response = md5(ha1 + \":\" + nonce + \":\" + ha2);
// 构造Authorization头部
String authHeaderValue = \"Digest username=\\\"admin\\\", realm=\\\"\" + realm +
\"\\\", nonce=\\\"\" + nonce + \"\\\", uri=\\\"/onvif-http/snapshot?Profile_2\\\", \" +
\"response=\\\"\" + response + \"\\\"\";
// 发送认证请求
http.addHeader(\"Authorization\", authHeaderValue);
httpCode = http.GET();
}
return httpCode;
}
注意点:网上有代码缺少collectHeaders过程,导致http.header(...)得不到内容!
以下是使用的代码:
#define USE_SERIAL Serial
//从http获取
HTTPClient http;
USE_SERIAL.print(\"[HTTP] begin...\\n\");
// configure traged server and url
//http.begin(\"http://user:password@192.168.1.64/onvif-http/snapshot?Profile_2\");//这种默认是basic认证,新海康摄像头用不了
http.begin(\"http://192.168.1.64/onvif-http/snapshot?Profile_2\");
USE_SERIAL.print(\"[HTTP] GET...\\n\");
// start connection and send HTTP header
//int httpCode = http.GET();//这是直接get,会得到401,要求认证
int httpCode = digestAuthRequest(http);//这里会判断401,则去digest认证
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
USE_SERIAL.printf(\"[HTTP] GET... code: %d,%d\\n\", httpCode,httpCode2);
// file found at server
if (httpCode == HTTP_CODE_OK) {
// create buffer for read
uint8_t buff[1024] = {0};
int len = http.getSize();
USE_SERIAL.println(len);
// get tcp stream
NetworkClient *stream = http.getStreamPtr();
// read all data from server
while (http.connected() && len > 0) {
if(http.connected() && len>0) {
// get available data size
size_t size = stream->available();
if (size) {
int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
// write it to tcp
tcp.write(buff, c);//我这里是转发给服务器,再保存为png文件
if (len > 0) {
len -= c;
}
}
delay(1);
}
}
}
}
使用的代码很平常,arduino的HTTPClient例子很多,我就替换了http.GET()这函数,在返回401的时候加上了认证。其实更应该完善HTTPClient这个库更合理,我找到头文件中有
void setAuthorization(const char *user, const char *password);
void setAuthorization(const char *auth);
void setAuthorizationType(const char *authType);
可惜setAuthorizationType这函数只是会把认证类型直接填上,后面的加密方式还是直接按base64,有这么段代码:
if (_base64Authorization.length()) {
_base64Authorization.replace(\"\\n\", \"\");
header += F(\"Authorization: \");
header += _authorizationType;
header += \" \";
header += _base64Authorization;
header += \"\\r\\n\";
}
不深究了,自己的需求是实现了。
附新摄像头配置:
1、配置->高级配置->集成协议中勾选 启用onvif
2、请你为onvif协议设置用户名和密码(admin,password,管理员),这是onvif协议认证时候需要的
3、关闭如下“开启非法登录锁定”设置(可选)