1use super::descriptor_body;
9use super::hdr_wcg_idc::HdrWcgIdc;
10use crate::error::{Error, Result};
11use dvb_common::{Parse, Serialize};
12
13pub const TAG: u8 = 0x38;
15const HEADER_LEN: usize = 2;
16const COPIED_44_MASK: u64 = (1 << 44) - 1;
17const FIXED_BODY_LEN: u8 = 12;
19const TEMPORAL_SUB_LEN: u8 = 2;
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25pub struct HevcTemporalSub {
26 pub temporal_id_min: u8,
28 pub temporal_id_max: u8,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
36pub struct HevcVideoDescriptor {
37 pub profile_space: u8,
39 pub tier_flag: bool,
41 pub profile_idc: u8,
43 pub profile_compatibility_indication: u32,
45 pub progressive_source_flag: bool,
47 pub interlaced_source_flag: bool,
49 pub non_packed_constraint_flag: bool,
51 pub frame_only_constraint_flag: bool,
53 pub copied_44bits: u64,
55 pub level_idc: u8,
57 pub temporal_layer_subset_flag: bool,
59 pub hevc_still_present_flag: bool,
61 pub hevc_24hr_picture_present_flag: bool,
63 pub sub_pic_hrd_params_not_present_flag: bool,
65 pub hdr_wcg_idc: HdrWcgIdc,
67 pub temporal_sub: Option<HevcTemporalSub>,
69}
70
71impl<'a> Parse<'a> for HevcVideoDescriptor {
72 type Error = crate::error::Error;
73
74 fn parse(bytes: &'a [u8]) -> Result<Self> {
75 let body = descriptor_body(
76 bytes,
77 TAG,
78 "HevcVideoDescriptor",
79 "unexpected tag for HEVC_video_descriptor",
80 )?;
81 if body.len() < (FIXED_BODY_LEN as usize) {
82 return Err(Error::InvalidDescriptor {
83 tag: TAG,
84 reason: "HEVC_video_descriptor too short",
85 });
86 }
87
88 let b0 = body[0]; let profile_space = b0 >> 6;
90 let tier_flag = (b0 & 0x20) != 0;
91 let profile_idc = b0 & 0x1F;
92
93 let profile_compatibility_indication =
95 u32::from_be_bytes([body[1], body[2], body[3], body[4]]);
96
97 let b5 = body[5]; let progressive_source_flag = (b5 & 0x80) != 0;
99 let interlaced_source_flag = (b5 & 0x40) != 0;
100 let non_packed_constraint_flag = (b5 & 0x20) != 0;
101 let frame_only_constraint_flag = (b5 & 0x10) != 0;
102
103 let copied_44bits_hi: u64 = (b5 as u64 & 0x0F) << 40;
105 let copied_44bits_lo: u64 = ((body[6] as u64) << 32)
106 | ((body[7] as u64) << 24)
107 | ((body[8] as u64) << 16)
108 | ((body[9] as u64) << 8)
109 | (body[10] as u64);
110 let copied_44bits = (copied_44bits_hi | copied_44bits_lo) & COPIED_44_MASK;
111
112 let b11 = body[11]; let temporal_layer_subset_flag;
115 let hevc_still_present_flag;
116 let hevc_24hr_picture_present_flag;
117 let sub_pic_hrd_params_not_present_flag;
118 let hdr_wcg_idc;
119 let temporal_sub;
120
121 if body.len() > (FIXED_BODY_LEN as usize) {
122 let b12 = body[12];
124 temporal_layer_subset_flag = (b12 & 0x80) != 0;
125 hevc_still_present_flag = (b12 & 0x40) != 0;
126 hevc_24hr_picture_present_flag = (b12 & 0x20) != 0;
127 sub_pic_hrd_params_not_present_flag = (b12 & 0x10) != 0;
128 hdr_wcg_idc = HdrWcgIdc::from_u8(b12 & 0x03);
129
130 if temporal_layer_subset_flag {
131 if body.len() < (FIXED_BODY_LEN + TEMPORAL_SUB_LEN) as usize {
132 return Err(Error::InvalidDescriptor {
133 tag: TAG,
134 reason: "HEVC_video_descriptor too short for temporal sub-block",
135 });
136 }
137 let b13 = body[13];
138 let b14 = body[14];
139 temporal_sub = Some(HevcTemporalSub {
140 temporal_id_min: b13 >> 5,
141 temporal_id_max: b14 >> 5,
142 });
143 } else {
144 temporal_sub = None;
145 }
146 } else {
147 temporal_layer_subset_flag = false;
149 hevc_still_present_flag = false;
150 hevc_24hr_picture_present_flag = false;
151 sub_pic_hrd_params_not_present_flag = false;
152 hdr_wcg_idc = HdrWcgIdc::NoIndication;
153 temporal_sub = None;
154 }
155
156 Ok(Self {
157 profile_space,
158 tier_flag,
159 profile_idc,
160 profile_compatibility_indication,
161 progressive_source_flag,
162 interlaced_source_flag,
163 non_packed_constraint_flag,
164 frame_only_constraint_flag,
165 copied_44bits,
166 level_idc: b11,
167 temporal_layer_subset_flag,
168 hevc_still_present_flag,
169 hevc_24hr_picture_present_flag,
170 sub_pic_hrd_params_not_present_flag,
171 hdr_wcg_idc,
172 temporal_sub,
173 })
174 }
175}
176
177impl Serialize for HevcVideoDescriptor {
178 type Error = crate::error::Error;
179
180 fn serialized_len(&self) -> usize {
181 let extra = if self.temporal_layer_subset_flag {
182 TEMPORAL_SUB_LEN
183 } else {
184 0
185 };
186 HEADER_LEN + (FIXED_BODY_LEN as usize) + 1 + (extra as usize)
187 }
188
189 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
190 let len = self.serialized_len();
191 if buf.len() < len {
192 return Err(Error::OutputBufferTooSmall {
193 need: len,
194 have: buf.len(),
195 });
196 }
197 let body_len = (len - HEADER_LEN) as u8;
198 buf[0] = TAG;
199 buf[1] = body_len;
200
201 buf[HEADER_LEN] =
203 (self.profile_space << 6) | ((self.tier_flag as u8) << 5) | (self.profile_idc & 0x1F);
204
205 buf[HEADER_LEN + 1..HEADER_LEN + 5]
207 .copy_from_slice(&self.profile_compatibility_indication.to_be_bytes());
208
209 let copied = self.copied_44bits & COPIED_44_MASK;
211 buf[HEADER_LEN + 5] = ((self.progressive_source_flag as u8) << 7)
212 | ((self.interlaced_source_flag as u8) << 6)
213 | ((self.non_packed_constraint_flag as u8) << 5)
214 | ((self.frame_only_constraint_flag as u8) << 4)
215 | (((copied >> 40) & 0x0F) as u8);
216
217 buf[HEADER_LEN + 6] = ((copied >> 32) & 0xFF) as u8;
219 buf[HEADER_LEN + 7] = ((copied >> 24) & 0xFF) as u8;
220 buf[HEADER_LEN + 8] = ((copied >> 16) & 0xFF) as u8;
221 buf[HEADER_LEN + 9] = ((copied >> 8) & 0xFF) as u8;
222 buf[HEADER_LEN + 10] = (copied & 0xFF) as u8;
223
224 buf[HEADER_LEN + 11] = self.level_idc;
226
227 buf[HEADER_LEN + 12] = ((self.temporal_layer_subset_flag as u8) << 7)
229 | ((self.hevc_still_present_flag as u8) << 6)
230 | ((self.hevc_24hr_picture_present_flag as u8) << 5)
231 | ((self.sub_pic_hrd_params_not_present_flag as u8) << 4)
232 | (self.hdr_wcg_idc.to_u8() & 0x03);
233
234 if self.temporal_layer_subset_flag {
235 if let Some(ref ts) = self.temporal_sub {
236 buf[HEADER_LEN + 13] = ts.temporal_id_min << 5;
237 buf[HEADER_LEN + 14] = ts.temporal_id_max << 5;
238 }
239 }
240
241 Ok(len)
242 }
243}
244
245impl<'a> crate::traits::DescriptorDef<'a> for HevcVideoDescriptor {
246 const TAG: u8 = TAG;
247 const NAME: &'static str = "HEVC_VIDEO";
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn round_trip_no_temporal() {
256 let orig = HevcVideoDescriptor {
257 profile_space: 1,
258 tier_flag: true,
259 profile_idc: 2,
260 profile_compatibility_indication: 0xDEADBEEF,
261 progressive_source_flag: true,
262 interlaced_source_flag: false,
263 non_packed_constraint_flag: true,
264 frame_only_constraint_flag: false,
265 copied_44bits: 0x123456789AB & COPIED_44_MASK,
266 level_idc: 0x99,
267 temporal_layer_subset_flag: false,
268 hevc_still_present_flag: true,
269 hevc_24hr_picture_present_flag: false,
270 sub_pic_hrd_params_not_present_flag: true,
271 hdr_wcg_idc: HdrWcgIdc::HdrAndWcg,
272 temporal_sub: None,
273 };
274 let mut buf = vec![0u8; orig.serialized_len()];
275 orig.serialize_into(&mut buf).unwrap();
276 let reparsed = HevcVideoDescriptor::parse(&buf).unwrap();
277 assert_eq!(orig, reparsed);
278 }
279
280 #[test]
281 fn hdr_wcg_idc_is_low_two_bits_of_byte12() {
282 let body12 = 0b0000_1110u8; let buf = [
287 TAG, 13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, body12,
288 ];
289 let d = HevcVideoDescriptor::parse(&buf).unwrap();
290 assert_eq!(d.hdr_wcg_idc, HdrWcgIdc::HdrAndWcg);
291 let mut out = vec![0u8; d.serialized_len()];
293 d.serialize_into(&mut out).unwrap();
294 assert_eq!(out[14] & 0x03, 0b10, "HDR_WCG_idc must occupy bits [1:0]");
296 assert_eq!(
297 out[14] & 0x0C,
298 0,
299 "reserved bits [3:2] must serialize as zero"
300 );
301 }
302
303 #[test]
304 fn round_trip_with_temporal() {
305 let orig = HevcVideoDescriptor {
306 profile_space: 3,
307 tier_flag: false,
308 profile_idc: 0x0A,
309 profile_compatibility_indication: 0xCAFEBABE,
310 progressive_source_flag: false,
311 interlaced_source_flag: true,
312 non_packed_constraint_flag: false,
313 frame_only_constraint_flag: true,
314 copied_44bits: 0xABCDEF01234 & COPIED_44_MASK,
315 level_idc: 0x5A,
316 temporal_layer_subset_flag: true,
317 hevc_still_present_flag: false,
318 hevc_24hr_picture_present_flag: true,
319 sub_pic_hrd_params_not_present_flag: false,
320 hdr_wcg_idc: HdrWcgIdc::Sdr,
321 temporal_sub: Some(HevcTemporalSub {
322 temporal_id_min: 5,
323 temporal_id_max: 7,
324 }),
325 };
326 let mut buf = vec![0u8; orig.serialized_len()];
327 orig.serialize_into(&mut buf).unwrap();
328 let reparsed = HevcVideoDescriptor::parse(&buf).unwrap();
329 assert_eq!(orig, reparsed);
330 }
331
332 #[test]
333 fn round_trip_nonzero_copied_44bits() {
334 let orig = HevcVideoDescriptor {
336 profile_space: 0,
337 tier_flag: false,
338 profile_idc: 1,
339 profile_compatibility_indication: 0,
340 progressive_source_flag: false,
341 interlaced_source_flag: false,
342 non_packed_constraint_flag: false,
343 frame_only_constraint_flag: false,
344 copied_44bits: COPIED_44_MASK, level_idc: 0x3C,
346 temporal_layer_subset_flag: false,
347 hevc_still_present_flag: false,
348 hevc_24hr_picture_present_flag: false,
349 sub_pic_hrd_params_not_present_flag: false,
350 hdr_wcg_idc: HdrWcgIdc::NoIndication,
351 temporal_sub: None,
352 };
353 let mut buf = vec![0u8; orig.serialized_len()];
354 orig.serialize_into(&mut buf).unwrap();
355 let reparsed = HevcVideoDescriptor::parse(&buf).unwrap();
356 assert_eq!(orig, reparsed);
357 assert_eq!(reparsed.copied_44bits, COPIED_44_MASK);
358 }
359
360 #[test]
361 fn parse_rejects_wrong_tag() {
362 let buf = [
363 0x02, 13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
364 ];
365 let err = HevcVideoDescriptor::parse(&buf).unwrap_err();
366 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
367 }
368
369 #[test]
370 fn parse_rejects_too_short() {
371 let err = HevcVideoDescriptor::parse(&[TAG, 2, 0x00, 0x00]).unwrap_err();
373 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
374 }
375}