Skip to main content

oximedia_codec/
lib.rs

1//! Audio and video codec implementations for `OxiMedia`.
2//!
3//! This crate provides encoding and decoding for royalty-free codecs:
4//!
5//! ## Video Codecs
6//!
7//! - **AV1** — Alliance for Open Media codec (primary, feature `av1`)
8//! - **VP9** — Google's royalty-free codec (feature `vp9`)
9//! - **VP8** — Google's earlier royalty-free codec (feature `vp8`)
10//! - **Theora** — Xiph.Org Foundation codec, VP3-based (feature `theora`)
11//! - **MJPEG** — Motion JPEG, one JPEG baseline frame per video frame (feature `mjpeg`)
12//! - **APV** — Advanced Professional Video, ISO/IEC 23009-13 (feature `apv`)
13//! - **FFV1** — Lossless video codec, RFC 9043 (feature `ffv1`)
14//!
15//! ## Audio Codecs
16//!
17//! - **Opus** — Modern low-latency audio codec (feature `opus`)
18//! - **Vorbis** — Xiph.Org audio codec (always available)
19//! - **FLAC** — Lossless audio (always available)
20//! - **PCM** — Uncompressed audio (always available)
21//!
22//! ## Image Codecs
23//!
24//! - **JPEG-XL** — ISO/IEC 18181, royalty-free (feature `jpegxl`)
25//! - **WebP** — Google royalty-free (always available)
26//! - **GIF** — Patents expired 2004 (always available)
27//! - **PNG** / **APNG** — W3C royalty-free (always available)
28//! - **AVIF** — AV1-based still image format (always available)
29//!
30//! ## Codec Feature Matrix
31//!
32//! Full encode/decode support, bit-depth, and chroma subsampling per codec:
33//!
34//! | Codec | Encode | Decode | Bit Depths | Chroma |
35//! |-------|--------|--------|------------|--------|
36//! | AV1 | ✓ | ✓ | 8, 10, 12 | 4:2:0, 4:4:4 |
37//! | VP9 | ✓ | ✓ | 8, 10 | 4:2:0 |
38//! | VP8 | ✓ | ✓ | 8 | 4:2:0 |
39//! | Theora | ✓ | ✓ | 8 | 4:2:0 |
40//! | MJPEG | ✓ | ✓ | 8 | 4:2:0, 4:2:2, 4:4:4 |
41//! | APV | ✓ | ✓ | 8, 10, 12 | 4:2:0, 4:2:2, 4:4:4 |
42//! | FFV1 | ✓ | ✓ | 8 | 4:2:0, 4:2:2, 4:4:4 |
43//! | H.263 | ✓ | ✓ | 8 | 4:2:0 |
44//! | JPEG-XL | ✓ | ✓ | 8, 10, 12 | 4:2:0, 4:2:2, 4:4:4 |
45//! | AVIF/AV1 | ✓ | ✓ | 8, 10, 12 | 4:2:0, 4:4:4 |
46//! | APNG/PNG | ✓ | ✓ | 8, 16 | RGBA, Grayscale |
47//! | GIF | ✓ | ✓ | 8 | Paletted |
48//! | WebP | ✓ | ✓ | 8 | 4:2:0 (lossy), lossless |
49//! | Opus | ✓ | ✓ | — | Mono/Stereo/Surround |
50//! | Vorbis | ✓ | ✓ | — | Mono/Stereo/Surround |
51//! | FLAC | ✓ | ✓ | 16, 24 | Mono/Stereo/Multi |
52//! | PCM | ✓ | ✓ | 8, 16, 24, 32, f32 | Any |
53//!
54//! Enable optional codecs via Cargo features:
55//!
56//! ```toml
57//! [dependencies]
58//! oximedia-codec = { version = "0.1.4", features = ["av1", "vp9", "opus", "jpegxl"] }
59//! ```
60//!
61//! Available features: `av1` (default), `vp9`, `vp8`, `theora`, `h263`, `opus`,
62//! `ffv1` (default), `jpegxl` (default), `mjpeg`, `apv`, `image-io` (default).
63//!
64//! # Architecture
65//!
66//! All codecs implement unified traits:
67//!
68//! - [`VideoDecoder`] — Decode compressed packets to frames
69//! - [`VideoEncoder`] — Encode frames to compressed packets
70//!
71//! ## JPEG-XL: ISOBMFF Container and Streaming Decode
72//!
73//! The `jpegxl` module provides two output paths for animated JXL sequences:
74//!
75//! ### `AnimatedJxlEncoder::finish_isobmff()`
76//!
77//! Wraps the bare JXL codestream in an ISOBMFF container (ISO Base Media File Format):
78//!
79//! - `ftyp` box — major brand `jxl `, compatible brands `jxl ` + `isom`
80//! - `jxll` box — JXL level 5
81//! - `jxlp` box — the bare codestream with the `is_last` bit set
82//!
83//! ```ignore
84//! use oximedia_codec::jpegxl::{AnimatedJxlEncoder, JxlAnimation};
85//!
86//! let anim = JxlAnimation { ticks_per_second: 30, ..Default::default() };
87//! let mut encoder = AnimatedJxlEncoder::new(640, 480, 3, 8, 7, anim);
88//! encoder.add_frame(&frame_rgb, 1)?;
89//!
90//! // Produces ftyp + jxll + jxlp bytes, decodable by JxlStreamingDecoder
91//! let isobmff_bytes: Vec<u8> = encoder.finish_isobmff()?;
92//! ```
93//!
94//! ### `JxlStreamingDecoder<R: Read>` — Streaming Frame Iterator
95//!
96//! `JxlStreamingDecoder` is a lazy frame iterator that auto-detects whether its
97//! source is an ISOBMFF container or a bare native codestream:
98//!
99//! | Format  | Detection | Producer |
100//! |---------|-----------|----------|
101//! | ISOBMFF | `bytes[4..8] == b"ftyp"` and `bytes[8..12] == b"jxl "` | `finish_isobmff()` |
102//! | Native  | `bytes[0..2] == [0xFF, 0x0A]` | `finish()` |
103//!
104//! ```ignore
105//! use oximedia_codec::jpegxl::{AnimatedJxlEncoder, JxlAnimation, JxlStreamingDecoder};
106//! use std::io::Cursor;
107//!
108//! // Encode
109//! let anim = JxlAnimation { ticks_per_second: 30, ..Default::default() };
110//! let mut encoder = AnimatedJxlEncoder::new(640, 480, 3, 8, 7, anim);
111//! encoder.add_frame(&frame_rgb, 1)?;
112//! let bytes = encoder.finish_isobmff()?;
113//!
114//! // Stream-decode frame by frame
115//! for frame_result in JxlStreamingDecoder::new(Cursor::new(bytes))? {
116//!     let frame = frame_result?;
117//!     println!("frame {}x{}", frame.width, frame.height);
118//! }
119//! ```
120//!
121//! ## MJPEG: Baseline JPEG Compliance
122//!
123//! The `mjpeg` module wraps `oximedia-image`'s pure-Rust JPEG baseline encoder and decoder.
124//! The JPEG encoder emits DQT segments with quantization tables in the correct zigzag scan
125//! order and achieves ≥28 dB PSNR at quality 85 for typical natural images.
126//!
127//! # Example
128//!
129//! ```ignore
130//! use oximedia_codec::{VideoDecoder, Av1Decoder};
131//!
132//! let mut decoder = Av1Decoder::new(&codec_params)?;
133//! decoder.send_packet(&packet)?;
134//! while let Some(frame) = decoder.receive_frame()? {
135//!     // Process decoded frame
136//! }
137//! ```
138//!
139//! # Audio Example
140//!
141//! ```ignore
142//! use oximedia_codec::opus::OpusDecoder;
143//!
144//! let mut decoder = OpusDecoder::new(48000, 2)?;
145//! let audio_frame = decoder.decode_packet(&packet_data)?;
146//! ```
147
148#![deny(unsafe_code)]
149#![warn(missing_docs)]
150#![allow(clippy::unused_self)]
151#![allow(clippy::missing_errors_doc)]
152#![allow(clippy::unnecessary_wraps)]
153#![allow(clippy::must_use_candidate)]
154#![allow(clippy::match_same_arms)]
155#![allow(clippy::needless_pass_by_value)]
156#![allow(clippy::doc_markdown)]
157#![allow(clippy::cast_precision_loss)]
158#![allow(clippy::cast_possible_wrap)]
159#![allow(clippy::cast_lossless)]
160#![allow(clippy::trivially_copy_pass_by_ref)]
161#![allow(clippy::unnecessary_cast)]
162#![allow(clippy::struct_field_names)]
163#![allow(clippy::self_assignment)]
164#![allow(clippy::redundant_else)]
165#![allow(clippy::no_effect_underscore_binding)]
166#![allow(clippy::needless_range_loop)]
167#![allow(clippy::missing_panics_doc)]
168#![allow(clippy::map_unwrap_or)]
169#![allow(clippy::manual_div_ceil)]
170#![allow(clippy::if_not_else)]
171#![allow(clippy::derivable_impls)]
172#![allow(clippy::bool_to_int_with_if)]
173#![allow(unused_variables)]
174#![allow(unused_imports)]
175// Additional clippy allows for codec implementation
176#![allow(clippy::unreadable_literal)]
177#![allow(clippy::cast_possible_truncation)]
178#![allow(clippy::cast_sign_loss)]
179#![allow(clippy::similar_names)]
180#![allow(clippy::many_single_char_names)]
181#![allow(clippy::uninlined_format_args)]
182#![allow(clippy::field_reassign_with_default)]
183#![allow(clippy::manual_range_contains)]
184#![allow(clippy::too_many_arguments)]
185#![allow(clippy::missing_const_for_fn)]
186#![allow(clippy::redundant_closure)]
187#![allow(clippy::wildcard_imports)]
188#![allow(clippy::explicit_iter_loop)]
189#![allow(clippy::implicit_clone)]
190#![allow(clippy::assertions_on_constants)]
191#![allow(clippy::no_effect)]
192#![allow(clippy::unnecessary_operation)]
193#![allow(clippy::collapsible_if)]
194#![allow(clippy::manual_strip)]
195#![allow(clippy::fn_params_excessive_bools)]
196#![allow(clippy::struct_excessive_bools)]
197#![allow(dead_code)]
198#![allow(clippy::missing_fields_in_debug)]
199#![allow(clippy::useless_vec)]
200#![allow(clippy::used_underscore_binding)]
201#![allow(clippy::unnecessary_unwrap)]
202#![allow(clippy::needless_late_init)]
203#![allow(clippy::never_loop)]
204#![allow(clippy::while_let_on_iterator)]
205#![allow(clippy::manual_let_else)]
206#![allow(clippy::clone_on_copy)]
207#![allow(clippy::bool_assert_comparison)]
208#![allow(clippy::overly_complex_bool_expr)]
209#![allow(clippy::double_comparisons)]
210#![allow(clippy::needless_borrow)]
211#![allow(clippy::float_cmp)]
212#![allow(clippy::manual_slice_size_calculation)]
213#![allow(clippy::option_as_ref_deref)]
214#![allow(clippy::single_match_else)]
215#![allow(clippy::cast_abs_to_unsigned)]
216#![allow(clippy::semicolon_if_nothing_returned)]
217#![allow(clippy::range_plus_one)]
218#![allow(clippy::only_used_in_recursion)]
219#![allow(clippy::iter_without_into_iter)]
220#![allow(clippy::collapsible_else_if)]
221#![allow(clippy::naive_bytecount)]
222#![allow(clippy::manual_clamp)]
223#![allow(clippy::unnecessary_literal_unwrap)]
224#![allow(clippy::default_trait_access)]
225#![allow(clippy::return_self_not_must_use)]
226#![allow(clippy::match_wildcard_for_single_variants)]
227#![allow(clippy::unnecessary_filter_map)]
228#![allow(clippy::ref_binding_to_reference)]
229#![allow(clippy::manual_memcpy)]
230#![allow(clippy::borrowed_box)]
231#![allow(clippy::needless_question_mark)]
232#![allow(clippy::type_complexity)]
233#![allow(clippy::bind_instead_of_map)]
234#![allow(clippy::redundant_closure_for_method_calls)]
235#![allow(clippy::ref_option)]
236#![allow(clippy::new_without_default)]
237#![allow(clippy::erasing_op)]
238#![allow(clippy::identity_op)]
239#![allow(clippy::op_ref)]
240#![allow(clippy::manual_flatten)]
241#![allow(clippy::while_let_loop)]
242#![allow(clippy::from_over_into)]
243#![allow(clippy::match_like_matches_macro)]
244#![allow(clippy::collapsible_match)]
245#![allow(clippy::inefficient_to_string)]
246#![allow(clippy::items_after_statements)]
247#![allow(clippy::enum_glob_use)]
248#![allow(clippy::cloned_ref_to_slice_refs)]
249#![allow(clippy::verbose_bit_mask)]
250#![allow(clippy::let_and_return)]
251#![allow(clippy::if_same_then_else)]
252#![allow(clippy::comparison_chain)]
253#![allow(clippy::self_only_used_in_recursion)]
254#![allow(clippy::unnecessary_map_or)]
255
256pub mod av1_obu;
257pub mod avif;
258pub mod bitrate_model;
259pub mod bitstream_filter;
260pub mod bitstream_writer;
261pub mod codec_caps;
262pub mod codec_probe;
263pub mod codec_profile;
264pub mod codec_registry;
265pub mod color_range;
266/// ISOBMFF (ISO Base Media File Format) box utilities for container formats.
267pub mod container;
268pub mod entropy_coding;
269pub mod entropy_tables;
270pub mod error;
271pub mod error_concealment;
272pub mod features;
273pub mod flac_codec;
274pub mod frame;
275pub mod frame_queue;
276pub mod frame_types;
277pub mod gop_structure;
278pub mod hdr;
279pub mod intra;
280pub mod motion;
281pub mod multipass;
282pub mod multipass_quality;
283pub mod nal_unit;
284pub mod packet_builder;
285pub mod packet_queue;
286pub mod packet_splitter;
287pub mod picture_timing;
288pub mod profile_level;
289pub mod psycho_visual;
290pub mod quality_metrics;
291pub mod rate_control;
292pub mod rate_control_accuracy;
293pub mod reconstruct;
294pub mod reference_frames;
295pub mod scene_change_idr;
296pub mod sei_nal;
297pub mod simd;
298pub mod slice_header;
299pub mod stats;
300pub mod stream_info;
301pub mod tile;
302pub mod tile_encoder;
303pub mod traits;
304pub mod vbr_twopass;
305
306// Audio support
307pub mod audio;
308
309// Standalone SILK frame decoding types
310pub mod silk;
311
312// Standalone CELT frame decoding types
313pub mod celt;
314
315// PNG codec
316pub mod png;
317
318// APNG (Animated PNG) top-level encoder/decoder
319pub mod apng;
320
321// Vorbis audio codec (encoder + decoder)
322pub mod vorbis;
323pub use vorbis::{
324    SimpleVorbisEncoder, VorbisConfig, VorbisEncConfig, VorbisEncoder, VorbisPacket, VorbisQuality,
325};
326
327// FLAC lossless audio codec (encoder + decoder)
328pub mod flac;
329
330// PCM raw audio codec (encoder + decoder)
331pub mod pcm;
332
333// GIF codec
334pub mod gif;
335
336// WebP codec
337pub mod webp;
338
339// JPEG-XL codec
340#[cfg(feature = "jpegxl")]
341pub mod jpegxl;
342
343// Image I/O support
344#[cfg(feature = "image-io")]
345pub mod image;
346
347#[cfg(feature = "av1")]
348pub mod av1;
349
350#[cfg(feature = "vp9")]
351pub mod vp9;
352
353#[cfg(feature = "vp8")]
354pub mod vp8;
355
356#[cfg(feature = "theora")]
357pub mod theora;
358
359#[cfg(feature = "h263")]
360pub mod h263;
361
362#[cfg(feature = "opus")]
363pub mod opus;
364
365#[cfg(feature = "ffv1")]
366pub mod ffv1;
367
368#[cfg(feature = "mjpeg")]
369pub mod mjpeg;
370
371#[cfg(feature = "apv")]
372pub mod apv;
373
374// Re-exports
375pub use audio::{AudioFrame, ChannelLayout, SampleFormat};
376pub use error::{CodecError, CodecResult};
377pub use frame::{
378    ColorInfo, ColorPrimaries, FrameType, MatrixCoefficients, Plane, TransferCharacteristics,
379    VideoFrame,
380};
381pub use multipass::{
382    allocation::AllocationStrategy, Allocator, Analyzer, Buffer, BufferConfig, ComplexityStats,
383    EncoderConfig as MultiPassConfig, EncoderError, EncodingResult, FrameAllocation,
384    FrameComplexity, LookaheadAnalysis, LookaheadFrame, MultiPassEncoder, PassStats, PassType,
385    SceneChangeDetector, Stats, VbvStatistics,
386};
387pub use rate_control::{
388    AdaptiveAllocation, AdaptiveQuantization, AllocationResult, AnalysisResult, AqMode,
389    BitrateAllocator, BlockQpMap, BufferModel, CbrController, ComplexityEstimator,
390    ContentAdaptiveAllocator, ContentAnalyzer, ContentMetrics, ContentType, CqpController,
391    CrfController, FrameStats, GopAllocationStatus, GopStats, Lookahead, QpResult, QpSelector,
392    QpStrategy, RateControlMode, RateController, RcConfig, RcOutput,
393    SceneChangeDetector as RcSceneChangeDetector, SceneChangeThreshold, SceneContentType,
394    TextureMetrics, VbrController,
395};
396pub use reconstruct::{
397    BufferPool, CdefApplicator, CdefBlockConfig, CdefFilterResult, ChromaSubsampling,
398    DeblockFilter, DeblockParams, DecoderPipeline, EdgeFilter, FilmGrainParams,
399    FilmGrainSynthesizer, FilterDirection, FilterStrength, FrameBuffer, GrainBlock,
400    LoopFilterPipeline, OutputConfig, OutputFormat, OutputFormatter, PipelineConfig, PipelineStage,
401    PlaneBuffer, PlaneType, ReconstructResult, ReconstructionError, ResidualBuffer, ResidualPlane,
402    StageResult, SuperResConfig, SuperResUpscaler, UpscaleMethod,
403};
404pub use tile::{
405    assemble_tiles, decode_tile_stream, decode_tiles_parallel, HeaderedTileEncodeOp,
406    RawLumaEncodeOp, TileConfig, TileCoord, TileDecodeOp, TileDecodeResult, TileEncodeOp,
407    TileEncodeStats, TileEncoder, TileResult,
408};
409
410pub use pcm::{ByteOrder, PcmConfig, PcmDecoder, PcmEncoder, PcmFormat};
411pub use traits::{
412    BitrateMode, DecoderConfig, EncodedPacket, EncoderConfig, EncoderPreset, VideoDecoder,
413    VideoEncoder,
414};
415
416#[cfg(feature = "av1")]
417pub use av1::{Av1Decoder, Av1Encoder};
418
419#[cfg(feature = "vp9")]
420pub use vp9::{
421    SimpleVp9Encoder, Vp9Decoder, Vp9EncConfig, Vp9Encoder, Vp9EncoderConfig, Vp9Packet, Vp9Profile,
422};
423
424#[cfg(feature = "vp8")]
425pub use vp8::{
426    SimpleVp8Encoder, Vp8Decoder, Vp8EncConfig, Vp8Encoder, Vp8EncoderConfig, Vp8Packet,
427};
428
429#[cfg(feature = "theora")]
430pub use theora::{TheoraConfig, TheoraDecoder, TheoraEncoder};
431
432#[cfg(feature = "h263")]
433pub use h263::{H263Decoder, H263Encoder, PictureFormat};
434
435#[cfg(feature = "opus")]
436pub use opus::{OpusDecoder, OpusEncoder, OpusEncoderConfig};
437
438#[cfg(feature = "ffv1")]
439pub use ffv1::{Ffv1Decoder, Ffv1Encoder};
440
441#[cfg(feature = "mjpeg")]
442pub use mjpeg::{MjpegConfig, MjpegDecoder, MjpegEncoder, MjpegError};
443
444#[cfg(feature = "apv")]
445pub use apv::{ApvConfig, ApvDecoder, ApvEncoder, ApvError};
446
447#[cfg(feature = "image-io")]
448pub use image::{
449    convert_rgb_to_yuv420p, convert_yuv420p_to_rgb, rgb_to_yuv, yuv_to_rgb,
450    EncoderConfig as ImageEncoderConfig, ImageDecoder, ImageEncoder, ImageFormat,
451};
452
453pub use png::{
454    batch_encode as batch_encode_png, best_encoder, decode as decode_png,
455    encode_grayscale as encode_png_grayscale, encode_rgb as encode_png_rgb,
456    encode_rgba as encode_png_rgba, encoder_from_profile, fast_encoder, get_info as png_info,
457    is_png, optimize as optimize_png, transcode as transcode_png, validate as validate_png,
458    Chromaticity, ColorType as PngColorType, CompressionLevel as PngCompressionLevel,
459    DecodedImage as PngImage, EncoderBuilder as PngEncoderBuilder,
460    EncoderConfig as PngEncoderConfig, EncodingProfile, EncodingStats, FilterStrategy, FilterType,
461    ImageHeader as PngHeader, PaletteEntry, PaletteOptimizer, ParallelPngEncoder,
462    PhysicalDimensions, PngDecoder, PngDecoderExtended, PngEncoder, PngEncoderExtended, PngInfo,
463    PngMetadata, SignificantBits, TextChunk,
464};
465
466pub use gif::{
467    is_gif, DisposalMethod, DitheringMethod, GifDecoder, GifEncoder, GifEncoderConfig, GifFrame,
468    GifFrameConfig, GraphicsControlExtension, ImageDescriptor, LogicalScreenDescriptor,
469    QuantizationMethod,
470};
471
472#[cfg(feature = "jpegxl")]
473pub use jpegxl::{
474    AnsDecoder, AnsDistribution, AnsEncoder, BitReader as JxlBitReader, BitWriter as JxlBitWriter,
475    DecodedImage as JxlImage, JxlColorSpace, JxlConfig, JxlDecoder, JxlEncoder, JxlFrameEncoding,
476    JxlHeader, JxlStreamingDecoder, ModularDecoder, ModularEncoder,
477};
478
479pub use avif::{AvifConfig, AvifDecoder, AvifEncoder, AvifImage, AvifProbeResult, YuvFormat};