我们知道TSINGSEE青犀视频EasyGBS国标平台可以作为上级平台或下级平台,可以与支持国标协议的平台进行级联,上文提到与宇视等平台进行级联(EasyGBS如何实现视频流的向上级联)就是一个很好的案例。其实除了与市面上支持的国标平台做级联,Easy系列产品之间也可以与EasyGBS做级联。最近,有客户需要同时使用EasyGBS和EasyNVR两个平台融合使用,此时我们就可以将EasyNVR接入到EasyGBS中,使其方便管理。
由于文章内容比较多,我们分两块来讲一下这个配置过程。本文详细描述了EasyNVR平台到国标EasyGBS上的注册和注销。
EasyNVR视频监控直播解决方案
EasyGBS国标视频云直播解决方案
EasyGBS采用gb28181协议接入摄像头,我们将EasyNVR当作边缘设备,通过gb28181协议和EasyGBS平台进行对接。
EasyGBS有级联注册到其他EasyGBS的功能,EasyNVR接入到EasyGBS中其实就是将EasyNVR注册到EasyGBS中,我们可以仿照现有的EasyGBS级联注册功能仿写。从EasyGBS中找到级联注册功能那块代码,仿写EasyNVR的代码。此处是EasyNVR与EasyGBS交互的客户端:
func NewClient(cascade Cascade) *Client { client := &Client{ Stoped: false, Cascade: cascade, AckTimeout: cascade.AckTimeout, LocalHost: cascade.LocalHost, LocalPort: cascade.LocalPort, LocalWanIP: "", LocalSerial: cascade.LocalSerial, handleMap: make(map[string][]HandleFunc), dispatcher: events.NewDispatcher(), KeepAliveQuit: make(chan bool, 0), RegisterQuit: make(chan bool, 0), DevCache: cache.New(time.Duration(cascade.KeepaliveTimeout)*time.Second, 10*time.Second), } return client }
以上我们已经完成了客户端的配置,接下来就可以启动客户端正式与EasyGBS交互了。
func (c *Client) Run() { if c.LocalPort == 0 { log.Println("没有可用的UDP级联端口") return } lUdpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", c.LocalPort)) if err != nil { return } rUdpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", c.Cascade.Host, c.Cascade.Port)) if err != nil { return } c.udpConn, err = net.DialUDP("udp", lUdpAddr, rUdpAddr) if err != nil { log.Printf("uac start error:%v", err) return } if e := c.udpConn.SetReadBuffer(UDP_BUF_SIZE); e != nil { log.Printf("sip udp set connection read buffer[%d] error, %v", UDP_BUF_SIZE, e) } if e := c.udpConn.SetWriteBuffer(UDP_BUF_SIZE); e != nil { log.Printf("sip udp set connection write buffer[%d] error, %v", UDP_BUF_SIZE, e) } defer c.udpConn.Close() c.Transport = c.NewUDPTransport(rUdpAddr, c.udpConn) go c.Register(false) log.Printf("uac start ok:%s", c.udpConn.LocalAddr().String()) c.Stoped = false c.Handle("MESSAGE", HandleMessage) c.Handle("INVITE", HandleInvite) c.Handle("BYE", HandleBye) c.Handle("INFO", HandleInfo) for !c.Stoped { bufUDP := make([]byte, UDP_BUF_SIZE) n, _, err := c.udpConn.ReadFromUDP(bufUDP) if err != nil { log.Printf("udp read error, %v", err) continue } sipBuf := bufUDP[:n] sipRaw := string(sipBuf) sipMsg := siprocket.Parse(sipBuf) statusCode := string(sipMsg.Req.StatusCode) contentLen, _ := strconv.Atoi(string(sipMsg.ContLen.Value)) header := sipRaw[:len(sipRaw)-contentLen] body := sipRaw[len(sipRaw)-contentLen:] Logf("级联消息:[%s]<<<<<<[%s]<<<<<< %v", c.udpConn.LocalAddr().String(), c.udpConn.RemoteAddr().String(), sipRaw) if statusCode == "" { // request req, err := UnmarshalRequest(header, body) if err != nil { log.Printf("unmarshal request failed, %v", err) continue } req.FixVia(c.Transport) c.HandleRequest(req, &sipMsg) } else { // response res, err := UnmarshalResponse(header, body) if err != nil { log.Printf("unmarshal response failed, %v", err) continue } callid := res.HeaderMap["Call-ID"] cseq := res.HeaderMap["CSeq"] c.dispatcher.Dispatch(fmt.Sprintf("%s:%s", callid, cseq), res) } } }
上面两段代码都是实现EasyNVR注册到EasyGBS中,然后就需要将其整合到一起。
func Start() { cascade := GetGbsConfig() if cascade.Enable != 0 { client = NewClient(cascade) go client.Run() } return } func Stop() { if client != nil { if !client.Stoped { client.Stoped = true client.RegisterQuit <- true client.KeepAliveQuit <- true } client = nil log.Println("级联stop") } }
实现后效果如下:
这个设备就是虚拟构造的EasyNVR设备。我们接入的是16通道的EasyNVR,所以此处应该显示16个通道,然而现在注册上来并没有显示通道,所以我们还需要继续配置EasyGBS级联调阅EasyNVR的通道列表:EasyGBS如何实现调阅EasyNVR的视频通道。