Documentation
¶
Overview ¶
Package domain defines core types shared across Open Streamer modules.
Index ¶
- Constants
- func AggregateBitrateKbps(tracks []MediaTrackInfo) int
- func CodecLabel(c AVCodec) string
- func IsCopyInput(in Input) bool
- func IsCopyShapeError(err error) bool
- func IsMixerInput(in Input) bool
- func IsMixerShapeError(err error) bool
- func ResolveAudioEncoder(codec AudioCodec) string
- func ResolveVideoEncoder(codec VideoCodec, hw HWAccel) string
- func StreamMainBufferIsTS(s *Stream) bool
- func ValidateCopyShape(s *Stream, lookup StreamLookup) error
- func ValidateMixerShape(s *Stream, lookup StreamLookup) error
- func ValidateStreamCode(s string) error
- func ValidateVODName(s string) error
- func ValidateWatermarkAssetID(id string) error
- type AVCodec
- type AVPacket
- type AudioCodec
- type AudioConfig
- type AudioTranscodeConfig
- type CopyShapeError
- type DVRGap
- type DVRIndex
- type DecoderConfig
- type ErrorEntry
- type Event
- type EventType
- type GlobalConfig
- type HWAccel
- type Hook
- type HookID
- type HookType
- type Input
- type InputNetConfig
- type InterlaceMode
- type MediaTrackInfo
- type MediaTrackKind
- type MixerShapeError
- type OutputProtocols
- type PlaySession
- type PushDestination
- type PushStatus
- type Recording
- type RecordingID
- type RecordingStatus
- type ResizeMode
- type SessionCloseReason
- type SessionNamedBy
- type SessionProto
- type Stream
- type StreamCode
- type StreamCodeFilter
- type StreamDVRConfig
- type StreamLookup
- type StreamStatus
- type ThumbnailConfig
- type TranscoderConfig
- type TranscoderGlobalConfig
- type TranscoderMode
- type VODMount
- type VODName
- type VideoCodec
- type VideoProfile
- type VideoTranscodeConfig
- type WatermarkAsset
- type WatermarkAssetID
- type WatermarkConfig
- type WatermarkPosition
- type WatermarkType
Constants ¶
const ( // DefaultBufferCapacity is the per-subscriber channel buffer size in // MPEG-TS packets. 1024 packets ≈ 1 MiB ≈ ~1.5s of 1080p60 video at // 5Mbps — enough headroom for HLS pull bursts (one segment per Read) // without growing memory unbounded across many subscribers. DefaultBufferCapacity = 1024 // DefaultInputPacketTimeoutSec is the manager's failover threshold: the // maximum gap (in seconds) without a successful read on the active // input before it is marked failed and the next-priority input takes // over. DefaultInputPacketTimeoutSec = 30 // DefaultLiveSegmentSec is the live HLS / DASH segment target length // in seconds when the operator leaves the field unset. DefaultLiveSegmentSec = 2 // DefaultLiveWindow is the segment count in the sliding playlist // window (HLS / DASH live). DefaultLiveWindow = 12 // DefaultLiveHistory is extra segments retained on disk after they // leave the live window — 0 = drop immediately after sliding out. DefaultLiveHistory = 0 // DefaultDVRSegmentDuration is the DVR segment length in seconds when // StreamDVRConfig.SegmentDuration is zero. DefaultDVRSegmentDuration = 4 // DefaultDVRRoot is the on-disk root directory used as the parent of // "{DefaultDVRRoot}/{streamCode}" when StreamDVRConfig.StoragePath is // empty. DefaultDVRRoot = "./out/dvr" // DefaultPushTimeoutSec is the publish handshake budget for an outbound // push destination when PushDestination.TimeoutSec is zero. DefaultPushTimeoutSec = 10 // DefaultPushRetryTimeoutSec is the delay between retry attempts. DefaultPushRetryTimeoutSec = 5 // DefaultHookMaxRetries is the per-hook retry budget when the user // leaves Hook.MaxRetries=0. For HTTP hooks this caps retries WITHIN a // single batch flush; events still re-queue across flushes regardless. DefaultHookMaxRetries = 3 // DefaultHookTimeoutSec is the per-attempt delivery timeout when // Hook.TimeoutSec=0. DefaultHookTimeoutSec = 10 // DefaultHookBatchMaxItems is the cap on events per HTTP POST body // when neither Hook.BatchMaxItems nor HooksConfig.BatchMaxItems is set. // Picked to keep payloads under typical 1 MiB receiver caps (each event // ~1-3 KiB after JSON encoding). File hooks ignore this — they always // write one line per event. DefaultHookBatchMaxItems = 100 // DefaultHookBatchFlushIntervalSec is the maximum time a batch waits // before flushing even when below BatchMaxItems. Trade-off: lower = // fresher delivery, more requests; higher = bigger batches, more lag // for low-volume hooks. DefaultHookBatchFlushIntervalSec = 5 // DefaultHookBatchMaxQueueItems caps the per-hook in-memory queue. // Events that fail delivery are re-queued for the next flush; if the // downstream stays unreachable, the queue eventually overflows and the // OLDEST events are dropped (with a warn log) to bound memory. DefaultHookBatchMaxQueueItems = 10000 // DefaultVideoBitrateK is the fallback target video bitrate (kbps) // when a profile leaves Bitrate=0. DefaultVideoBitrateK = 2500 // DefaultAudioBitrateK is the audio bitrate (kbps) when // AudioTranscodeConfig.Bitrate is zero. DefaultAudioBitrateK = 128 // DefaultHLSPlaylistTimeoutSec is the upstream HLS pull playlist GET // timeout (seconds) when InputNetConfig.TimeoutSec is zero. DefaultHLSPlaylistTimeoutSec = 15 // DefaultHLSSegmentTimeoutSec is the upstream HLS segment GET timeout // floor — segments can be many MB so this is held above the playlist // timeout. DefaultHLSSegmentTimeoutSec = 60 // DefaultHLSMaxSegmentBuffer caps pre-fetched HLS segments held in // memory per stream when IngestorConfig.HLSMaxSegmentBuffer is zero. DefaultHLSMaxSegmentBuffer = 8 // DefaultSRTLatencyMS is the SRT ARQ latency window (milliseconds) when // SRTListenerConfig.LatencyMS is zero. 120ms matches Haivision's // reference for low-latency contribution links. DefaultSRTLatencyMS = 120 // DefaultFFmpegPath is the executable name resolved against $PATH when // TranscoderConfig.FFmpegPath is empty. Operators on bespoke layouts // (e.g. /opt/ffmpeg/bin/ffmpeg) must set the full path explicitly. DefaultFFmpegPath = "ffmpeg" // DefaultListenHost is the bind address used for RTMP / RTSP / SRT // listeners when ListenHost is empty. "0.0.0.0" listens on all // interfaces — the standard server behaviour. DefaultListenHost = "0.0.0.0" // DefaultRTMPTimeoutSec is the dial timeout (seconds) for RTMP pull // when InputNetConfig.TimeoutSec is zero. DefaultRTMPTimeoutSec = 10 // DefaultRTSPTimeoutSec is the dial / read timeout (seconds) for RTSP // pull when InputNetConfig.TimeoutSec is zero. DefaultRTSPTimeoutSec = 10 )
Default* constants are the runtime-applied values when the matching configuration field is left zero / empty by the user. They are the SINGLE SOURCE OF TRUTH for implicit defaults — every consumer (handler, packager, transcoder, validator) must reference these instead of inlining its own literal so behaviour stays consistent across services.
When changing a default here, audit references with:
grep -RIn 'Default[A-Z][A-Za-z]*' internal/ config/
to make sure no stale literal still exists in a service module.
const MaxStreamCodeLen = 128
MaxStreamCodeLen is the maximum length of a user-defined stream code.
const MaxVODNameLen = 64
MaxVODNameLen bounds the length of a VOD mount name used as a URL host component.
const MaxWatermarkAssetBytes int64 = 8 * 1024 * 1024 // 8 MiB
MaxWatermarkAssetBytes caps a single uploaded watermark. Logos are typically a few hundred KB at most; the cap defends the assets directory against a runaway upload accidentally filling the volume.
const MaxWatermarkAssetIDLen = 64
MaxWatermarkAssetIDLen is the maximum length of an asset ID. The bound matches the rendered filename length the watermarks service produces on disk so it can never overflow PATH_MAX in practice.
const MaxWatermarkAssetNameLen = 128
MaxWatermarkAssetNameLen caps the human display name. UI dropdowns and activity logs render this; oversize values would push the layout around.
Variables ¶
This section is empty.
Functions ¶
func AggregateBitrateKbps ¶ added in v0.0.47
func AggregateBitrateKbps(tracks []MediaTrackInfo) int
AggregateBitrateKbps sums every track's BitrateKbps into a single number, the value the runtime envelope publishes as "input total" / "output total".
func CodecLabel ¶ added in v0.0.47
CodecLabel returns the canonical lowercase string used in MediaTrackInfo.Codec for an AVCodec — kept here (rather than on the AVCodec type) so the JSON contract lives next to the struct that exposes it.
"mp2a" is the FourCC for MPEG-1/2 Audio (Layer I/II/III). Matches the Flussonic UI label convention so operators familiar with that tooling see the same string for the same codec on both servers; the underlying format covers everything that arrives via TS stream_type 0x03 or 0x04.
func IsCopyInput ¶ added in v0.0.20
IsCopyInput reports whether the given Input is a `copy://` reference. Convenience helper for callers that want to short-circuit per-input logic (e.g. skip network reconnect logic for copy inputs).
func IsCopyShapeError ¶ added in v0.0.20
IsCopyShapeError reports whether err is a *CopyShapeError.
func IsMixerInput ¶ added in v0.0.22
IsMixerInput reports whether the given Input is a `mixer://` reference.
func IsMixerShapeError ¶ added in v0.0.22
IsMixerShapeError reports whether err is a *MixerShapeError.
func ResolveAudioEncoder ¶ added in v0.0.30
func ResolveAudioEncoder(codec AudioCodec) string
ResolveAudioEncoder maps a user-facing codec to FFmpeg's encoder name. Empty / "copy" → AAC default (since copy is decided separately via AudioTranscodeConfig.Copy).
MP2 (mp2a) maps to FFmpeg's built-in `mp2` encoder — MPEG-1 Layer II, always available in standard FFmpeg builds (no external library needed, unlike libtwolame). Valid bitrates 32-384 kbps; FFmpeg silently rounds to the nearest valid Layer II rate. Used for DVB broadcast contribution feeds and legacy IPTV headends that consume Layer II input.
func ResolveVideoEncoder ¶ added in v0.0.30
func ResolveVideoEncoder(codec VideoCodec, hw HWAccel) string
ResolveVideoEncoder maps a user-facing codec string + global HW backend to the FFmpeg encoder name that buildFFmpegArgs will emit.
Routing highlights:
- "" / "h264" / "avc" + HW=nvenc → "h264_nvenc"; else "libx264"
- "h265" / "hevc" + HW=nvenc → "hevc_nvenc"; else "libx265"
- VAAPI / QSV / VideoToolbox: no implicit routing — caller must spell the full encoder name (e.g. "h264_vaapi"); empty stays "libx264".
func StreamMainBufferIsTS ¶ added in v0.0.62
StreamMainBufferIsTS reports whether the stream's main playback buffer contains raw MPEG-TS bytes (Packet.TS) rather than decoded AVPackets (Packet.AV). Three sources of TS in the main buffer:
- any transcoder is active → buffer holds the transcoder's stdout TS;
- source is a raw-TS protocol (UDP / HLS / SRT / File) → ingestor uses TSPassthroughPacketReader and writes Packet.TS directly;
- rendition buffers always hold TS (transcoder per-profile output) — but this helper reports on the MAIN buffer; callers that need to know about rendition shape should use streamHasRenditions instead.
Used by mixer / copy paths to decide whether to subscribe-and-read AV packets vs run a TS demuxer over the buffer's chunks. Misclassifying the shape silently drops audio (rare AV packets but a mode-mismatched consumer filters them all out).
func ValidateCopyShape ¶ added in v0.0.20
func ValidateCopyShape(s *Stream, lookup StreamLookup) error
ValidateCopyShape enforces copy:// constraints on a single stream:
- Self-copy (`copy://A` inside stream A) is rejected — a stream cannot re-stream itself; that's an infinite fan-out loop.
- When upstream X has an ABR ladder, downstream MUST satisfy: a) `copy://X` is the SOLE input (no fallback in v1) b) downstream's own `transcoder` field is nil (ladder is inherited from upstream; configuring local re-transcode is ambiguous)
- When mixing copy:// inputs with regular network inputs in the same priority list, all referenced upstreams must be single-stream (no ABR), since failover between rendition shapes is not supported in v1.
`lookup` resolves upstream streams. Missing upstream is treated as "shape unknown" — the rule it enables is skipped, never failed. The coordinator validates upstream presence at start time as a hard error.
func ValidateMixerShape ¶ added in v0.0.22
func ValidateMixerShape(s *Stream, lookup StreamLookup) error
ValidateMixerShape enforces mixer:// constraints on a single stream:
- Self-mix (`mixer://A,X` or `mixer://X,A` inside stream A) is rejected — a stream can't mix with itself.
- mixer:// must be the SOLE input — fallback inputs are not supported in v1 (failure semantics differ from regular failover; mixing across two unrelated mixer specs is undefined).
ABR upstreams ARE allowed:
- When the video upstream has an ABR ladder AND the downstream has no own transcoder, the runtime mirrors the video ladder (N rungs out, audio fanned-out across them). When the downstream HAS its own transcoder, the runtime taps only the best rendition of each upstream and feeds the encoder.
- Either upstream's audio is always single-source — the best rendition of the audio upstream when it's ABR.
`lookup` resolves upstream streams. Missing upstream is treated as "shape unknown" — the rule it enables is skipped, never failed. The MixerReader catches missing upstream at runtime as a hard error.
func ValidateStreamCode ¶
ValidateStreamCode reports whether s is a non-empty valid stream code.
func ValidateVODName ¶
ValidateVODName reports whether s is a non-empty, URL-host-safe VOD mount name.
func ValidateWatermarkAssetID ¶ added in v0.0.44
ValidateWatermarkAssetID enforces the safe filename charset. Asset IDs flow into filesystem paths, so we forbid anything that would let a caller escape the assets directory or collide with sidecar metadata names.
Types ¶
type AVCodec ¶
type AVCodec uint8
AVCodec identifies elementary stream codec for AVPacket payloads.
const ( AVCodecUnknown AVCodec = iota AVCodecH264 AVCodecH265 AVCodecAAC // AVCodecRawTSChunk is a marker codec used to carry raw MPEG-TS bytes // through the AVPacket-shaped pipeline without forcing a demux/remux // round-trip. The buffer-write step recognises this codec and writes the // chunk to `buffer.Packet.TS` instead of `buffer.Packet.AV`. Consumers // that prefer raw TS (HLS/DASH segmenters, transcoder ffmpeg-stdin pump) // then forward bytes verbatim — preserving PCR continuity and original // PIDs that a demux/remux cycle would lose. // // Used only by sources that ingest pre-muxed MPEG-TS (UDP, HLS, SRT, // File). RTSP / RTMP / Copy / Mixer continue to emit decoded AVPackets. AVCodecRawTSChunk // AVCodecMP2 is MPEG-1 / MPEG-2 Audio Layer I or II. Common in DVB // radio channels (e.g. VOH FM) and SD TV audio. TS stream_type 0x03 / // 0x04 both land here — the container doesn't encode Layer; we look // at the frame header to distinguish Layer III (= MP3, AVCodecMP3). AVCodecMP2 // AVCodecMP3 is MPEG-1 / MPEG-2 Audio Layer III (the familiar "MP3"). // Same TS stream_type as MP2 (0x03 / 0x04); detected by parsing the // MPEG audio frame header so the UI label and downstream codec-aware // consumers (mixer, transcoder configs targeting "mp3") see the right // codec instead of the generic mp2a fallback. AVCodecMP3 // AVCodecAC3 is Dolby Digital. TS stream_type 0x81 in ATSC headends, // or 0x06 + AC-3 descriptor (tag 0x6A) in DVB. Common in HD broadcast // audio for premium / international channels. AVCodecAC3 // AVCodecEAC3 is Enhanced AC-3 / Dolby Digital Plus. TS stream_type // 0x87 in ATSC, or 0x06 + Enhanced AC-3 descriptor (tag 0x7A) in DVB. // Carries 5.1+ surround for premium HD/UHD broadcast. AVCodecEAC3 // AVCodecAV1 is the AOMedia AV1 video codec. TS support added in DVB // TS 101 154 v2.7.1 via stream_type 0x06 + registration descriptor // format ID "AV01" — modern UHD streaming uses this. Encode via // libsvtav1; ingest recognises the descriptor. AVCodecAV1 // AVCodecMPEG2Video is MPEG-2 Part 2 (H.262) video. TS stream_type // 0x02 (also 0x01 for legacy MPEG-1 video, lumped here since the // downstream pipeline treats them identically). Standard for DVB SD // television broadcast — many older IPTV channels still carry video // in this codec. AVCodecMPEG2Video )
AVCodec values.
type AVPacket ¶
type AVPacket struct {
Codec AVCodec
Data []byte
PTSms uint64
DTSms uint64
KeyFrame bool
Discontinuity bool
}
AVPacket is one decoded video access unit (Annex B H.264/H.265) or one AAC frame (ADTS in Data). PTSms and DTSms are presentation / decode timestamps in milliseconds (MPEG-TS / gomedia convention).
type AudioCodec ¶
type AudioCodec string
AudioCodec identifies the audio compression format.
const ( AudioCodecAAC AudioCodec = "aac" // default for HLS/DASH AudioCodecMP2 AudioCodec = "mp2a" // MPEG-1/2 Audio Layer II — DVB broadcast contribution feeds AudioCodecMP3 AudioCodec = "mp3" // legacy compatibility AudioCodecAC3 AudioCodec = "ac3" // Dolby Digital — broadcast use AudioCodecEAC3 AudioCodec = "eac3" // Dolby Digital Plus — premium HD/UHD broadcast (5.1+) AudioCodecCopy AudioCodec = "copy" // passthrough — no re-encode )
AudioCodec values name supported output audio codecs.
type AudioConfig ¶
type AudioConfig struct {
Codec AudioCodec `json:"codec" yaml:"codec"`
// Bitrate is the audio bitrate in kbps. Typical: 128 (stereo), 192 (high quality).
Bitrate int `json:"bitrate" yaml:"bitrate"`
// SampleRate is the output sample rate in Hz. Typical: 44100, 48000.
SampleRate int `json:"sample_rate" yaml:"sample_rate"`
// Channels: 1 = mono, 2 = stereo, 6 = 5.1 surround.
Channels int `json:"channels" yaml:"channels"`
// Language is the ISO 639-1 code embedded in HLS/DASH metadata, e.g. "en", "vi".
Language string `json:"language" yaml:"language"`
// Normalize applies EBU R128 loudness normalization (-23 LUFS).
// Useful for broadcast compliance.
Normalize bool `json:"normalize" yaml:"normalize"`
}
AudioConfig defines the audio encoding settings applied to all output profiles.
type AudioTranscodeConfig ¶
type AudioTranscodeConfig struct {
// Copy copies origin audio without re-encoding.
Copy bool `json:"copy" yaml:"copy"`
Codec AudioCodec `json:"codec" yaml:"codec"`
// Bitrate is the audio bitrate in kbps.
Bitrate int `json:"bitrate" yaml:"bitrate"`
// SampleRate is output sample rate in Hz.
SampleRate int `json:"sample_rate" yaml:"sample_rate"`
// Channels: 1 = mono, 2 = stereo, 6 = 5.1.
Channels int `json:"channels" yaml:"channels"`
// Language is ISO 639-1 code, e.g. "en", "vi".
Language string `json:"language" yaml:"language"`
// Normalize applies EBU R128 loudness normalization.
Normalize bool `json:"normalize" yaml:"normalize"`
}
AudioTranscodeConfig defines audio transcoding behavior.
type CopyShapeError ¶ added in v0.0.20
type CopyShapeError struct {
StreamCode StreamCode
Reason string
}
CopyShapeError reports a copy:// configuration that violates the v1 constraints (e.g. local transcoder set when upstream has ABR, mixed shapes in the input list). The Reason string is API-surface text — the handler returns it verbatim in the 400 response.
func (*CopyShapeError) Error ¶ added in v0.0.20
func (e *CopyShapeError) Error() string
type DVRGap ¶
type DVRGap struct {
From time.Time `json:"from" yaml:"from"` // wall time gap started
To time.Time `json:"to" yaml:"to"` // wall time recording resumed
Duration time.Duration `json:"duration" yaml:"duration"` // To - From
}
DVRGap represents a period of signal loss or server downtime.
type DVRIndex ¶
type DVRIndex struct {
StreamCode StreamCode `json:"stream_code" yaml:"stream_code"`
StartedAt time.Time `json:"started_at" yaml:"started_at"`
LastSegmentAt time.Time `json:"last_segment_at,omitempty" yaml:"last_segment_at,omitempty"`
SegmentCount int `json:"segment_count" yaml:"segment_count"`
TotalSizeBytes int64 `json:"total_size_bytes" yaml:"total_size_bytes"`
// Gaps is the list of known signal-loss / server-restart interruptions.
Gaps []DVRGap `json:"gaps,omitempty" yaml:"gaps,omitempty"`
}
DVRIndex is the on-disk metadata index for a stream's DVR recording. Written atomically to {SegmentDir}/index.json after every segment flush.
Deliberately lightweight — no per-segment details. Per-segment timeline (wall time, duration, discontinuity) lives in playlist.m3u8 via #EXT-X-PROGRAM-DATE-TIME and #EXTINF tags.
type DecoderConfig ¶
type DecoderConfig struct {
// Name is the FFmpeg decoder name.
// "" = let FFmpeg choose automatically.
// Examples: "h264_cuvid", "h264_qsv".
Name string `json:"name,omitempty" yaml:"name,omitempty"`
}
DecoderConfig defines decoder behavior.
type ErrorEntry ¶ added in v0.0.8
ErrorEntry is a single recorded error with the time it occurred. Used by manager (per input) and transcoder (per profile) to expose a short rolling history of errors via their RuntimeStatus APIs.
type Event ¶
type Event struct {
ID string `json:"id"` // UUID for idempotent delivery
Type EventType `json:"type"`
StreamCode StreamCode `json:"stream_code"`
OccurredAt time.Time `json:"occurred_at"`
Payload map[string]any `json:"payload,omitempty"` // event-specific fields
}
Event is an immutable fact describing a domain state change.
type EventType ¶
type EventType string
EventType identifies the kind of domain event that occurred.
const ( // Stream lifecycle — published by coordinator and API handler. EventStreamCreated EventType = "stream.created" EventStreamStarted EventType = "stream.started" EventStreamStopped EventType = "stream.stopped" EventStreamDeleted EventType = "stream.deleted" // Input health — published by ingestor worker and stream manager. EventInputConnected EventType = "input.connected" // source connected successfully EventInputReconnecting EventType = "input.reconnecting" // transient error, retrying EventInputDegraded EventType = "input.degraded" // error detected by manager EventInputFailed EventType = "input.failed" // worker exited / non-retriable EventInputFailover EventType = "input.failover" // switched to lower-priority input // DVR recordings — published by dvr.Service. EventRecordingStarted EventType = "recording.started" EventRecordingStopped EventType = "recording.stopped" EventRecordingFailed EventType = "recording.failed" EventSegmentWritten EventType = "segment.written" // Transcoder — published by transcoder.Service. EventTranscoderStarted EventType = "transcoder.started" EventTranscoderStopped EventType = "transcoder.stopped" EventTranscoderError EventType = "transcoder.error" )
EventType values are emitted on the event bus for stream lifecycle, inputs, recordings, and segments.
type GlobalConfig ¶
type GlobalConfig struct {
Server *config.ServerConfig `json:"server,omitempty" yaml:"server,omitempty"`
Listeners *config.ListenersConfig `json:"listeners,omitempty" yaml:"listeners,omitempty"`
Ingestor *config.IngestorConfig `json:"ingestor,omitempty" yaml:"ingestor,omitempty"`
Buffer *config.BufferConfig `json:"buffer,omitempty" yaml:"buffer,omitempty"`
Transcoder *config.TranscoderConfig `json:"transcoder,omitempty" yaml:"transcoder,omitempty"`
Publisher *config.PublisherConfig `json:"publisher,omitempty" yaml:"publisher,omitempty"`
Manager *config.ManagerConfig `json:"manager,omitempty" yaml:"manager,omitempty"`
Hooks *config.HooksConfig `json:"hooks,omitempty" yaml:"hooks,omitempty"`
Sessions *config.SessionsConfig `json:"sessions,omitempty" yaml:"sessions,omitempty"`
Watermarks *config.WatermarksConfig `json:"watermarks,omitempty" yaml:"watermarks,omitempty"`
Log *config.LogConfig `json:"log,omitempty" yaml:"log,omitempty"`
}
GlobalConfig holds all runtime configuration that is persisted in the store (as opposed to config.StorageConfig which is bootstrap-only from config.yaml/env).
Pointer fields: nil means the section is not configured and the corresponding service should not start. This allows users to enable/disable entire subsystems by adding or removing config sections via the API.
type HWAccel ¶
type HWAccel string
HWAccel selects the hardware acceleration backend for encoding/decoding.
const ( HWAccelNone HWAccel = "none" // CPU only (libx264, libx265) HWAccelNVENC HWAccel = "nvenc" // NVIDIA GPU (h264_nvenc, hevc_nvenc) HWAccelVAAPI HWAccel = "vaapi" // Intel/AMD GPU via VA-API (Linux) HWAccelVideoToolbox HWAccel = "videotoolbox" // Apple GPU (macOS) HWAccelQSV HWAccel = "qsv" // Intel Quick Sync Video )
HWAccel values map to FFmpeg hardware device options.
type Hook ¶
type Hook struct {
ID HookID `json:"id" yaml:"id"`
Name string `json:"name" yaml:"name"`
Type HookType `json:"type" yaml:"type"`
Target string `json:"target" yaml:"target"` // HTTP(S) URL or absolute file path
Secret string `json:"secret" yaml:"secret"` // HMAC-SHA256 signing secret (HTTP only)
// EventTypes filters which events trigger delivery. Empty = all events.
EventTypes []EventType `json:"event_types,omitempty" yaml:"event_types,omitempty"`
// StreamCodes filters delivery by stream code.
// Only and Except are mutually exclusive; Only takes precedence when both are set.
// Omitting the field (nil) means all streams are included.
StreamCodes *StreamCodeFilter `json:"stream_codes,omitempty" yaml:"stream_codes,omitempty"`
// Metadata holds user-defined key-value pairs merged into every event payload
// delivered by this hook. Useful for tagging events with custom context
// (e.g. environment, tenant ID, region) without modifying the server config.
Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Enabled bool `json:"enabled" yaml:"enabled"`
// MaxRetries is the number of delivery attempts before giving up.
// 0 means use the server default (3). For HTTP hooks this caps retries
// inside a single batch flush; events that still fail are re-queued
// for the next flush regardless of MaxRetries.
MaxRetries int `json:"max_retries" yaml:"max_retries"`
// TimeoutSec is the per-attempt delivery timeout in seconds.
// 0 means use the server default (10s).
TimeoutSec int `json:"timeout_sec" yaml:"timeout_sec"`
// BatchMaxItems caps the number of events bundled into one HTTP POST
// body. 0 = use HooksConfig.BatchMaxItems, then DefaultHookBatchMaxItems.
// Ignored for File hooks (they always write one event per line).
BatchMaxItems int `json:"batch_max_items,omitempty" yaml:"batch_max_items,omitempty"`
// BatchFlushIntervalSec is the maximum time a batch may sit before being
// flushed even when below BatchMaxItems. 0 = use HooksConfig default,
// then DefaultHookBatchFlushIntervalSec.
BatchFlushIntervalSec int `json:"batch_flush_interval_sec,omitempty" yaml:"batch_flush_interval_sec,omitempty"`
// BatchMaxQueueItems caps the per-hook in-memory queue (pending +
// re-queued failures). When exceeded, the OLDEST events are dropped
// with a warning log so the queue never grows unbounded against an
// unreachable target. 0 = use HooksConfig default, then
// DefaultHookBatchMaxQueueItems.
BatchMaxQueueItems int `json:"batch_max_queue_items,omitempty" yaml:"batch_max_queue_items,omitempty"`
}
Hook is a registered external integration that receives domain events.
type HookType ¶
type HookType string
HookType is the delivery mechanism for a hook.
const ( HookTypeHTTP HookType = "http" // HookTypeFile appends each event as a single JSON line to the path // in Hook.Target. Useful for downstream log shippers (Filebeat, // Vector, Promtail) and for ops-friendly local audit trails without // running an HTTP receiver. HookTypeFile HookType = "file" )
HookType values name supported hook transports.
type Input ¶
type Input struct {
// URL is the source endpoint. See the package doc for supported formats.
URL string `json:"url" yaml:"url"`
// Priority determines failover order. Lower value = higher priority.
// The Stream Manager always prefers the lowest-priority alive input.
Priority int `json:"priority" yaml:"priority"`
// Headers are arbitrary HTTP headers sent with every request for HTTP/HLS inputs.
// Common uses:
// "Authorization": "Bearer <token>"
// "Authorization": "Basic <base64(user:pass)>"
// "X-Custom-Token": "secret"
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
// Params are extra URL query parameters merged into the source URL before
// connecting. Used for protocols that carry credentials or options in the
// query string (SRT ?passphrase=, S3 ?access_key= / ?secret_key=, etc.).
// "passphrase": "my-srt-passphrase"
// "access_key": "AKID..." (S3)
// "secret_key": "wJal..." (S3)
Params map[string]string `json:"params,omitempty" yaml:"params,omitempty"`
// Program selects a single MPEG-TS program when the source is a
// multi-program transport stream (MPTS) — common in DVB headend feeds
// where one multicast carries many channels. When > 0, the ingest
// pipeline rewrites the PAT to advertise only the chosen program and
// drops PMT / ES packets belonging to other programs, producing a
// clean SPTS for downstream HLS / DASH / push consumers.
//
// Zero (default) disables filtering — the entire stream is forwarded
// unchanged. Currently applies to UDP only; HLS / SRT / File ingest
// are SPTS by convention so the filter is not wired for those (extend
// reader.go if a real MPTS file/SRT use case arises). Ignored for
// RTSP / RTMP, which are single-program by protocol design.
Program int `json:"program,omitempty" yaml:"program,omitempty"`
// Pids is an explicit allowlist of TS PIDs to keep — every other PID
// is dropped at ingest. Used when the source PSI is unreliable (legacy
// encoders with malformed PAT/PMT) or when the operator wants to cherry-
// pick a subset (e.g. drop a teletext PID, keep only one of N audio
// languages). The filter is purely PID-level: no PAT/PMT rewrite, no
// CRC recompute. Operators must include every PID needed for playback
// (typically PID 0 for PAT, the PMT PID, and the desired ES PIDs).
//
// Layers with Program when both are set: Program runs first (auto-
// detect ES PIDs + rewrite PAT to single-program), then Pids further
// restricts the output. Empty (default) disables the filter.
//
// Currently applies to UDP only — same rationale as Program.
Pids []int `json:"pids,omitempty" yaml:"pids,omitempty"`
// Net controls reconnect and timeout behaviour.
Net InputNetConfig `json:"net,omitempty" yaml:"net,omitempty"`
// Alive is a runtime-only field updated by the Stream Manager health checker.
// Not persisted to storage.
Alive bool `json:"-" yaml:"-"`
}
Input is a single ingest source for a stream. Multiple inputs can be configured; the Stream Manager selects the active one based on Priority and runtime health (Alive flag).
The only required field is URL. The ingestor derives the ingest protocol and connection mode (pull vs push-listen) automatically from the URL scheme and host — no additional protocol configuration is needed.
Supported URL formats:
Pull (server connects to remote source): rtmp://server.com/live/stream_key RTMP pull from remote rtsp://camera.local:554/stream RTSP pull (IP camera) http://cdn.example.com/live.ts HTTP MPEG-TS stream https://cdn.example.com/playlist.m3u8 HLS pull (grafov m3u8 parser) udp://239.1.1.1:5000 UDP multicast MPEG-TS srt://relay.example.com:9999 SRT pull (caller mode) file:///recordings/source.ts local file (loops if ?loop=true) Push (external encoder connects to our server): rtmp://0.0.0.0:1935/live/stream_key RTMP push — our RTMP server listens srt://0.0.0.0:9999?streamid=stream_key SRT push — our SRT server listens
Push mode is detected automatically when the URL host is a wildcard address (0.0.0.0, ::, empty) and the scheme is rtmp or srt.
type InputNetConfig ¶
type InputNetConfig struct {
// TimeoutSec is the per-protocol operation budget the reader applies
// when this input is opened. Semantics differ by protocol:
//
// - HLS: HTTP request timeout (entire round-trip incl. body) for
// the playlist GET. Segment GETs derive from this — typically
// 4× the playlist budget, floored at the segment default.
// - RTMP: TCP dial timeout (handshake budget).
// - RTSP: dial + initial read timeout (until first packet).
// - SRT: connection / handshake timeout.
//
// Zero uses the reader's per-protocol default
// (DefaultHLSPlaylistTimeoutSec for HLS; DefaultRTMPTimeoutSec /
// DefaultRTSPTimeoutSec for the rest).
TimeoutSec int `json:"timeout_sec,omitempty" yaml:"timeout_sec,omitempty"`
// InsecureTLS disables TLS certificate verification for HTTPS pulls
// (HLS playlist + segment GETs). Default false — leave secure-by-default
// for production. Use only when the source uses a self-signed,
// expired, or otherwise-invalid certificate that you trust at the
// network level (private VLAN, fixed IP allowlist).
InsecureTLS bool `json:"insecure_tls,omitempty" yaml:"insecure_tls,omitempty"`
}
InputNetConfig controls per-input network behaviour.
Reconnect / silence-detection knobs were removed because they were declared but never consumed by any reader: pull workers use a hardcoded exponential backoff on transient errors (worker.go), and stream-level liveness is the manager's job (manager.input_packet_timeout_sec). Reintroduce specific knobs only when a reader actually wires them.
type InterlaceMode ¶ added in v0.0.6
type InterlaceMode string
InterlaceMode selects deinterlacing behavior for the source.
const ( InterlaceAuto InterlaceMode = "auto" // detect parity each frame InterlaceTopField InterlaceMode = "tff" // top field first (BBC HD, most ATSC) InterlaceBottomField InterlaceMode = "bff" // bottom field first (legacy DV) InterlaceProgressive InterlaceMode = "progressive" // assert source is progressive — skip filter )
InterlaceMode values map to yadif/yadif_cuda parameters. "" disables the filter.
type MediaTrackInfo ¶ added in v0.0.47
type MediaTrackInfo struct {
Kind MediaTrackKind `json:"kind"`
Codec string `json:"codec"` // "h264" | "h265" | "aac" | "mp2a"
Width int `json:"width,omitempty"` // video only
Height int `json:"height,omitempty"` // video only
BitrateKbps int `json:"bitrate_kbps"`
}
MediaTrackInfo describes one elementary track in a stream's input or output.
The runtime envelope exposes lists of these so the UI can render the "Input media info / Output media info" panels (codec, resolution, bitrate) without inferring shape from per-protocol config blobs.
Fields are best-effort:
- Width/Height are populated only after the first keyframe whose SPS could be parsed; zero means "not yet decoded".
- BitrateKbps is an EWMA over the last few seconds of bytes seen for this codec on this input; zero means "no bytes yet" (just-connected stream, or output not running).
func OutputTracks ¶ added in v0.0.47
func OutputTracks(tc *TranscoderConfig) []MediaTrackInfo
OutputTracks derives the list of output media tracks from a stream's persisted Transcoder config. One MediaTrackInfo per ABR rendition is emitted, plus one entry for the audio output. Passthrough renditions (Codec=="copy") show codec="copy" with zero resolution/bitrate — the UI can render them as "follows input" or fall back to the input track.
When `tc` is nil the stream has no transcoder configured — caller is expected to fall back to input tracks (no-transcode mirror).
type MediaTrackKind ¶ added in v0.0.47
type MediaTrackKind string
MediaTrackKind identifies whether a track carries video or audio.
const ( MediaTrackVideo MediaTrackKind = "video" MediaTrackAudio MediaTrackKind = "audio" )
MediaTrackKind values.
type MixerShapeError ¶ added in v0.0.22
type MixerShapeError struct {
StreamCode StreamCode
Reason string
}
MixerShapeError reports a mixer:// configuration that violates the v1 constraints. Reason is API-surface text — handler returns it verbatim.
func (*MixerShapeError) Error ¶ added in v0.0.22
func (e *MixerShapeError) Error() string
type OutputProtocols ¶
type OutputProtocols struct {
// HLS enables Apple HTTP Live Streaming (m3u8 + segments over HTTP).
// Compatible with browsers, iOS, Android, Smart TVs.
HLS bool `json:"hls" yaml:"hls"`
// DASH enables MPEG-DASH packaging over HTTP.
// Required for Widevine/PlayReady DRM.
DASH bool `json:"dash" yaml:"dash"`
// RTSP opens an RTSP listener for pull clients (VLC, broadcast tools).
RTSP bool `json:"rtsp" yaml:"rtsp"`
// RTMP opens an RTMP publish endpoint for legacy players/CDNs.
RTMP bool `json:"rtmp" yaml:"rtmp"`
// SRT opens an SRT listener port for contribution-quality pull.
SRT bool `json:"srt" yaml:"srt"`
// MPEGTS exposes raw MPEG-TS over chunked HTTP at /<code>/mpegts —
// the lowest-latency relay path between Open-Streamer instances (and
// any HTTP client that can consume chunked TS, e.g. ffmpeg / VLC).
// Latency is bounded only by network RTT and one buffer-hub chunk
// (typically 50–200 ms vs 4–10 s for HLS / DASH).
//
// No goroutine is started per-stream; the endpoint subscribes to the
// playback buffer on demand. Disabling the flag turns the endpoint
// into a 404 for that stream so operators can opt out per-stream
// without changing the global router.
MPEGTS bool `json:"mpegts" yaml:"mpegts"`
}
OutputProtocols defines which delivery protocols are opened for a stream. Each enabled protocol starts a corresponding listener or packager. Protocol-level settings (ports, segment duration, CDN URL, etc.) are configured globally in the server config.
type PlaySession ¶ added in v0.0.42
type PlaySession struct {
// ID uniquely identifies the session across the server's lifetime. For
// segment protocols (HLS/DASH) it is the deterministic fingerprint hash
// (so reconnects within the idle window collapse onto one session); for
// connection-bound protocols (RTMP/SRT/RTSP) it is a random UUID.
ID string `json:"id"`
// StreamCode is the foreign key to Stream.Code.
StreamCode StreamCode `json:"stream_code"`
// Protocol is the wire protocol used to deliver this session.
Protocol SessionProto `json:"proto"`
// IP is the remote peer address (no port). For HTTP-based protocols this
// is the X-Forwarded-For head when present, otherwise the RemoteAddr.
IP string `json:"ip"`
// UserAgent is the browser/player User-Agent (HTTP) or the equivalent
// flashVer field for RTMP. May be empty.
UserAgent string `json:"user_agent,omitempty"`
// Referer is the HTTP Referer when present (HLS/DASH only).
Referer string `json:"referer,omitempty"`
// QueryString is the raw query of the FIRST request that opened the
// session (HLS/DASH) — useful when token/abr-variant info is encoded there.
QueryString string `json:"query_string,omitempty"`
// Token is the value of `?token=` from the first request, when present.
// When set, NamedBy is SessionNamedByToken.
Token string `json:"token,omitempty"`
// UserName is a human label. Resolved from token claims if available,
// otherwise the fingerprint hash short form. Empty when no identity could
// be resolved.
UserName string `json:"user_name,omitempty"`
// NamedBy records how UserName was resolved.
NamedBy SessionNamedBy `json:"named_by,omitempty"`
// Country is an ISO 3166-1 alpha-2 country code from the configured
// GeoIP resolver, or "" when GeoIP is disabled / lookup failed.
Country string `json:"country,omitempty"`
// Bytes is the cumulative bytes sent to this client since session open.
// HLS/DASH: sum of segment + manifest response bodies. RTMP/SRT: write
// bytes from the publisher's outbound pipeline.
Bytes int64 `json:"bytes"`
// Secure is true when the underlying transport was TLS / SRTS / RTMPS.
Secure bool `json:"secure"`
// DVR is true when playback originated from the DVR window (timeshift),
// false for live edge. Open-Streamer's DVR delivery is not yet routed
// through here so this is currently always false; field reserved.
DVR bool `json:"dvr"`
// OpenedAt is the time the first activity for this session was observed.
OpenedAt time.Time `json:"opened_at"`
// StartedAt is when the first byte of media was delivered (segment data
// for HLS/DASH, first frame after RTMP play handshake). Zero before
// media flows.
StartedAt time.Time `json:"started_at,omitempty"`
// UpdatedAt is the time of the most recent activity. Idle reaper closes
// sessions whose UpdatedAt is older than (now - idle_timeout).
UpdatedAt time.Time `json:"updated_at"`
// ClosedAt is set when the session ends. nil while active.
ClosedAt *time.Time `json:"closed_at,omitempty"`
// CloseReason explains why the session ended. Empty while active.
CloseReason SessionCloseReason `json:"close_reason,omitempty"`
}
PlaySession is the audit record for one client watching a stream. It is created when the first activity for a given (stream, fingerprint) pair is observed and stays live until the idle timer expires or the transport-level connection closes.
All time fields are wall-clock UTC; default Go RFC3339 JSON encoding.
func (*PlaySession) Active ¶ added in v0.0.42
func (s *PlaySession) Active() bool
Active reports whether the session is still considered open (ClosedAt unset).
func (*PlaySession) Duration ¶ added in v0.0.42
func (s *PlaySession) Duration() time.Duration
Duration returns the elapsed time between OpenedAt and either ClosedAt (when set) or now. Zero when OpenedAt is the zero value.
type PushDestination ¶
type PushDestination struct {
// URL is the destination ingest endpoint.
// Supported schemes:
// rtmp:// — plain TCP, default port 1935 (e.g. rtmp://a.rtmp.youtube.com/live2/{key})
// rtmps:// — TLS-wrapped RTMP, default port 443 (e.g. rtmps://live-api-s.facebook.com:443/rtmp/{key})
URL string `json:"url" yaml:"url"`
// Enabled controls whether this destination is active.
Enabled bool `json:"enabled" yaml:"enabled"`
// TimeoutSec is the connection/write timeout in seconds.
TimeoutSec int `json:"timeout_sec" yaml:"timeout_sec"`
// RetryTimeoutSec is the delay between retry attempts in seconds.
RetryTimeoutSec int `json:"retry_timeout_sec" yaml:"retry_timeout_sec"`
// Limit is the maximum number of retry attempts. 0 = unlimited.
Limit int `json:"limit" yaml:"limit"`
// Comment is a human-readable note for this destination.
Comment string `json:"comment" yaml:"comment"`
// Status is a runtime-only field updated by the publisher.
// Not persisted to storage.
Status PushStatus `json:"status,omitempty" yaml:"-"`
}
PushDestination is an external endpoint the server actively pushes the stream to.
type PushStatus ¶
type PushStatus string
PushStatus is the runtime state of a push destination.
const ( PushStatusIdle PushStatus = "idle" PushStatusConnecting PushStatus = "connecting" PushStatusActive PushStatus = "active" PushStatusRetrying PushStatus = "retrying" PushStatusFailed PushStatus = "failed" PushStatusDisabled PushStatus = "disabled" )
PushStatus values describe outbound publisher connectivity.
type Recording ¶
type Recording struct {
ID RecordingID `json:"id" yaml:"id"`
StreamCode StreamCode `json:"stream_code" yaml:"stream_code"`
StartedAt time.Time `json:"started_at" yaml:"started_at"`
StoppedAt *time.Time `json:"stopped_at,omitempty" yaml:"stopped_at,omitempty"`
Status RecordingStatus `json:"status" yaml:"status"`
// SegmentDir is the absolute path to the directory holding TS files,
// playlist.m3u8, and index.json.
SegmentDir string `json:"segment_dir" yaml:"segment_dir"`
}
Recording represents the lifecycle metadata for a DVR recording session. ID equals StreamCode — one persistent recording per stream. Segment data lives in DVRIndex (index.json on disk), not here.
type RecordingID ¶
type RecordingID string
RecordingID is the unique identifier for a DVR recording. Always equals the stream code — one recording per stream.
type RecordingStatus ¶
type RecordingStatus string
RecordingStatus represents the lifecycle state of a recording.
const ( RecordingStatusRecording RecordingStatus = "recording" RecordingStatusStopped RecordingStatus = "stopped" RecordingStatusFailed RecordingStatus = "failed" )
RecordingStatus values.
type ResizeMode ¶ added in v0.0.6
type ResizeMode string
ResizeMode controls how the source frame is fitted into the output dimensions.
const ( ResizeModePad ResizeMode = "pad" // letterbox: keep aspect, fill remainder with black ResizeModeCrop ResizeMode = "crop" // fill: keep aspect, crop excess ResizeModeStretch ResizeMode = "stretch" // distort: scale to W:H, ignore source aspect ResizeModeFit ResizeMode = "fit" // keep aspect, no padding (output may be smaller than W:H) )
ResizeMode values match Flussonic's resize modes. "" defaults to ResizeModePad.
func ResolveResizeMode ¶ added in v0.0.30
func ResolveResizeMode(m ResizeMode) ResizeMode
ResolveResizeMode normalizes a free-form resize mode to the canonical constant. Empty / unknown → ResizeModePad.
type SessionCloseReason ¶ added in v0.0.42
type SessionCloseReason string
SessionCloseReason is set on PlaySession.CloseReason when the session ends.
const ( SessionCloseIdle SessionCloseReason = "idle" // no activity within the configured idle window SessionCloseClient SessionCloseReason = "client_gone" // TCP/UDP peer closed (RTMP/SRT/RTSP) SessionCloseShutdown SessionCloseReason = "shutdown" // server shutting down SessionCloseKicked SessionCloseReason = "kicked" // operator force-closed via API )
SessionCloseReason values.
type SessionNamedBy ¶ added in v0.0.42
type SessionNamedBy string
SessionNamedBy describes how the session's UserName was resolved. Mirrors Flussonic's `named_by` so external dashboards expecting that vocabulary keep working.
const ( SessionNamedByToken SessionNamedBy = "token" // resolved from a `token` query param SessionNamedByConfig SessionNamedBy = "config" // resolved from server-side config (future: signed URL) SessionNamedByFingerprint SessionNamedBy = "fingerprint" // synthesised hash of ip+ua+stream )
SessionNamedBy values.
type SessionProto ¶ added in v0.0.42
type SessionProto string
SessionProto identifies the network protocol carrying a play session. It mirrors the on-the-wire transport — application-layer flavours (e.g. browser-embedded HLS.js vs native Safari) are not distinguished.
const ( SessionProtoHLS SessionProto = "hls" SessionProtoDASH SessionProto = "dash" SessionProtoRTMP SessionProto = "rtmp" SessionProtoSRT SessionProto = "srt" SessionProtoRTSP SessionProto = "rtsp" )
SessionProto values for each playback protocol Open-Streamer serves.
type Stream ¶
type Stream struct {
// Code is the unique key chosen by the user ([a-zA-Z0-9_]).
Code StreamCode `json:"code" yaml:"code"`
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`
Tags []string `json:"tags" yaml:"tags"`
// StreamKey is used to authenticate RTMP/SRT push ingest.
StreamKey string `json:"stream_key" yaml:"stream_key"`
// Status is the runtime lifecycle state.
// It is never persisted — always computed from the coordinator's in-memory
// state and overlaid by the API layer before returning responses to clients.
Status StreamStatus `json:"-" yaml:"-"`
// Disabled when true excludes the stream from server bootstrap and rejects pipeline Start.
Disabled bool `json:"disabled" yaml:"disabled"`
// Inputs are the available ingest sources ordered by Priority.
// The Stream Manager monitors health and switches between them on failure.
Inputs []Input `json:"inputs" yaml:"inputs"`
// Transcoder controls encoding/decoding settings.
// nil means no transcoding for this stream.
Transcoder *TranscoderConfig `json:"transcoder,omitempty" yaml:"transcoder,omitempty"`
// Protocols defines which delivery protocols are opened for this stream.
// The server opens a listener/packager for each enabled protocol.
// Protocol-level config (ports, segment duration, CDN URL) lives in server config.
Protocols OutputProtocols `json:"protocols" yaml:"protocols"`
// Push is the list of external destinations the server actively pushes to.
// Each entry defines one push target (YouTube, Facebook, Twitch, CDN relay, etc.).
Push []PushDestination `json:"push" yaml:"push"`
// DVR overrides the global DVR settings for this specific stream.
// If nil, the global config is used (when DVR is enabled globally).
DVR *StreamDVRConfig `json:"dvr,omitempty" yaml:"dvr,omitempty"`
// Watermark is an optional text or image overlay applied before encoding.
Watermark *WatermarkConfig `json:"watermark,omitempty" yaml:"watermark,omitempty"`
// Thumbnail controls periodic screenshot generation for preview images.
Thumbnail *ThumbnailConfig `json:"thumbnail,omitempty" yaml:"thumbnail,omitempty"`
}
Stream is the central domain entity. It describes everything needed to ingest, process, and deliver a live stream.
func (*Stream) ValidateInputPriorities ¶
ValidateInputPriorities enforces that input priorities are contiguous and sorted. For N inputs, expected priorities are exactly 0..N-1 in ascending order.
func (*Stream) ValidateUniqueInputs ¶
ValidateUniqueInputs enforces that inputs in one stream are not duplicated. Two inputs are considered duplicates if their URL (trimmed) is identical.
type StreamCode ¶
type StreamCode string
StreamCode is the user-assigned primary key for a stream. Allowed characters: a-z, A-Z, 0-9, underscore.
func CopyInputTarget ¶ added in v0.0.20
func CopyInputTarget(in Input) (StreamCode, error)
CopyInputTarget extracts the upstream code from a copy:// input. Returns ("", error) for non-copy or malformed inputs. The error wraps protocol.CopyTarget's error so callers can preserve the user-facing message.
func MixerInputSpec ¶ added in v0.0.22
func MixerInputSpec(in Input) (videoCode, audioCode StreamCode, audioFailureContinue bool, err error)
MixerInputSpec parses a mixer:// input. Returns ("", "", false, error) for non-mixer or malformed URLs. Wraps protocol.MixerTargets so callers can preserve the user-facing message.
type StreamCodeFilter ¶
type StreamCodeFilter struct {
// Only delivers events only for streams in this list.
Only []StreamCode `json:"only,omitempty" yaml:"only,omitempty"`
// Except delivers events for all streams except those in this list.
Except []StreamCode `json:"except,omitempty" yaml:"except,omitempty"`
}
StreamCodeFilter defines include/exclude rules for stream code matching. Only and Except are mutually exclusive; Only takes precedence when both are set.
func (*StreamCodeFilter) Matches ¶
func (f *StreamCodeFilter) Matches(code StreamCode) bool
Matches reports whether the given stream code passes the filter.
type StreamDVRConfig ¶
type StreamDVRConfig struct {
Enabled bool `json:"enabled" yaml:"enabled"`
// RetentionSec is the retention window in seconds.
// 0 = keep forever.
RetentionSec int `json:"retention_sec" yaml:"retention_sec"`
// SegmentDuration overrides the global segment length in seconds.
// 0 = use default (4s).
SegmentDuration int `json:"segment_duration" yaml:"segment_duration"`
// StoragePath overrides the default DVR root directory for this stream.
// "" = use "./dvr/{streamCode}".
StoragePath string `json:"storage_path" yaml:"storage_path"`
// MaxSizeGB caps total disk usage. Oldest segments pruned when exceeded.
// 0 = no limit.
MaxSizeGB float64 `json:"max_size_gb" yaml:"max_size_gb"`
}
StreamDVRConfig overrides the global DVR settings for a specific stream.
type StreamLookup ¶ added in v0.0.20
type StreamLookup func(StreamCode) (*Stream, bool)
StreamLookup returns the upstream stream by code. Used by ValidateCopyShape to inspect upstream transcoder shape without coupling domain to the repository layer. The bool reflects "found"; missing upstream is not an error here (handled at runtime by the coordinator).
type StreamStatus ¶
type StreamStatus string
StreamStatus represents the lifecycle state of a stream.
const ( StatusIdle StreamStatus = "idle" StatusActive StreamStatus = "active" StatusDegraded StreamStatus = "degraded" StatusStopped StreamStatus = "stopped" )
StreamStatus values are used by the stream manager and API.
type ThumbnailConfig ¶
type ThumbnailConfig struct {
Enabled bool `json:"enabled" yaml:"enabled"`
// IntervalSec generates one thumbnail every N seconds.
IntervalSec int `json:"interval_sec" yaml:"interval_sec"`
// Width and Height of the output thumbnail in pixels.
// 0 = match source resolution.
Width int `json:"width" yaml:"width"`
Height int `json:"height" yaml:"height"`
// Quality is the JPEG quality (1–31, lower = better). Default: 5.
Quality int `json:"quality" yaml:"quality"`
// OutputDir is relative to the publisher HLS directory.
// E.g. "thumbnails" → written to {hls_dir}/{stream_code}/thumbnails/thumb.jpg
OutputDir string `json:"output_dir" yaml:"output_dir"`
}
ThumbnailConfig controls periodic screenshot generation for stream preview. Thumbnails are written as JPEG files alongside HLS segments.
type TranscoderConfig ¶
type TranscoderConfig struct {
// Mode selects the FFmpeg process topology. Empty = TranscoderModeMulti.
Mode TranscoderMode `json:"mode,omitempty" yaml:"mode,omitempty"`
Video VideoTranscodeConfig `json:"video" yaml:"video"`
Audio AudioTranscodeConfig `json:"audio" yaml:"audio"`
Decoder DecoderConfig `json:"decoder" yaml:"decoder"`
Global TranscoderGlobalConfig `json:"global" yaml:"global"`
// ExtraArgs are raw FFmpeg arguments appended after the generated command.
// Use with caution — may conflict with generated arguments.
ExtraArgs []string `json:"extra_args,omitempty" yaml:"extra_args,omitempty"`
// Watermark is a runtime-only field populated by the coordinator from
// Stream.Watermark before each transcoder.Start. Not persisted on the
// transcoder section (json/yaml "-") so the API surface keeps
// Stream.Watermark as the single source of truth.
Watermark *WatermarkConfig `json:"-" yaml:"-"`
}
TranscoderConfig is the complete transcoding configuration for a stream.
func (*TranscoderConfig) IsMultiOutput ¶ added in v0.0.51
func (t *TranscoderConfig) IsMultiOutput() bool
IsMultiOutput reports whether the resolved transcoder mode is multi-output. Treats empty Mode as the default (multi). Hot path so callers don't have to repeat the empty-string fallback.
func (*TranscoderConfig) ValidateMode ¶ added in v0.0.51
func (t *TranscoderConfig) ValidateMode() error
ValidateMode rejects unknown TranscoderMode values at save time so a typo can't silently pin the stream into legacy / multi without operator awareness. Empty Mode is allowed and routes to multi via IsMultiOutput.
type TranscoderGlobalConfig ¶
type TranscoderGlobalConfig struct {
// HW selects the acceleration backend.
HW HWAccel `json:"hw" yaml:"hw"`
// FPS sets output framerate. 0 = source/default.
FPS int `json:"fps" yaml:"fps"`
// GOP sets keyframe interval in frames. 0 = encoder default.
GOP int `json:"gop" yaml:"gop"`
// DeviceID selects hardware device index.
DeviceID int `json:"deviceid" yaml:"deviceid"`
}
TranscoderGlobalConfig holds global transcoder parameters.
type TranscoderMode ¶ added in v0.0.51
type TranscoderMode string
TranscoderMode picks the FFmpeg process topology for a stream.
Per-stream so operators can mix policies on the same server — e.g. a flaky SRT source on legacy mode for per-rendition isolation, while stable studio feeds run multi to halve decode work.
const ( // TranscoderModeMulti runs ONE FFmpeg per stream that decodes the input // once and emits every rendition through its own output pipe. Default // when Mode is empty. Saves ~40% RAM and ~50% NVDEC sessions on ABR // streams; trade-off is that any FFmpeg-fatal input glitch (corrupt // frame, source restart) takes down all renditions together for the // 2–3 s it takes to respawn. TranscoderModeMulti TranscoderMode = "multi" // TranscoderModePerProfile runs ONE FFmpeg per rendition. Higher RAM / // decode cost in exchange for per-rendition crash isolation: a single // rendition's encoder failing doesn't disrupt the others. Recommended // for upstreams known to deliver bursts of malformed packets. TranscoderModePerProfile TranscoderMode = "per_profile" )
TranscoderMode values.
type VODMount ¶
type VODMount struct {
Name VODName `json:"name" yaml:"name"`
Storage string `json:"storage" yaml:"storage"`
Comment string `json:"comment,omitempty" yaml:"comment,omitempty"`
}
VODMount registers a host filesystem directory as a named media library. The system does not maintain a file index; file lookups and listings are resolved live against Storage.
Ingest URLs take the form file://<Name>/<relative/path/inside/storage>. Any path that would escape Storage (via "..", absolute components, or symlink traversal) must be rejected by the resolver.
func (*VODMount) ValidateStorage ¶
ValidateStorage checks that Storage is a syntactically valid absolute path. The directory itself is not required to exist at validation time — operators may create it later — but the path must be absolute to avoid surprises from the server's working directory.
type VODName ¶
type VODName string
VODName identifies a VOD mount. It appears as the URL host in ingest sources (e.g. file://<name>/path/to/file.mp4), so it must be URL-host-safe.
type VideoCodec ¶
type VideoCodec string
VideoCodec identifies the video compression format.
const ( VideoCodecH264 VideoCodec = "h264" // AVC — widest device support VideoCodecH265 VideoCodec = "h265" // HEVC — ~50% smaller than H.264 VideoCodecAV1 VideoCodec = "av1" // royalty-free, best compression (high CPU) VideoCodecMPEG2 VideoCodec = "mp2v" // MPEG-2 Part 2 — DVB legacy contribution feeds, transmitter chains VideoCodecCopy VideoCodec = "copy" // passthrough — no re-encode )
VideoCodec values name supported output video codecs.
type VideoProfile ¶
type VideoProfile struct {
// Width and Height define the output resolution.
// Set to 0 to keep the source dimensions (Width=0 & Height=0 = no scaling).
Width int `json:"width" yaml:"width"`
Height int `json:"height" yaml:"height"`
// Bitrate is the target video bitrate in kbps. 0 = encoder auto.
Bitrate int `json:"bitrate" yaml:"bitrate"`
// MaxBitrate caps the peak bitrate in kbps (CBR/VBR ceiling). 0 = no cap.
MaxBitrate int `json:"max_bitrate" yaml:"max_bitrate"`
// Framerate is the output frame rate (fps). 0 = match source.
Framerate float64 `json:"framerate" yaml:"framerate"`
// KeyframeInterval is the GOP size in seconds.
// Must match or be a multiple of the HLS/DASH segment duration.
KeyframeInterval int `json:"keyframe_interval" yaml:"keyframe_interval"`
Codec VideoCodec `json:"codec" yaml:"codec"`
// Preset controls the encoder speed/quality tradeoff.
// libx264: "ultrafast" | "superfast" | "veryfast" | "faster" | "fast" | "medium" | "slow" | "veryslow"
// NVENC: "p1" (fastest) .. "p7" (highest quality)
Preset string `json:"preset" yaml:"preset"`
// Profile controls the H.264/H.265 encoding profile.
// "baseline" | "main" | "high" (H.264); "main" | "main10" (H.265)
Profile string `json:"profile" yaml:"profile"`
// Level controls the H.264/H.265 encoding level.
// Common: "3.1", "4.0", "4.1", "4.2", "5.0", "5.1"
Level string `json:"level" yaml:"level"`
// Bframes is the number of consecutive B-frames the encoder may emit.
// nil = encoder default; 0 = explicit none (low-latency live);
// 2-3 = typical VOD; NVENC HW B-ref pyramid is auto-enabled when >0.
Bframes *int `json:"bframes,omitempty" yaml:"bframes,omitempty"`
// Refs is the number of reference frames. nil = encoder default.
// Higher = better compression at cost of CPU/latency. NVENC has its own caps.
Refs *int `json:"refs,omitempty" yaml:"refs,omitempty"`
// SAR is the output Sample Aspect Ratio, "N:M". "" = inherit from source.
// Use "1:1" for square pixels (web); "16:11", "59:54" etc. for anamorphic.
SAR string `json:"sar,omitempty" yaml:"sar,omitempty"`
// ResizeMode chooses how the source is fitted to Width/Height.
// "" defaults to ResizeModePad.
ResizeMode ResizeMode `json:"resize_mode,omitempty" yaml:"resize_mode,omitempty"`
}
VideoProfile is a single rendition in the ABR (Adaptive Bitrate) ladder. The Transcoder produces one FFmpeg output per profile. Stable rendition ids are derived from slice order: track_1, track_2, track_3, … (1-based).
type VideoTranscodeConfig ¶
type VideoTranscodeConfig struct {
// Copy copies origin video without re-encoding.
Copy bool `json:"copy" yaml:"copy"`
// Interlace selects the deinterlace pre-filter applied before scaling.
// Applies once per FFmpeg subprocess (i.e. per profile in the ABR ladder).
// "" disables the filter; use ResizeModeProgressive to assert progressive source.
Interlace InterlaceMode `json:"interlace,omitempty" yaml:"interlace,omitempty"`
// Profiles defines ABR renditions when re-encoding.
Profiles []VideoProfile `json:"profiles,omitempty" yaml:"profiles,omitempty"`
}
VideoTranscodeConfig defines video transcoding behavior.
type WatermarkAsset ¶ added in v0.0.44
type WatermarkAsset struct {
// ID is the immutable filesystem-safe identifier (also the on-disk basename).
ID WatermarkAssetID `json:"id" yaml:"id"`
// Name is the human display label shown in UIs. Defaults to the upload's
// original filename when the operator doesn't override.
Name string `json:"name" yaml:"name"`
// FileName is the original filename at upload time, preserved for
// audit / download responses. Includes extension (e.g. "logo.png").
FileName string `json:"file_name" yaml:"file_name"`
// ContentType is the MIME type sniffed at upload time
// (e.g. "image/png", "image/jpeg"). Used for Content-Type on /raw GET.
ContentType string `json:"content_type" yaml:"content_type"`
// SizeBytes is the on-disk size of the image. Sidecar JSON is excluded.
SizeBytes int64 `json:"size_bytes" yaml:"size_bytes"`
// UploadedAt is the wall-clock UTC time the asset was first stored.
UploadedAt time.Time `json:"uploaded_at" yaml:"uploaded_at"`
}
WatermarkAsset is the persisted metadata for one uploaded watermark image. The actual image bytes live next to the metadata sidecar in the assets directory. Both files share the asset ID basename:
<assets_dir>/<id>.<ext> ← image <assets_dir>/<id>.json ← this struct serialised
The simple two-file layout means we don't need a separate database for watermark metadata; an `os.ReadDir` rebuilds the registry after any restart.
type WatermarkAssetID ¶ added in v0.0.44
type WatermarkAssetID string
WatermarkAssetID is the stable identifier for an uploaded watermark asset. It is the basename (without extension) of the file on disk and also the identifier referenced from Stream.Watermark.AssetID.
type WatermarkConfig ¶
type WatermarkConfig struct {
Enabled bool `json:"enabled" yaml:"enabled"`
Type WatermarkType `json:"type" yaml:"type"`
// Text is the string to render. Supports strftime directives for live timestamps.
// E.g. "LIVE %{localtime:%H:%M:%S}"
Text string `json:"text" yaml:"text"`
// FontFile is the path to a .ttf/.otf font file.
// "" = FFmpeg default font.
FontFile string `json:"font_file" yaml:"font_file"`
// FontSize in pixels. Default: 24.
FontSize int `json:"font_size" yaml:"font_size"`
// FontColor in FFmpeg color syntax. E.g. "white", "#FFFFFF", "white@0.8".
FontColor string `json:"font_color" yaml:"font_color"`
// AssetID, when set, references a WatermarkAsset uploaded via the
// /watermarks API. Coordinator resolves it to an on-disk file path
// before passing the config to the transcoder. Takes precedence over
// ImagePath when both are set.
AssetID WatermarkAssetID `json:"asset_id,omitempty" yaml:"asset_id,omitempty"`
// ImagePath is the absolute path to a watermark image (PNG with alpha
// recommended). Use this for assets pre-staged on the host outside the
// /watermarks library. Mutually exclusive with AssetID.
ImagePath string `json:"image_path,omitempty" yaml:"image_path,omitempty"`
// Opacity controls transparency: 0.0 = fully transparent, 1.0 = fully opaque.
Opacity float64 `json:"opacity" yaml:"opacity"`
// Position selects how the (X, Y) of the overlay are computed:
// - top_left/top_right/bottom_left/bottom_right/center: convenience
// anchors. OffsetX/Y act as inward padding from the chosen edge
// (Center ignores offsets).
// - custom: X / Y below are used as raw FFmpeg expressions —
// pixel ints ("100"), expressions ("main_w-overlay_w-50"), or
// time-aware fades ("if(gt(t,5),10,-100)") all work.
Position WatermarkPosition `json:"position" yaml:"position"`
// OffsetX and OffsetY are pixel offsets from the chosen position edge.
// Ignored when Position == custom.
OffsetX int `json:"offset_x" yaml:"offset_x"`
OffsetY int `json:"offset_y" yaml:"offset_y"`
// X / Y are raw FFmpeg coordinate expressions used only when Position
// == custom. Empty string defaults to "0". The exact variables exposed
// depend on the filter:
// - drawtext (text watermark): w/h = frame size, tw/th = text size
// - overlay (image watermark): W/H = main video size, w/h = overlay size
X string `json:"x,omitempty" yaml:"x,omitempty"`
Y string `json:"y,omitempty" yaml:"y,omitempty"`
// Resize, when true, makes the watermark render at a consistent on-screen
// ratio across every rendition in an ABR ladder. The largest profile
// renders the asset at its NATIVE pixel size (operators design at the top
// rendition); smaller profiles shrink the asset by the ratio of their
// width to the largest profile's width. Pixel-scale fields (FontSize for
// text, OffsetX/OffsetY for both) shrink with the same factor so corner
// padding and glyph height stay visually proportional.
//
// When false (default), the watermark uses native pixel dimensions and
// fixed offsets on every profile — appearing larger on lower-resolution
// renditions because they cover fewer pixels of frame.
Resize bool `json:"resize,omitempty" yaml:"resize,omitempty"`
}
WatermarkConfig defines an overlay applied to the video before encoding. Applied via FFmpeg drawtext (text) or overlay (image) filter.
func (*WatermarkConfig) IsActive ¶ added in v0.0.44
func (w *WatermarkConfig) IsActive() bool
IsActive reports whether the watermark should actually be applied. Returns false for nil, disabled, or fully-transparent configs (any of which would emit a no-op filter — easier to just skip).
For image watermarks, the ImagePath check accepts EITHER an explicit path OR an AssetID — coordinator resolves AssetID to ImagePath before transcoder picks up the config, so by the time the filter is built ImagePath is always populated when the watermark is active.
func (*WatermarkConfig) Resolved ¶ added in v0.0.44
func (w *WatermarkConfig) Resolved() *WatermarkConfig
Resolved returns a copy with empty / zero fields replaced by defaults. Caller treats the returned value as fully populated. Returns nil when the input is nil so callers can chain safely.
func (*WatermarkConfig) Validate ¶ added in v0.0.44
func (w *WatermarkConfig) Validate() error
Validate enforces invariants the FFmpeg filter graph relies on. Called from the API layer at save time so misconfigured streams never reach the transcoder. Disabled watermarks (Enabled=false) skip validation entirely so an operator can park a draft config without tripping errors.
type WatermarkPosition ¶
type WatermarkPosition string
WatermarkPosition controls where the overlay is placed in the frame.
const ( WatermarkTopLeft WatermarkPosition = "top_left" WatermarkTopRight WatermarkPosition = "top_right" WatermarkBottomLeft WatermarkPosition = "bottom_left" WatermarkBottomRight WatermarkPosition = "bottom_right" WatermarkCenter WatermarkPosition = "center" WatermarkCustom WatermarkPosition = "custom" )
WatermarkPosition values name corners, center, or `custom` for overlay placement. `custom` activates the X / Y fields below — operators set raw FFmpeg expressions ("100", "main_w-overlay_w-50", "if(gt(t,5),10,-100)") for full positional flexibility while the corners stay convenient defaults.
type WatermarkType ¶
type WatermarkType string
WatermarkType determines whether the overlay is text or an image.
const ( WatermarkTypeText WatermarkType = "text" WatermarkTypeImage WatermarkType = "image" )
WatermarkType values select overlay content kind.