低空智控平台 后端go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

117 lines
4.3 KiB

package live
import (
"crypto/md5"
cryptorand "crypto/rand"
"encoding/hex"
"fmt"
"strconv"
"time"
)
type DisabledAdapter struct{}
func (DisabledAdapter) Provider() string { return "disabled" }
func (DisabledAdapter) CreateStream(StreamRequest) (StreamCredentials, error) {
return StreamCredentials{}, ErrDisabled
}
func (DisabledAdapter) CreatePlayURLs(PlayRequest) (PlayURLs, error) { return PlayURLs{}, ErrDisabled }
func (DisabledAdapter) QueryOnline(string) (OnlineStatus, error) { return OnlineStatus{}, ErrDisabled }
// FakeAdapter is a local provider that never contacts a cloud service.
type FakeAdapter struct {
PlayDomain string
PushDomain string
AppName string
}
func (f FakeAdapter) Provider() string { return "fake" }
func (f FakeAdapter) CreateStream(req StreamRequest) (StreamCredentials, error) {
return StreamCredentials{
Provider: f.Provider(),
PushURL: "fake://" + f.PushDomain + "/" + f.AppName + "/" + req.StreamName,
ExpiresAt: req.ExpiresAt,
}, nil
}
func (f FakeAdapter) CreatePlayURLs(req PlayRequest) (PlayURLs, error) {
return PlayURLs{
HLS: "fake://" + f.PlayDomain + "/" + f.AppName + "/" + req.StreamName + ".m3u8",
ExpiresAt: req.ExpiresAt,
}, nil
}
func (f FakeAdapter) QueryOnline(string) (OnlineStatus, error) {
return OnlineStatus{Online: true}, nil
}
type AliyunAdapter struct {
cfg Config
client *aliyunLiveClient
}
func NewAliyunAdapter(cfg Config) (Adapter, error) {
if !cfg.AllowRealCloud || cfg.PushDomain == "" || cfg.PlayDomain == "" || cfg.AppName == "" || cfg.AuthKey == "" || cfg.PlayAuthKey == "" || cfg.AccessKeyID == "" || cfg.AccessKeySecret == "" || cfg.RegionID == "" {
return nil, ErrInvalidConfig
}
if cfg.Protocol == "" {
cfg.Protocol = "srt"
}
if cfg.Protocol != "srt" && cfg.Protocol != "rtmps" {
return nil, ErrInvalidConfig
}
if cfg.SRTPort <= 0 {
cfg.SRTPort = 1105
}
client, err := newAliyunLiveClient(cfg)
if err != nil {
return nil, err
}
return AliyunAdapter{cfg: cfg, client: client}, nil
}
func (a AliyunAdapter) Provider() string { return "aliyun" }
func (a AliyunAdapter) CreateStream(req StreamRequest) (StreamCredentials, error) {
return StreamCredentials{Provider: a.Provider(), PushURL: a.pushURL(req.StreamName, req.ExpiresAt), ExpiresAt: req.ExpiresAt}, nil
}
func (a AliyunAdapter) CreatePlayURLs(req PlayRequest) (PlayURLs, error) {
expires := req.ExpiresAt
path := "/" + a.cfg.AppName + "/" + req.StreamName
// Alibaba Live authenticates the complete playback path. HLS and FLV
// therefore need independent signatures because their file extensions are
// part of the URI used by the CDN auth check.
hlsPath := path + ".m3u8"
flvPath := path + ".flv"
hlsSignature := sign(hlsPath, expires.Unix(), a.cfg.PlayAuthKey)
flvSignature := sign(flvPath, expires.Unix(), a.cfg.PlayAuthKey)
base := "http://" + a.cfg.PlayDomain + "/" + a.cfg.AppName + "/" + req.StreamName
rtmpPath := path
rtmpSignature := sign(rtmpPath, expires.Unix(), a.cfg.PlayAuthKey)
rtmp := "rtmp://" + a.cfg.PlayDomain + "/" + a.cfg.AppName + "/" + req.StreamName + "?auth_key=" + rtmpSignature
return PlayURLs{
HLS: base + ".m3u8?auth_key=" + hlsSignature,
FLV: base + ".flv?auth_key=" + flvSignature,
RTMP: rtmp,
ExpiresAt: expires,
}, nil
}
func (a AliyunAdapter) QueryOnline(streamName string) (OnlineStatus, error) {
return a.client.queryOnline(streamName)
}
func sign(path string, expiresAt int64, key string) string {
var random [4]byte
if _, err := cryptorand.Read(random[:]); err != nil {
panic(fmt.Sprintf("generate live URL nonce: %v", err))
}
rand := strconv.FormatUint(uint64(random[0])<<24|uint64(random[1])<<16|uint64(random[2])<<8|uint64(random[3]), 10)
sum := md5.Sum([]byte(fmt.Sprintf("%s-%d-%s-0-%s", path, expiresAt, rand, key)))
return fmt.Sprintf("%d-%s-0-%s", expiresAt, rand, hex.EncodeToString(sum[:]))
}
func (a AliyunAdapter) pushURL(streamName string, expires time.Time) string {
path := "/" + a.cfg.AppName + "/" + streamName
auth := sign(path, expires.Unix(), a.cfg.AuthKey)
if a.cfg.Protocol == "srt" {
return "srt://" + a.cfg.PushDomain + ":" + strconv.Itoa(a.cfg.SRTPort) + "?streamid=#!::h=" + a.cfg.PushDomain + ",r=" + path + "?auth_key=" + auth + ",m=publish"
}
return "rtmps://" + a.cfg.PushDomain + "/" + a.cfg.AppName + "/" + streamName + "?auth_key=" + auth
}