move gst pipelines to codec.

This commit is contained in:
Miroslav Šedivý 2021-02-05 15:10:41 +01:00
parent 36b7eca43c
commit ebd7e7c065
4 changed files with 92 additions and 162 deletions

View File

@ -13,10 +13,8 @@ import (
"unsafe"
"demodesk/neko/internal/types"
"demodesk/neko/internal/types/codec"
)
// Pipeline is a wrapper for a GStreamer Pipeline
type Pipeline struct {
Pipeline *C.GstElement
Sample chan types.Sample
@ -28,127 +26,11 @@ var pipelines = make(map[int]*Pipeline)
var pipelinesLock sync.Mutex
var registry *C.GstRegistry
const (
videoSrc = "ximagesrc display-name=%s show-pointer=false use-damage=false ! video/x-raw ! videoconvert ! queue ! "
audioSrc = "pulsesrc device=%s ! audio/x-raw,channels=2 ! audioconvert ! "
appSink = " ! appsink name=appsink"
)
func init() {
C.gstreamer_init()
registry = C.gst_registry_get()
}
func GetRTMPPipeline(audioDevice string, videoDisplay string, pipelineSrc string) string {
video := fmt.Sprintf(videoSrc, videoDisplay)
audio := fmt.Sprintf(audioSrc, audioDevice)
var pipelineStr string
if pipelineSrc != "" {
pipelineStr = fmt.Sprintf(pipelineSrc, audioDevice, videoDisplay)
} else {
pipelineStr = fmt.Sprintf("flvmux name=mux ! rtmpsink location='{url} live=1' %s audio/x-raw,channels=2 ! audioconvert ! voaacenc ! mux. %s x264enc bframes=0 key-int-max=60 byte-stream=true tune=zerolatency speed-preset=veryfast ! mux.", audio, video)
}
return pipelineStr
}
func GetJPEGPipeline(videoDisplay string, pipelineSrc string, rate string, quality string) string {
var pipelineStr string
if pipelineSrc != "" {
pipelineStr = fmt.Sprintf(pipelineSrc, videoDisplay)
} else {
pipelineStr = fmt.Sprintf("ximagesrc display-name=%s show-pointer=true use-damage=false ! videoconvert ! videoscale ! videorate ! video/x-raw,framerate=%s ! jpegenc quality=%s" + appSink, videoDisplay, rate, quality)
}
return pipelineStr
}
func GetAppPipeline(codecRTP codec.RTPCodec, pipelineDevice string, pipelineSrc string) (string, error) {
if pipelineSrc != "" {
return fmt.Sprintf(pipelineSrc + appSink, pipelineDevice), nil
}
var pipelineStr string
switch codecRTP.Name {
case "vp8":
// https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html
// gstreamer1.0-plugins-good
if err := CheckPlugins([]string{"ximagesrc", "vpx"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(videoSrc + "vp8enc cpu-used=16 threads=4 deadline=1 error-resilient=partitions keyframe-max-dist=15 static-threshold=20" + appSink, pipelineDevice)
case "vp9":
// https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html
// gstreamer1.0-plugins-good
if err := CheckPlugins([]string{"ximagesrc", "vpx"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(videoSrc + "vp9enc cpu-used=16 threads=4 deadline=1 keyframe-max-dist=15 static-threshold=20" + appSink, pipelineDevice)
case "h264":
var err error
if err = CheckPlugins([]string{"ximagesrc"}); err != nil {
return "", err
}
// https://gstreamer.freedesktop.org/documentation/x264/index.html
// gstreamer1.0-plugins-ugly
if err = CheckPlugins([]string{"x264"}); err == nil {
pipelineStr = fmt.Sprintf(videoSrc + "video/x-raw,format=I420 ! x264enc threads=4 bitrate=4096 key-int-max=15 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream" + appSink, pipelineDevice)
break
}
// https://gstreamer.freedesktop.org/documentation/openh264/openh264enc.html
// gstreamer1.0-plugins-bad
if err = CheckPlugins([]string{"openh264"}); err == nil {
pipelineStr = fmt.Sprintf(videoSrc + "openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 ! video/x-h264,stream-format=byte-stream" + appSink, pipelineDevice)
break
}
return "", err
case "opus":
// https://gstreamer.freedesktop.org/documentation/opus/opusenc.html
// gstreamer1.0-plugins-base
if err := CheckPlugins([]string{"pulseaudio", "opus"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(audioSrc + "opusenc bitrate=128000" + appSink, pipelineDevice)
case "g722":
// https://gstreamer.freedesktop.org/documentation/libav/avenc_g722.html
// gstreamer1.0-libav
if err := CheckPlugins([]string{"pulseaudio", "libav"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(audioSrc + "avenc_g722" + appSink, pipelineDevice)
case "pcmu":
// https://gstreamer.freedesktop.org/documentation/mulaw/mulawenc.html
// gstreamer1.0-plugins-good
if err := CheckPlugins([]string{"pulseaudio", "mulaw"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(audioSrc + "audio/x-raw, rate=8000 ! mulawenc" + appSink, pipelineDevice)
case "pcma":
// https://gstreamer.freedesktop.org/documentation/alaw/alawenc.html
// gstreamer1.0-plugins-good
if err := CheckPlugins([]string{"pulseaudio", "alaw"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(audioSrc + "audio/x-raw, rate=8000 ! alawenc" + appSink, pipelineDevice)
default:
return "", fmt.Errorf("unknown codec %s", codecRTP.Name)
}
return pipelineStr, nil
}
// CreatePipeline creates a GStreamer Pipeline
func CreatePipeline(pipelineStr string) (*Pipeline, error) {
pipelineStrUnsafe := C.CString(pipelineStr)
defer C.free(unsafe.Pointer(pipelineStrUnsafe))
@ -177,17 +59,14 @@ func CreatePipeline(pipelineStr string) (*Pipeline, error) {
return p, nil
}
// Start starts the GStreamer Pipeline
func (p *Pipeline) Start() {
C.gstreamer_send_start_pipeline(p.Pipeline, C.int(p.id))
}
// Play starts the GStreamer Pipeline
func (p *Pipeline) Play() {
C.gstreamer_send_play_pipeline(p.Pipeline)
}
// Stop stops the GStreamer Pipeline
func (p *Pipeline) Stop() {
C.gstreamer_send_stop_pipeline(p.Pipeline)
}

View File

@ -1,6 +1,7 @@
package capture
import (
"fmt"
"sync"
"github.com/rs/zerolog"
@ -8,7 +9,6 @@ import (
"demodesk/neko/internal/types"
"demodesk/neko/internal/config"
"demodesk/neko/internal/capture/gst"
)
type CaptureManagerCtx struct {
@ -25,37 +25,59 @@ type CaptureManagerCtx struct {
func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCtx {
logger := log.With().Str("module", "capture").Logger()
broadcastPipeline := gst.GetRTMPPipeline(
config.Device,
config.Display,
config.BroadcastPipeline,
)
screencastPipeline := gst.GetJPEGPipeline(
config.Display,
config.ScreencastPipeline,
config.ScreencastRate,
config.ScreencastQuality,
)
audioPipeline, err := gst.GetAppPipeline(
config.AudioCodec,
config.Device,
config.AudioParams,
)
if err != nil {
logger.Panic().Err(err).Msg("unable to get pipeline")
broadcastPipeline := config.BroadcastPipeline
if broadcastPipeline == "" {
broadcastPipeline = fmt.Sprintf(
"flvmux name=mux ! rtmpsink location='{url} live=1' " +
"pulsesrc device=%s " +
"! audio/x-raw,channels=2 " +
"! audioconvert " +
"! queue " +
"! voaacenc " +
"! mux. " +
"ximagesrc display-name=%s show-pointer=true use-damage=false " +
"! video/x-raw " +
"! videoconvert " +
"! queue " +
"! x264enc threads=4 bitrate=4096 key-int-max=15 byte-stream=true byte-stream=true tune=zerolatency speed-preset=veryfast " +
"! mux.", config.Device, config.Display,
)
}
videoPipeline, err := gst.GetAppPipeline(
config.VideoCodec,
config.Display,
config.VideoParams,
)
screencastPipeline := config.ScreencastPipeline
if screencastPipeline == "" {
screencastPipeline = fmt.Sprintf(
"ximagesrc display-name=%s show-pointer=true use-damage=false " +
"! video/x-raw,framerate=%s " +
"! videoconvert " +
"! queue " +
"! jpegenc quality=%s " +
"! appsink name=appsink", config.Display, config.ScreencastRate, config.ScreencastQuality,
)
}
if err != nil {
logger.Panic().Err(err).Msg("unable to get pipeline")
audioPipeline := config.AudioPipeline
if audioPipeline == "" {
audioPipeline = fmt.Sprintf(
"pulsesrc device=%s " +
"! audio/x-raw,channels=2 " +
"! audioconvert " +
"! queue " +
"! %s " +
"! appsink name=appsink", config.Device, config.AudioCodec.Pipeline,
)
}
videoPipeline := config.VideoPipeline
if videoPipeline == "" {
videoPipeline = fmt.Sprintf(
"ximagesrc display-name=%s show-pointer=false use-damage=false " +
"! video/x-raw " +
"! videoconvert " +
"! queue " +
"! %s " +
"! appsink name=appsink", config.Display, config.VideoCodec.Pipeline,
)
}
return &CaptureManagerCtx{

View File

@ -8,17 +8,19 @@ import (
)
type Capture struct {
Display string
Device string
AudioCodec codec.RTPCodec
AudioParams string
VideoCodec codec.RTPCodec
VideoParams string
Device string
AudioCodec codec.RTPCodec
AudioPipeline string
BroadcastPipeline string
Screencast bool
ScreencastRate string
ScreencastQuality string
Display string
VideoCodec codec.RTPCodec
VideoPipeline string
BroadcastPipeline string
Screencast bool
ScreencastRate string
ScreencastQuality string
ScreencastPipeline string
}
@ -139,12 +141,14 @@ func (s *Capture) Set() {
s.Device = viper.GetString("device")
s.AudioCodec = audioCodec
s.AudioParams = viper.GetString("audio")
s.AudioPipeline = viper.GetString("audio")
s.Display = viper.GetString("display")
s.VideoCodec = videoCodec
s.VideoParams = viper.GetString("video")
s.VideoPipeline = viper.GetString("video")
s.BroadcastPipeline = viper.GetString("broadcast_pipeline")
s.Screencast = viper.GetBool("screencast")
s.ScreencastRate = viper.GetString("screencast_rate")
s.ScreencastQuality = viper.GetString("screencast_quality")

View File

@ -7,6 +7,7 @@ type RTPCodec struct {
PayloadType webrtc.PayloadType
Type webrtc.RTPCodecType
Capability webrtc.RTPCodecCapability
Pipeline string
}
func (codec *RTPCodec) Register(engine *webrtc.MediaEngine) error {
@ -28,6 +29,9 @@ func VP8() RTPCodec {
SDPFmtpLine: "",
RTCPFeedback: nil,
},
// https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html
// gstreamer1.0-plugins-good
Pipeline: "vp8enc cpu-used=16 threads=4 deadline=1 error-resilient=partitions keyframe-max-dist=15 static-threshold=20",
}
}
@ -44,6 +48,9 @@ func VP9() RTPCodec {
SDPFmtpLine: "profile-id=0",
RTCPFeedback: nil,
},
// https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html
// gstreamer1.0-plugins-good
Pipeline: "vp9enc cpu-used=16 threads=4 deadline=1 keyframe-max-dist=15 static-threshold=20",
}
}
@ -60,6 +67,12 @@ func H264() RTPCodec {
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
RTCPFeedback: nil,
},
// https://gstreamer.freedesktop.org/documentation/x264/index.html
// gstreamer1.0-plugins-ugly
Pipeline: "video/x-raw,format=I420 ! x264enc threads=4 bitrate=4096 key-int-max=15 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream",
// https://gstreamer.freedesktop.org/documentation/openh264/openh264enc.html
// gstreamer1.0-plugins-bad
//Pipeline: "openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 ! video/x-h264,stream-format=byte-stream",
}
}
@ -75,6 +88,9 @@ func Opus() RTPCodec {
SDPFmtpLine: "",
RTCPFeedback: nil,
},
// https://gstreamer.freedesktop.org/documentation/opus/opusenc.html
// gstreamer1.0-plugins-base
Pipeline: "opusenc bitrate=128000",
}
}
@ -90,6 +106,9 @@ func G722() RTPCodec {
SDPFmtpLine: "",
RTCPFeedback: nil,
},
// https://gstreamer.freedesktop.org/documentation/libav/avenc_g722.html
// gstreamer1.0-libav
Pipeline: "avenc_g722",
}
}
@ -105,6 +124,9 @@ func PCMU() RTPCodec {
SDPFmtpLine: "",
RTCPFeedback: nil,
},
// https://gstreamer.freedesktop.org/documentation/mulaw/mulawenc.html
// gstreamer1.0-plugins-good
Pipeline: "audio/x-raw, rate=8000 ! mulawenc",
}
}
@ -120,5 +142,8 @@ func PCMA() RTPCodec {
SDPFmtpLine: "",
RTCPFeedback: nil,
},
// https://gstreamer.freedesktop.org/documentation/alaw/alawenc.html
// gstreamer1.0-plugins-good
Pipeline: "audio/x-raw, rate=8000 ! alawenc",
}
}