Skip to main content

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};