From fdd98377b38e1c33c98a3da63f51147207feb6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 23 Jan 2021 15:17:52 +0100 Subject: [PATCH] lazy screencast. --- internal/api/room/handler.go | 2 +- internal/api/room/screen.go | 7 ++- internal/capture/manager.go | 10 +--- internal/capture/screencast.go | 89 +++++++++++++++++++++++++++------- internal/types/capture.go | 5 +- 5 files changed, 83 insertions(+), 30 deletions(-) diff --git a/internal/api/room/handler.go b/internal/api/room/handler.go index f00b2d56..0718cbd6 100644 --- a/internal/api/room/handler.go +++ b/internal/api/room/handler.go @@ -63,7 +63,7 @@ func (h *RoomHandler) Route(r chi.Router) { r.Route("/screen", func(r chi.Router) { r.With(auth.CanWatchOnly).Get("/", h.screenConfiguration) 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).Get("/configurations", h.screenConfigurationsList) diff --git a/internal/api/room/screen.go b/internal/api/room/screen.go index d06d4a4f..70645cc9 100644 --- a/internal/api/room/screen.go +++ b/internal/api/room/screen.go @@ -103,7 +103,12 @@ func (h *RoomHandler) screenCastGet(w http.ResponseWriter, r *http.Request) { 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("Content-Type", "image/jpeg") w.Write(bytes) diff --git a/internal/capture/manager.go b/internal/capture/manager.go index ee0988e7..46770ac2 100644 --- a/internal/capture/manager.go +++ b/internal/capture/manager.go @@ -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() { if manager.Streaming() { manager.destroyVideoPipeline() @@ -66,7 +60,7 @@ func (manager *CaptureManagerCtx) Start() { manager.broadcast.destroyPipeline() } - if manager.screencast.Enabled() { + if manager.screencast.Started() { 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 { manager.logger.Panic().Err(err).Msg("unable to recreate screencast pipeline") } diff --git a/internal/capture/screencast.go b/internal/capture/screencast.go index da2f9ccf..f5589710 100644 --- a/internal/capture/screencast.go +++ b/internal/capture/screencast.go @@ -1,7 +1,9 @@ package capture import ( - "bytes" + "fmt" + "time" + "sync" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -13,13 +15,17 @@ import ( type ScreencastManagerCtx struct { logger zerolog.Logger + mu sync.Mutex config *config.Capture pipeline *gst.Pipeline enabled bool + started bool shutdown chan bool refresh chan bool + expires time.Time + timeout time.Duration sample chan types.Sample - image *bytes.Buffer + image types.Sample } 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(), config: config, enabled: config.Screencast, + started: false, shutdown: make(chan bool), refresh: make(chan bool), - image: new(bytes.Buffer), + timeout: 10 * time.Second, + } + + if !manager.enabled { + return manager } go func() { + ticker := time.NewTicker(1 * time.Second) manager.logger.Debug().Msg("subroutine started") for { select { case <-manager.shutdown: manager.logger.Debug().Msg("shutting down") + ticker.Stop() return case <-manager.refresh: manager.logger.Debug().Msg("subroutine updated") case sample := <-manager.sample: - manager.image.Reset() - manager.image.Write(sample.Data) + manager.image = sample + 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 } -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 { return manager.enabled } -func (manager *ScreencastManagerCtx) Image() []byte { - return manager.image.Bytes() +func (manager *ScreencastManagerCtx) Started() bool { + 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 { + manager.mu.Lock() + defer manager.mu.Unlock() + var err error manager.logger.Info(). @@ -98,6 +150,9 @@ func (manager *ScreencastManagerCtx) createPipeline() error { } func (manager *ScreencastManagerCtx) destroyPipeline() { + manager.mu.Lock() + defer manager.mu.Unlock() + if manager.pipeline == nil { return } diff --git a/internal/types/capture.go b/internal/types/capture.go index 9bd90a7e..6481dfe4 100644 --- a/internal/types/capture.go +++ b/internal/types/capture.go @@ -13,10 +13,9 @@ type BroadcastManager interface { } type ScreencastManager interface { - Start() error - Stop() Enabled() bool - Image() []byte + Started() bool + Image() ([]byte, error) } type CaptureManager interface {