lazy screencast.

This commit is contained in:
Miroslav Šedivý 2021-01-23 15:17:52 +01:00
parent 75393905e8
commit fdd98377b3
5 changed files with 83 additions and 30 deletions

View File

@ -63,7 +63,7 @@ func (h *RoomHandler) Route(r chi.Router) {
r.Route("/screen", func(r chi.Router) { r.Route("/screen", func(r chi.Router) {
r.With(auth.CanWatchOnly).Get("/", h.screenConfiguration) r.With(auth.CanWatchOnly).Get("/", h.screenConfiguration)
r.With(auth.CanWatchOnly).Get("/image", h.screenImageGet) r.With(auth.CanWatchOnly).Get("/image", h.screenImageGet)
r.With(auth.CanWatchOnly).Get("/cast", h.screenCastGet) r.With(auth.CanWatchOnly).Get("/cast.jpg", h.screenCastGet)
r.With(auth.AdminsOnly).Post("/", h.screenConfigurationChange) r.With(auth.AdminsOnly).Post("/", h.screenConfigurationChange)
r.With(auth.AdminsOnly).Get("/configurations", h.screenConfigurationsList) r.With(auth.AdminsOnly).Get("/configurations", h.screenConfigurationsList)

View File

@ -103,7 +103,12 @@ func (h *RoomHandler) screenCastGet(w http.ResponseWriter, r *http.Request) {
return return
} }
bytes := screencast.Image() bytes, err := screencast.Image()
if err != nil {
utils.HttpInternalServerError(w, err)
return
}
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Set("Content-Type", "image/jpeg") w.Header().Set("Content-Type", "image/jpeg")
w.Write(bytes) w.Write(bytes)

View File

@ -51,12 +51,6 @@ func (manager *CaptureManagerCtx) Start() {
} }
} }
if manager.screencast.Enabled() {
if err := manager.screencast.createPipeline(); err != nil {
manager.logger.Panic().Err(err).Msg("unable to create screencast pipeline")
}
}
manager.desktop.OnBeforeScreenSizeChange(func() { manager.desktop.OnBeforeScreenSizeChange(func() {
if manager.Streaming() { if manager.Streaming() {
manager.destroyVideoPipeline() manager.destroyVideoPipeline()
@ -66,7 +60,7 @@ func (manager *CaptureManagerCtx) Start() {
manager.broadcast.destroyPipeline() manager.broadcast.destroyPipeline()
} }
if manager.screencast.Enabled() { if manager.screencast.Started() {
manager.screencast.destroyPipeline() manager.screencast.destroyPipeline()
} }
}) })
@ -82,7 +76,7 @@ func (manager *CaptureManagerCtx) Start() {
} }
} }
if manager.screencast.Enabled() { if manager.screencast.Started() {
if err := manager.screencast.createPipeline(); err != nil { if err := manager.screencast.createPipeline(); err != nil {
manager.logger.Panic().Err(err).Msg("unable to recreate screencast pipeline") manager.logger.Panic().Err(err).Msg("unable to recreate screencast pipeline")
} }

View File

@ -1,7 +1,9 @@
package capture package capture
import ( import (
"bytes" "fmt"
"time"
"sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -13,13 +15,17 @@ import (
type ScreencastManagerCtx struct { type ScreencastManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
mu sync.Mutex
config *config.Capture config *config.Capture
pipeline *gst.Pipeline pipeline *gst.Pipeline
enabled bool enabled bool
started bool
shutdown chan bool shutdown chan bool
refresh chan bool refresh chan bool
expires time.Time
timeout time.Duration
sample chan types.Sample sample chan types.Sample
image *bytes.Buffer image types.Sample
} }
func screencastNew(config *config.Capture) *ScreencastManagerCtx { func screencastNew(config *config.Capture) *ScreencastManagerCtx {
@ -27,24 +33,34 @@ func screencastNew(config *config.Capture) *ScreencastManagerCtx {
logger: log.With().Str("module", "capture").Str("submodule", "screencast").Logger(), logger: log.With().Str("module", "capture").Str("submodule", "screencast").Logger(),
config: config, config: config,
enabled: config.Screencast, enabled: config.Screencast,
started: false,
shutdown: make(chan bool), shutdown: make(chan bool),
refresh: make(chan bool), refresh: make(chan bool),
image: new(bytes.Buffer), timeout: 10 * time.Second,
}
if !manager.enabled {
return manager
} }
go func() { go func() {
ticker := time.NewTicker(1 * time.Second)
manager.logger.Debug().Msg("subroutine started") manager.logger.Debug().Msg("subroutine started")
for { for {
select { select {
case <-manager.shutdown: case <-manager.shutdown:
manager.logger.Debug().Msg("shutting down") manager.logger.Debug().Msg("shutting down")
ticker.Stop()
return return
case <-manager.refresh: case <-manager.refresh:
manager.logger.Debug().Msg("subroutine updated") manager.logger.Debug().Msg("subroutine updated")
case sample := <-manager.sample: case sample := <-manager.sample:
manager.image.Reset() manager.image = sample
manager.image.Write(sample.Data) case <-ticker.C:
if manager.started && time.Now().After(manager.expires) {
manager.stop()
}
} }
} }
}() }()
@ -52,25 +68,61 @@ func screencastNew(config *config.Capture) *ScreencastManagerCtx {
return manager return manager
} }
func (manager *ScreencastManagerCtx) Start() error {
manager.enabled = true
return manager.createPipeline()
}
func (manager *ScreencastManagerCtx) Stop() {
manager.enabled = false
manager.destroyPipeline()
}
func (manager *ScreencastManagerCtx) Enabled() bool { func (manager *ScreencastManagerCtx) Enabled() bool {
return manager.enabled return manager.enabled
} }
func (manager *ScreencastManagerCtx) Image() []byte { func (manager *ScreencastManagerCtx) Started() bool {
return manager.image.Bytes() return manager.started
}
func (manager *ScreencastManagerCtx) Image() ([]byte, error) {
manager.expires = time.Now().Add(manager.timeout)
if !manager.started {
err := manager.start()
if err != nil {
return nil, err
}
select {
case sample := <-manager.sample:
return sample.Data, nil
case <-time.After(1 * time.Second):
return nil, fmt.Errorf("timeouted")
}
}
if manager.image.Data == nil {
return nil, fmt.Errorf("image sample not found")
}
return manager.image.Data, nil
}
func (manager *ScreencastManagerCtx) start() error {
if !manager.enabled {
return fmt.Errorf("screenshot pipeline not enabled")
}
err := manager.createPipeline()
if err != nil {
return err
}
manager.started = true
return nil
}
func (manager *ScreencastManagerCtx) stop() {
manager.started = false
manager.destroyPipeline()
} }
func (manager *ScreencastManagerCtx) createPipeline() error { func (manager *ScreencastManagerCtx) createPipeline() error {
manager.mu.Lock()
defer manager.mu.Unlock()
var err error var err error
manager.logger.Info(). manager.logger.Info().
@ -98,6 +150,9 @@ func (manager *ScreencastManagerCtx) createPipeline() error {
} }
func (manager *ScreencastManagerCtx) destroyPipeline() { func (manager *ScreencastManagerCtx) destroyPipeline() {
manager.mu.Lock()
defer manager.mu.Unlock()
if manager.pipeline == nil { if manager.pipeline == nil {
return return
} }

View File

@ -13,10 +13,9 @@ type BroadcastManager interface {
} }
type ScreencastManager interface { type ScreencastManager interface {
Start() error
Stop()
Enabled() bool Enabled() bool
Image() []byte Started() bool
Image() ([]byte, error)
} }
type CaptureManager interface { type CaptureManager interface {