How OpenScreen Handles MP4 Encoding with Quality-Based Bitrate

OpenScreen's video export pipeline translates user-selected quality presets into specific resolution and bitrate combinations, then configures the WebCodecs VideoEncoder with hardware-accelerated H.264 encoding to generate optimized MP4 files.

OpenScreen is an open-source video editor that streamlines screen recording workflows through an intelligent export system. The application implements MP4 encoding with quality-based bitrate controls via the VideoExporter class, dynamically calculating bandwidth requirements based on pixel density and user-selected quality tiers. When a user initiates an export from the editor UI, the system maps abstract quality labels to concrete encoding parameters before invoking the browser's native media APIs.

Architecture of the Export Pipeline

The export flow originates in src/components/video-editor/VideoEditor.tsx, where the handleExport function (lines 3126–3262) gathers settings and computes target parameters. This UI layer hands off configuration to the core VideoExporter class defined in src/lib/exporter/videoExporter.ts, which manages the VideoEncoder lifecycle and hardware acceleration preferences. Finally, encoded chunks flow into VideoMuxer (src/lib/exporter/muxer.ts) for MP4 containerization.

The pipeline supports three quality presets:

  • source – Preserves original resolution with visually lossless bitrates
  • good – Targets 1080p resolution with balanced compression
  • medium – Targets 720p resolution for smaller file sizes

Mapping Quality Presets to Encoding Parameters

The critical translation logic resides in VideoEditor.tsx (lines 1327–1385). The system first determines the active quality setting, then calculates export dimensions and bitrates based on total pixel count.

Source Quality: Visually Lossless Encoding

When quality === "source", OpenScreen maintains the original video dimensions (or cropped region) and assigns bitrates that scale with resolution to prevent artifacts:

// From VideoEditor.tsx lines 1329-1344
if (quality === "source") {
    // Compute exportWidth/exportHeight from source dimensions
    bitrate = 30_000_000; // 30 Mbps baseline
    
    if (totalPixels > 1920 * 1080 && totalPixels <= 2560 * 1440) {
        bitrate = 50_000_000; // 50 Mbps for 1440p range
    } else if (totalPixels > 2560 * 1440) {
        bitrate = 80_000_000; // 80 Mbps for 4K+
    }
}

This tier ensures minimal generation loss for archival purposes or further editing.

Good and Medium Quality: Targeted Resolutions

For compressed outputs, the system downscales to standard heights while preserving aspect ratios:

// From VideoEditor.tsx lines 1345-1359
const targetHeight = quality === "medium" ? 720 : 1080;
exportHeight = Math.floor(targetHeight / 2) * 2; // Ensure even dimensions
exportWidth = Math.floor((exportHeight * aspectRatioValue) / 2) * 2;

// Bitrate tuned for the chosen resolution
if (totalPixels <= 1280 * 720) {
    bitrate = 10_000_000; // 10 Mbps for 720p and below
} else if (totalPixels <= 1920 * 1080) {
    bitrate = 20_000_000; // 20 Mbps for 1080p
} else {
    bitrate = 30_000_000; // 30 Mbps for higher resolutions
}

The Math.floor(... / 2) * 2 pattern ensures dimensions conform to H.264 macroblock requirements.

WebCodecs Configuration and Hardware Acceleration

With parameters calculated, VideoEditor.tsx instantiates the exporter (lines 1242–1249):

const exporter = new VideoExporter({
    videoUrl: videoPath,
    width: exportWidth,
    height: exportHeight,
    frameRate: 60,
    bitrate,
    codec: "avc1.640033", // H.264 High profile
    // ... additional visual settings
});

Inside videoExporter.ts, the getEncoderPreferences() method (lines 511–516) attempts hardware acceleration first:

const preferences = [
    { type: "video", codec: config.codec, hardware: "prefer-hardware" },
    { type: "video", codec: config.codec, hardware: "prefer-software" }
];

If the browser supports hardware-accelerated H.264 encoding, OpenScreen utilizes it for real-time performance; otherwise, it falls back to software encoding to ensure compatibility.

MP4 Container Assembly

As the VideoEncoder produces EncodedVideoChunk objects, the VideoMuxer class writes them into an MP4 container structure. The first chunk carries the codec configuration—including the computed bitrate, resolution, and color space—ensuring the resulting Blob is self-contained and playable across devices. This muxing logic in src/lib/exporter/muxer.ts handles the ISO Base Media File Format headers and sample tables required for proper MP4 structure.

Practical Implementation Examples

Direct VideoExporter Usage

For programmatic exports outside the standard UI:

import { VideoExporter } from "@/lib/exporter";

const exporter = new VideoExporter({
    videoUrl: "/path/to/input.webm",
    width: 1920,
    height: 1080,
    frameRate: 60,
    bitrate: 20_000_000,      // 20 Mbps for "good" quality 1080p
    codec: "avc1.640033",     // H.264 High profile
    wallpaper: "/wallpapers/bg.jpg",
    zoomRegions: [],
    trimRegions: [],
    speedRegions: [],
    cropRegion: { x: 0, y: 0, width: 1920, height: 1080 },
    // ... other ExportConfig fields
});

exporter.export().then(result => {
    if (result.success) {
        const url = URL.createObjectURL(result.blob);
        console.log("Exported MP4:", url);
    }
});

Bitrate Calculation Logic

To replicate the quality-to-bitrate mapping in custom scripts:

function bitrateForQuality(
    quality: "source" | "good" | "medium", 
    width: number, 
    height: number
): number {
    const totalPixels = width * height;
    
    if (quality === "source") {
        if (totalPixels > 2560 * 1440) return 80_000_000;
        if (totalPixels > 1920 * 1080) return 50_000_000;
        return 30_000_000;
    }
    
    // Good or medium quality
    if (totalPixels <= 1280 * 720) return 10_000_000;
    if (totalPixels <= 1920 * 1080) return 20_000_000;
    return 30_000_000;
}

Summary

  • Quality presets drive encoding parameters – OpenScreen translates "source", "good", and "medium" selections into specific resolution targets and bitrates ranging from 10 Mbps to 80 Mbps.
  • Hardware acceleration is prioritized – The VideoExporter attempts prefer-hardware encoding first, falling back to software via getEncoderPreferences() in videoExporter.ts.
  • Dimensions are sanitized – The pipeline ensures even-numbered widths and heights using Math.floor division to satisfy H.264 codec requirements.
  • MP4 containerization is native – Encoded chunks are multiplexed into standard MP4 format via VideoMuxer in muxer.ts, with codec configuration preserved in the initial chunk.

Frequently Asked Questions

How does OpenScreen determine the final bitrate for MP4 exports?

OpenScreen calculates the final bitrate based on the selected quality preset and total pixel count of the output resolution. For source quality, it uses visually lossless tiers (30–80 Mbps) that scale with resolution. For good and medium qualities, it applies fixed bandwidth caps (10–30 Mbps) appropriate for web distribution and streaming.

Can I force software-only encoding if hardware acceleration causes issues?

While the UI does not expose a manual toggle, the underlying VideoExporter class in src/lib/exporter/videoExporter.ts automatically handles encoder selection. It attempts prefer-hardware first, then falls back to prefer-software if the browser reports hardware encoder unavailability or failure during configuration.

Why does the "source" quality preset use variable bitrates instead of a fixed value?

The source preset aims for visually lossless preservation regardless of input resolution. A 4K screen recording contains significantly more visual information than a 1080p clip, requiring higher bandwidth (80 Mbps vs. 30 Mbps) to maintain perceptual quality without compression artifacts during re-encoding.

Does the quality-based bitrate affect the frame rate?

No, the frame rate remains locked at 60 FPS across all quality presets according to the VideoExporter configuration in VideoEditor.tsx. The quality setting influences only spatial resolution (width/height) and the bitrate budget allocated per frame, not temporal smoothness.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →