package app import ( "github.com/datarhei/core/v16/process" ) type ConfigIOCleanup struct { Pattern string `json:"pattern"` MaxFiles uint `json:"max_files"` MaxFileAge uint `json:"max_file_age_seconds"` PurgeOnDelete bool `json:"purge_on_delete"` } type ConfigIO struct { ID string `json:"id"` Address string `json:"address"` Options []string `json:"options"` Cleanup []ConfigIOCleanup `json:"cleanup"` } // 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 { 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"` } // 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 } // 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 } 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 { 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"` } func (config *Config) Clone() *Config { clone := &Config{ ID: config.ID, Reference: config.Reference, FFVersion: config.FFVersion, Reconnect: config.Reconnect, ReconnectDelay: config.ReconnectDelay, Autostart: config.Autostart, StaleTimeout: config.StaleTimeout, LimitCPU: config.LimitCPU, LimitMemory: config.LimitMemory, LimitWaitFor: config.LimitWaitFor, WebRTC: config.WebRTC.Clone(), WHIPIngest: config.WHIPIngest.Clone(), } 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 } // 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 } type Process struct { ID string `json:"id"` Reference string `json:"reference"` Config *Config `json:"config"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` Order string `json:"order"` } func (process *Process) Clone() *Process { clone := &Process{ ID: process.ID, Reference: process.Reference, Config: process.Config.Clone(), CreatedAt: process.CreatedAt, UpdatedAt: process.UpdatedAt, 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 }