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]) {
59 buf[0] = (self.entry_max_number << 6)
60 | (self.colour_component_type << 4)
61 | (self.output_bit_depth << 1)
62 | (self.reserved & 0x01);
63 buf[1] = self.dynamic_range_and_colour_gamut;
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize))]
70#[repr(u8)]
71#[non_exhaustive]
72pub enum OutputBitDepth {
73 Bit8 = 0x00,
75 Bit10 = 0x01,
77 Reserved(u8),
79}
80
81impl OutputBitDepth {
82 #[must_use]
84 pub fn name(&self) -> &'static str {
85 match self {
86 Self::Bit8 => "8-bit",
87 Self::Bit10 => "10-bit",
88 Self::Reserved(_) => "reserved",
89 }
90 }
91}
92
93broadcast_common::impl_spec_display!(OutputBitDepth, Reserved);
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97#[cfg_attr(feature = "serde", derive(serde::Serialize))]
98#[repr(u8)]
99#[non_exhaustive]
100pub enum DynamicRangeColourGamut {
101 SdrBt709 = 0x00,
103 SdrBt2020 = 0x01,
105 HdrBt2100Pq = 0x02,
107 HdrBt2100Hlg = 0x03,
109 Reserved(u8),
111}
112
113impl DynamicRangeColourGamut {
114 #[must_use]
116 pub fn name(&self) -> &'static str {
117 match self {
118 Self::SdrBt709 => "SDR_BT.709",
119 Self::SdrBt2020 => "SDR_BT.2020",
120 Self::HdrBt2100Pq => "HDR_BT.2100_PQ",
121 Self::HdrBt2100Hlg => "HDR_BT.2100_HLG",
122 Self::Reserved(_) => "reserved",
123 }
124 }
125}
126
127broadcast_common::impl_spec_display!(DynamicRangeColourGamut, Reserved);
128
129#[derive(Debug, Clone, PartialEq, Eq)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize))]
132pub struct AlternativeClutEntry {
133 pub luma_value: u16,
135 pub chroma1_value: u16,
137 pub chroma2_value: u16,
139 pub t_value: u16,
141}
142
143impl AlternativeClutEntry {
144 fn serialized_len(output_bit_depth: u8) -> usize {
145 match output_bit_depth {
146 0 => ENTRY_8BIT_LEN,
147 1 => ENTRY_10BIT_LEN,
148 _ => ENTRY_8BIT_LEN,
149 }
150 }
151
152 fn serialize_into(&self, buf: &mut [u8], output_bit_depth: u8) {
153 if output_bit_depth == 1 {
154 let v0 = self.luma_value;
155 let v1 = self.chroma1_value;
156 let v2 = self.chroma2_value;
157 let v3 = self.t_value;
158 buf[0] = (v0 >> 2) as u8;
159 buf[1] = (((v0 & 0x03) << 6) | ((v1 & 0x3FC) >> 4)) as u8;
160 buf[2] = (((v1 & 0x0F) << 4) | ((v2 & 0x3F0) >> 6)) as u8;
161 buf[3] = (((v2 & 0x3F) << 2) | ((v3 & 0x300) >> 8)) as u8;
162 buf[4] = (v3 & 0xFF) as u8;
163 } else {
164 buf[0] = self.luma_value as u8;
165 buf[1] = self.chroma1_value as u8;
166 buf[2] = self.chroma2_value as u8;
167 buf[3] = self.t_value as u8;
168 }
169 }
170
171 fn parse(bytes: &[u8], output_bit_depth: u8) -> Result<Self> {
172 let entry_len = Self::serialized_len(output_bit_depth);
173 if bytes.len() < entry_len {
174 return Err(Error::BufferTooShort {
175 need: entry_len,
176 have: bytes.len(),
177 what: "alternative_CLUT_entry",
178 });
179 }
180 let (luma, c1, c2, t) = if output_bit_depth == 1 {
181 let v0 = ((bytes[0] as u16) << 2) | ((bytes[1] as u16) >> 6);
182 let v1 = (((bytes[1] as u16) & 0x3F) << 4) | ((bytes[2] as u16) >> 4);
183 let v2 = (((bytes[2] as u16) & 0x0F) << 6) | ((bytes[3] as u16) >> 2);
184 let v3 = (((bytes[3] as u16) & 0x03) << 8) | (bytes[4] as u16);
185 (v0, v1, v2, v3)
186 } else {
187 (
188 bytes[0] as u16,
189 bytes[1] as u16,
190 bytes[2] as u16,
191 bytes[3] as u16,
192 )
193 };
194 Ok(AlternativeClutEntry {
195 luma_value: luma,
196 chroma1_value: c1,
197 chroma2_value: c2,
198 t_value: t,
199 })
200 }
201}
202
203#[derive(Debug, Clone, PartialEq, Eq)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize))]
206pub struct AlternativeClutSegment {
207 pub page_id: u16,
209 pub clut_id: u8,
211 pub clut_version_number: u8,
213 pub reserved: u8,
215 pub clut_parameters: ClutParameters,
217 pub entries: alloc::vec::Vec<AlternativeClutEntry>,
219}
220
221impl<'a> Parse<'a> for AlternativeClutSegment {
222 type Error = Error;
223
224 fn parse(bytes: &'a [u8]) -> Result<Self> {
225 if bytes.len() < HEADER_LEN + FIXED_LEN {
226 return Err(Error::BufferTooShort {
227 need: HEADER_LEN + FIXED_LEN,
228 have: bytes.len(),
229 what: "alternative_CLUT_segment",
230 });
231 }
232 if bytes[0] != crate::pes_data_field::SYNC_BYTE {
233 return Err(Error::BadSyncByte(bytes[0]));
234 }
235 if bytes[1] != SEGMENT_TYPE {
236 return Err(Error::UnknownSegmentType(bytes[1]));
237 }
238 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
239 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
240 let total = HEADER_LEN + segment_length;
241 if bytes.len() < total {
242 return Err(Error::BufferTooShort {
243 need: total,
244 have: bytes.len(),
245 what: "alternative_CLUT_segment data",
246 });
247 }
248 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
249 if body.len() < FIXED_LEN {
250 return Err(Error::BufferTooShort {
251 need: FIXED_LEN,
252 have: body.len(),
253 what: "alternative_CLUT_segment body",
254 });
255 }
256 let clut_id = body[0];
257 let clut_version_number = body[1] >> 4;
258 let reserved = body[1] & 0x0F;
259 let clut_parameters = ClutParameters::parse(&body[2..4])?;
260 let output_bit_depth = clut_parameters.output_bit_depth;
261 let entry_len = AlternativeClutEntry::serialized_len(output_bit_depth);
262
263 let entry_data = &body[FIXED_LEN..];
264 if !entry_data.len().is_multiple_of(entry_len) {
269 return Err(Error::TrailingEntryBytes {
270 what: "alternative_CLUT_segment entries",
271 entry_len,
272 extra: entry_data.len() % entry_len,
273 });
274 }
275 let num_entries = entry_data.len() / entry_len;
276 let mut entries = alloc::vec::Vec::with_capacity(num_entries);
277 for i in 0..num_entries {
278 let off = i * entry_len;
279 entries.push(AlternativeClutEntry::parse(
280 &entry_data[off..off + entry_len],
281 output_bit_depth,
282 )?);
283 }
284
285 Ok(AlternativeClutSegment {
286 page_id,
287 clut_id,
288 clut_version_number,
289 reserved,
290 clut_parameters,
291 entries,
292 })
293 }
294}
295
296impl Serialize for AlternativeClutSegment {
297 type Error = Error;
298
299 fn serialized_len(&self) -> usize {
300 let entry_len = AlternativeClutEntry::serialized_len(self.clut_parameters.output_bit_depth);
301 HEADER_LEN + FIXED_LEN + self.entries.len() * entry_len
302 }
303
304 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
305 let len = self.serialized_len();
306 if buf.len() < len {
307 return Err(Error::BufferTooShort {
308 need: len,
309 have: buf.len(),
310 what: "alternative_CLUT_segment serialize",
311 });
312 }
313 buf[0] = 0x0F;
314 buf[1] = SEGMENT_TYPE;
315 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
316 let seg_len = crate::segments::check_segment_length(len - HEADER_LEN)?;
317 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
318
319 buf[6] = self.clut_id;
320 buf[7] = (self.clut_version_number << 4) | (self.reserved & 0x0F);
321 self.clut_parameters.serialize_into(&mut buf[8..10]);
322
323 let entry_len = AlternativeClutEntry::serialized_len(self.clut_parameters.output_bit_depth);
324 for (i, entry) in self.entries.iter().enumerate() {
325 let off = HEADER_LEN + FIXED_LEN + i * entry_len;
326 entry.serialize_into(
327 &mut buf[off..off + entry_len],
328 self.clut_parameters.output_bit_depth,
329 );
330 }
331 Ok(len)
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use broadcast_common::{Parse, Serialize};
339
340 #[test]
341 fn round_trip_8bit() {
342 let bytes = [
343 0x0F, 0x16, 0x00, 0x01, 0x00, 0x08, 0x03, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
344 ];
345 let seg = AlternativeClutSegment::parse(&bytes).unwrap();
346 assert_eq!(seg.clut_id, 3);
347 assert_eq!(seg.entries.len(), 1);
348 assert_eq!(seg.entries[0].luma_value, 128);
349 let out = seg.to_bytes();
350 assert_eq!(out, bytes);
351
352 let mut seg2 = seg.clone();
354 seg2.entries[0].luma_value = 200;
355 let out2 = seg2.to_bytes();
356 assert_ne!(out2, bytes);
357 let reparse = AlternativeClutSegment::parse(&out2).unwrap();
358 assert_eq!(reparse.entries[0].luma_value, 200);
359 }
360
361 #[test]
362 fn round_trip_10bit() {
363 let bytes = [
364 0x0F, 0x16, 0x00, 0x01, 0x00, 0x09, 0x03, 0x10, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00,
365 0x00,
366 ];
367 let seg = AlternativeClutSegment::parse(&bytes).unwrap();
368 assert_eq!(seg.clut_parameters.output_bit_depth, 1);
369 assert_eq!(seg.entries.len(), 1);
370 let out = seg.to_bytes();
371 assert_eq!(out, bytes);
372
373 let mut seg2 = seg.clone();
375 seg2.clut_id = 7;
376 let out2 = seg2.to_bytes();
377 assert_ne!(out2, bytes);
378 let reparse = AlternativeClutSegment::parse(&out2).unwrap();
379 assert_eq!(reparse.clut_id, 7);
380 }
381
382 #[test]
384 fn rejects_bad_sync_byte() {
385 let bytes = [
386 0x00, 0x16, 0x00, 0x01, 0x00, 0x08, 0x03, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
387 ];
388 assert!(matches!(
389 AlternativeClutSegment::parse(&bytes),
390 Err(Error::BadSyncByte(0x00))
391 ));
392 }
393
394 #[test]
398 fn rejects_trailing_partial_entry() {
399 let bytes = [
400 0x0F, 0x16, 0x00, 0x01, 0x00, 0x0A, 0x03, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
401 0xAA, 0xBB,
402 ];
403 assert!(matches!(
404 AlternativeClutSegment::parse(&bytes),
405 Err(Error::TrailingEntryBytes {
406 entry_len: 4,
407 extra: 2,
408 ..
409 })
410 ));
411 }
412}