1use crate::error::{Error, Result};
6use broadcast_common::len::{need_in, need_out};
7use broadcast_common::{Parse, Serialize};
8
9pub const SEGMENT_TYPE: u8 = 0x16;
11pub const HEADER_LEN: usize = 6;
13pub const FIXED_LEN: usize = 4;
15pub const ENTRY_8BIT_LEN: usize = 4;
17pub const ENTRY_10BIT_LEN: usize = 5;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub struct ClutParameters {
24 pub entry_max_number: u8,
26 pub colour_component_type: u8,
28 pub output_bit_depth: u8,
30 pub reserved: u8,
32 pub dynamic_range_and_colour_gamut: u8,
34}
35
36impl ClutParameters {
37 pub fn parse(bytes: &[u8]) -> Result<Self> {
39 need_in(bytes, 2, "CLUT_parameters")?;
40 let entry_max_number = bytes[0] >> 6;
41 let colour_component_type = (bytes[0] >> 4) & 0x03;
42 let output_bit_depth = (bytes[0] >> 1) & 0x07;
43 let reserved = bytes[0] & 0x01;
44 Ok(ClutParameters {
45 entry_max_number,
46 colour_component_type,
47 output_bit_depth,
48 reserved,
49 dynamic_range_and_colour_gamut: bytes[1],
50 })
51 }
52
53 fn serialize_into(&self, buf: &mut [u8]) -> Result<()> {
54 buf[0] =
55 (broadcast_common::len::fit_mask(self.entry_max_number, 0x03, "entry_max_number")?
56 << 6)
57 | (broadcast_common::len::fit_mask(
58 self.colour_component_type,
59 0x03,
60 "colour_component_type",
61 )? << 4)
62 | (broadcast_common::len::fit_mask(
63 self.output_bit_depth,
64 0x07,
65 "output_bit_depth",
66 )? << 1)
67 | broadcast_common::len::fit_mask(self.reserved, 0x01, "reserved")?;
68 buf[1] = self.dynamic_range_and_colour_gamut;
69 Ok(())
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize))]
76#[repr(u8)]
77#[non_exhaustive]
78pub enum OutputBitDepth {
79 Bit8 = 0x00,
81 Bit10 = 0x01,
83 Reserved(u8),
85}
86
87impl OutputBitDepth {
88 #[must_use]
90 pub fn name(&self) -> &'static str {
91 match self {
92 Self::Bit8 => "8-bit",
93 Self::Bit10 => "10-bit",
94 Self::Reserved(_) => "reserved",
95 }
96 }
97}
98
99broadcast_common::impl_spec_display!(OutputBitDepth, Reserved);
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize))]
104#[repr(u8)]
105#[non_exhaustive]
106pub enum DynamicRangeColourGamut {
107 SdrBt709 = 0x00,
109 SdrBt2020 = 0x01,
111 HdrBt2100Pq = 0x02,
113 HdrBt2100Hlg = 0x03,
115 Reserved(u8),
117}
118
119impl DynamicRangeColourGamut {
120 #[must_use]
122 pub fn name(&self) -> &'static str {
123 match self {
124 Self::SdrBt709 => "SDR_BT.709",
125 Self::SdrBt2020 => "SDR_BT.2020",
126 Self::HdrBt2100Pq => "HDR_BT.2100_PQ",
127 Self::HdrBt2100Hlg => "HDR_BT.2100_HLG",
128 Self::Reserved(_) => "reserved",
129 }
130 }
131}
132
133broadcast_common::impl_spec_display!(DynamicRangeColourGamut, Reserved);
134
135#[derive(Debug, Clone, PartialEq, Eq)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize))]
138pub struct AlternativeClutEntry {
139 pub luma_value: u16,
141 pub chroma1_value: u16,
143 pub chroma2_value: u16,
145 pub t_value: u16,
147}
148
149impl AlternativeClutEntry {
150 fn serialized_len(output_bit_depth: u8) -> usize {
151 match output_bit_depth {
152 0 => ENTRY_8BIT_LEN,
153 1 => ENTRY_10BIT_LEN,
154 _ => ENTRY_8BIT_LEN,
155 }
156 }
157
158 fn serialize_into(&self, buf: &mut [u8], output_bit_depth: u8) -> Result<()> {
159 if output_bit_depth == 1 {
160 let v0 = broadcast_common::len::fit_mask(self.luma_value, 0x03FF, "luma_value")?;
162 let v1 = broadcast_common::len::fit_mask(self.chroma1_value, 0x03FF, "chroma1_value")?;
163 let v2 = broadcast_common::len::fit_mask(self.chroma2_value, 0x03FF, "chroma2_value")?;
164 let v3 = broadcast_common::len::fit_mask(self.t_value, 0x03FF, "t_value")?;
165 buf[0] = (v0 >> 2) as u8;
166 buf[1] = (((v0 & 0x03) << 6) | ((v1 & 0x3FC) >> 4)) as u8;
167 buf[2] = (((v1 & 0x0F) << 4) | ((v2 & 0x3F0) >> 6)) as u8;
168 buf[3] = (((v2 & 0x3F) << 2) | ((v3 & 0x300) >> 8)) as u8;
169 buf[4] = (v3 & 0xFF) as u8;
170 } else {
171 buf[0] = broadcast_common::len::fit_mask(self.luma_value, 0x00FF, "luma_value")? as u8;
173 buf[1] =
174 broadcast_common::len::fit_mask(self.chroma1_value, 0x00FF, "chroma1_value")? as u8;
175 buf[2] =
176 broadcast_common::len::fit_mask(self.chroma2_value, 0x00FF, "chroma2_value")? as u8;
177 buf[3] = broadcast_common::len::fit_mask(self.t_value, 0x00FF, "t_value")? as u8;
178 }
179 Ok(())
180 }
181
182 fn parse(bytes: &[u8], output_bit_depth: u8) -> Result<Self> {
183 let entry_len = Self::serialized_len(output_bit_depth);
184 need_in(bytes, entry_len, "alternative_CLUT_entry")?;
185 let (luma, c1, c2, t) = if output_bit_depth == 1 {
186 let v0 = ((bytes[0] as u16) << 2) | ((bytes[1] as u16) >> 6);
187 let v1 = (((bytes[1] as u16) & 0x3F) << 4) | ((bytes[2] as u16) >> 4);
188 let v2 = (((bytes[2] as u16) & 0x0F) << 6) | ((bytes[3] as u16) >> 2);
189 let v3 = (((bytes[3] as u16) & 0x03) << 8) | (bytes[4] as u16);
190 (v0, v1, v2, v3)
191 } else {
192 (
193 bytes[0] as u16,
194 bytes[1] as u16,
195 bytes[2] as u16,
196 bytes[3] as u16,
197 )
198 };
199 Ok(AlternativeClutEntry {
200 luma_value: luma,
201 chroma1_value: c1,
202 chroma2_value: c2,
203 t_value: t,
204 })
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
210#[cfg_attr(feature = "serde", derive(serde::Serialize))]
211pub struct AlternativeClutSegment {
212 pub page_id: u16,
214 pub clut_id: u8,
216 pub clut_version_number: u8,
218 pub reserved: u8,
220 pub clut_parameters: ClutParameters,
222 pub entries: alloc::vec::Vec<AlternativeClutEntry>,
224}
225
226impl<'a> Parse<'a> for AlternativeClutSegment {
227 type Error = Error;
228
229 fn parse(bytes: &'a [u8]) -> Result<Self> {
230 need_in(bytes, HEADER_LEN + FIXED_LEN, "alternative_CLUT_segment")?;
231 if bytes[0] != crate::pes_data_field::SYNC_BYTE {
232 return Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
233 what: "sync_byte",
234 expected: u32::from(crate::pes_data_field::SYNC_BYTE),
235 found: u32::from(bytes[0]),
236 }));
237 }
238 if bytes[1] != SEGMENT_TYPE {
239 return Err(Error::UnknownSegmentType(bytes[1]));
240 }
241 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
242 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
243 let total = HEADER_LEN + segment_length;
244 need_in(bytes, total, "alternative_CLUT_segment data")?;
245 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
246 need_in(body, FIXED_LEN, "alternative_CLUT_segment body")?;
247 let clut_id = body[0];
248 let clut_version_number = body[1] >> 4;
249 let reserved = body[1] & 0x0F;
250 let clut_parameters = ClutParameters::parse(&body[2..4])?;
251 let output_bit_depth = clut_parameters.output_bit_depth;
252 let entry_len = AlternativeClutEntry::serialized_len(output_bit_depth);
253
254 let entry_data = &body[FIXED_LEN..];
255 if !entry_data.len().is_multiple_of(entry_len) {
260 return Err(Error::TrailingEntryBytes {
261 what: "alternative_CLUT_segment entries",
262 entry_len,
263 extra: entry_data.len() % entry_len,
264 });
265 }
266 let num_entries = entry_data.len() / entry_len;
267 let mut entries = alloc::vec::Vec::with_capacity(num_entries);
268 for i in 0..num_entries {
269 let off = i * entry_len;
270 entries.push(AlternativeClutEntry::parse(
271 &entry_data[off..off + entry_len],
272 output_bit_depth,
273 )?);
274 }
275
276 Ok(AlternativeClutSegment {
277 page_id,
278 clut_id,
279 clut_version_number,
280 reserved,
281 clut_parameters,
282 entries,
283 })
284 }
285}
286
287impl Serialize for AlternativeClutSegment {
288 type Error = Error;
289
290 fn serialized_len(&self) -> usize {
291 let entry_len = AlternativeClutEntry::serialized_len(self.clut_parameters.output_bit_depth);
292 HEADER_LEN + FIXED_LEN + self.entries.len() * entry_len
293 }
294
295 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
296 let len = self.serialized_len();
297 need_out(buf, len)?;
298 buf[0] = 0x0F;
299 buf[1] = SEGMENT_TYPE;
300 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
301 let seg_len = crate::segments::check_segment_length(len - HEADER_LEN)?;
302 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
303
304 buf[6] = self.clut_id;
305 buf[7] = (broadcast_common::len::fit_mask(
306 self.clut_version_number,
307 0x0F,
308 "clut_version_number",
309 )? << 4)
310 | broadcast_common::len::fit_mask(self.reserved, 0x0F, "reserved")?;
311 self.clut_parameters.serialize_into(&mut buf[8..10])?;
312
313 let entry_len = AlternativeClutEntry::serialized_len(self.clut_parameters.output_bit_depth);
314 for (i, entry) in self.entries.iter().enumerate() {
315 let off = HEADER_LEN + FIXED_LEN + i * entry_len;
316 entry.serialize_into(
317 &mut buf[off..off + entry_len],
318 self.clut_parameters.output_bit_depth,
319 )?;
320 }
321 Ok(len)
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328 use broadcast_common::{Parse, Serialize};
329
330 #[test]
331 fn round_trip_8bit() {
332 let bytes = [
333 0x0F, 0x16, 0x00, 0x01, 0x00, 0x08, 0x03, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
334 ];
335 let seg = AlternativeClutSegment::parse(&bytes).unwrap();
336 assert_eq!(seg.clut_id, 3);
337 assert_eq!(seg.entries.len(), 1);
338 assert_eq!(seg.entries[0].luma_value, 128);
339 let out = seg.try_to_bytes().unwrap();
340 assert_eq!(out, bytes);
341
342 let mut seg2 = seg.clone();
344 seg2.entries[0].luma_value = 200;
345 let out2 = seg2.try_to_bytes().unwrap();
346 assert_ne!(out2, bytes);
347 let reparse = AlternativeClutSegment::parse(&out2).unwrap();
348 assert_eq!(reparse.entries[0].luma_value, 200);
349 }
350
351 #[test]
352 fn round_trip_10bit() {
353 let bytes = [
354 0x0F, 0x16, 0x00, 0x01, 0x00, 0x09, 0x03, 0x10, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00,
355 0x00,
356 ];
357 let seg = AlternativeClutSegment::parse(&bytes).unwrap();
358 assert_eq!(seg.clut_parameters.output_bit_depth, 1);
359 assert_eq!(seg.entries.len(), 1);
360 let out = seg.try_to_bytes().unwrap();
361 assert_eq!(out, bytes);
362
363 let mut seg2 = seg.clone();
365 seg2.clut_id = 7;
366 let out2 = seg2.try_to_bytes().unwrap();
367 assert_ne!(out2, bytes);
368 let reparse = AlternativeClutSegment::parse(&out2).unwrap();
369 assert_eq!(reparse.clut_id, 7);
370 }
371
372 #[test]
374 fn rejects_bad_sync_byte() {
375 let bytes = [
376 0x00, 0x16, 0x00, 0x01, 0x00, 0x08, 0x03, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
377 ];
378 assert!(matches!(
379 AlternativeClutSegment::parse(&bytes),
380 Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
381 found: 0x00,
382 ..
383 }))
384 ));
385 }
386
387 #[test]
391 fn rejects_trailing_partial_entry() {
392 let bytes = [
393 0x0F, 0x16, 0x00, 0x01, 0x00, 0x0A, 0x03, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
394 0xAA, 0xBB,
395 ];
396 assert!(matches!(
397 AlternativeClutSegment::parse(&bytes),
398 Err(Error::TrailingEntryBytes {
399 entry_len: 4,
400 extra: 2,
401 ..
402 })
403 ));
404 }
405}