mirror of
https://github.com/Monibuca/engine.git
synced 2025-10-07 09:30:56 +08:00
初步与rtmp插件调通
This commit is contained in:
175
stream.go
175
stream.go
@@ -2,6 +2,8 @@ package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -18,7 +20,8 @@ const (
|
||||
STATE_WAITTRACK // 等待Track
|
||||
STATE_PUBLISHING // 正在发布流状态
|
||||
STATE_WAITCLOSE // 等待关闭状态(自动关闭延时开启)
|
||||
STATE_CLOSED
|
||||
STATE_CLOSED // 流已关闭,不可使用
|
||||
STATE_DESTROYED // 资源已释放
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,7 +33,7 @@ const (
|
||||
ACTION_FIRSTENTER // 第一个订阅者进入
|
||||
)
|
||||
|
||||
var StreamFSM = [STATE_CLOSED + 1]map[StreamAction]StreamState{
|
||||
var StreamFSM = [STATE_DESTROYED + 1]map[StreamAction]StreamState{
|
||||
{
|
||||
ACTION_PUBLISH: STATE_WAITTRACK,
|
||||
ACTION_LASTLEAVE: STATE_CLOSED,
|
||||
@@ -53,125 +56,174 @@ var StreamFSM = [STATE_CLOSED + 1]map[StreamAction]StreamState{
|
||||
ACTION_FIRSTENTER: STATE_PUBLISHING,
|
||||
ACTION_CLOSE: STATE_CLOSED,
|
||||
},
|
||||
{},
|
||||
{
|
||||
ACTION_TIMEOUT: STATE_DESTROYED,
|
||||
},
|
||||
}
|
||||
|
||||
// Streams 所有的流集合
|
||||
var Streams = util.Map[string, *Stream]{Map: make(map[string]*Stream)}
|
||||
|
||||
type SubscribeAction *Subscriber
|
||||
type UnSubscibeAction *Subscriber
|
||||
type PublishAction struct{}
|
||||
type UnPublishAction struct{}
|
||||
type StreamTimeoutConfig struct {
|
||||
WaitTimeout time.Duration
|
||||
PublishTimeout time.Duration
|
||||
WaitCloseTimeout time.Duration
|
||||
}
|
||||
|
||||
// Stream 流定义
|
||||
type Stream struct {
|
||||
context.Context
|
||||
cancel context.CancelFunc
|
||||
Publisher
|
||||
StreamTimeoutConfig
|
||||
*url.URL
|
||||
Publisher IPublisher
|
||||
State StreamState
|
||||
timeout *time.Timer //当前状态的超时定时器
|
||||
actionChan chan any
|
||||
Config StreamConfig
|
||||
URL string //远程地址,仅远程拉流有值
|
||||
StreamPath string
|
||||
RemoteURL string //远程地址,仅远程拉流有值
|
||||
StartTime time.Time //流的创建时间
|
||||
Subscribers util.Slice[*Subscriber] // 订阅者
|
||||
Tracks
|
||||
FrameCount uint32 //帧总数
|
||||
AppName string
|
||||
StreamName string
|
||||
}
|
||||
|
||||
func (r *Stream) Register(streamPath string) (result bool) {
|
||||
if r == nil {
|
||||
r = &Stream{
|
||||
Config: config.StreamConfig,
|
||||
}
|
||||
func (s *Stream) UnPublish() {
|
||||
if !s.IsClosed() {
|
||||
s.actionChan <- UnPublishAction{}
|
||||
}
|
||||
r.StreamPath = streamPath
|
||||
if result = Streams.Add(streamPath, r); result {
|
||||
r.actionChan = make(chan any, 1)
|
||||
r.StartTime = time.Now()
|
||||
r.timeout = time.NewTimer(r.Config.WaitTimeout.Duration())
|
||||
r.Context, r.cancel = context.WithCancel(Ctx)
|
||||
r.Init(r)
|
||||
go r.run()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ForceRegister 强制注册流,会将已有的流踢掉
|
||||
func (r *Stream) ForceRegister(streamPath string) {
|
||||
if ok := r.Register(streamPath); !ok {
|
||||
if s := Streams.Get(streamPath); s != nil {
|
||||
s.Close()
|
||||
<-s.Done()
|
||||
}
|
||||
r.ForceRegister(streamPath)
|
||||
func findOrCreateStream(streamPath string, waitTimeout time.Duration) (s *Stream, created bool) {
|
||||
streamPath = strings.Trim(streamPath, "/")
|
||||
u, err := url.Parse(streamPath)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
p := strings.Split(u.Path, "/")
|
||||
if len(p) < 2 {
|
||||
util.Println(Red("Stream Path Format Error:"), streamPath)
|
||||
return nil, false
|
||||
}
|
||||
if s, ok := Streams.Map[u.Path]; ok {
|
||||
util.Println(Green("Stream Found:"), u.Path)
|
||||
return s, false
|
||||
} else {
|
||||
return
|
||||
util.Println(Green("Stream Created:"), u.Path)
|
||||
p := strings.Split(u.Path, "/")
|
||||
s = &Stream{
|
||||
URL: u,
|
||||
AppName: p[0],
|
||||
StreamName: p[len(p)-1],
|
||||
}
|
||||
s.WaitTimeout = waitTimeout
|
||||
Streams.Map[u.Path] = s
|
||||
s.actionChan = make(chan any, 1)
|
||||
s.StartTime = time.Now()
|
||||
s.timeout = time.NewTimer(waitTimeout)
|
||||
s.Context, s.cancel = context.WithCancel(Ctx)
|
||||
s.Init(s)
|
||||
return s, true
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Stream) action(action StreamAction) {
|
||||
func (r *Stream) action(action StreamAction) bool {
|
||||
if next, ok := StreamFSM[r.State][action]; ok {
|
||||
if r.Publisher == nil || r.OnStateChange(r.State, next) {
|
||||
util.Print(Yellow("Stream "), BrightCyan(r.StreamPath), " state changed :", r.State, "->", next)
|
||||
if r.Publisher == nil || r.Publisher.OnStateChange(r.State, next) {
|
||||
util.Print(Yellow("Stream "), BrightCyan(r.Path), action, " :", r.State, "->", next)
|
||||
r.State = next
|
||||
switch next {
|
||||
case STATE_WAITPUBLISH:
|
||||
r.timeout.Reset(r.Config.WaitTimeout.Duration())
|
||||
r.Publisher = nil
|
||||
Bus.Publish(Event_REQUEST_PUBLISH, r)
|
||||
r.timeout.Reset(r.WaitTimeout)
|
||||
case STATE_WAITTRACK:
|
||||
r.timeout.Reset(time.Second * 5)
|
||||
case STATE_PUBLISHING:
|
||||
r.WaitDone()
|
||||
r.timeout.Reset(r.Config.PublishTimeout.Duration())
|
||||
r.timeout.Reset(r.PublishTimeout)
|
||||
Bus.Publish(Event_PUBLISH, r)
|
||||
case STATE_WAITCLOSE:
|
||||
r.timeout.Reset(r.Config.WaitCloseTimeout.Duration())
|
||||
r.timeout.Reset(r.WaitCloseTimeout)
|
||||
case STATE_CLOSED:
|
||||
r.cancel()
|
||||
if r.Publisher != nil {
|
||||
r.Publisher.Close()
|
||||
}
|
||||
r.WaitDone()
|
||||
Bus.Publish(Event_STREAMCLOSE, r)
|
||||
Streams.Delete(r.Path)
|
||||
r.timeout.Reset(time.Second) // 延迟1秒钟销毁,防止访问到已关闭的channel
|
||||
case STATE_DESTROYED:
|
||||
close(r.actionChan)
|
||||
Streams.Delete(r.StreamPath)
|
||||
fallthrough
|
||||
default:
|
||||
r.timeout.Stop()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (r *Stream) IsClosed() bool {
|
||||
if r == nil {
|
||||
return true
|
||||
}
|
||||
return r.State == STATE_CLOSED
|
||||
}
|
||||
|
||||
func (r *Stream) Close() {
|
||||
r.actionChan <- ACTION_CLOSE
|
||||
if !r.IsClosed() {
|
||||
r.actionChan <- ACTION_CLOSE
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Stream) UnSubscribe(sub *Subscriber) {
|
||||
r.actionChan <- UnSubscibeAction(sub)
|
||||
if !r.IsClosed() {
|
||||
r.actionChan <- UnSubscibeAction(sub)
|
||||
}
|
||||
}
|
||||
func (r *Stream) Subscribe(sub *Subscriber) {
|
||||
r.actionChan <- SubscribeAction(sub)
|
||||
if !r.IsClosed() {
|
||||
sub.Stream = r
|
||||
sub.Context, sub.cancel = context.WithCancel(r)
|
||||
r.actionChan <- sub
|
||||
}
|
||||
}
|
||||
func (r *Stream) run() {
|
||||
for {
|
||||
select {
|
||||
case <-r.timeout.C:
|
||||
util.Print(Yellow("Stream "), BrightCyan(r.StreamPath), "timeout:", r.State)
|
||||
util.Print(Yellow("Stream "), BrightCyan(r.Path), " timeout:", r.State)
|
||||
r.action(ACTION_TIMEOUT)
|
||||
case <-r.Done():
|
||||
r.action(ACTION_CLOSE)
|
||||
|
||||
case action, ok := <-r.actionChan:
|
||||
if ok {
|
||||
switch v := action.(type) {
|
||||
case PublishAction:
|
||||
r.action(ACTION_PUBLISH)
|
||||
case UnPublishAction:
|
||||
r.action(ACTION_PUBLISHLOST)
|
||||
case StreamAction:
|
||||
r.action(v)
|
||||
case SubscribeAction:
|
||||
v.Stream = r
|
||||
v.Context, v.cancel = context.WithCancel(r)
|
||||
case *Subscriber:
|
||||
r.Subscribers.Add(v)
|
||||
util.Print(Sprintf(Yellow("%s subscriber %s added remains:%d"), BrightCyan(r.StreamPath), Cyan(v.ID), Blue(len(r.Subscribers))))
|
||||
Bus.Publish(Event_SUBSCRIBE, v)
|
||||
util.Print(Sprintf(Yellow("%s subscriber %s added remains:%d"), BrightCyan(r.Path), Cyan(v.ID), Blue(len(r.Subscribers))))
|
||||
if r.Subscribers.Len() == 1 {
|
||||
r.action(ACTION_FIRSTENTER)
|
||||
}
|
||||
case UnSubscibeAction:
|
||||
if r.Subscribers.Delete(v) {
|
||||
util.Print(Sprintf(Yellow("%s subscriber %s removed remains:%d"), BrightCyan(r.StreamPath), Cyan(v.ID), Blue(len(r.Subscribers))))
|
||||
if r.Subscribers.Len() == 0 && r.Config.WaitCloseTimeout > 0 {
|
||||
Bus.Publish(Event_UNSUBSCRIBE, v)
|
||||
util.Print(Sprintf(Yellow("%s subscriber %s removed remains:%d"), BrightCyan(r.Path), Cyan(v.ID), Blue(len(r.Subscribers))))
|
||||
if r.Subscribers.Len() == 0 && r.WaitCloseTimeout > 0 {
|
||||
r.action(ACTION_LASTLEAVE)
|
||||
}
|
||||
}
|
||||
@@ -186,7 +238,7 @@ func (r *Stream) run() {
|
||||
// Update 更新数据重置超时定时器
|
||||
func (r *Stream) Update() uint32 {
|
||||
if r.State == STATE_PUBLISHING {
|
||||
r.timeout.Reset(r.Config.PublishTimeout.Duration())
|
||||
r.timeout.Reset(r.PublishTimeout)
|
||||
}
|
||||
return atomic.AddUint32(&r.FrameCount, 1)
|
||||
}
|
||||
@@ -198,7 +250,12 @@ func (r *Stream) NewVideoTrack() (vt *track.UnknowVideo) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Stream) NewAudioTrack() (at *track.UnknowAudio) {
|
||||
at = &track.UnknowAudio{
|
||||
Stream: r,
|
||||
}
|
||||
return
|
||||
}
|
||||
func (r *Stream) NewH264Track() (vt *track.H264) {
|
||||
return track.NewH264(r)
|
||||
}
|
||||
@@ -211,19 +268,3 @@ func (r *Stream) NewH265Track() (vt *track.H265) {
|
||||
// t := <-r.WaitTrack(names...)
|
||||
// return t.(DataTrack)
|
||||
// }
|
||||
|
||||
func (r *Stream) WaitVideoTrack(names ...string) track.Video {
|
||||
if !r.Config.EnableVideo {
|
||||
return nil
|
||||
}
|
||||
t := <-r.WaitTrack(names...)
|
||||
return t.(track.Video)
|
||||
}
|
||||
|
||||
func (r *Stream) WaitAudioTrack(names ...string) track.Audio {
|
||||
if !r.Config.EnableAudio {
|
||||
return nil
|
||||
}
|
||||
t := <-r.WaitTrack(names...)
|
||||
return t.(track.Audio)
|
||||
}
|
||||
|
Reference in New Issue
Block a user