feat: RTMP URL

This commit is contained in:
Rob Watson 2025-01-21 20:26:04 +01:00
parent 0cf9a3db42
commit 8c12683a3c
5 changed files with 38 additions and 16 deletions

View File

@ -4,4 +4,5 @@ package domain
type AppState struct { type AppState struct {
ContainerRunning bool ContainerRunning bool
IngressLive bool IngressLive bool
IngressURL string
} }

11
main.go
View File

@ -43,7 +43,7 @@ func run(ctx context.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("start media server: %w", err) return fmt.Errorf("start media server: %w", err)
} }
state.ContainerRunning = true applyServerState(srv.State(), state)
ui, err := terminal.StartActor(ctx, terminal.StartActorParams{Logger: logger.With("component", "ui")}) ui, err := terminal.StartActor(ctx, terminal.StartActorParams{Logger: logger.With("component", "ui")})
if err != nil { if err != nil {
@ -61,8 +61,7 @@ func run(ctx context.Context) error {
return nil return nil
case serverState, ok := <-srv.C(): case serverState, ok := <-srv.C():
if ok { if ok {
state.ContainerRunning = serverState.ContainerRunning applyServerState(serverState, state)
state.IngressLive = serverState.IngressLive
updateUI() updateUI()
} else { } else {
logger.Info("State channel closed, shutting down...") logger.Info("State channel closed, shutting down...")
@ -71,3 +70,9 @@ func run(ctx context.Context) error {
} }
} }
} }
func applyServerState(serverState mediaserver.State, appState *domain.AppState) {
appState.ContainerRunning = serverState.ContainerRunning
appState.IngressLive = serverState.IngressLive
appState.IngressURL = serverState.IngressURL
}

View File

@ -13,12 +13,16 @@ import (
"git.netflux.io/rob/termstream/container" "git.netflux.io/rob/termstream/container"
) )
const imageNameMediaMTX = "bluenviron/mediamtx" const (
imageNameMediaMTX = "bluenviron/mediamtx"
rtmpPath = "live"
)
// State contains the current state of the media server. // State contains the current state of the media server.
type State struct { type State struct {
ContainerRunning bool ContainerRunning bool
IngressLive bool IngressLive bool
IngressURL string
} }
// action is an action to be performed by the actor. // action is an action to be performed by the actor.
@ -79,7 +83,9 @@ func StartActor(ctx context.Context, params StartActorParams) (*Actor, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("run container: %w", err) return nil, fmt.Errorf("run container: %w", err)
} }
actor.state.ContainerRunning = true actor.state.ContainerRunning = true
actor.state.IngressURL = "rtmp://localhost:1935/" + rtmpPath
go actor.actorLoop(containerDone) go actor.actorLoop(containerDone)
@ -192,7 +198,7 @@ func (s *Actor) fetchIngressStateFromServer() (bool, error) {
} }
for _, conn := range resp.Items { for _, conn := range resp.Items {
if conn.Path == "live" && conn.State == "publish" { if conn.Path == rtmpPath && conn.State == "publish" {
return true, nil return true, nil
} }
} }

View File

@ -47,7 +47,10 @@ func TestMediaServerStartStop(t *testing.T) {
"container not in RUNNING state", "container not in RUNNING state",
) )
require.False(t, actor.State().IngressLive) state := actor.State()
assert.False(t, state.IngressLive)
assert.Equal(t, "rtmp://localhost:1935/live", state.IngressURL)
launchFFMPEG(t, "rtmp://localhost:1935/live") launchFFMPEG(t, "rtmp://localhost:1935/live")
require.Eventually( require.Eventually(
t, t,

View File

@ -48,7 +48,7 @@ func StartActor(ctx context.Context, params StartActorParams) (*Actor, error) {
flex := tview.NewFlex(). flex := tview.NewFlex().
SetDirection(tview.FlexRow). SetDirection(tview.FlexRow).
AddItem(serverBox, 7, 0, false). AddItem(serverBox, 9, 0, false).
AddItem(destBox, 0, 1, false) AddItem(destBox, 0, 1, false)
container := tview.NewFlex(). container := tview.NewFlex().
@ -127,20 +127,27 @@ func generateServerStatus(state domain.AppState) string {
var s strings.Builder var s strings.Builder
s.WriteString("\n") s.WriteString("\n")
s.WriteString("Container status: ")
s.WriteString("[grey]Container status: ")
if state.ContainerRunning { if state.ContainerRunning {
s.WriteString("[green]running[white]") s.WriteString("[green]running")
} else { } else {
s.WriteString("[red]stopped[white]") s.WriteString("[red]stopped")
} }
s.WriteString("\n\n") s.WriteString("\n\n")
s.WriteString("Ingress stream: ")
if state.IngressLive { s.WriteString("[grey]RTMP URL: ")
s.WriteString("[green]on-air[white]") if state.IngressURL != "" {
} else { s.WriteString("[white:grey]" + state.IngressURL)
s.WriteString("[yellow]off-air[white]") }
s.WriteString("\n\n")
s.WriteString("[grey:black]Ingress stream: ")
if state.IngressLive {
s.WriteString("[green]on-air")
} else {
s.WriteString("[yellow]off-air")
} }
s.WriteString("\n\n\n")
return s.String() return s.String()
} }