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[1] != SEGMENT_TYPE {
233 return Err(Error::UnknownSegmentType(bytes[1]));
234 }
235 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
236 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
237 let total = HEADER_LEN + segment_length;
238 if bytes.len() < total {
239 return Err(Error::BufferTooShort {
240 need: total,
241 have: bytes.len(),
242 what: "alternative_CLUT_segment data",
243 });
244 }
245 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
246 if body.len() < FIXED_LEN {
247 return Err(Error::BufferTooShort {
248 need: FIXED_LEN,
249 have: body.len(),
250 what: "alternative_CLUT_segment body",
251 });
252 }
253 let clut_id = body[0];
254 let clut_version_number = body[1] >> 4;
255 let reserved = body[1] & 0x0F;
256 let clut_parameters = ClutParameters::parse(&body[2..4])?;
257 let output_bit_depth = clut_parameters.output_bit_depth;
258 let entry_len = AlternativeClutEntry::serialized_len(output_bit_depth);
259
260 let entry_data = &body[FIXED_LEN..];
261 let num_entries = entry_data.len() / entry_len;
262 let mut entries = alloc::vec::Vec::with_capacity(num_entries);
263 for i in 0..num_entries {
264 let off = i * entry_len;
265 entries.push(AlternativeClutEntry::parse(
266 &entry_data[off..off + entry_len],
267 output_bit_depth,
268 )?);
269 }
270
271 Ok(AlternativeClutSegment {
272 page_id,
273 clut_id,
274 clut_version_number,
275 reserved,
276 clut_parameters,
277 entries,
278 })
279 }
280}
281
282impl Serialize for AlternativeClutSegment {
283 type Error = Error;
284
285 fn serialized_len(&self) -> usize {
286 let entry_len = AlternativeClutEntry::serialized_len(self.clut_parameters.output_bit_depth);
287 HEADER_LEN + FIXED_LEN + self.entries.len() * entry_len
288 }
289
290 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
291 let len = self.serialized_len();
292 if buf.len() < len {
293 return Err(Error::BufferTooShort {
294 need: len,
295 have: buf.len(),
296 what: "alternative_CLUT_segment serialize",
297 });
298 }
299 buf[0] = 0x0F;
300 buf[1] = SEGMENT_TYPE;
301 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
302 let seg_len = (len - HEADER_LEN) as u16;
303 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
304
305 buf[6] = self.clut_id;
306 buf[7] = (self.clut_version_number << 4) | (self.reserved & 0x0F);
307 self.clut_parameters.serialize_into(&mut buf[8..10]);
308
309 let entry_len = AlternativeClutEntry::serialized_len(self.clut_parameters.output_bit_depth);
310 for (i, entry) in self.entries.iter().enumerate() {
311 let off = HEADER_LEN + FIXED_LEN + i * entry_len;
312 entry.serialize_into(
313 &mut buf[off..off + entry_len],
314 self.clut_parameters.output_bit_depth,
315 );
316 }
317 Ok(len)
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324 use broadcast_common::{Parse, Serialize};
325
326 #[test]
327 fn round_trip_8bit() {
328 let bytes = [
329 0x0F, 0x16, 0x00, 0x01, 0x00, 0x08, 0x03, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
330 ];
331 let seg = AlternativeClutSegment::parse(&bytes).unwrap();
332 assert_eq!(seg.clut_id, 3);
333 assert_eq!(seg.entries.len(), 1);
334 assert_eq!(seg.entries[0].luma_value, 128);
335 let out = seg.to_bytes();
336 assert_eq!(out, bytes);
337
338 let mut seg2 = seg.clone();
340 seg2.entries[0].luma_value = 200;
341 let out2 = seg2.to_bytes();
342 assert_ne!(out2, bytes);
343 let reparse = AlternativeClutSegment::parse(&out2).unwrap();
344 assert_eq!(reparse.entries[0].luma_value, 200);
345 }
346
347 #[test]
348 fn round_trip_10bit() {
349 let bytes = [
350 0x0F, 0x16, 0x00, 0x01, 0x00, 0x09, 0x03, 0x10, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00,
351 0x00,
352 ];
353 let seg = AlternativeClutSegment::parse(&bytes).unwrap();
354 assert_eq!(seg.clut_parameters.output_bit_depth, 1);
355 assert_eq!(seg.entries.len(), 1);
356 let out = seg.to_bytes();
357 assert_eq!(out, bytes);
358
359 let mut seg2 = seg.clone();
361 seg2.clut_id = 7;
362 let out2 = seg2.to_bytes();
363 assert_ne!(out2, bytes);
364 let reparse = AlternativeClutSegment::parse(&out2).unwrap();
365 assert_eq!(reparse.clut_id, 7);
366 }
367}