编程语言:C
目标平台:arm(hi3519)注:因为代码是纯C语言按道理可以移植到各种平台
基础库:librtsp(存在功能补全,有一定的bug)
因为项目需求需要在海思平台实现IPC的全部功能,实现RTSPServer的方案很多,冲浪了github平台发现有很多开源的方案,但是也有一些假开源比如EasyRtspServer(一个平台授权几万米^_^),有一些RtspServer是用C++写的,目前海思的交叉编译工具对C++高版本(大于C++11)支持并不好虽然可以编译过去但是运行的时候会有很多意向不到的问题,这也是本文为什么选用librtsp的原因。
因为以前接触过librtsp,并在海思平台实现了RtspServer(相对来讲还是比较稳定的),但项目需求需要新增鉴权的功能(使用用户名密码登录),翻遍github发现能支持鉴权的开源项目并不多而且多数移植存在兼容性的问题比如前面提到的问题,因此下定决心在librtsp的基础上新增用户名密码鉴权的功能,并修复以前就存在闪退的bug。
1、首先通读代码
主要的代码集中在了rtsp_demo.c和rtsp_msg.c里,demo里主要解决客户端连接和相关消息发送的功能,msg主要封装了协议解析的部分,因此本文的功能新增也都是在这两个文件中处理的。
2、熟悉鉴权协议和信息交互流程
新增鉴权的功能,首先需要了解RTSP协议鉴权的步骤《参见》。
3、抓包分析
3.1、找来海康的摄像头,使用Wireshark抓包工具对鉴权的过程进行分析,如下图
3.2、未通过验证客户端(VLC)的发送和服务器应答如下(海康摄像头应答)
VLC发送
服务器应答如下(海康摄像头)
其中红框内指明了密码加密的方式,目前有两种一种是Digest,MD5加解密,一种是Basic,base64加解密,因为base64相对好实现一些因此本文先采用Basic的验证方式(VLC播放器会自动匹配),当然服务器(相机)可以做两种鉴权方式都支持(接下来再去完善MD5加解密的功能)。
3.3、客户端回复如下(VLC播放器)
3.4、通过以上的抓包分析可知,只需要在librtsp内完善DESCRIBE请求中的验证和解密即可(因为本文先用BASIC即base64加解密)
4、修改代码
4.1、首先在rtsp client结构体内新增passed标志,表明该连接是否通过验证,未通过验证则不发送数据包,如下图
4.2、在rtsp_do_event函数修改发送的地方新增passed判断(数据发送地方很多自行查找,涉及到音频和视频的发送)
4.3、处理DESCRIBE请求
在rtsp_handle_DESCRIBE函数内,新增加解密的功能,如下
4.4、处理完DESCRIBE请求后还有socket的接收和发送的处理,完善authorization的相关处理函数
4.5、base64代码如下 《参考》
static int base64Decode(uint8_t *input)
{
unsigned int inlen = strlen(input);
uint8_t output[1024] = {0};
//解码需要一张反着的表
const char *base64_tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int off = 0;
unsigned int i;
uint8_t reverse_tbl[256] = {0};
if (NULL == input) {
return -1;
}
if (inlen == 0) {
return -2;
}
uint8_t *p = input;
while (isprint(*p) && *p != ' ') {
p++;
}
p++;
inlen = inlen - (p - input);
if (inlen % 4 != 0) {
/*error not a base64*/
dbprt("decode auth message is not a base64 message.");
return -1;
}
for (i = 0; i < 64; i++) {
reverse_tbl[base64_tbl[i]] = i;
}
for (i = 0; i < inlen - 4; i += 4) {
output[off++] = (((reverse_tbl[p[i]] << 2) | (reverse_tbl[p[i + 1]] >> 4)) & 0xFF);
output[off++] = (((reverse_tbl[p[i + 1]] << 4) | (reverse_tbl[p[i + 2]] >> 2)) & 0xFF);
output[off++] = (((reverse_tbl[p[i + 2]] << 6) | (reverse_tbl[p[i + 3]])) & 0xFF);
}
if (p[i + 2] == '=') {
output[off++] = (((reverse_tbl[p[i]] << 2) | (reverse_tbl[p[i + 1]] >> 4)) & 0xFF);
}
else if (p[i + 3] == '=') {
output[off++] = (((reverse_tbl[p[i]] << 2) | (reverse_tbl[p[i + 1]] >> 4)) & 0xFF);
output[off++] = (((reverse_tbl[p[i + 1]] << 4) | (reverse_tbl[p[i + 2]] >> 2)) & 0xFF);
}
else {
output[off++] = (((reverse_tbl[p[i]] << 2) | (reverse_tbl[p[i + 1]] >> 4)) & 0xFF);
output[off++] = (((reverse_tbl[p[i + 1]] << 4) | (reverse_tbl[p[i + 2]] >> 2)) & 0xFF);
output[off++] = (((reverse_tbl[p[i + 2]] << 6) | (reverse_tbl[p[i + 3]])) & 0xFF);
}
dbprt("decode auth message is : %s", output);
if (strstr(output, "admin:123456") == NULL) {
return -1;
}
return 0;
}