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