Compare commits

...

10 Commits
v3.0.6 ... v3

Author SHA1 Message Date
dexter
6ffc5f4fc8 Merge pull request #120 from bigbeer1/v3
build:更新库
2024-09-20 10:22:23 +08:00
dabenxiong
887c4546ab build:更新库 2024-09-20 10:20:10 +08:00
charlestamz
a590151935 Merge pull request #69 from bigbeer1/v3
fix(解决公网无法创建视频通道): 解决这边连接创建成功后的通道IP
2022-09-22 15:37:34 +08:00
dabenxiong
97dcf6a1f1 fix(解决公网无法创建视频通道): 解决这边连接创建成功后的通道IP
解决这边连接创建成功后的通道IP
2022-09-22 11:19:37 +08:00
charlestamz
dcabca1ee8 Merge pull request #45 from INeverCry/v3
1.目录订阅head添加Contact,部分厂商必须要该字段 2.添加订阅设备位置 3.添加Notify消息处理
2022-04-11 10:26:56 +08:00
lqh
7414285731 1.目录订阅head添加Contact,部分厂商必须要该字段
2.添加订阅设备位置
3.添加Notify消息处理
2022-04-09 23:00:49 +08:00
charlie
931f8d888d 1. added LogVerbose parameter in config
2. fixed  problems that public IP address communicates to private networking devices
****attention:
language spec was updated to 1.18
2022-04-05 05:19:18 +08:00
dexter
c9709d0e48 Merge pull request #42 from GeekChengZ/v3
Update channel.go
2022-03-08 13:54:05 +08:00
GeekCheng
61441cc372 Update channel.go
查询所有录像产生类型
2022-03-08 11:11:15 +08:00
charlie
803543fbdf 处理invite发送过快导致callid随机字符串一致的问题 2022-02-28 14:10:33 +08:00
9 changed files with 373 additions and 107 deletions

View File

@@ -29,6 +29,9 @@ type ChannelEx struct {
recordEndTime time.Time
state int32
tcpPortIndex uint16
GpsTime time.Time //gps时间
Longitude string //经度
Latitude string //纬度
}
// Channel 通道
@@ -80,7 +83,7 @@ func (channel *Channel) QueryRecord(startTime, endTime string) int {
<StartTime>%s</StartTime>
<EndTime>%s</EndTime>
<Secrecy>0</Secrecy>
<Type>time</Type>
<Type>all</Type>
</Query>`, d.sn, requestMsg.To.Uri.UserInfo(), startTime, endTime)
requestMsg.ContentLength = len(requestMsg.Body)
resp, err := d.SipRequestForResponse(requestMsg)

193
device.go
View File

@@ -2,17 +2,17 @@ package gb28181
import (
"fmt"
"github.com/Monibuca/engine/v3"
"log"
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/Monibuca/engine/v3"
"github.com/Monibuca/plugin-gb28181/v3/sip"
"github.com/Monibuca/plugin-gb28181/v3/transaction"
"github.com/Monibuca/plugin-gb28181/v3/utils"
. "github.com/Monibuca/utils/v3"
// . "github.com/logrusorgru/aurora"
)
@@ -58,6 +58,7 @@ type Device struct {
to *sip.Contact
Addr string
SipIP string //暴露的IP
SourceAddr net.Addr
channelMap map[string]*Channel
channelMutex sync.RWMutex
subscriber struct {
@@ -97,7 +98,9 @@ func (d *Device) UpdateChannels(list []*Channel) {
path := strings.Split(c.ParentID, "/")
parentId := path[len(path)-1]
if parent, ok := d.channelMap[parentId]; ok {
parent.Children = append(parent.Children, c)
if c.DeviceID != parentId {
parent.Children = append(parent.Children, c)
}
} else {
d.addChannel(c)
}
@@ -106,25 +109,25 @@ func (d *Device) UpdateChannels(list []*Channel) {
}
if old, ok := d.channelMap[c.DeviceID]; ok {
c.ChannelEx = old.ChannelEx
if len(old.Children) == 0 {
if config.PreFetchRecord {
n := time.Now()
n = time.Date(n.Year(), n.Month(), n.Day(), 0, 0, 0, 0, time.Local)
if len(c.Records) == 0 || (n.Format(TIME_LAYOUT) == c.RecordStartTime &&
n.Add(time.Hour*24-time.Second).Format(TIME_LAYOUT) == c.RecordEndTime) {
go c.QueryRecord(n.Format(TIME_LAYOUT), n.Add(time.Hour*24-time.Second).Format(TIME_LAYOUT))
}
}
if config.AutoInvite && c.LivePublisher == nil {
go c.Invite("", "")
if config.PreFetchRecord {
n := time.Now()
n = time.Date(n.Year(), n.Month(), n.Day(), 0, 0, 0, 0, time.Local)
if len(c.Records) == 0 || (n.Format(TIME_LAYOUT) == c.RecordStartTime &&
n.Add(time.Hour*24-time.Second).Format(TIME_LAYOUT) == c.RecordEndTime) {
go c.QueryRecord(n.Format(TIME_LAYOUT), n.Add(time.Hour*24-time.Second).Format(TIME_LAYOUT))
}
}
if config.AutoInvite &&
(c.LivePublisher == nil || (c.LivePublisher.VideoTracks.Size == 0 && c.LivePublisher.AudioTracks.Size == 0)) {
c.Invite("", "")
}
} else {
c.ChannelEx = &ChannelEx{
device: d,
}
if config.AutoInvite {
go c.Invite("", "")
c.Invite("", "")
}
}
if s := engine.FindStream("sub/" + c.DeviceID); s != nil {
@@ -145,15 +148,6 @@ func (d *Device) UpdateRecord(channelId string, list []*Record) {
func (d *Device) CreateMessage(Method sip.Method) (requestMsg *sip.Message) {
d.sn++
//if msg.Via.Transport == "UDP" {
deviceAddr, err2 := net.ResolveUDPAddr(strings.ToLower(d.SipNetwork), d.Addr)
//} else {
// pkt.Addr, err2 = net.ResolveTCPAddr("tcp", addr)
//}
if err2 != nil {
return nil
}
requestMsg = &sip.Message{
Mode: sip.SIP_MESSAGE_REQUEST,
MaxForwards: 70,
@@ -174,8 +168,30 @@ func (d *Device) CreateMessage(Method sip.Method) (requestMsg *sip.Message) {
ID: uint32(d.sn),
Method: Method,
}, CallID: utils.RandNumString(10),
Addr: d.Addr,
DestAdd: deviceAddr,
Addr: d.SourceAddr.String(),
}
var err2 error
requestMsg.DestAdd, err2 = d.ResolveAddress(requestMsg)
if err2 != nil {
return nil
}
//intranet ip , let's resolve it with public ip
var deviceIp, deviceSourceIP net.IP
switch addr := requestMsg.DestAdd.(type) {
case *net.UDPAddr:
deviceIp = addr.IP
case *net.TCPAddr:
deviceIp = addr.IP
}
switch addr2 := d.SourceAddr.(type) {
case *net.UDPAddr:
deviceSourceIP = addr2.IP
case *net.TCPAddr:
deviceSourceIP = addr2.IP
}
if deviceIp.IsPrivate() && !deviceSourceIP.IsPrivate() {
requestMsg.DestAdd = d.SourceAddr
}
return
}
@@ -188,6 +204,9 @@ func (d *Device) Subscribe() int {
requestMsg.Event = "Catalog"
d.subscriber.Timeout = time.Now().Add(time.Second * time.Duration(requestMsg.Expires))
requestMsg.ContentType = "Application/MANSCDP+xml"
requestMsg.Contact = &sip.Contact{
Uri: sip.NewURI(fmt.Sprintf("%s@%s:%d", d.Serial, d.SipIP, d.SipPort)),
}
requestMsg.Body = sip.BuildCatalogXML(d.sn, requestMsg.To.Uri.UserInfo())
requestMsg.ContentLength = len(requestMsg.Body)
@@ -223,7 +242,7 @@ func (d *Device) Catalog() int {
func (d *Device) QueryDeviceInfo(req *sip.Request) {
for i := time.Duration(5); i < 100; i++ {
Printf("device.QueryDeviceInfo:%s ipaddr:%s", d.ID, d.Addr)
fmt.Printf("device.QueryDeviceInfo:%s ipaddr:%s", d.ID, d.Addr)
time.Sleep(time.Second * i)
requestMsg := d.CreateMessage(sip.MESSAGE)
requestMsg.ContentType = "Application/MANSCDP+xml"
@@ -246,3 +265,123 @@ func (d *Device) QueryDeviceInfo(req *sip.Request) {
}
}
}
// MobilePositionSubscribe 移动位置订阅
func (d *Device) MobilePositionSubscribe(id string, expires int, interval int) (code int) {
mobilePosition := d.CreateMessage(sip.SUBSCRIBE)
if d.subscriber.CallID != "" {
mobilePosition.CallID = d.subscriber.CallID
}
mobilePosition.Expires = expires
mobilePosition.Event = "presence"
mobilePosition.Contact = &sip.Contact{
Uri: sip.NewURI(fmt.Sprintf("%s@%s:%d", d.Serial, d.SipIP, d.SipPort)),
}
d.subscriber.Timeout = time.Now().Add(time.Second * time.Duration(mobilePosition.Expires))
mobilePosition.ContentType = "Application/MANSCDP+xml"
mobilePosition.Body = sip.BuildDevicePositionXML(d.sn, id, interval)
mobilePosition.ContentLength = len(mobilePosition.Body)
msg := &sip.Request{Message: mobilePosition}
response, err := d.Core.SipRequestForResponse(msg)
if err == nil && response != nil {
if response.GetStatusCode() == 200 {
d.subscriber.CallID = mobilePosition.CallID
} else {
d.subscriber.CallID = ""
}
return response.GetStatusCode()
}
return http.StatusRequestTimeout
}
// UpdateChannelPosition 更新通道GPS坐标
func (d *Device) UpdateChannelPosition(channelId string, gpsTime string, lng string, lat string) {
if c, ok := d.channelMap[channelId]; ok {
c.ChannelEx.GpsTime, _ = time.ParseInLocation("2006-01-02 15:04:05", gpsTime, time.Local)
c.ChannelEx.Longitude = lng
c.ChannelEx.Latitude = lat
log.Printf("更新通道[%s]坐标成功\n", c.Name)
} else {
log.Printf("更新失败,未找到通道[%s]\n", channelId)
}
}
// UpdateChannelStatus 目录订阅消息处理:新增/移除/更新通道或者更改通道状态
func (d *Device) UpdateChannelStatus(deviceList []*notifyMessage) {
for _, v := range deviceList {
switch v.Event {
case "ON":
log.Println("收到通道上线通知")
d.channelOnline(v.DeviceID)
case "OFF":
log.Println("收到通道离线通知")
d.channelOffline(v.DeviceID)
case "VLOST":
log.Println("收到通道视频丢失通知")
d.channelOffline(v.DeviceID)
case "DEFECT":
log.Println("收到通道故障通知")
d.channelOffline(v.DeviceID)
case "ADD":
log.Println("收到通道新增通知")
channel := Channel{
DeviceID: v.DeviceID,
ParentID: v.ParentID,
Name: v.Name,
Manufacturer: v.Manufacturer,
Model: v.Model,
Owner: v.Owner,
CivilCode: v.CivilCode,
Address: v.Address,
Parental: v.Parental,
SafetyWay: v.SafetyWay,
RegisterWay: v.RegisterWay,
Secrecy: v.Secrecy,
Status: v.Status,
}
d.addChannel(&channel)
case "DEL":
//删除
log.Println("收到通道删除通知")
delete(d.channelMap, v.DeviceID)
case "UPDATE":
fmt.Println("收到通道更新通知")
// 更新通道
channel := &Channel{
DeviceID: v.DeviceID,
ParentID: v.ParentID,
Name: v.Name,
Manufacturer: v.Manufacturer,
Model: v.Model,
Owner: v.Owner,
CivilCode: v.CivilCode,
Address: v.Address,
Parental: v.Parental,
SafetyWay: v.SafetyWay,
RegisterWay: v.RegisterWay,
Secrecy: v.Secrecy,
Status: v.Status,
}
channels := []*Channel{channel}
d.UpdateChannels(channels)
}
}
}
func (d *Device) channelOnline(DeviceID string) {
if c, ok := d.channelMap[DeviceID]; ok {
c.Status = "ON"
log.Printf("通道[%s]在线\n", c.Name)
} else {
log.Printf("更新通道[%s]状态失败,未找到\n", DeviceID)
}
}
func (d *Device) channelOffline(DeviceID string) {
if c, ok := d.channelMap[DeviceID]; ok {
c.Status = "OFF"
log.Printf("通道[%s]离线\n", c.Name)
} else {
log.Printf("更新通道[%s]状态失败,未找到\n", DeviceID)
}
}

4
go.mod
View File

@@ -1,11 +1,11 @@
module github.com/Monibuca/plugin-gb28181/v3
go 1.13
go 1.16
require (
github.com/Monibuca/engine/v3 v3.5.0
github.com/Monibuca/utils/v3 v3.0.5
github.com/agiledragon/gomonkey/v2 v2.2.0
github.com/agiledragon/gomonkey/v2 v2.12.0
github.com/google/uuid v1.3.0 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/pion/rtp v1.7.4

62
go.sum
View File

@@ -5,12 +5,11 @@ github.com/Monibuca/engine/v3 v3.5.0 h1:hkuOdEXlnjcUhgDqOBPcrJ6fgv+HuWuMSpYhqvT3
github.com/Monibuca/engine/v3 v3.5.0/go.mod h1:yNiVKeHxgv+Ez+f2RHXMkXoa5Oxv+G7Ch+MJdHi7ing=
github.com/Monibuca/utils/v3 v3.0.5 h1:w14x0HkWTbF4MmHbINLlOwe4VJNoSOeaQChMk5E/4es=
github.com/Monibuca/utils/v3 v3.0.5/go.mod h1:RpNS95gapWs6gimwh8Xn2x72FN5tO7Powabj7dTFyvE=
github.com/agiledragon/gomonkey/v2 v2.2.0 h1:QJWqpdEhGV/JJy70sZ/LDnhbSlMrqHAWHcNOjz1kyuI=
github.com/agiledragon/gomonkey/v2 v2.2.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/agiledragon/gomonkey/v2 v2.3.0 h1:olsJjnSDpvqWl4FHByeMUs/r54/az+gQitU3VeEbC98=
github.com/agiledragon/gomonkey/v2 v2.3.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/agiledragon/gomonkey/v2 v2.12.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/cnotch/apirouter v0.0.0-20200731232942-89e243a791f3/go.mod h1:5deJPLON/x/s2dLOQfuKS0lenhOIT4xX0pvtN/OEIuY=
github.com/cnotch/ipchub v1.1.0 h1:hH0lh2mU3AZXPiqMwA0pdtqrwo7PFIMRGush9OobMUs=
github.com/cnotch/ipchub v1.1.0/go.mod h1:2PbeBs2q2VxxTVCn1eYCDwpAWuVXbq1+N0FU7GimOH4=
@@ -19,9 +18,8 @@ github.com/cnotch/queue v0.0.0-20200326024423-6e88bdbf2ad4/go.mod h1:zOssjAlNusO
github.com/cnotch/queue v0.0.0-20201224060551-4191569ce8f6/go.mod h1:zOssjAlNusOxvtaqT+EMA+Iyi8rrtKr4/XfzN1Fgoeg=
github.com/cnotch/scheduler v0.0.0-20200522024700-1d2da93eefc5/go.mod h1:F4GE3SZkJZ8an1Y0ZCqvSM3jeozNuKzoC67erG1PhIo=
github.com/cnotch/xlog v0.0.0-20201208005456-cfda439cd3a0/go.mod h1:RW9oHsR79ffl3sR3yMGgxYupMn2btzdtJUwoxFPUE5E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0=
@@ -38,11 +36,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/kelindar/process v0.0.0-20170730150328-69a29e249ec3/go.mod h1:+lTCLnZFXOkqwD8sLPl6u4erAc0cP8wFegQHfipz7KE=
github.com/kelindar/rate v1.0.0/go.mod h1:AjT4G+hTItNwt30lucEGZIz8y7Uk5zPho6vurIZ+1Es=
github.com/kelindar/tcp v1.0.0/go.mod h1:JB5hj1cshLU60XrLij2BBxW3JQ4hOye8vqbyvuKb52k=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
@@ -58,7 +51,6 @@ github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko
github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA=
github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pixelbender/go-sdp v1.1.0/go.mod h1:6IBlz9+BrUHoFTea7gcp4S54khtOhjCW/nVDLhmZBAs=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -69,77 +61,41 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -3,15 +3,17 @@ package gb28181
import (
"bytes"
"encoding/xml"
"github.com/Monibuca/plugin-gb28181/v3/sip"
"github.com/Monibuca/plugin-gb28181/v3/transaction"
"github.com/Monibuca/plugin-gb28181/v3/utils"
"github.com/logrusorgru/aurora"
. "github.com/Monibuca/utils/v3"
"golang.org/x/net/html/charset"
"net/http"
"time"
. "github.com/Monibuca/utils/v3"
"golang.org/x/net/html/charset"
)
func OnRegister(req *sip.Request, tx *transaction.GBTx) {
@@ -71,6 +73,7 @@ func OnMessage(req *sip.Request, tx *transaction.GBTx) {
if v, ok := Devices.Load(req.From.Uri.UserInfo()); ok {
d := v.(*Device)
d.SourceAddr = req.SourceAdd
if d.Status == string(sip.REGISTER) {
d.Status = "ONLINE"
go d.QueryDeviceInfo(req)
@@ -101,8 +104,20 @@ func OnMessage(req *sip.Request, tx *transaction.GBTx) {
case "Keepalive":
d.LastKeepaliveAt = time.Now()
//callID !="" 说明是订阅的事件类型信息
if d.Channels == nil || d.subscriber.CallID != "" && d.LastKeepaliveAt.After(d.subscriber.Timeout) {
if d.Channels == nil {
go d.Catalog()
} else {
if d.subscriber.CallID != "" && d.LastKeepaliveAt.After(d.subscriber.Timeout) {
go d.Catalog()
} else {
for _, c := range d.Channels {
if config.AutoInvite &&
(c.LivePublisher == nil || (c.LivePublisher.VideoTracks.Size == 0 && c.LivePublisher.AudioTracks.Size == 0)) {
c.Invite("", "")
}
}
}
}
d.CheckSubStream()
case "Catalog":
@@ -130,3 +145,75 @@ func OnMessage(req *sip.Request, tx *transaction.GBTx) {
tx.Respond(response)
}
}
func onBye(req *sip.Request, tx *transaction.GBTx) {
response := &sip.Response{req.BuildOK()}
_ = tx.Respond(response)
}
// OnNotify 订阅通知处理
func OnNotify(req *sip.Request, tx *transaction.GBTx) {
if v, ok := Devices.Load(req.From.Uri.UserInfo()); ok {
d := v.(*Device)
d.UpdateTime = time.Now()
temp := &struct {
XMLName xml.Name
CmdType string
DeviceID string
Time string //位置订阅-GPS时间
Longitude string //位置订阅-经度
Latitude string //位置订阅-维度
// Speed string //位置订阅-速度(km/h)(可选)
// Direction string //位置订阅-方向(取值为当前摄像头方向与正北方的顺时针夹角,取值范围0°~360°,单位:°)(可选)
// Altitude string //位置订阅-海拔高度,单位:m(可选)
DeviceList []*notifyMessage `xml:"DeviceList>Item"` //目录订阅
}{}
decoder := xml.NewDecoder(bytes.NewReader([]byte(req.Body)))
decoder.CharsetReader = charset.NewReaderLabel
err := decoder.Decode(temp)
if err != nil {
err = utils.DecodeGbk(temp, []byte(req.Body))
if err != nil {
Printf("decode catelog err: %s", err)
}
}
var body string
switch temp.CmdType {
case "Catalog":
//目录状态
d.UpdateChannelStatus(temp.DeviceList)
case "MobilePosition":
//更新channel的坐标
d.UpdateChannelPosition(temp.DeviceID, temp.Time, temp.Longitude, temp.Latitude)
// case "Alarm":
// //报警事件通知 TODO
default:
Println("DeviceID:", aurora.Red(d.ID), " Not supported CmdType : "+temp.CmdType+" body:\n", req.Body)
response := &sip.Response{req.BuildResponse(http.StatusBadRequest)}
tx.Respond(response)
return
}
buildOK := req.BuildOK()
buildOK.Body = body
response := &sip.Response{buildOK}
tx.Respond(response)
}
}
type notifyMessage struct {
DeviceID string
ParentID string
Name string
Manufacturer string
Model string
Owner string
CivilCode string
Address string
Parental int
SafetyWay int
RegisterWay int
Secrecy int
Status string
//状态改变事件 ON:上线,OFF:离线,VLOST:视频丢失,DEFECT:故障,ADD:增加,DEL:删除,UPDATE:更新(必选)
Event string
}

30
main.go
View File

@@ -73,7 +73,8 @@ var config = struct {
Username string
Password string
UdpCacheSize int //udp排序缓存
}{"34020000002000000001", "3402000000", "127.0.0.1:5060", 3600, 58200, false, -1, nil, false, 1, 600, false, "", "", 0}
LogVerbose bool
}{"34020000002000000001", "3402000000", "127.0.0.1:5060", 3600, 58200, false, -1, nil, false, 1, 600, false, "", "", 0, false}
func init() {
pc := engine.PluginConfig{
@@ -83,10 +84,7 @@ func init() {
pc.Install(run)
publishers.data = make(map[uint32]*Publisher)
}
func onBye(req *sip.Request, tx *transaction.GBTx) {
response := &sip.Response{req.BuildOK()}
_ = tx.Respond(response)
}
func storeDevice(id string, s *transaction.Core, req *sip.Message) {
var d *Device
@@ -154,11 +152,13 @@ func run() {
MediaIdleTimeout: 30,
RemoveBanInterval: config.RemoveBanInterval,
UdpCacheSize: config.UdpCacheSize,
LogVerbose: config.LogVerbose,
}
s := transaction.NewCore(serverConfig)
s.RegistHandler(sip.REGISTER, OnRegister)
s.RegistHandler(sip.MESSAGE, OnMessage)
s.RegistHandler(sip.NOTIFY, OnNotify)
s.RegistHandler(sip.BYE, onBye)
//OnStreamClosedHooks.AddHook(func(stream *Stream) {
@@ -254,6 +254,26 @@ func run() {
w.WriteHeader(404)
}
})
http.HandleFunc("/api/gb28181/position", func(w http.ResponseWriter, r *http.Request) {
CORS(w, r)
query := r.URL.Query()
//设备id
id := query.Get("id")
//订阅周期(单位:秒)
expires := query.Get("expires")
//订阅间隔(单位:秒)
interval := query.Get("interval")
expiresInt, _ := strconv.Atoi(expires)
intervalInt, _ := strconv.Atoi(interval)
if v, ok := Devices.Load(id); ok {
d := v.(*Device)
w.WriteHeader(d.MobilePositionSubscribe(id, expiresInt, intervalInt))
} else {
w.WriteHeader(404)
}
})
s.StartAndWait()
}

View File

@@ -11,15 +11,15 @@ type Request struct {
}
var (
// CatalogXML 获取设备列表xml样式
CatalogXML = `<?xml version="1.0"?><Query>
// CatalogXML 获取设备列表xml样式
CatalogXML = `<?xml version="1.0"?><Query>
<CmdType>Catalog</CmdType>
<SN>%d</SN>
<DeviceID>%s</DeviceID>
</Query>
`
// RecordInfoXML 获取录像文件列表xml样式
RecordInfoXML = `<?xml version="1.0"?>
// RecordInfoXML 获取录像文件列表xml样式
RecordInfoXML = `<?xml version="1.0"?>
<Query>
<CmdType>RecordInfo</CmdType>
<SN>%d</SN>
@@ -27,30 +27,43 @@ RecordInfoXML = `<?xml version="1.0"?>
<StartTime>%s</StartTime>
<EndTime>%s</EndTime>
<Secrecy>0</Secrecy>
<Type>time</Type>
<Type>all</Type>
</Query>
`
// DeviceInfoXML 查询设备详情xml样式
DeviceInfoXML = `<?xml version="1.0"?>
// DeviceInfoXML 查询设备详情xml样式
DeviceInfoXML = `<?xml version="1.0"?>
<Query>
<CmdType>DeviceInfo</CmdType>
<SN>%d</SN>
<DeviceID>%s</DeviceID>
</Query>
`
// DevicePositionXML 订阅设备位置
DevicePositionXML = `<?xml version="1.0"?>
<Query>
<CmdType>MobilePosition</CmdType>
<SN>%d</SN>
<DeviceID>%s</DeviceID>
<Interval>%d</Interval>
</Query>`
)
// BuildDeviceInfoXML 获取设备详情指令
func BuildDeviceInfoXML(sn int,id string) string {
return fmt.Sprintf(DeviceInfoXML,sn, id)
func BuildDeviceInfoXML(sn int, id string) string {
return fmt.Sprintf(DeviceInfoXML, sn, id)
}
// BuildCatalogXML 获取NVR下设备列表指令
func BuildCatalogXML(sn int, id string) string {
return fmt.Sprintf(CatalogXML,sn, id)
return fmt.Sprintf(CatalogXML, sn, id)
}
// BuildRecordInfoXML 获取录像文件列表指令
func BuildRecordInfoXML(sn int,id string, start, end int64) string {
return fmt.Sprintf(RecordInfoXML, sn,id, time.Unix(start, 0).Format("2006-01-02T15:04:05"), time.Unix(end, 0).Format("2006-01-02T15:04:05"))
func BuildRecordInfoXML(sn int, id string, start, end int64) string {
return fmt.Sprintf(RecordInfoXML, sn, id, time.Unix(start, 0).Format("2006-01-02T15:04:05"), time.Unix(end, 0).Format("2006-01-02T15:04:05"))
}
// BuildDevicePositionXML 订阅设备位置
func BuildDevicePositionXML(sn int, id string, interval int) string {
return fmt.Sprintf(DevicePositionXML, sn, id, interval)
}

View File

@@ -58,7 +58,8 @@ type Config struct {
MediaPortMax uint16
MediaIdleTimeout uint16 //推流超时时间,超过则断开链接,让设备重连
AudioEnable bool //是否开启音频
WaitKeyFrame bool //是否等待关键帧,如果等待,则在收到第一个关键帧之前,忽略所有媒体流
RemoveBanInterval int //移除禁止设备间隔
UdpCacheSize int //udp缓存大小
LogVerbose bool
WaitKeyFrame bool //是否等待关键帧,如果等待,则在收到第一个关键帧之前,忽略所有媒体流
RemoveBanInterval int //移除禁止设备间隔
UdpCacheSize int //udp缓存大小
}

View File

@@ -69,6 +69,9 @@ func (c *Core) handlerListen() {
if len(p.Data) < 5 {
continue
}
if c.LogVerbose {
Println("Received: \n", string(p.Data))
}
if err := c.HandleReceiveMessage(p); err != nil {
fmt.Println("handler sip response message failed:", err.Error())
continue
@@ -82,7 +85,7 @@ func (c *Core) handlerListen() {
//响应消息则需要匹配到请求让请求的transaction来处理。
//TODO参考srs和osip的流程以及文档做最终处理。需要将逻辑分成两层TU 层和 transaction 层
func (c *Core) HandleReceiveMessage(p *transport.Packet) (err error) {
// fmt.Println("packet content:", string(p.Data))
//Println("packet content:", string(p.Data))
//var msg *Message
msg, err := Decode(p.Data)
if err != nil {
@@ -172,6 +175,50 @@ func (c *Core) SipRequestForResponse(req *Request) (response *Response, err erro
}
return
}
func (c *Core) ResolveAddress(msg *Message) (destAddr net.Addr, err error) {
addr := msg.Addr
if addr == "" {
viaParams := msg.Via.Params
var host, port string
var ok1, ok2 bool
if host, ok1 = viaParams["maddr"]; !ok1 {
if host, ok2 = viaParams["received"]; !ok2 {
host = msg.Via.Host
}
}
//port
port = viaParams["rport"]
if port == "" || port == "0" || port == "-1" {
port = msg.Via.Port
}
if port == "" {
port = "5060"
}
addr = fmt.Sprintf("%s:%s", host, port)
}
// fmt.Println("dest addr:", addr)
var err1, err2 error
if msg.Via.Transport == "UDP" {
destAddr, err2 = net.ResolveUDPAddr("udp", addr)
} else {
destAddr, err2 = net.ResolveTCPAddr("tcp", addr)
}
if err1 != nil {
return nil, err1
}
if err2 != nil {
return nil, err2
}
return destAddr, nil
}
// Request Request
func (c *Core) Request(req *Request) (*GBTx, error) {