mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
update pipelines config settings extractor.
This commit is contained in:
parent
051ab9f426
commit
d84297fbec
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user