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