Skip to content

Commit

Permalink
chore: use a context to control the recording process
Browse files Browse the repository at this point in the history
This prevents the render goroutine from directly printing to stdout
and ensures a controlled tear-down.
  • Loading branch information
muesli committed Oct 30, 2022
1 parent 910a693 commit 45d5507
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 35 deletions.
18 changes: 16 additions & 2 deletions evaluator.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package main

import (
"context"
"errors"
"fmt"
"io"
"log"
"strings"
)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
78 changes: 45 additions & 33 deletions vhs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"log"
"os"
Expand Down Expand Up @@ -126,8 +127,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
Expand Down Expand Up @@ -163,48 +162,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.
Expand Down

0 comments on commit 45d5507

Please sign in to comment.