oxideav-mjpeg
Pure-Rust JPEG / Motion-JPEG codec and still-image container — decodes baseline (SOF0), extended-sequential (SOF1) and progressive (SOF2) 8-bit JPEGs, encodes baseline and progressive JPEG using the Annex K "typical" Huffman tables. YUV 4:4:4 / 4:2:2 / 4:2:0 and grayscale. Zero C dependencies.
Part of the oxideav framework but usable standalone.
Installation
[]
= "0.1"
= "0.1"
= "0.1"
= "0.1"
Quick use
A JPEG file is a single SOI..EOI byte stream, so the still-image container is a pass-through: open the file, pull one packet, decode. Motion-JPEG streams (inside AVI / MOV / AMV / etc.) reuse the same codec — each video packet is a full JPEG.
use CodecRegistry;
use ContainerRegistry;
use Frame;
let mut codecs = new;
let mut containers = new;
register;
register_containers;
let input: = Boxnew;
let mut dmx = containers.open?;
let stream = &dmx.streams;
let mut dec = codecs.make_decoder?;
let pkt = dmx.next_packet?;
dec.send_packet?;
if let Ok = dec.receive_frame
# Ok::
Encoder
use ;
let mut params = video;
params.width = Some;
params.height = Some;
params.pixel_format = Some;
let mut enc = codecs.make_encoder?;
enc.send_frame?;
let pkt = enc.receive_packet?;
The encoder accepts Yuv444P, Yuv422P, or Yuv420P planar input and
emits a standalone baseline JPEG per frame: SOI, JFIF APP0, DQT, SOF0,
DHT (Annex K typical tables), optional DRI, SOS, entropy scan, EOI.
Default quality factor is 75 (libjpeg style); encoder::encode_jpeg(frame, quality) is also exposed for sibling crates that wrap the same
bitstream in custom containers.
Restart markers (RSTn + DRI) are supported for interop and bitstream
resiliency. They are off by default — call
MjpegEncoder::set_restart_interval(n_mcus) (or use
encoder::encode_jpeg_with_opts(frame, quality, n_mcus)) to enable
them. A non-zero value writes a DRI segment before SOS and cycles
RST0..=RST7 every n_mcus macroblocks in the scan, resetting DC
predictors at each marker. Passing 0 preserves the historical
no-restart behaviour.
Progressive (SOF2) encode
Toggle progressive emission via MjpegEncoder::set_progressive(true)
on the concrete encoder (construct it with
MjpegEncoder::from_params), or call
encoder::encode_jpeg_progressive(frame, quality) directly. The
output is a standalone progressive JPEG with this scan decomposition:
- Interleaved DC-first scan (
Ss=0, Se=0, Ah=0, Al=0) covering every component. - Per-component low-band AC scan (
Ss=1, Se=5, Ah=0, Al=0) — luma, Cb, Cr. - Per-component high-band AC scan (
Ss=6, Se=63, Ah=0, Al=0).
That's 1 + 3 + 3 = 7 SOS segments. Successive-approximation
refinement scans (Ah ≥ 1) are not emitted; coefficients reach
the decoder in a single initial scan per band and round-trip through
our own progressive decoder. Restart markers are not emitted on the
progressive path. Compressed size is typically ~10% larger than the
equivalent baseline encode due to the extra SOS/DHT overhead and
per-scan EOB handling (no EOBn runs).
Codec / container IDs
- Codec:
"mjpeg". Decoder output / encoder input pixel formats:Yuv444P,Yuv422P,Yuv420P, plusGray8on the decode side. - Container:
"jpeg", matches.jpg/.jpeg/.jpe/.jfifby extension and byFF D8 FFmagic bytes. One frame per file; muxing is a pass-through of the codec packet.
Format coverage
Decoder:
- SOF0 (baseline sequential, Huffman, 8-bit).
- SOF1 (extended sequential, Huffman, 8-bit) — same scan structure as SOF0 at 8-bit, so the same code path handles it.
- SOF2 (progressive, Huffman, 8-bit) — multi-scan spectral selection and successive approximation (DC first + refinement, AC first + refinement with EOB-run).
- Non-interleaved sequential scans (SOF0/SOF1 with one SOS per component) — transparently routed through the shared coefficient accumulator.
- 12-bit precision sequential JPEGs (SOF0/SOF1,
P=12) → 16-bit-LEGray12Lefor grayscale andYuv420P12Lefor 4:2:0 YUV. Level shift is 2048 as per the spec. - Lossless JPEG (SOF3) — single-component grayscale at any
precision
P ∈ 2..=16. Annex H predictor reconstruction (bit-exact). Output:Gray8at P=8,Gray10Le/Gray12Leat P=10/12, elseGray16Le. Point transform (Pt = Al) honoured. - CMYK / YCCK 4-component JPEGs → packed
PixelFormat::Cmyk. Adobe APP14 transform flag honoured: transform=0 (Adobe CMYK, stored inverted) un-inverts on decode; transform=2 (YCCK) converts back to CMYK via BT.601 YCbCr→RGB→CMY and K inversion; no APP14 → plain ("regular", C=0 = no ink) pass-through. - Chroma subsampling: 4:4:4, 4:2:2, 4:2:0.
- Grayscale (single-component →
Gray8). - Restart markers (
RSTn) + DRI. - APP0..APP15 segments skipped cleanly (EXIF/XMP/ICC preserved at the container level, not parsed).
- Trailing garbage past EOI is stripped by the demuxer.
Encoder:
- SOF0 (baseline sequential) — 8-bit Huffman, Annex K tables.
- SOF2 (progressive) — spectral-selection decomposition: one
interleaved DC-first scan + per-component AC scans over bands
Ss=1..=5andSs=6..=63(Ah=0, Al=0). No successive-approximation refinement. - 4:4:4 / 4:2:2 / 4:2:0 input.
- Optional DRI +
RSTnemission on the baseline path (off by default; see the Encoder section above).
Not supported (decoder returns Error::Unsupported):
- Hierarchical (SOF5+), arithmetic-coded (SOF9..SOF15).
- 12-bit progressive (SOF2 with
P=12), 12-bit 4:2:2 / 4:4:4 YUV. - Progressive 4-component JPEGs.
- Multi-component lossless JPEGs.
License
MIT — see LICENSE.