From 4eca93a7ac7e34e53c6611f3edb056922e4078f3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 30 Oct 2022 02:38:34 +0100 Subject: [PATCH] chore: use a context to control the recording process This prevents the render goroutine from directly printing to stdout and ensures a controlled tear-down. --- evaluator.go | 18 ++++++++++-- vhs.go | 79 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/evaluator.go b/evaluator.go index 00108d14..8a6c8cdf 100644 --- a/evaluator.go +++ b/evaluator.go @@ -1,9 +1,11 @@ package main import ( + "context" "errors" "fmt" "io" + "log" "strings" ) @@ -68,9 +70,15 @@ func Evaluate(tape string, out io.Writer, opts ...EvaluatorOption) error { } // Begin recording frames as we are now in a recording state. - v.Record() + ctx, cancel := context.WithCancel(context.Background()) + ch := v.Record(ctx) - defer v.Cleanup() + // Log errors from the recording process. + go func() { + for err := range ch { + log.Print(err.Error()) + } + }() for _, cmd := range cmds[offset:] { // When changing the FontFamily, FontSize, LineHeight, Padding @@ -102,5 +110,11 @@ func Evaluate(tape string, out io.Writer, opts ...EvaluatorOption) error { opt(&v) } + // Stop recording frames. + cancel() + // Read from channel to ensure recorder is done. + <-ch + + v.Cleanup() return nil } diff --git a/vhs.go b/vhs.go index 1b676774..37ae75ee 100644 --- a/vhs.go +++ b/vhs.go @@ -1,8 +1,8 @@ package main import ( + "context" "fmt" - "log" "os" "os/exec" "path/filepath" @@ -126,8 +126,6 @@ const cleanupWaitTime = 100 * time.Millisecond // // It also begins the rendering process of the frames into videos. func (vhs *VHS) Cleanup() { - vhs.PauseRecording() - // Give some time for any commands executed (such as `rm`) to finish. // // If a user runs a long running command, they must sleep for the required time @@ -163,48 +161,61 @@ func (vhs *VHS) Cleanup() { const quality = 0.92 // Record begins the goroutine which captures images from the xterm.js canvases. -func (vhs *VHS) Record() { +func (vhs *VHS) Record(ctx context.Context) <-chan error { + ch := make(chan error) interval := time.Second / time.Duration(vhs.Options.Video.Framerate) time.Sleep(interval) + go func() { counter := 0 for { - if !vhs.recording { - time.Sleep(interval + interval) - continue - } - if vhs.Page != nil { - counter++ - start := time.Now() - cursor, cursorErr := vhs.CursorCanvas.CanvasToImage("image/png", quality) - text, textErr := vhs.TextCanvas.CanvasToImage("image/png", quality) - if textErr == nil && cursorErr == nil { - if err := os.WriteFile( - filepath.Join(vhs.Options.Video.Input, fmt.Sprintf(cursorFrameFormat, counter)), - cursor, - os.ModePerm, - ); err != nil { - log.Printf("error writing cursor frame: %v", err) + select { + case <-ctx.Done(): + close(ch) + return + + default: + if !vhs.recording { + time.Sleep(interval + interval) + continue + } + + if vhs.Page != nil { + counter++ + start := time.Now() + cursor, cursorErr := vhs.CursorCanvas.CanvasToImage("image/png", quality) + text, textErr := vhs.TextCanvas.CanvasToImage("image/png", quality) + if textErr == nil && cursorErr == nil { + if err := os.WriteFile( + filepath.Join(vhs.Options.Video.Input, fmt.Sprintf(cursorFrameFormat, counter)), + cursor, + os.ModePerm, + ); err != nil { + ch <- fmt.Errorf("error writing cursor frame: %w", err) + } + if err := os.WriteFile( + filepath.Join(vhs.Options.Video.Input, fmt.Sprintf(textFrameFormat, counter)), + text, + os.ModePerm, + ); err != nil { + ch <- fmt.Errorf("error writing text frame: %w", err) + } + } else { + ch <- fmt.Errorf("error: %v, %v", textErr, cursorErr) } - if err := os.WriteFile( - filepath.Join(vhs.Options.Video.Input, fmt.Sprintf(textFrameFormat, counter)), - text, - os.ModePerm, - ); err != nil { - log.Printf("error writing text frame: %v", err) + + elapsed := time.Since(start) + if elapsed >= interval { + continue + } else { + time.Sleep(interval - elapsed) } - } else { - log.Printf("error: %v, %v", textErr, cursorErr) - } - elapsed := time.Since(start) - if elapsed >= interval { - continue - } else { - time.Sleep(interval - elapsed) } } } }() + + return ch } // ResumeRecording indicates to VHS that the recording should be resumed.