2022-05-13 13:26:45 -04:00
|
|
|
package app
|
|
|
|
|
|
2022-07-04 14:20:15 -04:00
|
|
|
import (
|
|
|
|
|
"github.com/datarhei/core/v16/process"
|
|
|
|
|
)
|
2022-05-13 13:26:45 -04:00
|
|
|
|
|
|
|
|
type ConfigIOCleanup struct {
|
2022-06-03 11:21:52 -04:00
|
|
|
Pattern string `json:"pattern"`
|
|
|
|
|
MaxFiles uint `json:"max_files"`
|
|
|
|
|
MaxFileAge uint `json:"max_file_age_seconds"`
|
|
|
|
|
PurgeOnDelete bool `json:"purge_on_delete"`
|
2022-05-13 13:26:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ConfigIO struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Address string `json:"address"`
|
|
|
|
|
Options []string `json:"options"`
|
|
|
|
|
Cleanup []ConfigIOCleanup `json:"cleanup"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-17 09:50:28 -04:00
|
|
|
// ConfigWebRTC carries per-process WebRTC egress settings.
|
|
|
|
|
//
|
|
|
|
|
// When Enabled is true the restream manager will (via the app/webrtc
|
|
|
|
|
// subsystem) append an additional FFmpeg output leg that emits H.264/Opus
|
|
|
|
|
// RTP to a loopback UDP port the subsystem allocates. The subsystem reads
|
|
|
|
|
// that RTP and fans it out to WHEP subscribers.
|
|
|
|
|
type ConfigWebRTC struct {
|
2026-05-03 08:10:51 -04:00
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
|
VideoPT uint8 `json:"video_pt"`
|
|
|
|
|
AudioPT uint8 `json:"audio_pt"`
|
|
|
|
|
ForceTranscode bool `json:"force_transcode"`
|
|
|
|
|
|
|
|
|
|
// VideoMap / AudioMap select which input stream the WebRTC RTP
|
|
|
|
|
// legs draw from. Defaults are "0:v:0" and "0:a:0" — correct for
|
|
|
|
|
// any RTMP / SRT publisher (single input, both A and V on input
|
|
|
|
|
// 0). For multi-input pipelines (lavfi test sources, SDI capture
|
|
|
|
|
// fed alongside file audio, etc.) the operator can override.
|
|
|
|
|
VideoMap string `json:"video_map,omitempty"`
|
|
|
|
|
AudioMap string `json:"audio_map,omitempty"`
|
2026-04-17 09:50:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clone returns a deep copy of the WebRTC config (currently a value copy;
|
|
|
|
|
// provided for symmetry with other Clone methods and future-proofing).
|
|
|
|
|
func (w ConfigWebRTC) Clone() ConfigWebRTC { return w }
|
|
|
|
|
|
2026-05-09 16:18:43 -04:00
|
|
|
// ConfigWHIPIngest carries per-process WHIP ingest settings.
|
|
|
|
|
//
|
|
|
|
|
// When Enabled is true the app/webrtc subsystem will, at process start,
|
|
|
|
|
// allocate two adjacent loopback UDP ports and prepend them as RTP input
|
|
|
|
|
// legs to the FFmpeg command. A browser or OBS publisher then connects
|
|
|
|
|
// to POST /api/v3/whip/{id} and the received WebRTC tracks are forwarded
|
|
|
|
|
// to those ports, giving FFmpeg its video+audio input via WebRTC.
|
|
|
|
|
//
|
|
|
|
|
// Flow (symmetric to WHEP egress):
|
|
|
|
|
// browser → WHIP → Pion → UDP → FFmpeg input → FFmpeg outputs (RTMP/SRT/HLS…)
|
|
|
|
|
//
|
|
|
|
|
// VideoPT / AudioPT are the RTP payload types Pion will stamp on forwarded
|
|
|
|
|
// packets. Defaults match the WHEP egress defaults (102/111).
|
|
|
|
|
type ConfigWHIPIngest struct {
|
|
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
|
VideoPT uint8 `json:"video_pt"`
|
|
|
|
|
AudioPT uint8 `json:"audio_pt"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clone returns a value copy of the WHIP ingest config.
|
|
|
|
|
func (w ConfigWHIPIngest) Clone() ConfigWHIPIngest { return w }
|
|
|
|
|
|
2022-05-13 13:26:45 -04:00
|
|
|
func (io ConfigIO) Clone() ConfigIO {
|
|
|
|
|
clone := ConfigIO{
|
|
|
|
|
ID: io.ID,
|
|
|
|
|
Address: io.Address,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clone.Options = make([]string, len(io.Options))
|
|
|
|
|
copy(clone.Options, io.Options)
|
|
|
|
|
|
|
|
|
|
clone.Cleanup = make([]ConfigIOCleanup, len(io.Cleanup))
|
|
|
|
|
copy(clone.Cleanup, io.Cleanup)
|
|
|
|
|
|
|
|
|
|
return clone
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Config struct {
|
2026-05-09 16:18:43 -04:00
|
|
|
ID string `json:"id"`
|
|
|
|
|
Reference string `json:"reference"`
|
|
|
|
|
FFVersion string `json:"ffversion"`
|
|
|
|
|
Input []ConfigIO `json:"input"`
|
|
|
|
|
Output []ConfigIO `json:"output"`
|
|
|
|
|
Options []string `json:"options"`
|
|
|
|
|
Reconnect bool `json:"reconnect"`
|
|
|
|
|
ReconnectDelay uint64 `json:"reconnect_delay_seconds"` // seconds
|
|
|
|
|
Autostart bool `json:"autostart"`
|
|
|
|
|
StaleTimeout uint64 `json:"stale_timeout_seconds"` // seconds
|
|
|
|
|
LimitCPU float64 `json:"limit_cpu_usage"` // percent
|
|
|
|
|
LimitMemory uint64 `json:"limit_memory_bytes"` // bytes
|
|
|
|
|
LimitWaitFor uint64 `json:"limit_waitfor_seconds"` // seconds
|
|
|
|
|
WebRTC ConfigWebRTC `json:"webrtc"`
|
|
|
|
|
WHIPIngest ConfigWHIPIngest `json:"whip_ingest,omitempty"`
|
2022-05-13 13:26:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (config *Config) Clone() *Config {
|
|
|
|
|
clone := &Config{
|
|
|
|
|
ID: config.ID,
|
|
|
|
|
Reference: config.Reference,
|
2022-11-02 17:02:39 -04:00
|
|
|
FFVersion: config.FFVersion,
|
2022-05-13 13:26:45 -04:00
|
|
|
Reconnect: config.Reconnect,
|
|
|
|
|
ReconnectDelay: config.ReconnectDelay,
|
|
|
|
|
Autostart: config.Autostart,
|
|
|
|
|
StaleTimeout: config.StaleTimeout,
|
|
|
|
|
LimitCPU: config.LimitCPU,
|
|
|
|
|
LimitMemory: config.LimitMemory,
|
|
|
|
|
LimitWaitFor: config.LimitWaitFor,
|
2026-04-17 09:50:28 -04:00
|
|
|
WebRTC: config.WebRTC.Clone(),
|
2026-05-09 16:18:43 -04:00
|
|
|
WHIPIngest: config.WHIPIngest.Clone(),
|
2022-05-13 13:26:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clone.Input = make([]ConfigIO, len(config.Input))
|
|
|
|
|
for i, io := range config.Input {
|
|
|
|
|
clone.Input[i] = io.Clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clone.Output = make([]ConfigIO, len(config.Output))
|
|
|
|
|
for i, io := range config.Output {
|
|
|
|
|
clone.Output[i] = io.Clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clone.Options = make([]string, len(config.Options))
|
|
|
|
|
copy(clone.Options, config.Options)
|
|
|
|
|
|
|
|
|
|
return clone
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-04 14:20:15 -04:00
|
|
|
// CreateCommand created the FFmpeg command from this config.
|
|
|
|
|
func (config *Config) CreateCommand() []string {
|
|
|
|
|
var command []string
|
|
|
|
|
|
|
|
|
|
// Copy global options
|
|
|
|
|
command = append(command, config.Options...)
|
|
|
|
|
|
|
|
|
|
for _, input := range config.Input {
|
|
|
|
|
// Add the resolved input to the process command
|
|
|
|
|
command = append(command, input.Options...)
|
|
|
|
|
command = append(command, "-i", input.Address)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, output := range config.Output {
|
|
|
|
|
// Add the resolved output to the process command
|
|
|
|
|
command = append(command, output.Options...)
|
|
|
|
|
command = append(command, output.Address)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return command
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-13 13:26:45 -04:00
|
|
|
type Process struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Reference string `json:"reference"`
|
|
|
|
|
Config *Config `json:"config"`
|
|
|
|
|
CreatedAt int64 `json:"created_at"`
|
2023-04-24 06:05:01 -04:00
|
|
|
UpdatedAt int64 `json:"updated_at"`
|
2022-05-13 13:26:45 -04:00
|
|
|
Order string `json:"order"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (process *Process) Clone() *Process {
|
|
|
|
|
clone := &Process{
|
|
|
|
|
ID: process.ID,
|
|
|
|
|
Reference: process.Reference,
|
|
|
|
|
Config: process.Config.Clone(),
|
|
|
|
|
CreatedAt: process.CreatedAt,
|
2023-04-24 06:05:01 -04:00
|
|
|
UpdatedAt: process.UpdatedAt,
|
2022-05-13 13:26:45 -04:00
|
|
|
Order: process.Order,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return clone
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ProcessStates struct {
|
|
|
|
|
Finished uint64
|
|
|
|
|
Starting uint64
|
|
|
|
|
Running uint64
|
|
|
|
|
Finishing uint64
|
|
|
|
|
Failed uint64
|
|
|
|
|
Killed uint64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *ProcessStates) Marshal(s process.States) {
|
|
|
|
|
p.Finished = s.Finished
|
|
|
|
|
p.Starting = s.Starting
|
|
|
|
|
p.Running = s.Running
|
|
|
|
|
p.Finishing = s.Finishing
|
|
|
|
|
p.Failed = s.Failed
|
|
|
|
|
p.Killed = s.Killed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type State struct {
|
|
|
|
|
Order string // Current order, e.g. "start", "stop"
|
|
|
|
|
State string // Current state, e.g. "running"
|
|
|
|
|
States ProcessStates // Cumulated process states
|
|
|
|
|
Time int64 // Unix timestamp of last status change
|
|
|
|
|
Duration float64 // Runtime in seconds since last status change
|
|
|
|
|
Reconnect float64 // Seconds until next reconnect, negative if not reconnecting
|
|
|
|
|
LastLog string // Last recorded line from the process
|
|
|
|
|
Progress Progress // Progress data of the process
|
|
|
|
|
Memory uint64 // Current memory consumption in bytes
|
|
|
|
|
CPU float64 // Current CPU consumption in percent
|
|
|
|
|
Command []string // ffmpeg command line parameters
|
|
|
|
|
}
|