# How the Video Recorder Is Integrated with the Ego-Browser Runtime

> Discover how the Ego-Browser runtime integrates the video recorder using the screencast driver and VideoRecorder class. Learn more about page video recording in ego-lite.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: internals
- Published: 2026-08-07

---

**The Ego-Browser runtime records page video by coupling the `VideoRecorder` class with the `screencast` driver.**

The `citrolabs/ego-lite` repository provides a lightweight browser automation framework that captures viewport video through a tightly integrated recording pipeline. Understanding how the video recorder is integrated with the ego-browser runtime reveals an architecture where raw CDP (Chrome DevTools Protocol) screencast frames flow directly into an FFmpeg encoder. This design produces silent VP8 WebM files without requiring external screen capture utilities.

## Core Components: VideoRecorder and Screencast Driver

The integration centers on two primary modules that bridge the CDP session and the file system.

### VideoRecorder Implementation

Located in [`src/video-recorder.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/video-recorder.ts) (lines 19-86), the `VideoRecorder` class handles low-level encoding logic. It spawns an FFmpeg process configured for VP8 WebM output, buffers incoming JPEG frames, and atomically renames the temporary file upon completion. The class exposes three critical methods:

- `start()`: Launches the FFmpeg subprocess with the configured viewport size and quality settings.
- `writeFrame(buffer, timestamp)`: Accepts raw JPEG buffers and timestamps, streaming them to FFmpeg's stdin.
- `stop()`: Signals FFmpeg to finalize the WebM container and moves the temporary file to the final output path.

### Screencast Driver Integration

The [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts) module (lines 58-92) serves as the bridge between the CDP session and the `VideoRecorder`. When `startScreencast()` is invoked, the driver performs the following sequence:

1. Ensures an active CDP session via `ensureSession`.
2. Instantiates a `VideoRecorder` using the injectable `createRecorder` dependency (defaulting to `new VideoRecorder(options)`).
3. Subscribes to the `Page.screencastFrame` CDP event.
4. Decodes incoming base64 frames and forwards them to `recorder.writeFrame()` with millisecond timestamps.
5. Sends `Page.screencastFrameAck` acknowledgments to prevent browser buffer saturation.

## Recording Lifecycle and Frame Flow

### Starting the Recording

The recording begins when the driver calls `recorder.start()` after establishing the CDP subscription. At this point, Chrome begins emitting JPEG frames at the requested quality and maximum frame rate, which the driver immediately pipes to the FFmpeg process.

### Stopping and Finalizing Output

The `stopScreencast()` method (lines 29-67 in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts)) manages teardown:

- Unsubscribes from `Page.screencastFrame` events to halt incoming frames.
- Flushes pending frame promises to ensure all buffered data is written.
- Optionally captures a final screenshot if no frames were received during the session.
- Invokes `recorder.stop()` to close FFmpeg's stdin, wait for process exit, and atomically rename the temporary WebM file to the user-specified destination.

## Dependency Injection for Testability

The screencast driver accepts a `dependencies` object (lines 18-27 in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts)) that enables complete mocking of the integration. Through the `__testing.setOverrides()` API, unit tests can replace `createRecorder` with a stub to verify frame handling logic without spawning real FFmpeg processes or browser instances. This design keeps the video recorder tightly coupled to the runtime for performance while remaining fully testable.

## Practical Usage Examples

### Basic Screencast Recording

Record the current page viewport to a WebM file using the high-level API exposed through `page.screencast`:

```javascript
await page.screencast.start({ path: "output.webm", quality: 85 });
/* … perform browser interactions … */
await page.screencast.stop();   // Finalizes the VP8 WebM file

```

### Custom Recorder Configuration

Override the default FFmpeg path or arguments for specialized environments or custom codecs:

```javascript
import { startScreencast, __testing } from "ego-browser";

__testing.setOverrides({
  createRecorder: (opts) =>
    new VideoRecorder({ ...opts, ffmpegPath: "/usr/local/bin/ffmpeg" }),
});

await startScreencast({ path: "test.webm" });

```

### Direct VideoRecorder Access

For low-level control, instantiate the recorder directly without the screencast driver:

```javascript
import { VideoRecorder } from "ego-browser/video-recorder.js";

const recorder = new VideoRecorder({
  outputPath: "raw.webm",
  size: { width: 1280, height: 720 },
});
await recorder.start();
// Manually feed JPEG buffers from any source:
await recorder.writeFrame(jpegBuffer, Date.now());
await recorder.stop();

```

## Summary

- The video recorder is integrated with the ego-browser runtime through the `screencast` driver in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts), which bridges CDP sessions and the `VideoRecorder` class.
- `VideoRecorder` in [`src/video-recorder.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/video-recorder.ts) manages FFmpeg subprocesses, handling JPEG frame ingestion and VP8 WebM output via `start()`, `writeFrame()`, and `stop()` methods.
- Frame flow follows a strict pipeline: CDP `Page.screencastFrame` events are decoded, acknowledged via `Page.screencastFrameAck`, and written to the recorder.
- The integration supports dependency injection via `__testing.setOverrides()`, enabling unit tests to mock FFmpeg and CDP interactions without real browser instances.
- Output files are atomically renamed from temporary storage only after successful FFmpeg finalization, preventing corrupted partial recordings.

## Frequently Asked Questions

### What video format does the ego-browser runtime produce?

The integration generates silent VP8-encoded WebM files. According to the source code in [`src/video-recorder.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/video-recorder.ts), the `VideoRecorder` class spawns FFmpeg with VP8-specific codec arguments and writes to a temporary file before atomically renaming it to the final destination only upon successful completion.

### How does the screencast driver handle backpressure from the browser?

The driver implements CDP-level flow control by sending `Page.screencastFrameAck` acknowledgments immediately after processing each frame in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts). This prevents Chrome from emitting new frames until the previous frame has been decoded from base64 and written to the FFmpeg process, avoiding memory pressure in the browser.

### Can I customize FFmpeg arguments when recording video?

Yes. While the default `createRecorder` dependency instantiates `VideoRecorder` with standard options, you can inject a custom factory via `__testing.setOverrides()` to pass custom `ffmpegPath` locations or additional codec flags. This allows integration with custom FFmpeg builds or specific encoding requirements without modifying the core library.

### Where is the temporary video file stored during recording?

The `VideoRecorder` writes to a temporary path adjacent to the final output location, appending a temporary extension during the encoding process. Only upon successful `stop()` does it rename the file to the user-specified path, ensuring that incomplete recordings or process crashes do not leave corrupted files at the final destination.