mirror of
https://github.com/lkmio/gb-cms.git
synced 2025-09-26 19:51:22 +08:00
154 lines
5.0 KiB
Go
154 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/ghettovoice/gosip/sip"
|
|
"github.com/lkmio/avformat/utils"
|
|
"net/http"
|
|
"net/netip"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// GBPlatformRecord 国标上级信息持久化结构体
|
|
type GBPlatformRecord struct {
|
|
Username string `json:"username"` //用户名
|
|
SeverID string `json:"server_id"` //上级ID, 必选
|
|
ServerAddr string `json:"server_addr"` //上级地址, 必选
|
|
Transport string `json:"transport"` //上级通信方式, UDP/TCP
|
|
Password string `json:"password"` //密码
|
|
RegisterExpires int `json:"register_expires"` //注册有效期
|
|
KeepAliveInterval int `json:"keep_alive_interval"` //心跳间隔
|
|
}
|
|
|
|
type GBPlatform struct {
|
|
*Client
|
|
streams *streamManager // 保存与上级的所有级联会话
|
|
}
|
|
|
|
// OnBye 被上级挂断
|
|
func (g *GBPlatform) OnBye(request sip.Request) {
|
|
id, _ := request.CallID()
|
|
g.CloseStream(id.Value(), false, true)
|
|
}
|
|
|
|
// CloseStream 关闭级联会话
|
|
func (g *GBPlatform) CloseStream(id string, bye, ms bool) {
|
|
// 删除会话
|
|
stream := g.streams.RemoveWithCallId(id)
|
|
if stream == nil {
|
|
return
|
|
}
|
|
|
|
// 从国标源中删除当前转发流
|
|
sink := stream.RemoveForwardSink(id)
|
|
|
|
if ms {
|
|
// 通知媒体服务
|
|
go CloseSink(string(stream.ID), sink.id)
|
|
}
|
|
|
|
// SIP挂断
|
|
if bye {
|
|
byeRequest := CreateRequestFromDialog(sink.dialog, sip.BYE)
|
|
SipUA.SendRequest(byeRequest)
|
|
}
|
|
}
|
|
|
|
// OnInvite 被上级呼叫
|
|
func (g *GBPlatform) OnInvite(request sip.Request, user string) sip.Response {
|
|
Sugar.Infof("接收到上级预览 上级id: %s 请求通道id: %s sdp: %s", g.SeverId, user, request.Body())
|
|
|
|
channel := g.FindChannel(user)
|
|
|
|
// 查找通道对应的设备
|
|
device := DeviceManager.Find(channel.ParentID)
|
|
if device == nil {
|
|
Sugar.Errorf("级联转发失败 设备不存在 DeviceID: %s ChannelID: %s", channel.DeviceID, user)
|
|
return CreateResponseWithStatusCode(request, http.StatusNotFound)
|
|
}
|
|
|
|
parse, ssrc, speed, media, offerSetup, answerSetup, err := ParseGBSDP(request.Body())
|
|
if err != nil {
|
|
Sugar.Errorf("级联转发失败 解析上级SDP发生错误 err: %s sdp: %s", err.Error(), request.Body())
|
|
return CreateResponseWithStatusCode(request, http.StatusBadRequest)
|
|
}
|
|
|
|
// 解析时间范围
|
|
time := strings.Split(parse.Time, " ")
|
|
if len(time) < 2 {
|
|
Sugar.Errorf("级联转发失败 上级sdp的时间范围格式错误 time: %s sdp: %s", parse.Time, request.Body())
|
|
return CreateResponseWithStatusCode(request, http.StatusBadRequest)
|
|
}
|
|
|
|
var streamId StreamID
|
|
var inviteType InviteType
|
|
inviteType.SessionName2Type(strings.ToLower(parse.Session))
|
|
switch inviteType {
|
|
case InviteTypeLive:
|
|
streamId = GenerateStreamId(InviteTypeLive, channel.ParentID, user, "", "")
|
|
break
|
|
case InviteTypePlayback:
|
|
// 级联下载和回放不限制路数,也不共享流
|
|
streamId = GenerateStreamId(InviteTypePlayback, channel.ParentID, user, time[0], time[1]) + StreamID("."+utils.RandStringBytes(10))
|
|
break
|
|
case InviteTypeDownload:
|
|
streamId = GenerateStreamId(InviteTypeDownload, channel.ParentID, user, time[0], time[1]) + StreamID("."+utils.RandStringBytes(10))
|
|
break
|
|
}
|
|
|
|
var ok bool
|
|
stream := StreamManager.Find(streamId)
|
|
addr := fmt.Sprintf("%s:%d", parse.Addr, media.Port)
|
|
if stream == nil {
|
|
stream, ok = device.(*Device).StartStream(inviteType, streamId, user, time[0], time[1], offerSetup, 0, true)
|
|
if !ok {
|
|
Sugar.Errorf("级联转发失败 预览流失败 StreamID: %s", streamId)
|
|
return CreateResponseWithStatusCode(request, http.StatusBadRequest)
|
|
}
|
|
}
|
|
|
|
ssrcInt, _ := strconv.Atoi(ssrc)
|
|
ip, port, sinkID, err := AddForwardStreamSink(string(streamId), addr, offerSetup, uint32(ssrcInt))
|
|
if err != nil {
|
|
Sugar.Errorf("级联转发失败 向流媒体服务添加转发Sink失败 err: %s", err.Error())
|
|
|
|
if "play" != parse.Session {
|
|
CloseStream(streamId)
|
|
}
|
|
|
|
return CreateResponseWithStatusCode(request, http.StatusInternalServerError)
|
|
}
|
|
|
|
// answer添加contact头域
|
|
answer := BuildSDP(user, parse.Session, ip, port, time[0], time[1], answerSetup, speed, ssrc)
|
|
response := CreateResponseWithStatusCode(request, http.StatusOK)
|
|
response.RemoveHeader("Contact")
|
|
response.AppendHeader(GlobalContactAddress.AsContactHeader())
|
|
response.AppendHeader(&SDPMessageType)
|
|
response.SetBody(answer, true)
|
|
|
|
setToTag(response)
|
|
|
|
// 添加级联转发流
|
|
callID, _ := request.CallID()
|
|
stream.AddForwardSink(callID.Value(), &Sink{sinkID, g.ID, g.CreateDialogRequestFromAnswer(response, true)})
|
|
|
|
// 保存与上级的会话
|
|
g.streams.AddWithCallId(callID.Value(), stream)
|
|
return response
|
|
}
|
|
|
|
func NewGBPlatform(record *GBPlatformRecord, ua SipServer) (*GBPlatform, error) {
|
|
if len(record.SeverID) != 20 {
|
|
return nil, fmt.Errorf("SeverID must be exactly 20 characters long")
|
|
}
|
|
|
|
if _, err := netip.ParseAddrPort(record.ServerAddr); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := NewGBClient(record.Username, record.SeverID, record.ServerAddr, record.Transport, record.Password, record.RegisterExpires, record.KeepAliveInterval, ua)
|
|
return &GBPlatform{client.(*Client), NewStreamManager()}, nil
|
|
}
|