pass screen frame rate to gstreamer pipeline.

This commit is contained in:
m1k1o 2021-02-15 14:59:20 +01:00
parent 27740b0af0
commit 321e52ee4f
4 changed files with 30 additions and 7 deletions

View File

@ -29,6 +29,7 @@ This app uses Web RTC to stream a desktop inside of a docker container. This is
- Stereo sound (works properly only in Firefox host). - Stereo sound (works properly only in Firefox host).
- Added limited support for some mobile browsers with `playsinline` attribute. - Added limited support for some mobile browsers with `playsinline` attribute.
- Added `VIDEO_BITRATE` and `AUDIO_BITRATE` in kbit/s to control stream quality (in collaboration with @mbattista). - Added `VIDEO_BITRATE` and `AUDIO_BITRATE` in kbit/s to control stream quality (in collaboration with @mbattista).
- Added `MAX_FPS`, where you can specify max WebRTC frame rate. When set to `0`, frame rate won't be capped and you can enjoy your real `60fps` experience. Originally, it was constant at `25fps`.
### Bugs ### Bugs
- Fixed minor gst pipeline bug. - Fixed minor gst pipeline bug.

View File

@ -50,7 +50,7 @@ var pipelinesLock sync.Mutex
var registry *C.GstRegistry var registry *C.GstRegistry
const ( const (
videoSrc = "ximagesrc display-name=%s show-pointer=true use-damage=false ! video/x-raw,framerate=30/1 ! videoconvert ! queue ! " videoSrc = "ximagesrc display-name=%s show-pointer=true use-damage=false ! video/x-raw,framerate=%d/1 ! videoconvert ! queue ! "
audioSrc = "pulsesrc device=%s ! audio/x-raw,channels=2 ! audioconvert ! " audioSrc = "pulsesrc device=%s ! audio/x-raw,channels=2 ! audioconvert ! "
) )
@ -61,7 +61,7 @@ func init() {
// CreateRTMPPipeline creates a GStreamer Pipeline // CreateRTMPPipeline creates a GStreamer Pipeline
func CreateRTMPPipeline(pipelineDevice string, pipelineDisplay string, pipelineSrc string, pipelineRTMP string) (*Pipeline, error) { func CreateRTMPPipeline(pipelineDevice string, pipelineDisplay string, pipelineSrc string, pipelineRTMP string) (*Pipeline, error) {
video := fmt.Sprintf(videoSrc, pipelineDisplay) video := fmt.Sprintf(videoSrc, pipelineDisplay, 25)
audio := fmt.Sprintf(audioSrc, pipelineDevice) audio := fmt.Sprintf(audioSrc, pipelineDevice)
var pipelineStr string var pipelineStr string
@ -75,7 +75,7 @@ func CreateRTMPPipeline(pipelineDevice string, pipelineDisplay string, pipelineS
} }
// CreateAppPipeline creates a GStreamer Pipeline // CreateAppPipeline creates a GStreamer Pipeline
func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc string, bitrate uint) (*Pipeline, error) { func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc string, fps int, bitrate uint) (*Pipeline, error) {
pipelineStr := " ! appsink name=appsink" pipelineStr := " ! appsink name=appsink"
switch codecName { switch codecName {
@ -90,7 +90,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri
if pipelineSrc != "" { if pipelineSrc != "" {
pipelineStr = fmt.Sprintf(pipelineSrc+pipelineStr, pipelineDevice) pipelineStr = fmt.Sprintf(pipelineSrc+pipelineStr, pipelineDevice)
} else { } else {
pipelineStr = fmt.Sprintf(videoSrc+"vp8enc target-bitrate=%d cpu-used=-5 threads=4 deadline=1 error-resilient=partitions keyframe-max-dist=30 auto-alt-ref=true"+pipelineStr, pipelineDevice, bitrate*1000) pipelineStr = fmt.Sprintf(videoSrc+"vp8enc target-bitrate=%d cpu-used=-5 threads=4 deadline=1 error-resilient=partitions keyframe-max-dist=30 auto-alt-ref=true"+pipelineStr, pipelineDevice, fps, bitrate*1000)
} }
case "VP9": case "VP9":
// https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html?gi-language=c // https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html?gi-language=c
@ -104,7 +104,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri
if pipelineSrc != "" { if pipelineSrc != "" {
pipelineStr = fmt.Sprintf(pipelineSrc+pipelineStr, pipelineDevice) pipelineStr = fmt.Sprintf(pipelineSrc+pipelineStr, pipelineDevice)
} else { } else {
pipelineStr = fmt.Sprintf(videoSrc+"vp9enc target-bitrate=%d cpu-used=-5 threads=4 deadline=1 keyframe-max-dist=30 auto-alt-ref=true"+pipelineStr, pipelineDevice, bitrate*1000) pipelineStr = fmt.Sprintf(videoSrc+"vp9enc target-bitrate=%d cpu-used=-5 threads=4 deadline=1 keyframe-max-dist=30 auto-alt-ref=true"+pipelineStr, pipelineDevice, fps, bitrate*1000)
} }
case "H264": case "H264":
if err := CheckPlugins([]string{"ximagesrc"}); err != nil { if err := CheckPlugins([]string{"ximagesrc"}); err != nil {
@ -120,7 +120,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri
// gstreamer1.0-plugins-bad // gstreamer1.0-plugins-bad
// openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 // openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000
if err := CheckPlugins([]string{"openh264"}); err == nil { if err := CheckPlugins([]string{"openh264"}); err == nil {
pipelineStr = fmt.Sprintf(videoSrc+"openh264enc multi-thread=4 complexity=high bitrate=%d max-bitrate=%d ! video/x-h264,stream-format=byte-stream"+pipelineStr, pipelineDevice, bitrate*1000, (bitrate+1024)*1000) pipelineStr = fmt.Sprintf(videoSrc+"openh264enc multi-thread=4 complexity=high bitrate=%d max-bitrate=%d ! video/x-h264,stream-format=byte-stream"+pipelineStr, pipelineDevice, fps, bitrate*1000, (bitrate+1024)*1000)
break break
} }
@ -131,7 +131,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri
return nil, err return nil, err
} }
pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=I420 ! x264enc threads=4 bitrate=%d byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream"+pipelineStr, pipelineDevice, bitrate) pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=I420 ! x264enc threads=4 bitrate=%d byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream"+pipelineStr, pipelineDevice, fps, bitrate)
case "Opus": case "Opus":
// https://gstreamer.freedesktop.org/documentation/opus/opusenc.html // https://gstreamer.freedesktop.org/documentation/opus/opusenc.html
// gstreamer1.0-plugins-base // gstreamer1.0-plugins-base

View File

@ -130,11 +130,18 @@ func (manager *RemoteManager) Streaming() bool {
} }
func (manager *RemoteManager) createPipelines() { func (manager *RemoteManager) createPipelines() {
// handle maximum fps
rate := manager.config.ScreenRate
if manager.config.MaxFPS != 0 && manager.config.MaxFPS < manager.config.ScreenRate {
rate = manager.config.MaxFPS
}
var err error var err error
manager.video, err = gst.CreateAppPipeline( manager.video, err = gst.CreateAppPipeline(
manager.config.VideoCodec, manager.config.VideoCodec,
manager.config.Display, manager.config.Display,
manager.config.VideoParams, manager.config.VideoParams,
rate,
manager.config.VideoBitrate, manager.config.VideoBitrate,
) )
if err != nil { if err != nil {
@ -145,6 +152,7 @@ func (manager *RemoteManager) createPipelines() {
manager.config.AudioCodec, manager.config.AudioCodec,
manager.config.Device, manager.config.Device,
manager.config.AudioParams, manager.config.AudioParams,
0, // fps: n/a for audio
manager.config.AudioBitrate, manager.config.AudioBitrate,
) )
if err != nil { if err != nil {
@ -171,11 +179,17 @@ func (manager *RemoteManager) ChangeResolution(width int, height int, rate int)
return err return err
} }
// handle maximum fps
if manager.config.MaxFPS != 0 && manager.config.MaxFPS < rate {
rate = manager.config.MaxFPS
}
var err error var err error
manager.video, err = gst.CreateAppPipeline( manager.video, err = gst.CreateAppPipeline(
manager.config.VideoCodec, manager.config.VideoCodec,
manager.config.Display, manager.config.Display,
manager.config.VideoParams, manager.config.VideoParams,
rate,
manager.config.VideoBitrate, manager.config.VideoBitrate,
) )
if err != nil { if err != nil {

View File

@ -20,6 +20,7 @@ type Remote struct {
ScreenWidth int ScreenWidth int
ScreenHeight int ScreenHeight int
ScreenRate int ScreenRate int
MaxFPS int
} }
func (Remote) Init(cmd *cobra.Command) error { func (Remote) Init(cmd *cobra.Command) error {
@ -58,6 +59,11 @@ func (Remote) Init(cmd *cobra.Command) error {
return err return err
} }
cmd.PersistentFlags().Int("max_fps", 25, "maximum fps delivered via WebRTC, 0 is for no maximum")
if err := viper.BindPFlag("max_fps", cmd.PersistentFlags().Lookup("max_fps")); err != nil {
return err
}
// video codecs // video codecs
cmd.PersistentFlags().Bool("vp8", false, "use VP8 video codec") cmd.PersistentFlags().Bool("vp8", false, "use VP8 video codec")
if err := viper.BindPFlag("vp8", cmd.PersistentFlags().Lookup("vp8")); err != nil { if err := viper.BindPFlag("vp8", cmd.PersistentFlags().Lookup("vp8")); err != nil {
@ -146,4 +152,6 @@ func (s *Remote) Set() {
s.ScreenRate = int(rate) s.ScreenRate = int(rate)
} }
} }
s.MaxFPS = viper.GetInt("max_fps")
} }