Skip to content

Software-mode slow capture ends in FFmpeg exit 255 (SIGTERM): kill-reason observability + encoder exit-state propagation request #3744

Description

@aikdna

Summary

On a production-scale, DOM-heavy composition under forced software raster (--no-browser-gpu, SwiftShader), the render ends with Streaming encode failed: FFmpeg exited with code 255 whose stderr tail reads Exiting normally, received signal 15. — twice, ~19.7 min and ~13.6 min end-to-end, no usable output. We have not reproduced this with synthetic compositions, and we do not attribute the terminating signal source. This is an observability/robustness request, not a claimed root-cause report.

Environment (pinned)

component version
hyperframes CLI 0.6.52 (npm, local node_modules/.bin/hyperframes)
Node.js 24.19.0
OS macOS 26.2 (25C56), Apple Silicon
Chrome (puppeteer-launched) 152.0.7977.76 (system Chrome via PUPPETEER_EXECUTABLE_PATH)
ffmpeg 8.1.2

Observed

Composition: single-document assembly (root + 3 bare-id scene wrappers), 1920x1080@30, 19 s = 570 frames, ~60 KB inline DOM. Forced software raster, two attempts, both exit 1:

  • attempt 1: end-to-end ≈19.7 min; last ffmpeg progress line frame=265 fps=0.5 ... elapsed=0:08:13 inside the error's stderr tail; final error Streaming encode failed: FFmpeg exited with code 255, tail ends Exiting normally, received signal 15.
  • attempt 2: end-to-end ≈13.6 min; identical signature.
  • External 10 s process sampling around both attempts: the Chrome browser process was alive in every sample until HyperFrames tore down; the process that received SIGTERM is the ffmpeg streaming-encoder child.
  • GPU default mode on the same page: completes (~89 s incl. a 45 s player-ready wait — a separate topic, not chained causally here; the same warning preceded five successful renders and one no-warning control).

What isolation achieved: a canvas-driven, 570-frame, DOM-light repro under --no-browser-gpu finishes end-to-end in ≈115 s (≈5 s compile + 45 s unrelated wait + ≈65–70 s capture+encode, ~0.12 s/frame). So the abort appears tied to slow per-frame capture of a DOM-heavy page under software raster, not to frame count alone — and we could not shrink it further into a synthetic repro.

Unverified code reading, for triage only (not our diagnosis): in dist/cli.js (0.6.52), spawnStreamingEncoder (~L32025) attaches two SIGTERM paths to the ffmpeg child: an idle watchdog ffmpegStreamingTimeout (default 600 000 ms, env FFMPEG_STREAMING_TIMEOUT_MS) that is reset on writes Node reports as accepted, and the job-level abortSignal handler. Both could produce exactly "signal 15"; we cannot tell which one fired from outside the process. For completeness: the capture loop ignores writeFrame's boolean return — but in Node that return is Writable backpressure semantics (buffer threshold), not encoder liveness, and we deliberately do not propose treating false as fatal.

Asks

  1. Record the kill reason in the surfaced error (which watchdog/abort path fired, frames accepted vs attempted at kill time) so consumers don't have to read the bundle to enumerate suspects.
  2. Propagate encoder-child exit state (an explicit encoder error/dead status), so the capture loop can stop early instead of spending the remaining capture budget against a terminated encoder — failure at ~19.7 min cost us the full render twice.
  3. Optional guidance: is there a recommended producer configuration for pages whose software-mode capture rate is ≤1 fps (e.g. the enableChunkedEncode path that ships disabled by default)?

Package hygiene: redacted log excerpts available on request (consumer wrapper lines removed; scene ids replaced by host-a/b/c; terminal control sequences stripped, verified by scan with 0 remaining control bytes; no consumer-project paths or internal designations).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions