mirror of
https://github.com/m1k1o/neko.git
synced 2026-11-03 08:21:50 -06:00
fix(capture): derive video ids from pipelines and guard empty list (#711)
when NEKO_CAPTURE_VIDEO_PIPELINES is set without capture.video.ids the VideoIDs list stayed empty, so capture.Video().IDs() returned [] and the first websocket signal request crashed the whole server with "index out of range [0] with length 0" in signalRequest. now we fill VideoIDs from the configured pipeline keys (sorted by id) whenever no ids are set explicitly, and signalRequest returns a plain error instead of panicking when there is no video stream at all. added tests for the config loader, the capture manager and the signal handler so this path stays covered.
This commit is contained in:
committed by
GitHub
parent
bdd0428a60
commit
a2cb38dd10
@@ -3,6 +3,7 @@ package config
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
@@ -423,6 +424,20 @@ func (s *Capture) Set() {
|
||||
log.Warn().Msg("you are setting both single video pipeline and multiple video pipelines, ignoring single video pipeline")
|
||||
}
|
||||
|
||||
// If no video ids were configured explicitly, derive them from the configured
|
||||
// pipelines so the capture manager always has at least one stream to select.
|
||||
// Without this, NEKO_CAPTURE_VIDEO_PIPELINES alone left VideoIDs empty and the
|
||||
// first WebRTC signal request panicked on videos[0].
|
||||
if len(s.VideoIDs) == 0 && len(s.VideoPipelines) > 0 {
|
||||
ids := make([]string, 0, len(s.VideoPipelines))
|
||||
for id := range s.VideoPipelines {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
s.VideoIDs = ids
|
||||
log.Warn().Strs("video_ids", ids).Msg("no capture.video.ids configured, using all video pipelines sorted by id")
|
||||
}
|
||||
|
||||
s.VideoShowPointer = viper.GetBool("capture.video.show_pointer")
|
||||
if viper.IsSet("capture.video.show_pointer") {
|
||||
for k, p := range s.VideoPipelines {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// loadCaptureFromEnvWithIDs mirrors loadCaptureFromEnv but also allows setting
|
||||
// capture.video.ids directly, so we can check that explicit ids are preserved.
|
||||
func loadCaptureFromEnvWithIDs(t *testing.T, pipelines string, ids []string) *Capture {
|
||||
t.Helper()
|
||||
|
||||
viper.Reset()
|
||||
viper.SetEnvPrefix("NEKO")
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
t.Setenv("NEKO_CAPTURE_VIDEO_PIPELINES", pipelines)
|
||||
|
||||
c := &Capture{}
|
||||
cmd := &cobra.Command{}
|
||||
if err := c.Init(cmd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ids != nil {
|
||||
viper.Set("capture.video.ids", ids)
|
||||
}
|
||||
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Regression test for the panic in signalRequest: when video pipelines are
|
||||
// configured without an explicit capture.video.ids list, VideoIDs must be
|
||||
// derived from the pipeline keys so the capture manager always has a default
|
||||
// stream to hand out.
|
||||
func TestVideoIDsDerivedFromPipelines(t *testing.T) {
|
||||
const pipeline = "ximagesrc display-name={display} show-pointer=false ! appsink name=appsink"
|
||||
|
||||
t.Run("multiple pipelines without ids populate sorted ids", func(t *testing.T) {
|
||||
c := loadCaptureFromEnvWithIDs(t, `{"high":"`+pipeline+`","medium":"`+pipeline+`","low":"`+pipeline+`"}`, nil)
|
||||
|
||||
want := []string{"high", "low", "medium"}
|
||||
if !reflect.DeepEqual(c.VideoIDs, want) {
|
||||
t.Fatalf("expected video ids %v derived from pipeline keys, got %v (an empty list makes signalRequest panic)", want, c.VideoIDs)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("explicit ids are preserved", func(t *testing.T) {
|
||||
c := loadCaptureFromEnvWithIDs(t, `{"high":"`+pipeline+`","low":"`+pipeline+`"}`, []string{"low", "high"})
|
||||
|
||||
want := []string{"low", "high"}
|
||||
if !reflect.DeepEqual(c.VideoIDs, want) {
|
||||
t.Fatalf("expected explicit video ids %v to be preserved, got %v", want, c.VideoIDs)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user