2020-11-02 04:09:48 +13:00
|
|
|
package types
|
|
|
|
|
2021-02-02 11:50:18 +13:00
|
|
|
import (
|
2021-03-29 11:58:51 +13:00
|
|
|
"math"
|
|
|
|
"strings"
|
|
|
|
"fmt"
|
|
|
|
|
2021-02-02 11:50:18 +13:00
|
|
|
"github.com/pion/webrtc/v3/pkg/media"
|
2021-03-29 11:58:51 +13:00
|
|
|
"github.com/PaesslerAG/gval"
|
2021-02-02 11:50:18 +13:00
|
|
|
|
|
|
|
"demodesk/neko/internal/types/codec"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Sample media.Sample
|
2020-11-02 04:09:48 +13:00
|
|
|
|
2021-01-23 02:09:47 +13:00
|
|
|
type BroadcastManager interface {
|
|
|
|
Start(url string) error
|
|
|
|
Stop()
|
2021-02-06 02:03:53 +13:00
|
|
|
Started() bool
|
2021-01-23 02:09:47 +13:00
|
|
|
Url() string
|
|
|
|
}
|
|
|
|
|
2021-01-23 06:13:32 +13:00
|
|
|
type ScreencastManager interface {
|
|
|
|
Enabled() bool
|
2021-01-24 03:17:52 +13:00
|
|
|
Started() bool
|
|
|
|
Image() ([]byte, error)
|
2021-01-23 06:13:32 +13:00
|
|
|
}
|
|
|
|
|
2021-02-05 09:39:48 +13:00
|
|
|
type StreamManager interface {
|
|
|
|
Codec() codec.RTPCodec
|
2021-02-07 06:16:24 +13:00
|
|
|
|
2021-02-07 05:05:25 +13:00
|
|
|
AddListener(listener *func(sample Sample))
|
|
|
|
RemoveListener(listener *func(sample Sample))
|
2021-02-07 06:16:24 +13:00
|
|
|
ListenersCount() int
|
2021-02-05 09:39:48 +13:00
|
|
|
|
2021-02-06 00:41:02 +13:00
|
|
|
Start() error
|
2021-02-05 09:39:48 +13:00
|
|
|
Stop()
|
2021-02-06 02:03:53 +13:00
|
|
|
Started() bool
|
2021-02-05 09:39:48 +13:00
|
|
|
}
|
|
|
|
|
2020-11-02 04:09:48 +13:00
|
|
|
type CaptureManager interface {
|
|
|
|
Start()
|
|
|
|
Shutdown() error
|
|
|
|
|
2021-01-23 02:09:47 +13:00
|
|
|
Broadcast() BroadcastManager
|
2021-01-23 06:13:32 +13:00
|
|
|
Screencast() ScreencastManager
|
2021-02-05 09:39:48 +13:00
|
|
|
Audio() StreamManager
|
2021-02-06 06:07:58 +13:00
|
|
|
Video(videoID string) (StreamManager, bool)
|
2021-02-06 05:40:29 +13:00
|
|
|
VideoIDs() []string
|
2020-11-02 04:09:48 +13:00
|
|
|
}
|
2021-03-29 11:58:51 +13:00
|
|
|
|
|
|
|
type VideoConfig struct {
|
|
|
|
Codec string `mapstructure:"codec"`
|
|
|
|
Width string `mapstructure:"width"` // expression
|
|
|
|
Height string `mapstructure:"height"` // expression
|
|
|
|
Fps string `mapstructure:"fps"` // expression
|
|
|
|
GstEncoder string `mapstructure:"gst_encoder"`
|
|
|
|
GstParams map[string]string `mapstructure:"gst_params"` // map of expressions
|
|
|
|
GstPipeline string `mapstructure:"gst_pipeline"`
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
if config.GstPipeline != "" {
|
|
|
|
return config.GstPipeline, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
values := map[string]interface{}{
|
|
|
|
"width": screen.Width,
|
|
|
|
"height": screen.Height,
|
|
|
|
"fps": screen.Rate,
|
|
|
|
}
|
|
|
|
|
|
|
|
language := []gval.Language{
|
|
|
|
gval.Function("round", func(args ...interface{}) (interface{}, error) {
|
|
|
|
return (int)(math.Round(args[0].(float64))), nil
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
|
|
|
|
// get fps pipeline
|
|
|
|
fpsPipeline := "video/x-raw ! videoconvert ! queue"
|
|
|
|
if config.Fps != "" {
|
|
|
|
var err error
|
|
|
|
val, err := gval.Evaluate(config.Fps, values, language...)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if val != nil {
|
|
|
|
// TODO: To fraction.
|
|
|
|
fpsPipeline = fmt.Sprintf("video/x-raw,framerate=%v ! videoconvert ! queue", val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get scale pipeline
|
|
|
|
scalePipeline := ""
|
|
|
|
if config.Width != "" && config.Height != "" {
|
|
|
|
w, err := gval.Evaluate(config.Width, values, language...)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
h, err := gval.Evaluate(config.Height, values, language...)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if w != nil && h != nil {
|
|
|
|
scalePipeline = fmt.Sprintf("! videoscale ! video/x-raw,width=%v,height=%v ! queue", w, h)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get encoder pipeline
|
|
|
|
encPipeline := fmt.Sprintf("! %s", config.GstEncoder)
|
|
|
|
for key, expr := range config.GstParams {
|
|
|
|
if expr == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := gval.Evaluate(expr, values, language...)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if val != nil {
|
|
|
|
encPipeline += fmt.Sprintf(" %s=%v", key, val)
|
|
|
|
} else {
|
|
|
|
encPipeline += fmt.Sprintf(" %s=%s", key, expr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s %s %s", fpsPipeline, scalePipeline, encPipeline), nil
|
|
|
|
}
|