av_drift_detector/lib.rs
1//! Generate machine-readable A/V sync test videos and measure audio/video drift.
2//!
3//! Every video frame of a generated file carries its frame number three ways:
4//! a 48-bit barcode strip (with a CRC), a QR code, and a seven-segment
5//! timecode for people. Every frame's worth of audio carries the same number
6//! (mod 65536) as four simultaneous tones. Analysis decodes both sides of a
7//! captured or re-encoded copy and reports, frame by frame, how far the audio
8//! has drifted from the video. Precision is a small fraction of a millisecond.
9//!
10//! ```no_run
11//! use av_drift_detector::{AnalyzeOptions, GenerateOptions, analyze, generate};
12//!
13//! let fps = "29.97".parse()?;
14//! generate(&GenerateOptions::new("sync.mp4", fps, 10.0), |_| {})?;
15//! // ... play `sync.mp4` through the system under test and record it ...
16//! let report = analyze("captured.mp4".as_ref(), &AnalyzeOptions::default())?;
17//! println!("{}", report.summary_text());
18//! # Ok::<(), av_drift_detector::Error>(())
19//! ```
20//!
21//! The pattern rendering ([`pattern`]), audio code ([`audio`]) and the analysis
22//! itself ([`analyze::analyze_scan`]) are plain Rust. Only file input and output
23//! need FFmpeg: linked, with the `ffmpeg` feature (on by default), or run as the
24//! `ffmpeg` and `ffprobe` programs, with the `ffmpeg-cli` feature. Without
25//! either, the crate builds for targets such as Android and WebAssembly.
26
27pub mod analyze;
28pub mod audio;
29pub mod camera;
30mod error;
31pub mod media;
32pub mod pattern;
33pub mod report;
34pub mod timing;
35
36pub use analyze::AnalyzeOptions;
37#[cfg(feature = "ffmpeg")]
38pub use analyze::analyze;
39pub use error::{Error, Result};
40#[cfg(feature = "ffmpeg")]
41pub use media::encode::generate;
42pub use media::{AudioCodec, GenerateOptions, Progress, VideoCodec};
43pub use report::{CaptureMode, DriftReport, DriftSummary, FrameDrift, FrameStatus};
44pub use timing::{FrameRate, parse_time};
45
46// Compile the examples in the README and the library guide as doctests.
47#[cfg(all(doctest, feature = "ffmpeg"))]
48#[doc = include_str!("../../../README.md")]
49struct ReadmeDoctests;
50
51#[cfg(all(doctest, feature = "ffmpeg"))]
52#[doc = include_str!("../../../docs/library.md")]
53struct LibraryGuideDoctests;