From 97c6e9b6a0c49cfa128a2cdeac2f3f09145e81ff Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 7 Apr 2026 22:06:32 +0200 Subject: [PATCH] ingest: fix source defaults and discovery context handling --- internal/control/server.go | 23 +++++++++++++---------- internal/ingest/factory/factory.go | 22 ++++++++++++++-------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/internal/control/server.go b/internal/control/server.go index 9fcd5cd..fe538b1 100644 --- a/internal/control/server.go +++ b/internal/control/server.go @@ -8,20 +8,23 @@ import ( ) const ( - defaultReadTimeout = 5 * time.Second - defaultWriteTimeout = 10 * time.Second - defaultIdleTimeout = 60 * time.Second - defaultMaxHeaderBytes = 1 << 20 // 1 MiB + defaultReadHeaderTimeout = 5 * time.Second + defaultIdleTimeout = 60 * time.Second + defaultMaxHeaderBytes = 1 << 20 // 1 MiB ) // NewHTTPServer returns a configured HTTP server for the control plane. +// +// WriteTimeout is intentionally not set: /audio/stream accepts long-lived +// POST bodies (continuous PCM push) that would be cut off by a global write +// deadline. Individual endpoints are protected by MaxBytesReader limits. +// ReadHeaderTimeout guards against slow-header attacks. func NewHTTPServer(cfg config.Config, handler http.Handler) *http.Server { return &http.Server{ - Addr: cfg.Control.ListenAddress, - Handler: handler, - ReadTimeout: defaultReadTimeout, - WriteTimeout: defaultWriteTimeout, - IdleTimeout: defaultIdleTimeout, - MaxHeaderBytes: defaultMaxHeaderBytes, + Addr: cfg.Control.ListenAddress, + Handler: handler, + ReadHeaderTimeout: defaultReadHeaderTimeout, + IdleTimeout: defaultIdleTimeout, + MaxHeaderBytes: defaultMaxHeaderBytes, } } diff --git a/internal/ingest/factory/factory.go b/internal/ingest/factory/factory.go index 5f8696c..223e9db 100644 --- a/internal/ingest/factory/factory.go +++ b/internal/ingest/factory/factory.go @@ -42,7 +42,7 @@ type AES67DiscoverRequest struct { type AES67DiscoverFunc func(ctx context.Context, req AES67DiscoverRequest) (aoiprxkit.SAPAnnouncement, error) -func BuildSource(cfg config.Config, deps Deps) (ingest.Source, AudioIngress, error) { +func BuildSource(ctx context.Context, cfg config.Config, deps Deps) (ingest.Source, AudioIngress, error) { switch normalizeIngestKind(cfg.Ingest.Kind) { case "", "none": return nil, nil, nil @@ -83,7 +83,7 @@ func BuildSource(cfg config.Config, deps Deps) (ingest.Source, AudioIngress, err src := srt.New("srt-main", srtCfg, opts...) return src, nil, nil case "aes67", "aoip", "aoip-rtp": - aoipCfg, detail, origin, err := buildAES67Config(cfg, deps) + aoipCfg, detail, origin, err := buildAES67Config(ctx, cfg, deps) if err != nil { return nil, nil, err } @@ -115,7 +115,10 @@ func SampleRateForKind(cfg config.Config) int { return cfg.Ingest.HTTPRaw.SampleRateHz } case "icecast": - return 44100 + // 48000 Hz is the most common rate for modern Icecast streams. + // The ingest runtime will auto-correct to the actual decoded rate + // after the first PCM chunk arrives (see runtime.go handleChunk). + return 48000 case "srt": if cfg.Ingest.SRT.SampleRateHz > 0 { return cfg.Ingest.SRT.SampleRateHz @@ -125,14 +128,17 @@ func SampleRateForKind(cfg config.Config) int { return cfg.Ingest.AES67.SampleRateHz } } - return 44100 + // Default to 48000 Hz: the correct rate for professional sources + // (SRT, AES67) and modern streams. The ingest runtime corrects this + // dynamically from the first decoded chunk for compressed sources. + return 48000 } func normalizeIngestKind(kind string) string { return strings.ToLower(strings.TrimSpace(kind)) } -func buildAES67Config(cfg config.Config, deps Deps) (aoiprxkit.Config, string, *ingest.SourceOrigin, error) { +func buildAES67Config(ctx context.Context, cfg config.Config, deps Deps) (aoiprxkit.Config, string, *ingest.SourceOrigin, error) { base := aoiprxkit.DefaultConfig() ing := cfg.Ingest.AES67 if strings.TrimSpace(ing.InterfaceName) != "" { @@ -160,7 +166,7 @@ func buildAES67Config(cfg config.Config, deps Deps) (aoiprxkit.Config, string, * base.ReadBufferBytes = ing.ReadBufferBytes } - sdpText, discoveredStreamName, origin, err := resolveAES67SDP(ing, deps) + sdpText, discoveredStreamName, origin, err := resolveAES67SDP(ctx, ing, deps) if err != nil { return aoiprxkit.Config{}, "", nil, err } @@ -205,7 +211,7 @@ func buildAES67Config(cfg config.Config, deps Deps) (aoiprxkit.Config, string, * return base, "", origin, nil } -func resolveAES67SDP(ing config.IngestAES67Config, deps Deps) (string, string, *ingest.SourceOrigin, error) { +func resolveAES67SDP(ctx context.Context, ing config.IngestAES67Config, deps Deps) (string, string, *ingest.SourceOrigin, error) { sdpText := strings.TrimSpace(ing.SDP) if sdpText == "" && strings.TrimSpace(ing.SDPPath) != "" { sdpPath := filepath.Clean(ing.SDPPath) @@ -246,7 +252,7 @@ func resolveAES67SDP(ing config.IngestAES67Config, deps Deps) (string, string, * if discover == nil { discover = discoverAES67ViaSAP } - announcement, err := discover(context.Background(), req) + announcement, err := discover(ctx, req) if err != nil { return "", "", nil, fmt.Errorf("discover ingest.aes67 stream %q via SAP: %w", req.StreamName, err) }