Adds the ConfigWHIPIngest struct alongside the existing ConfigWebRTC. When Enabled=true the app/webrtc subsystem (next commit) will prepend RTP UDP input legs to the FFmpeg command, binding on loopback ports that WHIP publisher peers write received WebRTC tracks to.
207 lines
6.7 KiB
Go
207 lines
6.7 KiB
Go
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
|
|
}
|