A signaling server for GB28181

This commit is contained in:
Haibo Chen
2024-04-17 14:31:33 +08:00
committed by chenhaibo
parent 8774b234b4
commit 0b7126b12b
50 changed files with 11136 additions and 1 deletions

169
pkg/service/outbound.go Normal file
View File

@ -0,0 +1,169 @@
package service
import (
"fmt"
"strings"
"github.com/emiago/sipgo/sip"
"github.com/ossrs/go-oryx-lib/errors"
"github.com/ossrs/go-oryx-lib/logger"
"github.com/ossrs/srs-sip/pkg/service/stack"
"github.com/ossrs/srs-sip/pkg/utils"
)
func (s *UAS) AutoInvite(deviceID string, list ...ChannelInfo) {
for _, c := range list {
if c.Status == "ON" && utils.IsVideoChannel(c.DeviceID) {
if err := s.Invite(deviceID, c.DeviceID); err != nil {
logger.Ef(s.ctx, "invite error: %s", err.Error())
}
}
}
}
func (s *UAS) Invite(deviceID, channelID string) error {
if s.isPublishing(channelID) {
return nil
}
ssrc := utils.CreateSSRC(true)
mediaPort, err := s.signal.Publish(ssrc, ssrc)
if err != nil {
return errors.Wrapf(err, "api gb publish request error")
}
mediaHost := strings.Split(s.conf.MediaAddr, ":")[0]
if mediaHost == "" {
return errors.Errorf("media host is empty")
}
sdpInfo := []string{
"v=0",
fmt.Sprintf("o=%s 0 0 IN IP4 %s", channelID, mediaHost),
"s=" + "Play",
"u=" + channelID + ":0",
"c=IN IP4 " + mediaHost,
"t=0 0", // start time and end time
fmt.Sprintf("m=video %d TCP/RTP/AVP 96", mediaPort),
"a=recvonly",
"a=rtpmap:96 PS/90000",
"y=" + ssrc,
"\r\n",
}
if true { // support tcp only
sdpInfo = append(sdpInfo, "a=setup:passive", "a=connection:new")
}
// TODO: 需要考虑不同设备通道ID相同的情况
d, ok := DM.GetDeviceInfoByChannel(channelID)
if !ok {
return errors.Errorf("device not found by %s", channelID)
}
subject := fmt.Sprintf("%s:%s,%s:0", channelID, ssrc, s.conf.Serial)
req, err := stack.NewInviteRequest([]byte(strings.Join(sdpInfo, "\r\n")), subject, stack.OutboundConfig{
Via: d.SourceAddr,
To: d.DeviceID,
From: s.conf.Serial,
Transport: d.NetworkType,
})
if err != nil {
return errors.Wrapf(err, "build invite request error")
}
tx, err := s.sipCli.TransactionRequest(s.ctx, req)
if err != nil {
return errors.Wrapf(err, "transaction request error")
}
res, err := s.waitAnswer(tx)
if err != nil {
return errors.Wrapf(err, "wait answer error")
}
if res.StatusCode != 200 {
return errors.Errorf("invite response error: %s", res.String())
}
ack := sip.NewAckRequest(req, res, nil)
s.sipCli.WriteRequest(ack)
s.AddVideoChannelStatue(channelID, VideoChannelStatus{
ID: channelID,
ParentID: deviceID,
MediaHost: mediaHost,
MediaPort: mediaPort,
Ssrc: ssrc,
Status: "ON",
})
return nil
}
func (s *UAS) isPublishing(channelID string) bool {
c, err := s.GetVideoChannelStatue(channelID)
if !err {
return false
}
if p, err := s.signal.GetStreamStatus(c.Ssrc); err != nil || !p {
return false
}
return true
}
func (s *UAS) Bye() error {
return nil
}
func (s *UAS) Catalog(deviceID string) error {
var CatalogXML = `<?xml version="1.0"?><Query>
<CmdType>Catalog</CmdType>
<SN>%d</SN>
<DeviceID>%s</DeviceID>
</Query>
`
d, ok := DM.GetDevice(deviceID)
if !ok {
return errors.Errorf("device %s not found", deviceID)
}
body := fmt.Sprintf(CatalogXML, s.getSN(), deviceID)
req, err := stack.NewCatelogRequest([]byte(body), stack.OutboundConfig{
Via: d.SourceAddr,
To: d.DeviceID,
From: s.conf.Serial,
Transport: d.NetworkType,
})
if err != nil {
return errors.Wrapf(err, "build catalog request error")
}
tx, err := s.sipCli.TransactionRequest(s.ctx, req)
if err != nil {
return errors.Wrapf(err, "transaction request error")
}
res, err := s.waitAnswer(tx)
if err != nil {
return errors.Wrapf(err, "wait answer error")
}
logger.Tf(s.ctx, "catalog response: %s", res.String())
return nil
}
func (s *UAS) waitAnswer(tx sip.ClientTransaction) (*sip.Response, error) {
select {
case <-s.ctx.Done():
return nil, errors.Errorf("context done")
case res := <-tx.Responses():
if res.StatusCode == 100 || res.StatusCode == 101 || res.StatusCode == 180 || res.StatusCode == 183 {
return s.waitAnswer(tx)
}
return res, nil
}
}