update pipelines config settings extractor.

This commit is contained in:
Miroslav Šedivý 2021-03-30 00:36:13 +02:00
parent 051ab9f426
commit d84297fbec
3 changed files with 102 additions and 46 deletions

View File

@ -2,6 +2,7 @@ package capture
import ( import (
"fmt" "fmt"
"strings"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -56,19 +57,20 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
} }
videos := map[string]*StreamManagerCtx{} videos := map[string]*StreamManagerCtx{}
videoIDs := []string{} for key, cnf := range config.VideoPipelines {
for key, pipelineConf := range config.Video { pipelineConf := cnf
codec, err := pipelineConf.GetCodec()
if err != nil {
logger.Panic().Err(err).Str("video_key", key).Msg("unable to get video codec")
}
createPipeline := func() string { createPipeline := func() string {
screen := desktop.GetScreenSize() if pipelineConf.GstPipeline != "" {
return strings.Replace(pipelineConf.GstPipeline, "{display}", config.Display, 1)
}
screen := desktop.GetScreenSize()
pipeline, err := pipelineConf.GetPipeline(*screen) pipeline, err := pipelineConf.GetPipeline(*screen)
if err != nil { if err != nil {
logger.Panic().Err(err).Str("video_key", key).Msg("unable to get video pipeline") logger.Panic().Err(err).
Str("video_id", key).
Msg("unable to get video pipeline")
} }
return fmt.Sprintf( return fmt.Sprintf(
@ -78,11 +80,14 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
} }
// trigger function to catch evaluation errors at startup // trigger function to catch evaluation errors at startup
_ = createPipeline() pipeline := createPipeline()
logger.Info().
Str("video_id", key).
Str("pipeline", pipeline).
Msg("syntax check for video stream pipeline passed")
// append to videos // append to videos
videos[key] = streamNew(codec, createPipeline) videos[key] = streamNew(config.VideoCodec, createPipeline)
videoIDs = append(videoIDs, key)
} }
return &CaptureManagerCtx{ return &CaptureManagerCtx{
@ -106,7 +111,7 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
) )
}), }),
videos: videos, videos: videos,
videoIDs: videoIDs, videoIDs: config.VideoIDs,
} }
} }

View File

@ -15,7 +15,9 @@ import (
type Capture struct { type Capture struct {
Display string Display string
Video map[string] types.VideoConfig VideoCodec codec.RTPCodec
VideoIDs []string
VideoPipelines map[string]types.VideoConfig
AudioDevice string AudioDevice string
AudioCodec codec.RTPCodec AudioCodec codec.RTPCodec
@ -49,6 +51,22 @@ func (Capture) Init(cmd *cobra.Command) error {
return err return err
} }
// videos
cmd.PersistentFlags().String("capture.video.codec", "vp8", "video codec to be used")
if err := viper.BindPFlag("capture.video.codec", cmd.PersistentFlags().Lookup("capture.video.codec")); err != nil {
return err
}
cmd.PersistentFlags().StringSlice("capture.video.ids", []string{}, "ordered list of video ids")
if err := viper.BindPFlag("capture.video.ids", cmd.PersistentFlags().Lookup("capture.video.ids")); err != nil {
return err
}
cmd.PersistentFlags().String("capture.video.pipelines", "", "pipelines config in JSON used for video streaming")
if err := viper.BindPFlag("capture.video.pipelines", cmd.PersistentFlags().Lookup("capture.video.pipelines")); err != nil {
return err
}
// broadcast // broadcast
cmd.PersistentFlags().Int("capture.screencast.audio_bitrate", 128, "broadcast audio bitrate in KB/s") cmd.PersistentFlags().Int("capture.screencast.audio_bitrate", 128, "broadcast audio bitrate in KB/s")
if err := viper.BindPFlag("capture.screencast.audio_bitrate", cmd.PersistentFlags().Lookup("capture.screencast.audio_bitrate")); err != nil { if err := viper.BindPFlag("capture.screencast.audio_bitrate", cmd.PersistentFlags().Lookup("capture.screencast.audio_bitrate")); err != nil {
@ -99,10 +117,42 @@ func (s *Capture) Set() {
s.Display = os.Getenv("DISPLAY") s.Display = os.Getenv("DISPLAY")
// video // video
if err := viper.UnmarshalKey("capture.video", &s.Video, viper.DecodeHook( videoCodec := viper.GetString("capture.video.codec")
utils.JsonStringAutoDecode(s.Video), switch videoCodec {
case "vp8":
s.VideoCodec = codec.VP8()
case "vp9":
s.VideoCodec = codec.VP9()
case "h264":
s.VideoCodec = codec.H264()
default:
log.Warn().Str("codec", videoCodec).Msgf("unknown video codec, using Vp8")
s.VideoCodec = codec.VP8()
}
s.VideoIDs = viper.GetStringSlice("capture.video.ids")
if err := viper.UnmarshalKey("capture.video.pipelines", &s.VideoPipelines, viper.DecodeHook(
utils.JsonStringAutoDecode(s.VideoPipelines),
)); err != nil { )); err != nil {
log.Warn().Err(err).Msgf("unable to parse video settings") log.Warn().Err(err).Msgf("unable to parse video pipelines")
}
// default video
if len(s.VideoPipelines) == 0 {
log.Warn().Msgf("no video pipelines specified, using defaults")
s.VideoCodec = codec.VP8()
s.VideoPipelines = map[string]types.VideoConfig{
"main": types.VideoConfig{
GstPipeline: "ximagesrc display-name={display} show-pointer=false use-damage=false "+
"! video/x-raw "+
"! videoconvert "+
"! queue "+
"! vp8enc end-usage=cbr cpu-used=4 threads=4 deadline=1 keyframe-max-dist=25 "+
"! appsink name=appsink",
},
}
s.VideoIDs = []string{"main"}
} }
// audio // audio

View File

@ -3,6 +3,7 @@ package types
import ( import (
"math" "math"
"strings" "strings"
"context"
"fmt" "fmt"
"github.com/pion/webrtc/v3/pkg/media" "github.com/pion/webrtc/v3/pkg/media"
@ -50,33 +51,17 @@ type CaptureManager interface {
} }
type VideoConfig struct { type VideoConfig struct {
Codec string `mapstructure:"codec"`
Width string `mapstructure:"width"` // expression Width string `mapstructure:"width"` // expression
Height string `mapstructure:"height"` // expression Height string `mapstructure:"height"` // expression
Fps string `mapstructure:"fps"` // expression Fps string `mapstructure:"fps"` // expression
GstPrefix string `mapstructure:"gst_prefix"` // pipeline prefix, starts with !
GstEncoder string `mapstructure:"gst_encoder"` GstEncoder string `mapstructure:"gst_encoder"`
GstParams map[string]string `mapstructure:"gst_params"` // map of expressions GstParams map[string]string `mapstructure:"gst_params"` // map of expressions
GstPipeline string `mapstructure:"gst_pipeline"` GstSuffix string `mapstructure:"gst_suffix"` // pipeline suffix, starts with !
} GstPipeline string `mapstructure:"gst_pipeline"` // whole pipeline as a string
func (config *VideoConfig) GetCodec() (codec.RTPCodec, error) {
switch strings.ToLower(config.Codec) {
case "vp8":
return codec.VP8(), nil
case "vp9":
return codec.VP9(), nil
case "h264":
return codec.H264(), nil
default:
return codec.RTPCodec{}, fmt.Errorf("unknown codec")
}
} }
func (config *VideoConfig) GetPipeline(screen ScreenSize) (string, error) { func (config *VideoConfig) GetPipeline(screen ScreenSize) (string, error) {
if config.GstPipeline != "" {
return config.GstPipeline, nil
}
values := map[string]interface{}{ values := map[string]interface{}{
"width": screen.Width, "width": screen.Width,
"height": screen.Height, "height": screen.Height,
@ -92,34 +77,43 @@ func (config *VideoConfig) GetPipeline(screen ScreenSize) (string, error) {
// get fps pipeline // get fps pipeline
fpsPipeline := "! video/x-raw ! videoconvert ! queue" fpsPipeline := "! video/x-raw ! videoconvert ! queue"
if config.Fps != "" { if config.Fps != "" {
var err error eval, err := gval.Full(language...).NewEvaluable(config.Fps)
val, err := gval.Evaluate(config.Fps, values, language...)
if err != nil { if err != nil {
return "", err return "", err
} }
if val != nil { val, err := eval.EvalFloat64(context.Background(), values)
// TODO: To fraction. if err != nil {
fpsPipeline = fmt.Sprintf("! video/x-raw,framerate=%v ! videoconvert ! queue", val) return "", err
} }
fpsPipeline = fmt.Sprintf("! video/x-raw,framerate=%d/100 ! videoconvert ! queue", int(val*100))
} }
// get scale pipeline // get scale pipeline
scalePipeline := "" scalePipeline := ""
if config.Width != "" && config.Height != "" { if config.Width != "" && config.Height != "" {
w, err := gval.Evaluate(config.Width, values, language...) eval, err := gval.Full(language...).NewEvaluable(config.Width)
if err != nil { if err != nil {
return "", err return "", err
} }
h, err := gval.Evaluate(config.Height, values, language...) w, err := eval.EvalInt(context.Background(), values)
if err != nil { if err != nil {
return "", err return "", err
} }
if w != nil && h != nil { eval, err = gval.Full(language...).NewEvaluable(config.Height)
scalePipeline = fmt.Sprintf("! videoscale ! video/x-raw,width=%v,height=%v ! queue", w, h) if err != nil {
return "", err
} }
h, err := eval.EvalInt(context.Background(), values)
if err != nil {
return "", err
}
scalePipeline = fmt.Sprintf("! videoscale ! video/x-raw,width=%d,height=%d ! queue", w, h)
} }
// get encoder pipeline // get encoder pipeline
@ -141,5 +135,12 @@ func (config *VideoConfig) GetPipeline(screen ScreenSize) (string, error) {
} }
} }
return fmt.Sprintf("%s %s %s", fpsPipeline, scalePipeline, encPipeline), nil // join strings with space
return strings.Join([]string{
fpsPipeline,
scalePipeline,
config.GstPrefix,
encPipeline,
config.GstSuffix,
}[:]," "), nil
} }