Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

Muxide
The last mile from encoder to playable MP4.

Crates.io Downloads Documentation License MSRV CI

cargo add muxide


Muxide takes correctly-timestamped, already-encoded audio/video frames and produces a standards-compliant MP4 — pure Rust, minimal external dependencies, no FFmpeg.

Your Encoder
H.264 / HEVC / AV1
AAC / Opus
➡️Muxide
Pure Rust
Minimal external deps
➡️playable.mp4
Standards-compliant
Fast-start ready

Why Muxide Exists

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

ApproachTradeoff
FFmpeg CLI/libsExternal binary, GPL licensing concerns, "which build is this?"
GStreamerComplex plugin system, C dependencies, heavy runtime
Raw MP4 writingISO-BMFF expertise required (sample tables, interleaving, moov layout)
"Minimal" cratesOften 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.

Installation & Usage

As a Library

cargo add muxide
use muxide::api::{MuxerBuilder, VideoCodec}; let mut muxer = MuxerBuilder::new(file) .video(VideoCodec::H264, 1920, 1080, 30.0)? .build()?; // Write your encoded frames... muxer.write_video(0.0, &h264_frame, true)?; muxer.finish()?;

As a CLI Tool

# Install globally cargo install muxide # Or download pre-built binary from releases # Then use: muxide --help # Quick examples: muxide mux --video frames/ --output output.mp4 --width 1920 --height 1080 --fps 30 muxide mux --video video.h264 --audio audio.aac --output output.mp4 muxide validate --video frames/ --audio audio.aac muxide info input.mp4

The CLI tool accepts raw encoded frames from stdin or files and produces MP4 output.

Core Invariant

Muxide enforces a strict contract:

Your ResponsibilityMuxide'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

CategorySupportedNotes
VideoH.264/AVCAnnex B format
H.265/HEVCAnnex B with VPS/SPS/PPS
AV1OBU stream format
VP9Frame header parsing, resolution/bit-depth/color config extraction
AudioAACAll profiles: LC, Main, SSR, LTP, HE, HEv2
OpusRaw packets, 48kHz
ContainerFast-startmoov before mdat for web playback
B-framesExplicit PTS/DTS support
Fragmented MP4For DASH/HLS streaming
MetadataTitle, creation time, language
QualityWorld-class errorsDetailed diagnostics, hex dumps, JSON output
Production testedFFmpeg compatibility verified
Comprehensive testing80+ tests, property-based validation

Design Principles

PrincipleImplementation
🦀 Pure RustNo unsafe, no FFI, no C bindings
📦 Minimal depsOnly essential Rust crates — no external binaries
🧵 Thread-safeSend + Sync when writer is
Well-testedUnit, integration, property tests
📜 Permissive licenseDual-licensed: MIT OR Apache-2.0
🚨 Developer-friendlyExceptional error messages make debugging 10x faster

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


Quick Start

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(()) }
📹 More Examples: HEVC, AV1, Opus, Fragmented MP4

HEVC/H.265 (4K)

// 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

// 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

// 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)

use muxide::codec::vp9::Vp9Config; // H.264 let sps_bytes = vec![0x67, 0x42, 0x00, 0x1e, 0xda, 0x02, 0x80, 0x2d, 0x8b, 0x11]; let pps_bytes = vec![0x68, 0xce, 0x38, 0x80]; let mut muxer = MuxerBuilder::new(file) .video(VideoCodec::H264, 1920, 1080, 30.0) .with_sps(sps_bytes) .with_pps(pps_bytes) .new_with_fragment()?; // H.265 let vps_bytes = vec![0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00]; let sps_bytes = vec![0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00]; let pps_bytes = vec![0x44, 0x01, 0xc0, 0x73, 0xc0, 0x4c, 0x90]; let mut muxer = MuxerBuilder::new(file) .video(VideoCodec::H265, 1920, 1080, 30.0) .with_vps(vps_bytes) .with_sps(sps_bytes) .with_pps(pps_bytes) .new_with_fragment()?; // AV1 let seq_header_bytes = vec![ 0x0A, 0x10, // OBU header + size (example) 0x00, 0x00, 0x00, 0x00, ]; let mut muxer = MuxerBuilder::new(file) .video(VideoCodec::Av1, 1920, 1080, 30.0) .with_av1_sequence_header(seq_header_bytes) .new_with_fragment()?; // VP9 let vp9_config = Vp9Config { width: 1920, height: 1080, profile: 0, bit_depth: 8, color_space: 0, transfer_function: 0, matrix_coefficients: 0, level: 0, full_range_flag: 0, }; let mut muxer = MuxerBuilder::new(file) .video(VideoCodec::Vp9, 1920, 1080, 30.0) .with_vp9_config(vp9_config) .new_with_fragment()?; // Get init segment (ftyp + moov) let init_segment = muxer.init_segment(); // Write frames... muxer.write_video(0, 0, &frame, true)?; // Get media segments (moof + mdat) if let Some(segment) = muxer.flush_segment() { // Send segment to client }

B-Frames with Explicit DTS

// 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 )?;

Command Line Tool

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

# 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 SupportedWhy
Encoding/decodingUse openh264, x264, rav1e, etc.
TranscodingNot a codec library
Demuxing/reading MP4Write-only by design
Timestamp correctionGarbage in = error out
Non-MP4 containersMKV, WebM, AVI not supported
DRM/encryptionOut 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 (e.g., CrabCamera integration)
  • 🎬 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
$ 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.

ScenarioTimeThroughput
1000 H.264 frames264 µs3.7M frames/sec
1000 H.264 + fast-start362 µs2.8M frames/sec
1000 video + 1500 audio457 µs2.2M frames/sec
100 4K frames (~6.5 MB)14 ms464 MB/sec

Note: Benchmarks are based on development hardware. Encoding is typically the bottleneck—muxing overhead is negligible. Run cargo bench for your environment (dev-only benchmarks available).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)

Documentation

ResourceDescription
📚 API ReferenceComplete API documentation
📜 Design CharterArchitecture decisions and rationale
📋 API ContractInput/output guarantees

FAQ

Why not just use FFmpeg?

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 minimal external dependencies.

Can Muxide encode video?

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)
What if my timestamps are wrong?

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.

Is Muxide production-ready?

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


License

Licensed under either of:

at your option.


Muxide is designed to be boring in the best way:
predictable, strict, fast, and invisible once integrated.

关于 About

Pure Rust MP4 muxer — zero deps, no FFmpeg. Feed it encoded frames, get a standards-compliant MP4. H.264, H.265, AV1, AAC, Opus.
aacaudioav1ffmpeg-alternativeh264h265hevcmit-licensemp4multimediamuxerno-dependenciesopuspure-rustrecordingruststreamingtaurivideozero-dependencies

语言 Languages

Rust99.8%
Shell0.2%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
63
Total Commits
峰值: 47次/周
Less
More

核心贡献者 Contributors