谷歌无插件实时预览海康摄像头

时间:2024-03-05 08:39:39

之前使用的海康的前端开发包,利用插件进行视频预览,但是谷歌下面目前不支持插件,所以只能另寻他路。

技术方案

平台为Ubuntu+nginx,利用nginx的代理将web通讯转发给webserver,通讯利用websocket通讯。server端以libevent为基础,构建reactor模式服务器,这样可以大量接入连接。前端采用streamedian,这是开源的,他采用init,join两个数据通道。

nginx配置如下

nginx配置转发如下

location / {
        proxy_pass http://127.0.0.1:9004;
		#proxy_pass http://172.16.10.4:49240/ws;
		proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
		
		
        #proxy_redirect              off;
        #proxy_set_header            Host $host;
        #proxy_set_header            X-Real-IP $remote_addr;
        #proxy_read_timeout          3600s;
        #proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
       
    }

  

reactor模型

reactor模型主要是针对libevent的单线程单进程模式,所有的连接都设置成非租塞模式,通过不停地监听数据的写入,然后进行分类处理,监听结构配置如下:

 

 

flags = fcntl(rfd->fd, F_GETFL);
flags |= O_NONBLOCK;
 fcntl(rfd->fd, F_SETFL, flags);

 

配置成非阻塞是因为libevent全部是轮询,效率高,阻塞会影响到整体效率,但会有个问题,导致connect的时候会出现连接不上,需要采用以下方式:

if(ret == -1 && (REACTOR_InProgress || REACTOR_WOULDBLOCK))
    {
        debug("ACT:need check\r\n");
        rfd = setfd(base,fd,type_CONNECT,app,arg);;
        if(rfd != NULL)
        {
            return rfd;
        }
    }
if(    type != type_LISTEN){
        evWrite = event_new(rfd->base, rfd->fd, EV_WRITE,_canWrite,rfd);
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        flags = event_add(evWrite,&timeout );//&timeout//只要是可写的,write触发了的,就是正常连接
        rfd->evWrite = evWrite;
    }    

触发一个写的动作,并加上时间限制为5秒,这样写成功了,就连接成功了,如果返回超时信号,就没有连接成功。

 

websocket协议的解析

1.发送GET数据包,由client发起连接,其中会包含contrl命令,和sharekey(Sec-WebSocket-Protocol: control),建立INIT通道。
2.接收到包后添加control控制口令,和sharekey发送回client,
以上是websocket的套路,按规矩来就行了,接下来的就是streamedian的套路了
3.client发送cam的ip和port,由于用了掩码加密,需要解密
4.摄像头可以连接后,服务端发送wsp OK的数据包给服务端,同时还需要随机生成一个channel,此处我是用的SHA1加密的客户端和端口。
5.此时client会再次发起连接,此时建立数据JOIN通道。(Sec-WebSocket-Protocol: data),完成连接后发送wspOK。
6.建立连接完成后,client发送RTSP 指令,server收到后透明转发给摄像头,充当中间件的角色,这些交互都在INIT通道完成,最终摄像头发送rtp数据包给server,server通过join通道将rtp包发送给web页面。

rtp包的转发

rtp包的包头为“$”,接着为0x00,然后是两位数据的包的长度,可以根据包的长度来寻找下一个包的帧头,由于数据包可能会出现粘包,所以接收采用窗口接收的方式的来进行,保证每次接收的数据粘连在窗口的tail上,解析完了之后从head进行数据的释放。

 

源代码地址:

https://github.com/hpwang666/liveRtsp.git

测试效果

搜狗浏览器,谷歌浏览器(版本 75.0.3770.142(正式版本) (64 位))均可流畅查看。测试时间1个小时。

测试方式:首先运行./wsServer  ,他会监听9004端口,之前的nginx配置已经将视频请求信息转发给了9004端口