初步与rtmp插件调通

This commit is contained in:
dexter
2022-02-06 08:50:17 +08:00
parent b2489b2305
commit 4d8e2ca5d2
21 changed files with 682 additions and 261 deletions

175
stream.go
View File

@@ -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)
}