1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! SMPTE ST 291-1 — ancillary (ANC) data content.
//!
//! ST 291-1 defines the ANC data packet: the generic carrier for VANC/HANC
//! payloads (captions, AFD, timecode, audio metadata, …) multiplexed into a
//! professional video signal. This crate is about that **content**, not any
//! one carriage mechanism — ST 291-1 packets can be conveyed over more than
//! one transport, and this crate grows to cover each as it is added.
//!
//! ## Transports
//!
//! - **`ts`** (default) — SMPTE ST 2038:2021 carriage of ANC data packets in
//! an MPEG-2 Transport Stream. ST 2038 provides a transparent pipe so
//! ST 291-1 ANC data packets can be conveyed frame-accurately alongside the
//! video they belong to (ST 2038 §1); it is **not** for audio carriage and
//! **not** for EDH packets (Introduction). This crate implements the two
//! wire structures defined in ST 2038 §4:
//! - [`AncDataDescriptor`] — the `anc_data_descriptor` (tag `0xC4`) used in
//! the PMT ES loop, plus the `"VANC"` `registration_descriptor`
//! [`format_identifier`](VANC_FORMAT_IDENTIFIER) `0x56414E43` and the
//! [`ANC_STREAM_TYPE`] `0x06` (§4.1, Table 1).
//! - [`AncDataPacket`] — the ANC data PES packet (`stream_id == 0xBD`, PTS,
//! `PES_header_data_length == 0x05`) carrying a list of bit-packed
//! [`AncPacket`] records + trailing `0xFF` stuffing (§4.2, Table 2).
//! - **`rtp`** — RFC 8331 / ST 2110-40 carriage of ANC data packets over RTP
//! (issue #648). [`AncRtpPayload`] is the §2.1 payload (`Extended Sequence
//! Number`/`Length`/`ANC_Count`/[`FieldSense`]/`reserved` + a list of
//! [`RtpAncPacket`]s), riding on an `rtp_packet::RtpPacket`'s payload (the
//! RTP fixed header, RFC 3550, is the `rtp-packet` crate's responsibility).
//! See `docs/anc_rtp_8331.md`.
//!
//! The per-ANC-packet content — `DID`/`SDID`/`data_count`/`user_data_word`/
//! `checksum_word` — is a **contiguous MSB-first 10-bit bit stream**, walked
//! with [`broadcast_common::bits`], and is byte-for-byte **identical across
//! both transports**: it lives in the always-compiled [`AncContent`] type
//! (gated behind neither `ts` nor `rtp`), wrapped by each transport's own
//! placement fields ([`AncPacket`]'s three for ST 2038, [`RtpAncPacket`]'s
//! five for RFC 8331). Per §4.2.1/§2.1 the `user_data_word` loop counter uses
//! only the **low 8 bits** of `data_count`; the full 10-bit values are stored
//! verbatim and ST 291-1 parity/checksum is **not** validated (deferred to
//! ST 291-1, which is not vendored — see `docs/anc_packet_291.md`).
//!
//! Depends only on `broadcast-common` (plus `rtp-packet`, optionally, for the
//! `rtp` feature) and is `#![no_std]` (+ `alloc`). The ST 2038 PES header is
//! parsed inline (every field is fixed by ST 2038 Table 2, so the dedicated
//! `mpeg-pes` parser adds a dependency without simplifying the bit-packed
//! payload walk).
//!
//! # Examples
//!
//! Build an ANC PES packet from typed fields and round-trip it:
//!
//! ```
//! # #[cfg(feature = "ts")]
//! # fn main() {
//! use st291::{AncDataPacket, AncPacket};
//!
//! let pkt = AncDataPacket {
//! pes_priority: false,
//! copyright: false,
//! original_or_copy: false,
//! pts: 90_000,
//! anc_packets: vec![AncPacket {
//! c_not_y_channel_flag: false,
//! line_number: 9,
//! horizontal_offset: 0,
//! did: 0x161,
//! sdid: 0x101,
//! data_count: 0x002,
//! user_data_words: vec![0x2CF, 0x101],
//! checksum: 0x233,
//! }],
//! stuffing_bytes: 0,
//! };
//! let bytes = {
//! let mut b = vec![0u8; pkt.serialized_len()];
//! pkt.serialize_into(&mut b).unwrap();
//! b
//! };
//! assert_eq!(AncDataPacket::parse(&bytes).unwrap(), pkt);
//! # }
//! # #[cfg(not(feature = "ts"))]
//! # fn main() {}
//! ```
//!
//! Build an ANC-over-RTP payload from typed fields and round-trip it:
//!
//! ```
//! # #[cfg(feature = "rtp")]
//! # fn main() {
//! use broadcast_common::{Parse, Serialize};
//! use st291::{AncContent, AncRtpPayload, FieldSense, RtpAncPacket};
//!
//! let payload = AncRtpPayload {
//! extended_sequence_number: 0,
//! field_sense: FieldSense::ProgressiveOrUnspecified,
//! anc_packets: vec![RtpAncPacket {
//! c: false,
//! line_number: 9,
//! horizontal_offset: 0,
//! s: false,
//! stream_num: 0,
//! content: AncContent {
//! did: 0x161,
//! sdid: 0x101,
//! data_count: 0x002,
//! user_data_words: vec![0x2CF, 0x101],
//! checksum: 0x233,
//! },
//! }],
//! };
//! let bytes = {
//! let mut b = vec![0u8; payload.serialized_len()];
//! payload.serialize_into(&mut b).unwrap();
//! b
//! };
//! assert_eq!(AncRtpPayload::parse(&bytes).unwrap(), payload);
//! # }
//! # #[cfg(not(feature = "rtp"))]
//! # fn main() {}
//! ```
// Runnable examples, embedded so they render on docs.rs and stay in sync with
// the actual `examples/*.rs` files (shown, not compiled).
extern crate alloc;
pub use ;
pub use AncContent;
pub use ;
pub use ;
pub use ;