修复内置鉴权,音频轨道因IDR锁环并发问题,增加Internal配置,mp4重放能力

This commit is contained in:
langhuihui
2023-04-29 21:57:40 +08:00
parent 2eea5c3706
commit da92a2238f
12 changed files with 50 additions and 35 deletions

View File

@@ -29,6 +29,8 @@
- 获取engine信息 `/api/sysInfo` 返回值{Version:xxx,StartTime:xxx,IP:[xxx.xxx.xxx.xxx]} - 获取engine信息 `/api/sysInfo` 返回值{Version:xxx,StartTime:xxx,IP:[xxx.xxx.xxx.xxx]}
- 获取系统基本情况 `/api/summary` 返回值Summary数据 - 获取系统基本情况 `/api/summary` 返回值Summary数据
- 获取所有插件信息 `/api/plugins` 返回值Plugin数据 - 获取所有插件信息 `/api/plugins` 返回值Plugin数据
- 读取mp4文件再次发布为视频流 `/api/replay/mp4?streamPath=xxx&dump=filepath` filepath是文件路径
- 读取ts文件再次发布为视频流 `/api/replay/ts?streamPath=xxx&dump=filepath` filepath是文件路径
- 获取指定的配置信息 `/api/getconfig?name=xxx` 返回xxx插件的配置信息如果不带参数或参数为空则返回全局配置 - 获取指定的配置信息 `/api/getconfig?name=xxx` 返回xxx插件的配置信息如果不带参数或参数为空则返回全局配置
- 修改并保存配置信息 `/api/modifyconfig?name=xxx&yaml=1` 修改xxx插件的配置信息,在请求的body中传入修改后的配置yaml字符串 - 修改并保存配置信息 `/api/modifyconfig?name=xxx&yaml=1` 修改xxx插件的配置信息,在请求的body中传入修改后的配置yaml字符串
- 热更新配置信息 `/api/updateconfig?name=xxx` 热更新xxx插件的配置信息如果不带参数或参数为空则热更新全局配置 - 热更新配置信息 `/api/updateconfig?name=xxx` 热更新xxx插件的配置信息如果不带参数或参数为空则热更新全局配置

View File

@@ -62,6 +62,7 @@ type Subscribe struct {
Key string // 订阅鉴权key Key string // 订阅鉴权key
SecretArgName string `default:"secret"` // 订阅鉴权参数名 SecretArgName string `default:"secret"` // 订阅鉴权参数名
ExpireArgName string `default:"expire"` // 订阅鉴权失效时间参数名 ExpireArgName string `default:"expire"` // 订阅鉴权失效时间参数名
Internal bool `default:"false"` // 是否内部订阅
} }
func (c *Subscribe) GetSubscribeConfig() *Subscribe { func (c *Subscribe) GetSubscribeConfig() *Subscribe {

2
go.mod
View File

@@ -41,7 +41,7 @@ require (
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect github.com/tklauser/numcpus v0.6.0 // indirect
github.com/yapingcat/gomedia v0.0.0-20230222121919-c67df405bf33 github.com/yapingcat/gomedia v0.0.0-20230426092936-387031404274
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.4.0 // indirect golang.org/x/crypto v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect

2
go.sum
View File

@@ -150,6 +150,8 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/yapingcat/gomedia v0.0.0-20230222121919-c67df405bf33 h1:uyZY++dluUg7iTSsNzuOVln/mC2U2KXwgKLfKLCJ74Y= github.com/yapingcat/gomedia v0.0.0-20230222121919-c67df405bf33 h1:uyZY++dluUg7iTSsNzuOVln/mC2U2KXwgKLfKLCJ74Y=
github.com/yapingcat/gomedia v0.0.0-20230222121919-c67df405bf33/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc= github.com/yapingcat/gomedia v0.0.0-20230222121919-c67df405bf33/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc=
github.com/yapingcat/gomedia v0.0.0-20230426092936-387031404274 h1:cj4I+bvWX9I+Hg6tnZ7DAiOVxzhyLhdvYVKp+WpM/2c=
github.com/yapingcat/gomedia v0.0.0-20230426092936-387031404274/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=

23
http.go
View File

@@ -22,6 +22,11 @@ type GlobalConfig struct {
config.Engine config.Engine
} }
func ShouldYaml(r *http.Request) bool {
format := r.URL.Query().Get("format")
return r.URL.Query().Get("yaml") != "" || format == "yaml"
}
func (conf *GlobalConfig) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (conf *GlobalConfig) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("Monibuca API Server\n")) rw.Write([]byte("Monibuca API Server\n"))
for _, api := range apiList { for _, api := range apiList {
@@ -34,11 +39,11 @@ func fetchSummary() *Summary {
} }
func (conf *GlobalConfig) API_summary(rw http.ResponseWriter, r *http.Request) { func (conf *GlobalConfig) API_summary(rw http.ResponseWriter, r *http.Request) {
format := r.URL.Query().Get("format") y := ShouldYaml(r)
if r.Header.Get("Accept") == "text/event-stream" { if r.Header.Get("Accept") == "text/event-stream" {
summary.Add() summary.Add()
defer summary.Done() defer summary.Done()
if format == "yaml" { if y {
util.ReturnYaml(fetchSummary, time.Second, rw, r) util.ReturnYaml(fetchSummary, time.Second, rw, r)
} else { } else {
util.ReturnJson(fetchSummary, time.Second, rw, r) util.ReturnJson(fetchSummary, time.Second, rw, r)
@@ -47,7 +52,7 @@ func (conf *GlobalConfig) API_summary(rw http.ResponseWriter, r *http.Request) {
if !summary.Running() { if !summary.Running() {
summary.collect() summary.collect()
} }
if format == "yaml" { if y {
if err := yaml.NewEncoder(rw).Encode(&summary); err != nil { if err := yaml.NewEncoder(rw).Encode(&summary); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
} }
@@ -71,7 +76,11 @@ func (conf *GlobalConfig) API_plugins(rw http.ResponseWriter, r *http.Request) {
func (conf *GlobalConfig) API_stream(rw http.ResponseWriter, r *http.Request) { func (conf *GlobalConfig) API_stream(rw http.ResponseWriter, r *http.Request) {
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" { if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
if s := Streams.Get(streamPath); s != nil { if s := Streams.Get(streamPath); s != nil {
util.ReturnJson(func() *Stream { return s }, time.Second, rw, r) if ShouldYaml(r) {
util.ReturnYaml(func() *Stream { return s }, time.Second, rw, r)
} else {
util.ReturnJson(func() *Stream { return s }, time.Second, rw, r)
}
} else { } else {
http.Error(rw, NO_SUCH_STREAM, http.StatusNotFound) http.Error(rw, NO_SUCH_STREAM, http.StatusNotFound)
} }
@@ -113,7 +122,7 @@ func (conf *GlobalConfig) API_getConfig(w http.ResponseWriter, r *http.Request)
} else { } else {
p = Engine p = Engine
} }
if q.Get("yaml") != "" { if ShouldYaml(r) {
mm, err := yaml.Marshal(p.RawConfig) mm, err := yaml.Marshal(p.RawConfig)
if err != nil { if err != nil {
mm = []byte("") mm = []byte("")
@@ -145,7 +154,7 @@ func (conf *GlobalConfig) API_modifyConfig(w http.ResponseWriter, r *http.Reques
} else { } else {
p = Engine p = Engine
} }
if q.Has("yaml") { if ShouldYaml(r) {
err = yaml.NewDecoder(r.Body).Decode(&p.Modified) err = yaml.NewDecoder(r.Body).Decode(&p.Modified)
} else { } else {
err = json.NewDecoder(r.Body).Decode(&p.Modified) err = json.NewDecoder(r.Body).Decode(&p.Modified)
@@ -346,7 +355,7 @@ func (c *GlobalConfig) API_replay_ps(w http.ResponseWriter, r *http.Request) {
pub.SetIO(f) pub.SetIO(f)
if err = Engine.Publish(streamPath, &pub); err == nil { if err = Engine.Publish(streamPath, &pub); err == nil {
go pub.Replay(f) go pub.Replay(f)
w.Write([]byte("ok")) w.Write([]byte("ok"))
} else { } else {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }

30
io.go
View File

@@ -129,6 +129,20 @@ var (
OnAuthPub func(p *util.Promise[IPublisher]) error OnAuthPub func(p *util.Promise[IPublisher]) error
) )
func (io *IO) auth(key string, secret string, expire string) bool {
if unixTime, err := strconv.ParseInt(expire, 16, 64); err != nil || time.Now().Unix() > unixTime {
return false
}
trueSecret := md5.Sum([]byte(key + io.Stream.Path + expire))
for i := 0; i < 16; i++ {
hex, err := strconv.ParseInt(secret[i<<1:(i<<1)+2], 16, 16)
if trueSecret[i] != byte(hex) || err != nil {
return false
}
}
return true
}
// receive 用于接收发布或者订阅 // receive 用于接收发布或者订阅
func (io *IO) receive(streamPath string, specific IIO) error { func (io *IO) receive(streamPath string, specific IIO) error {
streamPath = strings.Trim(streamPath, "/") streamPath = strings.Trim(streamPath, "/")
@@ -206,13 +220,7 @@ func (io *IO) receive(streamPath string, specific IIO) error {
return err return err
} }
} else if conf.Key != "" { } else if conf.Key != "" {
secret := io.Args.Get(conf.SecretArgName) if !io.auth(conf.Key, io.Args.Get(conf.SecretArgName), io.Args.Get(conf.ExpireArgName)) {
t := io.Args.Get(conf.ExpireArgName)
if unixTime, err := strconv.ParseInt(t, 16, 64); err != nil || time.Now().Unix() > unixTime {
return ErrAuth
}
trueSecret := md5.Sum([]byte(conf.Key + s.StreamName + t))
if string(trueSecret[:]) != secret {
return ErrAuth return ErrAuth
} }
} }
@@ -247,13 +255,7 @@ func (io *IO) receive(streamPath string, specific IIO) error {
return err return err
} }
} else if conf := specific.(ISubscriber).GetSubscriber().Config; conf.Key != "" { } else if conf := specific.(ISubscriber).GetSubscriber().Config; conf.Key != "" {
secret := io.Args.Get(conf.SecretArgName) if !io.auth(conf.Key, io.Args.Get(conf.SecretArgName), io.Args.Get(conf.ExpireArgName)) {
t := io.Args.Get(conf.ExpireArgName)
if unixTime, err := strconv.ParseInt(t, 16, 64); err != nil || time.Now().Unix() > unixTime {
return ErrAuth
}
trueSecret := md5.Sum([]byte(conf.Key + s.StreamName + t))
if string(trueSecret[:]) != secret {
return ErrAuth return ErrAuth
} }
} }

View File

@@ -48,6 +48,7 @@ reamins: 剩余
"first frame read": 第一帧已读取 "first frame read": 第一帧已读取
"fu have no start": rtp的FU起始包丢了 "fu have no start": rtp的FU起始包丢了
"disabled by env": 被环境变量禁用 "disabled by env": 被环境变量禁用
"event cost too much time": 事件处理耗时过长
firstTs: 第一帧时间戳 firstTs: 第一帧时间戳
firstSeq: 第一帧序列号 firstSeq: 第一帧序列号
skipSeq: 跳过序列号 skipSeq: 跳过序列号

View File

@@ -50,11 +50,11 @@ func (p *MP4Publisher) ReadMP4Data(source io.ReadSeeker) error {
} }
switch pkg.Cid { switch pkg.Cid {
case mp4.MP4_CODEC_H264, mp4.MP4_CODEC_H265: case mp4.MP4_CODEC_H264, mp4.MP4_CODEC_H265:
p.VideoTrack.WriteAnnexB(uint32(pkg.Pts), uint32(pkg.Dts), pkg.Data) p.VideoTrack.WriteAnnexB(uint32(pkg.Pts*90), uint32(pkg.Dts*90), pkg.Data)
case mp4.MP4_CODEC_AAC: case mp4.MP4_CODEC_AAC:
p.AudioTrack.WriteADTS(uint32(pkg.Pts), pkg.Data) p.AudioTrack.WriteADTS(uint32(pkg.Pts*90), pkg.Data)
case mp4.MP4_CODEC_G711A, mp4.MP4_CODEC_G711U: case mp4.MP4_CODEC_G711A, mp4.MP4_CODEC_G711U:
p.AudioTrack.WriteRaw(uint32(pkg.Pts), pkg.Data) p.AudioTrack.WriteRaw(uint32(pkg.Pts*90), pkg.Data)
} }
} }
} }

View File

@@ -416,9 +416,6 @@ func (s *Stream) run() {
s.Subscribers.Broadcast(event) s.Subscribers.Broadcast(event)
} }
}) })
if s.State != STATE_PUBLISHING {
continue
}
if s.Tracks.Len() == 0 || (s.Publisher != nil && s.Publisher.IsClosed()) { if s.Tracks.Len() == 0 || (s.Publisher != nil && s.Publisher.IsClosed()) {
s.action(ACTION_PUBLISHLOST) s.action(ACTION_PUBLISHLOST)
} else { } else {
@@ -530,6 +527,8 @@ func (s *Stream) run() {
if _, ok := v.Value.(*track.Audio); ok && !s.GetPublisherConfig().PubVideo { if _, ok := v.Value.(*track.Audio); ok && !s.GetPublisherConfig().PubVideo {
s.Subscribers.AbortWait() s.Subscribers.AbortWait()
} }
// 这里重置的目的是当PublishTimeout设置很大的情况下需要及时取消订阅者的等待
s.timeout.Reset(time.Second * 5)
} else { } else {
v.Reject(ErrBadTrackName) v.Reject(ErrBadTrackName)
} }

View File

@@ -115,7 +115,6 @@ type TrackPlayer struct {
// Subscriber 订阅者实体定义 // Subscriber 订阅者实体定义
type Subscriber struct { type Subscriber struct {
IO IO
IsInternal bool //是否内部订阅,不放入订阅列表
Config *config.Subscribe Config *config.Subscribe
TrackPlayer `json:"-" yaml:"-"` TrackPlayer `json:"-" yaml:"-"`
} }
@@ -384,7 +383,7 @@ func (s *Subscriber) PlayBlock(subType byte) {
func (s *Subscriber) onStop() { func (s *Subscriber) onStop() {
if !s.Stream.IsClosed() { if !s.Stream.IsClosed() {
s.Info("stop") s.Info("stop")
if !s.IsInternal { if !s.Config.Internal {
s.Stream.Receive(s.Spesific) s.Stream.Receive(s.Spesific)
} }
} }

View File

@@ -105,7 +105,7 @@ func (s *Subscribers) Delete(suber ISubscriber) {
func (s *Subscribers) Add(suber ISubscriber, wait *waitTracks) { func (s *Subscribers) Add(suber ISubscriber, wait *waitTracks) {
io := suber.GetSubscriber() io := suber.GetSubscriber()
if io.IsInternal { if io.Config.Internal {
s.internal[suber] = wait s.internal[suber] = wait
io.Info("innersuber +1", zap.Int("remains", len(s.internal))) io.Info("innersuber +1", zap.Int("remains", len(s.internal)))
} else { } else {

View File

@@ -85,8 +85,8 @@ func (a *Audio) CompleteRTP(value *AVFrame) {
} }
func (a *Audio) Narrow() { func (a *Audio) Narrow() {
if a.HistoryRing == nil && a.IDRing != nil { // if a.HistoryRing == nil && a.IDRing != nil {
a.narrow(int(a.Value.Sequence - a.IDRing.Value.Sequence)) // a.narrow(int(a.Value.Sequence - a.IDRing.Value.Sequence))
} // }
a.AddIDR() a.AddIDR()
} }