muxide 0.1.2

Zero-dependency pure-Rust MP4 muxer for recording applications
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
<p align="center">
  <img src="https://raw.githubusercontent.com/Michael-A-Kuykendall/muxide/main/assets/muxide-logo.png" alt="Muxide" width="350"><br>
  <strong>The last mile from encoder to playable MP4.</strong><br><br>
  <a href="https://crates.io/crates/muxide"><img src="https://img.shields.io/crates/v/muxide.svg" alt="Crates.io"></a>
  <a href="https://docs.rs/muxide"><img src="https://docs.rs/muxide/badge.svg" alt="Documentation"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
  <a href="https://www.rust-lang.org"><img src="https://img.shields.io/badge/MSRV-1.70-blue.svg" alt="MSRV"></a>
  <a href="https://github.com/Michael-A-Kuykendall/muxide/actions"><img src="https://github.com/Michael-A-Kuykendall/muxide/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
</p>

<p align="center">
  <code>cargo add muxide</code>
</p>

---

> **Muxide** takes correctly-timestamped, already-encoded audio/video frames and produces a standards-compliant MP4 โ€” **pure Rust, zero runtime dependencies, no FFmpeg.**

<table>
<tr>
<td align="center"><strong>Your Encoder</strong><br><sub>H.264 / HEVC / AV1<br>AAC / Opus</sub></td>
<td align="center">โžก๏ธ</td>
<td align="center"><strong>Muxide</strong><br><sub>Pure Rust<br>Zero deps</sub></td>
<td align="center">โžก๏ธ</td>
<td align="center"><strong>playable.mp4</strong><br><sub>Standards-compliant<br>Fast-start ready</sub></td>
</tr>
</table>

---

## Why Muxide Exists

If you're building a recording pipeline in Rust, you know the tradeoffs:

| Approach | Tradeoff |
|----------|----------|
| **FFmpeg CLI/libs** | External binary, GPL licensing concerns, "which build is this?" |
| **GStreamer** | Complex plugin system, C dependencies, heavy runtime |
| **Raw MP4 writing** | ISO-BMFF expertise required (sample tables, interleaving, moov layout) |
| **"Minimal" crates** | Often missing fast-start, strict validation, or production ergonomics |

Muxide solves **one job cleanly**:

> Take already-encoded frames with correct timestamps โ†’ produce a **standards-compliant, immediately-playable MP4** โ†’ using **pure Rust**.

Nothing more. Nothing less.

## Core Invariant

Muxide enforces a strict contract:

| Your Responsibility | Muxide's Guarantee |
|:-------------------:|:------------------:|
| โœ“ Frames are already encoded | โœ“ Valid ISO-BMFF (MP4) |
| โœ“ Timestamps are monotonic | โœ“ Correct sample tables |
| โœ“ DTS provided for B-frames | โœ“ Fast-start layout |
| โœ“ Codec headers in keyframes | โœ“ No post-processing needed |

If input violates the contract, Muxide **fails fast** with explicit errorsโ€”no silent corruption, no guessing.

---

## Features

| Category | Supported | Notes |
|----------|-----------|-------|
| **Video** | H.264/AVC | Annex B format |
| | H.265/HEVC | Annex B with VPS/SPS/PPS |
| | AV1 | OBU stream format |
| **Audio** | AAC | All profiles: LC, Main, SSR, LTP, HE, HEv2 |
| | Opus | Raw packets, 48kHz |
| **Container** | Fast-start | `moov` before `mdat` for web playback |
| | B-frames | Explicit PTS/DTS support |
| | Fragmented MP4 | For DASH/HLS streaming |
| | Metadata | Title, creation time, language |
| **Quality** | World-class errors | Detailed diagnostics, hex dumps, JSON output |
| | Production tested | FFmpeg compatibility verified |
| | Comprehensive testing | 80+ tests, property-based validation |

### Design Principles

| Principle | Implementation |
|-----------|----------------|
| ๐Ÿฆ€ **Pure Rust** | No unsafe, no FFI, no C bindings |
| ๐Ÿ“ฆ **Zero deps** | Only `std` โ€” no runtime dependencies |
| ๐Ÿงต **Thread-safe** | `Send + Sync` when writer is |
| โœ… **Well-tested** | Unit, integration, property tests |
| ๐Ÿ“œ **MIT license** | No GPL, no copyleft concerns |
| ๐Ÿšจ **Developer-friendly** | Exceptional error messages make debugging 10x faster |

> **Note:** `no_std` is not supported. Muxide requires `std::io::Write`.

---

## Quick Start

```rust
use muxide::api::{MuxerBuilder, VideoCodec, AudioCodec, Metadata};
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file = File::create("recording.mp4")?;
    
    let mut muxer = MuxerBuilder::new(file)
        .video(VideoCodec::H264, 1920, 1080, 30.0)
        .audio(AudioCodec::Aac, 48000, 2)
        .with_metadata(Metadata::new().with_title("My Recording"))
        .with_fast_start(true)
        .build()?;

    // Write encoded frames (from your encoder)
    // muxer.write_video(pts_seconds, h264_annex_b_bytes, is_keyframe)?;
    // muxer.write_audio(pts_seconds, aac_adts_bytes)?;

    let stats = muxer.finish_with_stats()?;
    println!("Wrote {} frames, {} bytes", stats.video_frames, stats.bytes_written);
    Ok(())
}
```

<details>
<summary><strong>๐Ÿ“น More Examples: HEVC, AV1, Opus, Fragmented MP4</strong></summary>

### HEVC/H.265 (4K)

```rust
// Requires VPS, SPS, PPS in first keyframe
let mut muxer = MuxerBuilder::new(file)
    .video(VideoCodec::H265, 3840, 2160, 30.0)
    .build()?;
muxer.write_video(0.0, &hevc_annexb_with_vps_sps_pps, true)?;
```

### AV1

```rust
// Requires Sequence Header OBU in first keyframe
let mut muxer = MuxerBuilder::new(file)
    .video(VideoCodec::Av1, 1920, 1080, 60.0)
    .build()?;
muxer.write_video(0.0, &av1_obu_with_sequence_header, true)?;
```

### Opus Audio

```rust
// Opus always uses 48kHz internally (per spec)
let mut muxer = MuxerBuilder::new(file)
    .video(VideoCodec::H264, 1920, 1080, 30.0)
    .audio(AudioCodec::Opus, 48000, 2)
    .build()?;
muxer.write_audio(0.0, &opus_packet)?;
```

### Fragmented MP4 (DASH/HLS)

```rust
let mut muxer = MuxerBuilder::new(file)
    .video(VideoCodec::H264, 1920, 1080, 30.0)
    .fragmented(true)  // Enable fMP4 mode
    .build()?;

// Write frames, then flush fragments periodically
muxer.write_video(0.0, &frame, true)?;
muxer.flush_fragment()?;  // Writes an moof+mdat pair
```

### B-Frames with Explicit DTS

```rust
// When encoder produces B-frames, provide both PTS and DTS
muxer.write_video_with_dts(
    pts_seconds,  // Presentation timestamp
    dts_seconds,  // Decode timestamp (for B-frame ordering)
    &frame_data,
    is_keyframe
)?;
```

</details>

---

## Command Line Tool

Muxide includes a command-line tool for quick testing and development workflows:

```bash
# Install the CLI tool
cargo install muxide

# Basic video-only muxing
muxide mux \
  --video keyframes.h264 \
  --width 1920 --height 1080 --fps 30 \
  --output recording.mp4

# Video + audio with metadata
muxide mux \
  --video stream.h264 \
  --audio stream.aac \
  --video-codec h264 \
  --audio-codec aac-he \
  --width 1920 --height 1080 --fps 30 \
  --sample-rate 44100 --channels 2 \
  --title "My Recording" \
  --language eng \
  --output final.mp4

# JSON output for automation
muxide mux --json [args...] > stats.json

# Validate input files without muxing
muxide validate --video input.h264 --audio input.aac

# Get info about supported codecs
muxide info
```

**Supported Codecs:**
- **Video:** H.264 (AVC), H.265 (HEVC), AV1
- **Audio:** AAC (all profiles), Opus

**Features:**
- Progress reporting with `--verbose`
- JSON output for CI/CD integration
- Comprehensive error messages
- Fast-start MP4 layout by default
- Metadata support (title, language, creation time)

---

## What Muxide Is Not

Muxide is intentionally **focused**. It does **not**:

| Not Supported | Why |
|---------------|-----|
| Encoding/decoding | Use `openh264`, `x264`, `rav1e`, etc. |
| Transcoding | Not a codec library |
| Demuxing/reading MP4 | Write-only by design |
| Timestamp correction | Garbage in = error out |
| Non-MP4 containers | MKV, WebM, AVI not supported |
| DRM/encryption | Out of scope |

**Muxide is the last mile**: encoder output โ†’ playable file.

---

## Use Cases

Muxide is a great fit for:

- ๐ŸŽฅ **Screen recorders** โ€” capture โ†’ encode โ†’ mux โ†’ ship
- ๐Ÿ“น **Camera apps** โ€” webcam/IP camera recording pipelines
- ๐ŸŽฌ **Video editors** โ€” export timeline to MP4
- ๐Ÿ“ก **Streaming** โ€” generate fMP4 segments for DASH/HLS
- ๐Ÿญ **Embedded systems** โ€” single binary, no external deps
- ๐Ÿ”ฌ **Scientific apps** โ€” deterministic, reproducible output

Probably **not** a fit if you need encoding, demuxing, or legacy codecs (MPEG-2, etc.).

---

## Example: Fast-Start Proof

The `faststart_proof` example demonstrates a structural MP4 invariant:

- Two MP4 files are generated from the same encoded inputs
- One with fast-start enabled, one without
- No external tools are used at any stage

```text
$ cargo run --example faststart_proof --release

output: recording_faststart.mp4
    layout invariant: moov before mdat = YES

output: recording_normal.mp4
    layout invariant: moov before mdat = NO
```

When served over HTTP, the fast-start file can begin playback without waiting for the full download (player behavior varies, but the layout property is deterministic).

This example is intentionally minimal:

- Timestamps are generated in-code
- No B-frames/DTS paths are exercised
- The goal is container layout correctness, not encoding quality

---

## Performance

Muxide is designed for **minimal overhead**. Muxing should never be your bottleneck.

| Scenario | Time | Throughput |
|----------|------|------------|
| 1000 H.264 frames | 264 ยตs | **3.7M frames/sec** |
| 1000 H.264 + fast-start | 362 ยตs | 2.8M frames/sec |
| 1000 video + 1500 audio | 457 ยตs | 2.2M frames/sec |
| 100 4K frames (~6.5 MB) | 14 ms | **464 MB/sec** |

<details>
<summary><strong>Run benchmarks yourself</strong></summary>

```bash
cargo bench
```

Benchmarks run on standard development hardware. In practice, **encoding is always the bottleneck** โ€” muxing overhead is negligible.

</details>

---

## Input Format Requirements

<details>
<summary><strong>๐Ÿ“‹ Codec-specific requirements (click to expand)</strong></summary>

### H.264/AVC

- **Format:** Annex B (start codes: `00 00 00 01` or `00 00 01`)
- **First keyframe must contain:** SPS and PPS NAL units
- **NAL unit types:** IDR (keyframe), non-IDR, SPS, PPS

### H.265/HEVC

- **Format:** Annex B (start codes)
- **First keyframe must contain:** VPS, SPS, and PPS NAL units
- **NAL unit types:** IDR_W_RADL, IDR_N_LP, CRA, VPS, SPS, PPS

### AV1

- **Format:** OBU (Open Bitstream Unit) stream
- **First keyframe must contain:** Sequence Header OBU
- **OBU types:** Sequence Header, Frame, Frame Header, Tile Group

### AAC

- **Format:** ADTS (Audio Data Transport Stream)
- **Header:** 7-byte ADTS header per frame
- **Profiles:** LC-AAC recommended

### Opus

- **Format:** Raw Opus packets (no container)
- **Sample rate:** Always 48000 Hz (Opus specification)
- **Channels:** 1 (mono) or 2 (stereo)

</details>

---

## Documentation

| Resource | Description |
|----------|-------------|
| [๐Ÿ“š API Reference]https://docs.rs/muxide | Complete API documentation |
| [๐Ÿ“œ Design Charter]docs/charter.md | Architecture decisions and rationale |
| [๐Ÿ“‹ API Contract]docs/contract.md | Input/output guarantees |

---

## FAQ

<details>
<summary><strong>Why not just use FFmpeg?</strong></summary>

FFmpeg is excellent, but:
- External binary dependency (distribution complexity)
- GPL licensing concerns for some builds
- Process orchestration overhead
- "What flags was this built with?" debugging

Muxide is a single `cargo add` with zero external dependencies.

</details>

<details>
<summary><strong>Can Muxide encode video?</strong></summary>

No. Muxide is **muxing only**. For encoding, use:
- `openh264` โ€” H.264 encoding (BSD)
- `rav1e` โ€” AV1 encoding (BSD)
- `x264`/`x265` โ€” H.264/HEVC (GPL, via FFI)

</details>

<details>
<summary><strong>What if my timestamps are wrong?</strong></summary>

Muxide will reject non-monotonic timestamps with a clear error. It does not attempt to "fix" broken input โ€” this is by design to ensure predictable output.

</details>

<details>
<summary><strong>Is Muxide production-ready?</strong></summary>

Yes. Muxide has an extensive test suite (unit, integration, property-based tests) and is designed for predictable, deterministic behavior.

</details>

---

## License

MIT โ€” no GPL, no copyleft, no surprises.

---

<p align="center">
  <em>Muxide is designed to be <strong>boring</strong> in the best way:<br>predictable, strict, fast, and invisible once integrated.</em>
</p>