mirror of
https://github.com/m1k1o/neko.git
synced 2026-11-03 00:11:48 -06:00
fix(capture): accept a bare pipeline string in video pipelines entries (#701)
* fix(capture): accept a bare pipeline string in video pipelines entries
NEKO_CAPTURE_VIDEO_PIPELINES expects every entry to be a full pipeline config
object, but the singular capture.video.pipeline option trains people to write
the whole pipeline as a plain string, so a dict like {"high": "ximagesrc ...
! appsink"} failed to unmarshal, the error got reduced to a warn and neko fell
back to the default vp8 pipeline silently, also overriding a directly set
video codec
the json string hook decodes the env value into a generic map, so the shorthand
is restored in the mapstructure chain: a string value for an entry now becomes
VideoConfig{GstPipeline: ...}, the same shape the single pipeline option
produces, object entries are untouched
the new config test drives the production env flow (Init + Set) and fails on
master with the same two warnings the reporter saw
Fixes #689
* feat(capture): add shorthand support for pipeline entries as plain strings.
---------
Co-authored-by: Miroslav Šedivý <sedivy.miro@gmail.com>
This commit is contained in:
committed by
GitHub
parent
371f5c2c7b
commit
73b9d71b0d
@@ -2,8 +2,10 @@ package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -322,6 +324,17 @@ func (Capture) InitV2(cmd *cobra.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// videoPipelineShorthandHook accepts a bare pipeline string in place of a full
|
||||
// video pipeline config, mirroring the capture.video.pipeline shorthand.
|
||||
func videoPipelineShorthandHook() mapstructure.DecodeHookFunc {
|
||||
return func(from reflect.Type, to reflect.Type, data any) (any, error) {
|
||||
if from.Kind() == reflect.String && to == reflect.TypeOf(types.VideoConfig{}) {
|
||||
return types.VideoConfig{GstPipeline: data.(string)}, nil
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Capture) Set() {
|
||||
var ok bool
|
||||
|
||||
@@ -342,7 +355,10 @@ func (s *Capture) Set() {
|
||||
|
||||
s.VideoIDs = viper.GetStringSlice("capture.video.ids")
|
||||
if err := viper.UnmarshalKey("capture.video.pipelines", &s.VideoPipelines, viper.DecodeHook(
|
||||
utils.JsonStringAutoDecode(s.VideoPipelines),
|
||||
mapstructure.ComposeDecodeHookFunc(
|
||||
utils.JsonStringAutoDecode(s.VideoPipelines),
|
||||
videoPipelineShorthandHook(),
|
||||
),
|
||||
)); err != nil {
|
||||
log.Warn().Err(err).Msgf("unable to parse video pipelines")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// loadCaptureFromEnv mirrors the production config flow: env prefix setup,
|
||||
// flag registration via Init, then value population via Set.
|
||||
func loadCaptureFromEnv(t *testing.T, pipelines 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)
|
||||
}
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
func TestVideoPipelinesEnv(t *testing.T) {
|
||||
const pipeline = "ximagesrc display-name={display} show-pointer=false ! appsink name=appsink"
|
||||
|
||||
t.Run("object entries with gst_pipeline keep working", func(t *testing.T) {
|
||||
c := loadCaptureFromEnv(t, `{"high":{"gst_pipeline":"`+pipeline+`"}}`)
|
||||
|
||||
cfg, ok := c.VideoPipelines["high"]
|
||||
if !ok {
|
||||
t.Fatalf("pipeline entry missing, got %v", c.VideoPipelines)
|
||||
}
|
||||
if cfg.GstPipeline != pipeline {
|
||||
t.Errorf("expected pipeline %q, got %q", pipeline, cfg.GstPipeline)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("bare pipeline string entries are accepted", func(t *testing.T) {
|
||||
c := loadCaptureFromEnv(t, `{"high":"`+pipeline+`"}`)
|
||||
|
||||
cfg, ok := c.VideoPipelines["high"]
|
||||
if !ok {
|
||||
t.Fatalf("bare pipeline string entry was dropped and neko fell back to the default vp8 pipeline, got %v", c.VideoPipelines)
|
||||
}
|
||||
if cfg.GstPipeline != pipeline {
|
||||
t.Errorf("expected pipeline %q, got %q", pipeline, cfg.GstPipeline)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user