Expand description
DVB closed-caption carriage — cc_data() per ETSI TS 101 154 §B.5, Table B.9.
Parses the closed-caption carriage structure carried in MPEG-2 / AVC / HEVC
picture user_data (the DVB-native, normative form of the ATSC/CEA cc_data()).
Exposes the typed caption triplets (cc_valid, cc_type, cc_data_1/2) and a
CEA-608 vs CEA-708 split by cc_type.
Feed it the cc_data() bytes (the caller extracts them from the picture
user_data / SEI). Depends only on dvb-common, #![no_std] (+ alloc).
§Caption decode (decode feature)
With the default decode feature, this crate also interprets the demuxed
caption byte pairs — the decode module’s Cea608Decoder
(line-21 pop-on / roll-up / paint-on, PACs, mid-row codes, the standard /
special / extended character sets, channels CC1–CC4) and
Cea708Decoder (DTVCC packet / service-block
reassembly + the C0/C1/G0/G1/G2/G3 window / pen command interpreter, services
1–6) — and exposes the decoded caption screen / window text. Grounded in
ANSI/CTA-608-E, ANSI/CTA-708-E and 47 CFR §79.102 (see dvb-cc/docs/decode/).
§Examples
Runnable examples ship with this crate (cargo run -p dvb-cc --example <name>).
§parse_cc_data
//! Basic: parse a `cc_data()` byte sequence and split CEA-608 / CEA-708 triplets.
//!
//! Run with: `cargo run -p dvb-cc --example parse_cc_data`
use dvb_cc::CcData;
use dvb_common::Parse;
fn main() {
// cc_data: process_cc_data_flag=1, cc_count=2; one DTVCC-start triplet + one
// 608-field-1 triplet; trailing 0xFF marker.
#[rustfmt::skip]
let bytes = [
0b1100_0010, // reserved=1, process=1, zero=0, cc_count=2
0xFF, // reserved
0xFF, 0xC1, 0x02, // one_bit+rsvd(F8) | valid=1 | type=3 (708 start); data C1 02
0xFC, 0x94, 0x2C, // F8 | valid=1 | type=0 (608 field1); data 94 2C
0xFF, // marker
];
let cc = CcData::parse(&bytes).expect("valid cc_data");
println!("process_cc_data_flag : {}", cc.process_cc_data_flag);
println!("triplets : {}", cc.triplets.len());
for t in &cc.triplets {
println!(
" {:<16} valid={} data={:02X} {:02X}",
t.cc_type.name(),
t.cc_valid,
t.cc_data_1,
t.cc_data_2
);
}
println!("CEA-608 triplets : {}", cc.cea608().count());
println!("CEA-708 triplets : {}", cc.cea708().count());
}§build_cc_data
//! Advanced: build a `cc_data()` from typed triplets, serialize, and round-trip.
//!
//! Run with: `cargo run -p dvb-cc --example build_cc_data`
use dvb_cc::{CcData, CcTriplet, CcType};
use dvb_common::{Parse, Serialize};
fn main() {
let cc = CcData {
process_cc_data_flag: true,
triplets: vec![
CcTriplet {
cc_valid: true,
cc_type: CcType::Dtvcc708Start,
cc_data_1: 0xC1,
cc_data_2: 0x02,
},
CcTriplet {
cc_valid: true,
cc_type: CcType::Ntsc608Field1,
cc_data_1: 0x94,
cc_data_2: 0x2C,
},
],
};
let bytes = cc.to_bytes();
println!("serialized {} bytes: {:02X?}", bytes.len(), bytes);
let back = CcData::parse(&bytes).expect("round-trip parse");
assert_eq!(back, cc, "round-trip must be lossless");
println!(
"round-trip OK — {} triplets ({} 608, {} 708)",
back.triplets.len(),
back.cea608().count(),
back.cea708().count()
);
}§decode_cea608
//! Decode a CEA-608 (line-21) pop-on caption to on-screen text.
//!
//! Run with: `cargo run -p dvb-cc --example decode_cea608`
use dvb_cc::decode::{Cea608Channel, Cea608Decoder};
/// Add odd parity to a 7-bit value so the pairs look like real line-21 bytes.
fn par(v: u8) -> u8 {
if (v & 0x7F).count_ones() % 2 == 0 {
v | 0x80
} else {
v & 0x7F
}
}
fn main() {
let mut dec = Cea608Decoder::new();
// A pop-on caption on CC1 (field 1):
// RCL (14 20) — Resume Caption Loading (pop-on)
// PAC (14 70) — row 15, white, indent 0
// "HELLO"
// EOC (14 2F) — flip back buffer to the screen
let pairs: &[(u8, u8)] = &[
(0x14, 0x20),
(0x14, 0x70),
(b'H', b'E'),
(b'L', b'L'),
(b'O', 0x00),
(0x14, 0x2F),
];
for &(b1, b2) in pairs {
dec.push_pair(false, par(b1), par(b2));
}
println!("CC1 mode : {}", dec.mode(Cea608Channel::Cc1));
println!("CC1 text : {:?}", dec.channel_text(Cea608Channel::Cc1));
// A roll-up caption (2 rows) on CC1.
let mut dec2 = Cea608Decoder::new();
let roll: &[(u8, u8)] = &[
(0x14, 0x25), // RU2
(b'O', b'N'),
(b'E', 0x00),
(0x14, 0x2D), // CR
(b'T', b'W'),
(b'O', 0x00),
];
for &(b1, b2) in roll {
dec2.push_pair(false, par(b1), par(b2));
}
println!("\nRoll-up mode : {}", dec2.mode(Cea608Channel::Cc1));
println!("Roll-up text :\n{}", dec2.channel_text(Cea608Channel::Cc1));
}§decode_cea708
//! Decode a CEA-708 (DTVCC) caption packet to window text.
//!
//! Run with: `cargo run -p dvb-cc --example decode_cea708`
use dvb_cc::decode::Cea708Decoder;
fn main() {
let mut dec = Cea708Decoder::new();
// Build a Caption Channel Packet carrying one Service Block for service 1
// that: DefineWindow DF0 (visible, 2 rows × 16 cols), writes "HI THERE".
let block: &[u8] = &[
0x98, // DefineWindow DF0
0x20, // parm1: visible=YES, priority 0
0x00, // parm2: rp=0, anchor vertical 0
0x00, // parm3: anchor horizontal 0
0x01, // parm4: anchor point 0, row count = 1+1 = 2
0x0F, // parm5: column count = 15+1 = 16
0x00, // parm6: window/pen style 0 (auto)
b'H', b'I', b' ', b'T', b'H', b'E', b'R', b'E',
];
// Service Block header: service_number = 1, block_size = block.len().
let mut sb: Vec<u8> = Vec::new();
sb.push((1 << 5) | (block.len() as u8));
sb.extend_from_slice(block);
// CCP header: sequence_number 0, packet_size_code covers the data.
let size_code = (sb.len().div_ceil(2) + 1) as u8;
let mut ccp: Vec<u8> = Vec::new();
ccp.push(size_code & 0x3F);
ccp.extend_from_slice(&sb);
dec.push_packet(&ccp);
println!("Service 1 text : {:?}", dec.service_text(1));
if let Some(w) = dec.windows(1)[0].as_ref() {
println!("Window 0 state : {}", w.state);
println!("Window 0 rows : {}", w.row_count);
println!("Window 0 cols : {}", w.column_count);
println!("Window 0 text : {:?}", w.text());
}
}Modules§
- decode
decode - CEA-608 / CEA-708 caption decode — the meaning of the caption byte pairs
that the
crate::CcDatacarriage demuxes out ofcc_data().
Structs§
- CcData
cc_data()— the DVB closed-caption carriage structure (Table B.9).- CcTriplet
- One closed-caption construct (the per-
cc_countloop entry of Table B.9).
Enums§
- CcType
cc_type— the type of the caption data byte pair (TS 101 154 Table B.9 / CEA-708-E). 2-bit field.- Error
- A cc_data parse error.
Type Aliases§
- Result
- Result alias.