#137

Tab recording in 0.9.0: h264_nvenc segfaults on input aspect change, and seed screenshot can time out

Opensebinsuaopened 3d ago

Since 0.9.0, tab recording (recording.start) fails on my machine in two independent ways. 0.8.0 (which used chrome.tabCapture) worked.

Environment

  • playwriter CLI 0.9.0, extension 0.0.170
  • Linux x64 (Ubuntu 22.04), Node 24
  • ffmpeg 4.4.2 (distro package)
  • NVIDIA T1000, driver 550.120

1. ffmpeg segfaults with h264_nvenc when the input frame aspect ratio changes

For MP4 output the recorder picks the encoder from detectEncoder(), which chooses h264_nvenc here. Recording fails with:

Error: ffmpeg exited with SIGSEGV

I can reproduce it without Chrome. Two MJPEG frames whose aspect ratio differs (1920x992, then 1440x810), piped into the same arguments the recorder builds:

ffmpeg -hide_banner -loglevel error -nostdin -f image2pipe -c:v mjpeg \
  -framerate 30 -thread_queue_size 512 -i two-frames.mjpeg \
  -vf 'scale=1920:992:force_original_aspect_ratio=decrease,pad=1920:992:(ow-iw)/2:(oh-ih)/2,format=yuv420p' \
  -c:v h264_nvenc -b:v 5714k -movflags +faststart -y out.mp4
Inputh264_nvenclibx264
Constant 1920x992OKOK
1920x992 then 960x496 (same aspect)OKOK
1920x992 then 1440x810 (aspect changes)SIGSEGVOK
Aspect change with -reinit_filter 0SIGSEGVnot tested

gdb puts the top frames inside libnvidia-encode.so, called from avcodec_send_frame. The kernel log shows the live recording crash and the reproduction crashing at the same offset. So it may be an NVIDIA or ffmpeg 4.4 bug, but the recorder is what sends it frames that change size. The single-frame probe in detectEncoder() cannot catch this.

2. The first screenshot can time out before ffmpeg starts

Separately, recording.start sometimes fails before ffmpeg is spawned:

cdpSession.send: Protocol error (Page.captureScreenshot):
Extension request timeout after 30000ms: forwardCDPCommand

The stack is PageCapture.start → ScreenRecording.start → recording.start, on the initial Page.captureScreenshot. Raising the CLI --timeout does not help, because the 30 s limit is inside the extension. If I call Page.startScreencast on the same CDP session first, the screenshot returns in about 3 s. That worked in 5 of 5 recordings.

Workaround

There is no codec option on recording.start, and no env override. I work around both problems by using the exported PageCapture with a buildArgs callback that calls buildRecordingFfmpegArgs({ ..., codec: 'libx264' }), and by starting the screencast before recording.

Suggestions

  • Let recording.start accept a codec (or honour an env var), or fall back to libx264 when the hardware encoder exits abnormally.
  • Or keep the output size constant before encoding, so the encoder never sees a change in input size or aspect.
  • Start the screencast before taking the first screenshot, or don't block on that screenshot.

Activity

2
sebinsua3d ago

Both issues come from 323854137ead34345079ba1a148f12217eb040fd ("feat: record and stream via CDP screencast + ffmpeg, no extension click"):

  • Encoder choice: recording now picks its encoder with detectEncoder() (screen-recording.ts, const codec = extension === '.webm' ? 'libvpx' : (await detectEncoder()).codec). Before, detectEncoder() was only used for streaming.
  • First screenshot: PageCapture.start now takes a first Page.captureScreenshot ("Static pages never paint, so Chrome sends no screencast frame. Seed one.").
sebinsua3d ago

Follow-up on the NVENC crash. The trigger is a change in the sample aspect ratio (SAR), not the frame size:

  • What scale+pad does: when the input aspect ratio changes, scale=…:force_original_aspect_ratio=decrease,pad=… keeps the output at 1920x992 but rewrites the frame's SAR. With showinfo, frame 0 has sar:22351/22350 and frame 1 has sar:51088/46935.
  • What ffmpeg then does: ffmpeg passes the new SAR to the encoder. h264_nvenc sees a DAR change (aspect ratio change (DAR): 1442:745 -> 6592:3129 at -loglevel verbose) and calls nvEncReconfigureEncoder() mid-stream (libavcodec/nvenc.c, reconfig_encoder()). That's where it segfaults on driver 550.120. The same code is still in ffmpeg master, so a newer ffmpeg likely won't help.
  • The fix: add setsar=1 to the end of the recording filter, as below. With the two-frame repro: 3/3 crashes without it, 3/3 clean runs with it. A constant non-1 SAR (setsar=4/3) also passes, so the trigger is the change in SAR, not its value.
scale=W:H:force_original_aspect_ratio=decrease,pad=W:H:(ow-iw)/2:(oh-ih)/2,format=yuv420p,setsar=1