修复配置合并bug

This commit is contained in:
dexter
2022-02-12 21:15:14 +08:00
parent b08d03efba
commit 2c7ca7b781
18 changed files with 227 additions and 130 deletions

View File

@@ -2,18 +2,42 @@ package codec
import ( import (
"errors" "errors"
) )
type AudioCodecID byte
type VideoCodecID byte
const ( const (
ADTS_HEADER_SIZE = 7 ADTS_HEADER_SIZE = 7
CodecID_AAC = 0xA CodecID_AAC AudioCodecID = 0xA
CodecID_PCMA = 7 CodecID_PCMA AudioCodecID = 7
CodecID_PCMU = 8 CodecID_PCMU AudioCodecID = 8
CodecID_H264 = 7 CodecID_H264 VideoCodecID = 7
CodecID_H265 = 0xC CodecID_H265 VideoCodecID = 0xC
) )
func (codecId AudioCodecID) String() string {
switch codecId {
case CodecID_AAC:
return "aac"
case CodecID_PCMA:
return "pcma"
case CodecID_PCMU:
return "pcmu"
}
return "unknow"
}
func (codecId VideoCodecID) String() string {
switch codecId {
case CodecID_H264:
return "h264"
case CodecID_H265:
return "h265"
}
return "unknow"
}
// ISO/IEC 14496-3 38(52)/page // ISO/IEC 14496-3 38(52)/page
// //
// Audio // Audio
@@ -212,5 +236,3 @@ func ParseRTPAAC(payload []byte) (result [][]byte) {
} }
return return
} }

View File

@@ -94,7 +94,7 @@ func (rtp *RTPFrame) Marshal() *RTPFrame {
func (rtp *RTPFrame) Unmarshal(raw []byte) *RTPFrame { func (rtp *RTPFrame) Unmarshal(raw []byte) *RTPFrame {
rtp.Raw = raw rtp.Raw = raw
if err := rtp.Packet.Unmarshal(raw); err != nil { if err := rtp.Packet.Unmarshal(raw); err != nil {
logrus.Errorln(err) logrus.Error(err)
return nil return nil
} }
return rtp return rtp
@@ -154,11 +154,11 @@ func (avcc AVCCFrame) IsSequence() bool {
func (avcc AVCCFrame) CTS() uint32 { func (avcc AVCCFrame) CTS() uint32 {
return uint32(avcc[2])<<24 | uint32(avcc[3])<<8 | uint32(avcc[4]) return uint32(avcc[2])<<24 | uint32(avcc[3])<<8 | uint32(avcc[4])
} }
func (avcc AVCCFrame) VideoCodecID() byte { func (avcc AVCCFrame) VideoCodecID() codec.VideoCodecID {
return avcc[0] & 0x0F return codec.VideoCodecID(avcc[0] & 0x0F)
} }
func (avcc AVCCFrame) AudioCodecID() byte { func (avcc AVCCFrame) AudioCodecID() codec.AudioCodecID {
return avcc[0] >> 4 return codec.AudioCodecID(avcc[0] >> 4)
} }
// func (annexb AnnexBFrame) ToSlices() (ret []NALUSlice) { // func (annexb AnnexBFrame) ToSlices() (ret []NALUSlice) {

View File

@@ -8,6 +8,7 @@ type Track interface {
type AVTrack interface { type AVTrack interface {
Track Track
Attach()
WriteAVCC(ts uint32, frame AVCCFrame) //写入AVCC格式的数据 WriteAVCC(ts uint32, frame AVCCFrame) //写入AVCC格式的数据
Flush() Flush()
} }

View File

@@ -6,6 +6,8 @@ import (
"reflect" "reflect"
"strings" "strings"
"time" "time"
"github.com/sirupsen/logrus"
) )
type Config map[string]any type Config map[string]any
@@ -57,8 +59,13 @@ func (config Config) Unmarshal(s any) {
nameMap[strings.ToLower(name)] = name nameMap[strings.ToLower(name)] = name
} }
for k, v := range config { for k, v := range config {
name, ok := nameMap[k]
if !ok {
logrus.Error("no config named:", k)
continue
}
// 需要被写入的字段 // 需要被写入的字段
fv := el.FieldByName(nameMap[k]) fv := el.FieldByName(name)
if value := reflect.ValueOf(v); value.Kind() == reflect.Slice { if value := reflect.ValueOf(v); value.Kind() == reflect.Slice {
l := value.Len() l := value.Len()
s := reflect.MakeSlice(fv.Type(), l, value.Cap()) s := reflect.MakeSlice(fv.Type(), l, value.Cap())
@@ -110,8 +117,17 @@ func (config Config) Merge(source Config) {
} }
} }
func (config Config) Set(key string, value any) { func (config *Config) Set(key string, value any) {
config[strings.ToLower(key)] = value if *config == nil {
*config = Config{strings.ToLower(key): value}
} else {
(*config)[strings.ToLower(key)] = value
}
}
func (config Config) Get(key string) any {
v, _ := config[strings.ToLower(key)]
return v
} }
func (config Config) Has(key string) (ok bool) { func (config Config) Has(key string) (ok bool) {
@@ -148,13 +164,14 @@ func Struct2Config(s any) (config Config) {
} }
for i, j := 0, t.NumField(); i < j; i++ { for i, j := 0, t.NumField(); i < j; i++ {
ft := t.Field(i) ft := t.Field(i)
name := strings.ToLower(ft.Name)
switch ft.Type.Kind() { switch ft.Type.Kind() {
case reflect.Struct: case reflect.Struct:
config[ft.Name] = Struct2Config(v.Field(i)) config[name] = Struct2Config(v.Field(i))
case reflect.Slice: case reflect.Slice:
fallthrough fallthrough
default: default:
reflect.ValueOf(config).SetMapIndex(reflect.ValueOf(strings.ToLower(ft.Name)), v.Field(i)) reflect.ValueOf(config).SetMapIndex(reflect.ValueOf(name), v.Field(i))
} }
} }
return return

View File

@@ -33,6 +33,13 @@ type Push struct {
PushList map[string]string // 自动推流列表 PushList map[string]string // 自动推流列表
} }
func (p *Push) AddPush(streamPath string, url string) {
if p.PushList == nil {
p.PushList = make(map[string]string)
}
p.PushList[streamPath] = url
}
type Engine struct { type Engine struct {
Publish Publish
Subscribe Subscribe
@@ -44,7 +51,7 @@ type Engine struct {
} }
var Global = &Engine{ var Global = &Engine{
Publish{true, true, false, 10, 10}, Publish{true, true, false, 10, 0},
Subscribe{true, true, false, 10}, Subscribe{true, true, false, 10},
HTTP{ListenAddr: ":8080", CORS: true}, HTTP{ListenAddr: ":8080", CORS: true},
false, true, true, true, false, true, true, true,

View File

@@ -3,9 +3,10 @@ package engine
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
log "github.com/sirupsen/logrus"
"github.com/Monibuca/engine/v4/config" "github.com/Monibuca/engine/v4/config"
. "github.com/logrusorgru/aurora" . "github.com/logrusorgru/aurora"
log "github.com/sirupsen/logrus"
) )
type GlobalConfig struct { type GlobalConfig struct {
@@ -14,8 +15,9 @@ type GlobalConfig struct {
} }
func (cfg *GlobalConfig) Update(override config.Config) { func (cfg *GlobalConfig) Update(override config.Config) {
// 使得RawConfig具备全量配置信息用于合并到插件配置中
Engine.RawConfig = config.Struct2Config(cfg.Engine) Engine.RawConfig = config.Struct2Config(cfg.Engine)
log.Infoln(Green("api server start at"), BrightBlue(cfg.ListenAddr), BrightBlue(cfg.ListenAddrTLS)) log.Info(Green("api server start at"), BrightBlue(cfg.ListenAddr), BrightBlue(cfg.ListenAddrTLS))
cfg.Listen(Engine, cfg) cfg.Listen(Engine, cfg)
} }

30
log.go
View File

@@ -2,24 +2,31 @@ package engine
import ( import (
"io" "io"
"github.com/mattn/go-colorable" "strings"
"github.com/Monibuca/engine/v4/util" "github.com/Monibuca/engine/v4/util"
. "github.com/logrusorgru/aurora"
"github.com/mattn/go-colorable"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
var levelColors = []func(any) Value{Red, Red, Red, Yellow, Blue, Green, White}
// MultiLogWriter 可动态增减输出的多端写日志类 // MultiLogWriter 可动态增减输出的多端写日志类
type MultiLogWriter struct { type MultiLogWriter struct {
writers util.Slice[io.Writer] writers util.Slice[io.Writer]
io.Writer io.Writer
} }
var colorableStdout = colorable.NewColorableStdout() var colorableStdout = colorable.NewColorableStdout()
var LogWriter = MultiLogWriter{ var LogWriter = &MultiLogWriter{
writers: util.Slice[io.Writer]{colorableStdout}, writers: util.Slice[io.Writer]{colorableStdout},
Writer: colorableStdout, Writer: colorableStdout,
} }
func init() { func init() {
log.SetOutput(LogWriter) log.SetOutput(LogWriter)
log.SetFormatter(LogWriter)
} }
func (ml *MultiLogWriter) Add(w io.Writer) { func (ml *MultiLogWriter) Add(w io.Writer) {
@@ -31,3 +38,22 @@ func (ml *MultiLogWriter) Delete(w io.Writer) {
ml.writers.Delete(w) ml.writers.Delete(w)
ml.Writer = io.MultiWriter(ml.writers...) ml.Writer = io.MultiWriter(ml.writers...)
} }
func (ml *MultiLogWriter) Format(entry *log.Entry) (b []byte, err error) {
pl := entry.Data["plugin"]
if pl == nil {
pl = "Engine"
}
l := strings.ToUpper(entry.Level.String())[:1]
var props string
if stream := entry.Data["stream"]; stream != nil {
props = Sprintf("[s:%s] ", stream)
}
if puber := entry.Data["puber"]; puber != nil {
props += Sprintf("[pub:%s] ", puber)
}
if suber := entry.Data["suber"]; suber != nil {
props += Sprintf("[sub:%s] ", suber)
}
return []byte(Sprintf(levelColors[entry.Level]("%s [%s] [%s]\t %s%s\n"), l, entry.Time.Format("15:04:05"), pl, props, entry.Message)), nil
}

12
main.go
View File

@@ -49,27 +49,29 @@ func (p PullOnSubscribe) Pull(streamPath string) {
func Run(ctx context.Context, configFile string) (err error) { func Run(ctx context.Context, configFile string) (err error) {
Engine.Context = ctx Engine.Context = ctx
if err := util.CreateShutdownScript(); err != nil { if err := util.CreateShutdownScript(); err != nil {
log.Errorln("create shutdown script error:", err) log.Error("create shutdown script error:", err)
} }
StartTime = time.Now() StartTime = time.Now()
if ConfigRaw, err = ioutil.ReadFile(configFile); err != nil { if ConfigRaw, err = ioutil.ReadFile(configFile); err != nil {
log.Errorln("read config file error:", err) log.Error("read config file error:", err)
} }
settingDir = filepath.Join(filepath.Dir(configFile), ".m7s") settingDir = filepath.Join(filepath.Dir(configFile), ".m7s")
if err = os.MkdirAll(settingDir, 0755); err != nil { if err = os.MkdirAll(settingDir, 0755); err != nil {
log.Errorln("create dir .m7s error:", err) log.Error("create dir .m7s error:", err)
return return
} }
log.Infoln(BgGreen(White("Ⓜ starting m7s v4"))) log.Info(Blink("Ⓜ starting m7s v4"))
var cg config.Config var cg config.Config
if ConfigRaw != nil { if ConfigRaw != nil {
if err = yaml.Unmarshal(ConfigRaw, &cg); err == nil { if err = yaml.Unmarshal(ConfigRaw, &cg); err == nil {
Engine.RawConfig = cg.GetChild("global") Engine.RawConfig = cg.GetChild("global")
//将配置信息同步到结构体
Engine.RawConfig.Unmarshal(config.Global) Engine.RawConfig.Unmarshal(config.Global)
} }
} else { } else {
log.Warnln("no config file found , use default config values") log.Warn("no config file found , use default config values")
} }
Engine.Entry = log.WithContext(Engine)
Engine.registerHandler() Engine.registerHandler()
go EngineConfig.Update(Engine.RawConfig) go EngineConfig.Update(Engine.RawConfig)
for name, plugin := range Plugins { for name, plugin := range Plugins {

View File

@@ -35,7 +35,7 @@ func InstallPlugin(config config.Plugin) *Plugin {
if config != EngineConfig { if config != EngineConfig {
plugin.Entry = log.WithField("plugin", name) plugin.Entry = log.WithField("plugin", name)
Plugins[name] = plugin Plugins[name] = plugin
plugin.Infoln(Green("install"), BrightBlue(plugin.Version)) plugin.Info(Green("install"), BrightBlue(plugin.Version))
} }
return plugin return plugin
} }
@@ -72,12 +72,12 @@ func (opt *Plugin) HandleFunc(pattern string, handler func(http.ResponseWriter,
if opt != Engine { if opt != Engine {
pattern = "/" + strings.ToLower(opt.Name) + pattern pattern = "/" + strings.ToLower(opt.Name) + pattern
} }
opt.Infoln("http handle added:", pattern) opt.Info("http handle added:", pattern)
EngineConfig.HandleFunc(pattern, func(rw http.ResponseWriter, r *http.Request) { EngineConfig.HandleFunc(pattern, func(rw http.ResponseWriter, r *http.Request) {
if cors { if cors {
util.CORS(rw, r) util.CORS(rw, r)
} }
opt.Debugln(r.RemoteAddr, " -> ", pattern) opt.Debug(r.RemoteAddr, " -> ", pattern)
handler(rw, r) handler(rw, r)
}) })
} }
@@ -99,9 +99,9 @@ func (opt *Plugin) assign() {
// 用全局配置覆盖没有设置的配置 // 用全局配置覆盖没有设置的配置
for _, fname := range MergeConfigs { for _, fname := range MergeConfigs {
if _, ok := t.FieldByName(fname); ok { if _, ok := t.FieldByName(fname); ok {
if Engine.RawConfig.Has(fname) { if v, ok := Engine.RawConfig[strings.ToLower(fname)]; ok {
if !opt.RawConfig.Has(fname) { if !opt.RawConfig.Has(fname) {
opt.RawConfig.Set(fname, Engine.RawConfig[fname]) opt.RawConfig.Set(fname, v)
} else if opt.RawConfig.HasChild(fname) { } else if opt.RawConfig.HasChild(fname) {
opt.RawConfig.GetChild(fname).Merge(Engine.RawConfig.GetChild(fname)) opt.RawConfig.GetChild(fname).Merge(Engine.RawConfig.GetChild(fname))
} }

View File

@@ -13,7 +13,9 @@ type IPublisher interface {
Close() // 流关闭时或者被踢时触发 Close() // 流关闭时或者被踢时触发
OnStateChange(oldState StreamState, newState StreamState) bool OnStateChange(oldState StreamState, newState StreamState) bool
OnStateChanged(oldState StreamState, newState StreamState) OnStateChanged(oldState StreamState, newState StreamState)
Publish(streamPath string, specific IPublisher, config config.Publish) bool
} }
type IPuller interface { type IPuller interface {
IPublisher IPublisher
Pull(int) Pull(int)
@@ -35,10 +37,10 @@ func (pub *Publisher) Publish(streamPath string, specific IPublisher, config con
} }
if s.Publisher != nil { if s.Publisher != nil {
if config.KickExsit { if config.KickExsit {
s.Warnln("kick", s.Publisher) s.Warn("kick", s.Publisher)
s.Publisher.Close() s.Publisher.Close()
} else { } else {
s.Warnln("publisher exsit", s.Publisher) s.Warn("publisher exsit", s.Publisher)
return false return false
} }
} }

View File

@@ -62,6 +62,9 @@ var StreamFSM = [STATE_DESTROYED + 1]map[StreamAction]StreamState{
{ {
ACTION_TIMEOUT: STATE_DESTROYED, ACTION_TIMEOUT: STATE_DESTROYED,
}, },
{
},
} }
// Streams 所有的流集合 // Streams 所有的流集合
@@ -124,11 +127,11 @@ func findOrCreateStream(streamPath string, waitTimeout time.Duration) (s *Stream
} }
p := strings.Split(u.Path, "/") p := strings.Split(u.Path, "/")
if len(p) < 2 { if len(p) < 2 {
log.Warnln(Red("Stream Path Format Error:"), streamPath) log.Warn(Red("Stream Path Format Error:"), streamPath)
return nil, false return nil, false
} }
if s, ok := Streams.Map[u.Path]; ok { if s, ok := Streams.Map[u.Path]; ok {
s.Debugln(Green("Stream Found")) s.Debug(Green("Stream Found"))
return s, false return s, false
} else { } else {
p := strings.Split(u.Path, "/") p := strings.Split(u.Path, "/")
@@ -138,7 +141,7 @@ func findOrCreateStream(streamPath string, waitTimeout time.Duration) (s *Stream
StreamName: util.LastElement(p), StreamName: util.LastElement(p),
Entry: log.WithField("stream", u.Path), Entry: log.WithField("stream", u.Path),
} }
s.Infoln("created:", streamPath) s.Info("created")
s.WaitTimeout = waitTimeout s.WaitTimeout = waitTimeout
Streams.Map[u.Path] = s Streams.Map[u.Path] = s
s.actionChan = make(chan any, 1) s.actionChan = make(chan any, 1)
@@ -152,10 +155,16 @@ func findOrCreateStream(streamPath string, waitTimeout time.Duration) (s *Stream
} }
func (r *Stream) action(action StreamAction) bool { func (r *Stream) action(action StreamAction) bool {
r.Tracef("action:%d", action)
if next, ok := StreamFSM[r.State][action]; ok { if next, ok := StreamFSM[r.State][action]; ok {
if r.Publisher == nil || r.Publisher.OnStateChange(r.State, next) { if r.Publisher != nil {
// 给Publisher状态变更的回调方便进行远程拉流等操作
defer r.Publisher.OnStateChanged(r.State, next) defer r.Publisher.OnStateChanged(r.State, next)
r.Debugln(action, " :", r.State, "->", next) if !r.Publisher.OnStateChange(r.State, next) {
return false
}
}
r.Debug(action, " :", r.State, "->", next)
r.State = next r.State = next
switch next { switch next {
case STATE_WAITPUBLISH: case STATE_WAITPUBLISH:
@@ -188,7 +197,6 @@ func (r *Stream) action(action StreamAction) bool {
default: default:
r.timeout.Stop() r.timeout.Stop()
} }
}
return true return true
} }
return false return false
@@ -197,7 +205,7 @@ func (r *Stream) IsClosed() bool {
if r == nil { if r == nil {
return true return true
} }
return r.State == STATE_CLOSED return r.State >= STATE_CLOSED
} }
func (r *Stream) Close() { func (r *Stream) Close() {
@@ -207,13 +215,13 @@ func (r *Stream) Close() {
} }
func (r *Stream) UnSubscribe(sub *Subscriber) { func (r *Stream) UnSubscribe(sub *Subscriber) {
r.Debugln("unsubscribe", sub.ID) r.Debug("unsubscribe", sub.ID)
if !r.IsClosed() { if !r.IsClosed() {
r.actionChan <- UnSubscibeAction(sub) r.actionChan <- UnSubscibeAction(sub)
} }
} }
func (r *Stream) Subscribe(sub *Subscriber) { func (r *Stream) Subscribe(sub *Subscriber) {
r.Debugln("subscribe", sub.ID) r.Debug("subscribe", sub.ID)
if !r.IsClosed() { if !r.IsClosed() {
sub.Stream = r sub.Stream = r
sub.Context, sub.cancel = context.WithCancel(r) sub.Context, sub.cancel = context.WithCancel(r)
@@ -223,14 +231,15 @@ func (r *Stream) Subscribe(sub *Subscriber) {
// 流状态处理中枢,包括接收订阅发布指令等 // 流状态处理中枢,包括接收订阅发布指令等
func (r *Stream) run() { func (r *Stream) run() {
var done = r.Done()
for { for {
select { select {
case <-r.timeout.C: case <-r.timeout.C:
r.Debugln(r.State, "timeout") r.Debugf("%v timeout", r.State)
r.action(ACTION_TIMEOUT) r.action(ACTION_TIMEOUT)
case <-r.Done(): case <-done:
r.action(ACTION_CLOSE) r.action(ACTION_CLOSE)
done = nil
case action, ok := <-r.actionChan: case action, ok := <-r.actionChan:
if ok { if ok {
switch v := action.(type) { switch v := action.(type) {
@@ -243,14 +252,14 @@ func (r *Stream) run() {
case *Subscriber: case *Subscriber:
r.Subscribers.Add(v) r.Subscribers.Add(v)
Bus.Publish(Event_SUBSCRIBE, v) Bus.Publish(Event_SUBSCRIBE, v)
v.Infoln(Sprintf(Yellow("added remains:%d"), Cyan(v.ID), Blue(len(r.Subscribers)))) v.Info(Sprintf(Yellow("added remains:%d") ,len(r.Subscribers)))
if r.Subscribers.Len() == 1 { if r.Subscribers.Len() == 1 {
r.action(ACTION_FIRSTENTER) r.action(ACTION_FIRSTENTER)
} }
case UnSubscibeAction: case UnSubscibeAction:
if r.Subscribers.Delete(v) { if r.Subscribers.Delete(v) {
Bus.Publish(Event_UNSUBSCRIBE, v) Bus.Publish(Event_UNSUBSCRIBE, v)
(*Subscriber)(v).Infoln(Sprintf(Yellow("removed remains:%d"), Cyan(v.ID), Blue(len(r.Subscribers)))) (*Subscriber)(v).Info(Sprintf(Yellow("removed remains:%d"), len(r.Subscribers)))
if r.Subscribers.Len() == 0 && r.WaitCloseTimeout > 0 { if r.Subscribers.Len() == 0 && r.WaitCloseTimeout > 0 {
r.action(ACTION_LASTLEAVE) r.action(ACTION_LASTLEAVE)
} }
@@ -266,7 +275,7 @@ func (r *Stream) run() {
// Update 更新数据重置超时定时器 // Update 更新数据重置超时定时器
func (r *Stream) Update() uint32 { func (r *Stream) Update() uint32 {
if r.State == STATE_PUBLISHING { if r.State == STATE_PUBLISHING {
r.Traceln("update") r.Trace("update")
r.timeout.Reset(r.PublishTimeout) r.timeout.Reset(r.PublishTimeout)
} }
return atomic.AddUint32(&r.FrameCount, 1) return atomic.AddUint32(&r.FrameCount, 1)
@@ -274,31 +283,29 @@ func (r *Stream) Update() uint32 {
// 如果暂时不知道编码格式可以用这个 // 如果暂时不知道编码格式可以用这个
func (r *Stream) NewVideoTrack() (vt *track.UnknowVideo) { func (r *Stream) NewVideoTrack() (vt *track.UnknowVideo) {
r.Debugln("create unknow video track") r.Debug("create unknow video track")
vt = &track.UnknowVideo{ vt = &track.UnknowVideo{}
Stream: r, vt.Stream = r
}
return return
} }
func (r *Stream) NewAudioTrack() (at *track.UnknowAudio) { func (r *Stream) NewAudioTrack() (at *track.UnknowAudio) {
r.Debugln("create unknow audio track") r.Debug("create unknow audio track")
at = &track.UnknowAudio{ at = &track.UnknowAudio{}
Stream: r, at.Stream = r
}
return return
} }
func (r *Stream) NewH264Track() *track.H264 { func (r *Stream) NewH264Track() *track.H264 {
r.Debugln("create h264 track") r.Debug("create h264 track")
return track.NewH264(r) return track.NewH264(r)
} }
func (r *Stream) NewH265Track() *track.H265 { func (r *Stream) NewH265Track() *track.H265 {
r.Debugln("create h265 track") r.Debug("create h265 track")
return track.NewH265(r) return track.NewH265(r)
} }
func (r *Stream) NewAACTrack() *track.AAC { func (r *Stream) NewAACTrack() *track.AAC {
r.Debugln("create aac track") r.Debug("create aac track")
return track.NewAAC(r) return track.NewAAC(r)
} }

View File

@@ -48,7 +48,7 @@ func (sub *Subscriber) Subscribe(streamPath string, config config.Subscribe) boo
log.Info(sub.ID, "try to subscribe", streamPath) log.Info(sub.ID, "try to subscribe", streamPath)
s, created := findOrCreateStream(streamPath, config.WaitTimeout.Duration()) s, created := findOrCreateStream(streamPath, config.WaitTimeout.Duration())
if s.IsClosed() { if s.IsClosed() {
log.Warnln("stream is closed") log.Warn("stream is closed")
return false return false
} }
sub.Entry = s.Entry.WithField("suber", sub.ID) sub.Entry = s.Entry.WithField("suber", sub.ID)

View File

@@ -2,7 +2,6 @@ package track
import ( import (
"net" "net"
"strings"
"github.com/Monibuca/engine/v4/codec" "github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common" . "github.com/Monibuca/engine/v4/common"
@@ -15,15 +14,20 @@ var adcflv2 = []byte{0, 0, 0, 15}
type Audio struct { type Audio struct {
Media[AudioSlice] Media[AudioSlice]
CodecID codec.AudioCodecID
Channels byte Channels byte
avccHead []byte avccHead []byte
} }
func (av *Audio) GetName() string { func (at *Audio) Attach() {
if av.Name == "" { at.Stream.AddTrack(at)
return strings.ToLower(codec.SoundFormat[av.CodecID])
} }
return av.Name
func (at *Audio) GetName() string {
if at.Name == "" {
return at.CodecID.String()
}
return at.Name
} }
func (at *Audio) GetInfo() *Audio { func (at *Audio) GetInfo() *Audio {
return at return at
@@ -81,16 +85,15 @@ func (at *Audio) Flush() {
} }
type UnknowAudio struct { type UnknowAudio struct {
Name string Base
Stream IStream AudioTrack
Know AVTrack
} }
func (at *UnknowAudio) WriteAVCC(ts uint32, frame AVCCFrame) { func (at *UnknowAudio) WriteAVCC(ts uint32, frame AVCCFrame) {
if at.Know == nil { if at.AudioTrack == nil {
codecID := frame.AudioCodecID() codecID := frame.AudioCodecID()
if at.Name == "" { if at.Name == "" {
at.Name = strings.ToLower(codec.SoundFormat[codecID]) at.Name = codecID.String()
} }
switch codecID { switch codecID {
case codec.CodecID_AAC: case codec.CodecID_AAC:
@@ -98,11 +101,11 @@ func (at *UnknowAudio) WriteAVCC(ts uint32, frame AVCCFrame) {
return return
} }
a := NewAAC(at.Stream) a := NewAAC(at.Stream)
at.Know = a at.AudioTrack = a
a.SampleSize = 16 a.SampleSize = 16
a.avccHead = []byte{frame[0], 1} a.avccHead = []byte{frame[0], 1}
a.WriteAVCC(0, frame) a.WriteAVCC(0, frame)
a.Stream.AddTrack(&a.Audio) a.Attach()
case codec.CodecID_PCMA, case codec.CodecID_PCMA,
codec.CodecID_PCMU: codec.CodecID_PCMU:
alaw := true alaw := true
@@ -110,7 +113,7 @@ func (at *UnknowAudio) WriteAVCC(ts uint32, frame AVCCFrame) {
alaw = false alaw = false
} }
a := NewG711(at.Stream, alaw) a := NewG711(at.Stream, alaw)
at.Know = a at.AudioTrack = a
a.SampleRate = uint32(codec.SoundRate[(frame[0]&0x0c)>>2]) a.SampleRate = uint32(codec.SoundRate[(frame[0]&0x0c)>>2])
a.SampleSize = 16 a.SampleSize = 16
if frame[0]&0x02 == 0 { if frame[0]&0x02 == 0 {
@@ -118,9 +121,12 @@ func (at *UnknowAudio) WriteAVCC(ts uint32, frame AVCCFrame) {
} }
a.Channels = frame[0]&0x01 + 1 a.Channels = frame[0]&0x01 + 1
a.avccHead = frame[:1] a.avccHead = frame[:1]
a.Stream.AddTrack(&a.Audio) a.Attach()
at.AudioTrack.WriteAVCC(ts, frame)
default:
at.Stream.Errorf("audio codec not support yet:", codecID)
} }
} else { } else {
at.Know.WriteAVCC(ts, frame) at.AudioTrack.WriteAVCC(ts, frame)
} }
} }

View File

@@ -26,7 +26,6 @@ func (bt *Base) Flush(bf *BaseFrame) {
type Media[T RawSlice] struct { type Media[T RawSlice] struct {
Base Base
AVRing[T] `json:"-"` AVRing[T] `json:"-"`
CodecID byte
SampleRate uint32 SampleRate uint32
SampleSize byte SampleSize byte
DecoderConfiguration DecoderConfiguration[T] `json:"-"` //H264(SPS、PPS) H265(VPS、SPS、PPS) AAC(config) DecoderConfiguration DecoderConfiguration[T] `json:"-"` //H264(SPS、PPS) H265(VPS、SPS、PPS) AAC(config)
@@ -110,7 +109,7 @@ func (av *Media[T]) UnmarshalRTP(raw []byte) (frame *RTPFrame) {
av.lastSeq2 = av.lastSeq av.lastSeq2 = av.lastSeq
av.lastSeq = frame.SequenceNumber av.lastSeq = frame.SequenceNumber
if av.lastSeq != av.lastSeq2+1 { //序号不连续 if av.lastSeq != av.lastSeq2+1 { //序号不连续
av.Stream.Warnln("RTP SequenceNumber error", av.lastSeq2, av.lastSeq) av.Stream.Warn("RTP SequenceNumber error", av.lastSeq2, av.lastSeq)
return return
} }
} }

View File

@@ -113,7 +113,7 @@ func (vt *H264) WriteRTP(raw []byte) {
func (vt *H264) Flush() { func (vt *H264) Flush() {
if vt.Value.IFrame { if vt.Value.IFrame {
if vt.IDRing == nil { if vt.IDRing == nil {
defer vt.Stream.AddTrack(&vt.Video) defer vt.Attach()
} }
vt.Video.ComputeGOP() vt.Video.ComputeGOP()
} }

View File

@@ -116,7 +116,7 @@ func (vt *H265) WriteRTP(raw []byte) {
func (vt *H265) Flush() { func (vt *H265) Flush() {
if vt.Value.IFrame { if vt.Value.IFrame {
if vt.IDRing == nil { if vt.IDRing == nil {
defer vt.Stream.AddTrack(&vt.Video) defer vt.Attach()
} }
vt.Video.ComputeGOP() vt.Video.ComputeGOP()
} }

View File

@@ -3,7 +3,6 @@ package track
import ( import (
"bytes" "bytes"
"net" "net"
"strings"
"github.com/Monibuca/engine/v4/codec" "github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common" . "github.com/Monibuca/engine/v4/common"
@@ -13,6 +12,7 @@ import (
type Video struct { type Video struct {
Media[NALUSlice] Media[NALUSlice]
CodecID codec.VideoCodecID
IDRing *util.Ring[AVFrame[NALUSlice]] `json:"-"` //最近的关键帧位置,首屏渲染 IDRing *util.Ring[AVFrame[NALUSlice]] `json:"-"` //最近的关键帧位置,首屏渲染
SPSInfo codec.SPSInfo SPSInfo codec.SPSInfo
GOP int //关键帧间隔 GOP int //关键帧间隔
@@ -20,9 +20,13 @@ type Video struct {
idrCount int //缓存中包含的idr数量 idrCount int //缓存中包含的idr数量
} }
func (t *Video) Attach() {
t.Stream.AddTrack(t)
}
func (t *Video) GetName() string { func (t *Video) GetName() string {
if t.Name == "" { if t.Name == "" {
return strings.ToLower(codec.CodecID[t.CodecID]) return t.CodecID.String()
} }
return t.Name return t.Name
} }
@@ -86,7 +90,7 @@ func (vt *Video) WriteAVCC(ts uint32, frame AVCCFrame) {
vt.Value.AppendRaw(NALUSlice{nalus[vt.nalulenSize:end]}) vt.Value.AppendRaw(NALUSlice{nalus[vt.nalulenSize:end]})
nalus = nalus[end:] nalus = nalus[end:]
} else { } else {
vt.Stream.Errorln("WriteAVCC error,len %d,nalulenSize:%d,end:%d", len(nalus), vt.nalulenSize, end) vt.Stream.Error("WriteAVCC error,len %d,nalulenSize:%d,end:%d", len(nalus), vt.nalulenSize, end)
break break
} }
} }
@@ -100,7 +104,7 @@ func (vt *Video) Flush() {
} }
// AVCC格式补完 // AVCC格式补完
if vt.Value.AVCC == nil && (config.Global.EnableAVCC || config.Global.EnableFLV) { if vt.Value.AVCC == nil && (config.Global.EnableAVCC || config.Global.EnableFLV) {
b := []byte{vt.CodecID, 1, 0, 0, 0} b := []byte{byte(vt.CodecID), 1, 0, 0, 0}
if vt.Value.IFrame { if vt.Value.IFrame {
b[0] |= 0x10 b[0] |= 0x10
} else { } else {
@@ -147,9 +151,8 @@ func (vt *Video) Play(onVideo func(*AVFrame[NALUSlice]) error) {
} }
type UnknowVideo struct { type UnknowVideo struct {
Name string Base
Stream IStream VideoTrack
Know AVTrack
} }
/* /*
@@ -172,24 +175,27 @@ func (vt *UnknowVideo) WriteAnnexB(pts uint32, dts uint32, frame AnnexBFrame) {
} }
func (vt *UnknowVideo) WriteAVCC(ts uint32, frame AVCCFrame) { func (vt *UnknowVideo) WriteAVCC(ts uint32, frame AVCCFrame) {
if vt.Know == nil { if vt.VideoTrack == nil {
if frame.IsSequence() { if frame.IsSequence() {
ts = 0
codecID := frame.VideoCodecID() codecID := frame.VideoCodecID()
if vt.Name == "" { if vt.Name == "" {
vt.Name = strings.ToLower(codec.CodecID[codecID]) vt.Name = codecID.String()
} }
switch codecID { switch codecID {
case codec.CodecID_H264: case codec.CodecID_H264:
v := NewH264(vt.Stream) vt.VideoTrack = NewH264(vt.Stream)
vt.Know = v
v.WriteAVCC(0, frame)
case codec.CodecID_H265: case codec.CodecID_H265:
v := NewH265(vt.Stream) vt.VideoTrack = NewH265(vt.Stream)
vt.Know = v default:
v.WriteAVCC(0, frame) vt.Stream.Error("video codecID not support: ", codecID)
return
} }
vt.VideoTrack.WriteAVCC(ts, frame)
} else {
vt.Stream.Warnf("need sequence frame")
} }
} else { } else {
vt.Know.WriteAVCC(ts, frame) vt.VideoTrack.WriteAVCC(ts, frame)
} }
} }

View File

@@ -32,7 +32,7 @@ func (s *Stream) AddTrack(t Track) {
defer s.Tracks.Unlock() defer s.Tracks.Unlock()
name := t.GetName() name := t.GetName()
if _, ok := s.Tracks.m[name]; !ok { if _, ok := s.Tracks.m[name]; !ok {
s.Infoln("Track", name, "added") s.Infof("Track '%s' added", name)
if s.Tracks.m[name] = t; s.Tracks.Err() == nil { if s.Tracks.m[name] = t; s.Tracks.Err() == nil {
for _, ch := range s.Tracks.waiters[name] { for _, ch := range s.Tracks.waiters[name] {
if *ch != nil { if *ch != nil {