timed_metadata/webvtt/mod.rs
1//! CEA-608/708 -> WebVTT cue conversion (issue #568).
2//!
3//! Cite: `specs/rules/webvtt-rules.md` (curated W3C WebVTT §4 cue syntax +
4//! RFC 8216 §3.5 `X-TIMESTAMP-MAP`), and CTA-608-E / CTA-708-E for the
5//! caption semantics (decode owned entirely by the `cc-data` crate, feature
6//! `cc-data`).
7//!
8//! This module is split in two independent halves:
9//!
10//! - [`Cue`] + [`write_document`] / [`write_segment`] (always available):
11//! pure WebVTT serialization from an already-extracted cue list. No
12//! dependency on `cc-data`.
13//! - [`Cea608CueExtractor`] / [`Cea708CueExtractor`] (feature `cc-data`):
14//! turn a decoded CEA-608 CC1 channel / CEA-708 service into a [`Cue`]
15//! sequence by feeding one access unit's `cc_data()` triplets at a time,
16//! tagged with that access unit's 33-bit PTS.
17//! - [`TeletextCueExtractor`] (feature `teletext`): turns an EBU Teletext
18//! (ETSI EN 300 706) subtitle page into a [`Cue`] sequence by feeding one
19//! access unit's [`dvb_vbi::TeletextDataField`]s at a time. Unlike the CEA
20//! extractors, the protocol decode (Hamming-8/4 FEC, character sets, page
21//! composition) is NOT owned by the carriage crate (`dvb-vbi` is
22//! deliberately carriage-only — see its module docs) — it lives in
23//! [`teletext`] instead. See that module's docs for the full design.
24//!
25//! # Cue-boundary detection (the key design decision)
26//!
27//! `cc-data`'s decoders expose only *state* (`channel_text()` /
28//! `service_text()`), not a "just committed" event. This module derives cue
29//! boundaries by **diffing that displayed text before and after each fed
30//! frame**:
31//!
32//! - **Pop-on**: `channel_text()`/`screen()` reflect only the *displayed*
33//! memory; while composing a caption (RCL -> PAC -> characters) writes go
34//! to the *non-displayed* buffer and produce no diff. The buffers swap only
35//! on EOC, so a diff-detected boundary is *exactly* the EOC commit event,
36//! and the next diff (typically an EDM erase, or the next EOC) is exactly
37//! the "next erase/replace" the spec notes describe.
38//! - **Roll-up** / **paint-on**: characters are written directly to the
39//! displayed buffer, so a diff can fire on every visible change (finer
40//! grained than "one cue per committed row"). In practice a roll-up row is
41//! usually written in one batch before the next control code, so real
42//! streams still produce one boundary per row; pathologically slow
43//! per-character delivery would fragment further. Documented as a known
44//! simplification (see `webvtt-rules.md`'s 608/708 mapping notes).
45//!
46//! # Documented losses (round-trip is NOT claimed; conversion is lossy)
47//!
48//! - **Placement**: no `line`/`position`/`align` cue settings are emitted —
49//! `webvtt-rules.md` allows omitting them ("otherwise omit (player
50//! defaults)"); row/column/window-anchor information from 608 PACs / 708
51//! window geometry is dropped.
52//! - **Styling**: italics/underline/colour (608 mid-row/PAC attributes, 708
53//! pen attributes) are not carried into the payload as `<i>`/`<u>`/`<c>`
54//! tags; only plain decoded text is emitted.
55//! - **708 scope**: only a single service's window text (`service_text`) is
56//! read; multi-window overlap ordering beyond priority, and non-service-1
57//! services, are the caller's choice of `service_number` but are not
58//! auto-merged across services.
59//! - **Roll-up granularity**: see cue-boundary detection above.
60
61mod cue;
62#[cfg(feature = "teletext")]
63pub mod teletext;
64mod writer;
65
66pub use cue::Cue;
67#[cfg(feature = "teletext")]
68pub use cue::TeletextCueExtractor;
69#[cfg(feature = "cc-data")]
70pub use cue::{Cea608CueExtractor, Cea708CueExtractor};
71pub use writer::{cue_block, escape_payload, format_timestamp, write_document, write_segment};