domain

package
v0.0.37 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package domain defines core types shared across Open Streamer modules.

Index

Constants

View Source
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.
	DefaultHookMaxRetries = 3
	// DefaultHookTimeoutSec is the per-attempt delivery timeout when
	// Hook.TimeoutSec=0.
	DefaultHookTimeoutSec = 10

	// 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.ConnectTimeoutSec 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"

	// DefaultRTMPConnectTimeoutSec is the dial timeout (seconds) for RTMP
	// pull when InputNetConfig.ConnectTimeoutSec is zero.
	DefaultRTMPConnectTimeoutSec = 10
	// DefaultRTSPConnectTimeoutSec is the dial / read timeout (seconds)
	// for RTSP pull when InputNetConfig.ConnectTimeoutSec is zero.
	DefaultRTSPConnectTimeoutSec = 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.

View Source
const MaxStreamCodeLen = 128

MaxStreamCodeLen is the maximum length of a user-defined stream code.

View Source
const MaxVODNameLen = 64

MaxVODNameLen bounds the length of a VOD mount name used as a URL host component.

Variables

This section is empty.

Functions

func IsCopyInput added in v0.0.20

func IsCopyInput(in Input) bool

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

func IsCopyShapeError(err error) bool

IsCopyShapeError reports whether err is a *CopyShapeError.

func IsMixerInput added in v0.0.22

func IsMixerInput(in Input) bool

IsMixerInput reports whether the given Input is a `mixer://` reference.

func IsMixerShapeError added in v0.0.22

func IsMixerShapeError(err error) bool

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).

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 ValidateCopyShape added in v0.0.20

func ValidateCopyShape(s *Stream, lookup StreamLookup) error

ValidateCopyShape enforces copy:// constraints on a single stream:

  1. Self-copy (`copy://A` inside stream A) is rejected — a stream cannot re-stream itself; that's an infinite fan-out loop.
  2. 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)
  3. 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:

  1. Self-mix (`mixer://A,X` or `mixer://X,A` inside stream A) is rejected — a stream can't mix with itself.
  2. 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

func ValidateStreamCode(s string) error

ValidateStreamCode reports whether s is a non-empty valid stream code.

func ValidateVODName

func ValidateVODName(s string) error

ValidateVODName reports whether s is a non-empty, URL-host-safe VOD mount name.

Types

type AVCodec

type AVCodec uint8

AVCodec identifies elementary stream codec for AVPacket payloads.

const (
	AVCodecUnknown AVCodec = iota
	AVCodecH264
	AVCodecH265
	AVCodecAAC
)

AVCodec values.

func (AVCodec) IsAudio added in v0.0.22

func (c AVCodec) IsAudio() bool

IsAudio reports whether the codec carries an audio elementary stream.

func (AVCodec) IsVideo added in v0.0.22

func (c AVCodec) IsVideo() bool

IsVideo reports whether the codec carries a video elementary stream.

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).

func (*AVPacket) Clone

func (p *AVPacket) Clone() *AVPacket

Clone returns a deep copy suitable for buffer fan-out.

type AudioCodec

type AudioCodec string

AudioCodec identifies the audio compression format.

const (
	AudioCodecAAC  AudioCodec = "aac"  // default for HLS/DASH
	AudioCodecMP3  AudioCodec = "mp3"  // legacy compatibility
	AudioCodecOpus AudioCodec = "opus" // best for WebRTC / low-latency
	AudioCodecAC3  AudioCodec = "ac3"  // Dolby Digital — broadcast use
	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

type ErrorEntry struct {
	Message string    `json:"message"`
	At      time.Time `json:"at"`
}

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"`
	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 URL or Kafka topic
	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).
	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"`
}

Hook is a registered external integration that receives domain events.

type HookID

type HookID string

HookID is the unique identifier for a registered hook.

type HookType

type HookType string

HookType is the delivery mechanism for a hook.

const (
	HookTypeHTTP  HookType = "http"
	HookTypeKafka HookType = "kafka"
)

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"`

	// 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 {
	// ConnectTimeoutSec caps each HTTP round-trip (headers + full body) for pull
	// readers that use net/http (e.g. HLS playlist and segment GETs). Zero uses
	// the reader's default (30s for HLS).
	ConnectTimeoutSec int `json:"connect_timeout_sec,omitempty" yaml:"connect_timeout_sec,omitempty"`

	// ReadTimeoutSec is the max silence duration before declaring the input dead.
	ReadTimeoutSec int `json:"read_timeout_sec,omitempty" yaml:"read_timeout_sec,omitempty"`

	// Reconnect enables automatic reconnection when the input drops.
	Reconnect bool `json:"reconnect,omitempty" yaml:"reconnect,omitempty"`

	// ReconnectDelaySec is the initial delay before the first reconnect attempt.
	ReconnectDelaySec int `json:"reconnect_delay_sec,omitempty" yaml:"reconnect_delay_sec,omitempty"`

	// ReconnectMaxDelaySec caps the exponential backoff delay.
	ReconnectMaxDelaySec int `json:"reconnect_max_delay_sec,omitempty" yaml:"reconnect_max_delay_sec,omitempty"`

	// MaxReconnects is the total number of reconnect attempts (0 = unlimited).
	MaxReconnects int `json:"max_reconnects,omitempty" yaml:"max_reconnects,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 reconnect and timeout behaviour for an input.

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 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"`
}

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 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 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

func (s *Stream) ValidateInputPriorities() error

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

func (s *Stream) ValidateUniqueInputs() error

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 {
	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"`
}

TranscoderConfig is the complete transcoding configuration for a stream.

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 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

func (m *VODMount) ValidateStorage() error

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)
	VideoCodecVP9  VideoCodec = "vp9"  // Google codec, WebRTC-friendly
	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 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"`

	// ImagePath is the path to the watermark image (PNG with alpha recommended).
	ImagePath string `json:"image_path" yaml:"image_path"`

	// Opacity controls transparency: 0.0 = fully transparent, 1.0 = fully opaque.
	Opacity float64 `json:"opacity" yaml:"opacity"`

	// Position is the corner/center anchor for the watermark.
	Position WatermarkPosition `json:"position" yaml:"position"`

	// OffsetX and OffsetY are pixel offsets from the chosen position edge.
	OffsetX int `json:"offset_x" yaml:"offset_x"`
	OffsetY int `json:"offset_y" yaml:"offset_y"`
}

WatermarkConfig defines an overlay applied to the video before encoding. Applied via FFmpeg drawtext (text) or overlay (image) filter.

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"
)

WatermarkPosition values name corners and center for overlay placement.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL