# How OpenScreen Handles MP4 Encoding with Quality-Based Bitrate

> Discover how OpenScreen optimizes MP4 encoding with quality-based bitrate for efficient video export. Learn about hardware-accelerated H.264 and WebCodecs integration.

- Repository: [Sid/openscreen](https://github.com/siddharthvaddem/openscreen)
- Tags: internals
- Published: 2026-04-03

---

**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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/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:

```typescript
// 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:

```typescript
// 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`](https://github.com/siddharthvaddem/openscreen/blob/main/VideoEditor.tsx) instantiates the exporter (lines 1242–1249):

```typescript
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`](https://github.com/siddharthvaddem/openscreen/blob/main/videoExporter.ts), the `getEncoderPreferences()` method (lines 511–516) attempts hardware acceleration first:

```typescript
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`](https://github.com/siddharthvaddem/openscreen/blob/main/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:

```typescript
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:

```typescript
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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/VideoEditor.tsx). The quality setting influences only spatial resolution (width/height) and the bitrate budget allocated per frame, not temporal smoothness.