Skip to main content

dvb_si/descriptors/
avc_timing_and_hrd.rs

1//! AVC Timing and HRD Descriptor — ISO/IEC 13818-1 §2.6.66 (tag 0x2A).
2//!
3//! Provides encoding-parameters timestamps, HRD management validity,
4//! and frame-rate conversion flags for an AVC/H.264 elementary stream.
5//! The picture_and_timing_info block is conditional on
6//! `picture_and_timing_info_present`; the inner N/K fields within it are
7//! conditional on `90kHz_flag == 0`.
8
9use super::descriptor_body;
10use crate::error::{Error, Result};
11use dvb_common::{Parse, Serialize};
12
13/// Descriptor tag for AVC_timing_and_HRD_descriptor.
14pub const TAG: u8 = 0x2A;
15const HEADER_LEN: usize = 2;
16const FIXED_BODY_LEN: u8 = 1; // first byte always present (hrd_management_valid_flag..picture_and_timing_info_present)
17
18/// Picture and timing info block — present when `picture_and_timing_info_present` is true.
19#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21pub struct AvcPictureTiming {
22    /// 1 — 90 kHz clock; 0 — N/K clock used.
23    pub _90khz_flag: bool,
24    /// N parameter (when `_90khz_flag` is false).
25    pub n: Option<u32>,
26    /// K parameter (when `_90khz_flag` is false).
27    pub k: Option<u32>,
28    /// num_units_in_tick.
29    pub num_units_in_tick: u32,
30}
31
32/// AVC Timing and HRD Descriptor.
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
36pub struct AvcTimingAndHrdDescriptor {
37    /// HRD management valid flag.
38    pub hrd_management_valid_flag: bool,
39    /// Picture and timing info block, when present.
40    pub picture_timing: Option<AvcPictureTiming>,
41    /// Fixed frame rate flag.
42    pub fixed_frame_rate_flag: bool,
43    /// Temporal POC flag.
44    pub temporal_poc_flag: bool,
45    /// Picture to display conversion flag.
46    pub picture_to_display_conversion_flag: bool,
47}
48
49impl<'a> Parse<'a> for AvcTimingAndHrdDescriptor {
50    type Error = crate::error::Error;
51
52    fn parse(bytes: &'a [u8]) -> Result<Self> {
53        let body = descriptor_body(
54            bytes,
55            TAG,
56            "AvcTimingAndHrdDescriptor",
57            "unexpected tag for AVC_timing_and_HRD_descriptor",
58        )?;
59        if body.len() < (FIXED_BODY_LEN as usize) {
60            return Err(Error::InvalidDescriptor {
61                tag: TAG,
62                reason: "AVC_timing_and_HRD_descriptor too short",
63            });
64        }
65
66        let b0 = body[0];
67        let hrd_management_valid_flag = (b0 & 0x80) != 0;
68        let picture_and_timing_info_present = (b0 & 0x01) != 0;
69
70        let (picture_timing, flags_offset) = if picture_and_timing_info_present {
71            // picture_and_timing block: 1 byte (90kHz_flag + reserved) + optional 8 bytes (N+K)
72            if body.len() < 2 {
73                return Err(Error::InvalidDescriptor {
74                    tag: TAG,
75                    reason: "AVC_timing_and_HRD_descriptor too short for picture_timing block",
76                });
77            }
78            let b1 = body[1];
79            let _90khz_flag = (b1 & 0x80) != 0;
80
81            let (n, k, nk_len) = if _90khz_flag {
82                (None, None, 0)
83            } else {
84                if body.len() < 10 {
85                    return Err(Error::InvalidDescriptor {
86                        tag: TAG,
87                        reason: "AVC_timing_and_HRD_descriptor too short for N/K fields",
88                    });
89                }
90                let n = u32::from_be_bytes([body[2], body[3], body[4], body[5]]);
91                let k = u32::from_be_bytes([body[6], body[7], body[8], body[9]]);
92                (Some(n), Some(k), 8)
93            };
94
95            let num_units_offset = 2 + nk_len;
96            if body.len() < num_units_offset + 4 {
97                return Err(Error::InvalidDescriptor {
98                    tag: TAG,
99                    reason: "AVC_timing_and_HRD_descriptor too short for num_units_in_tick",
100                });
101            }
102            let num_units_in_tick = u32::from_be_bytes([
103                body[num_units_offset],
104                body[num_units_offset + 1],
105                body[num_units_offset + 2],
106                body[num_units_offset + 3],
107            ]);
108
109            let timing = AvcPictureTiming {
110                _90khz_flag,
111                n,
112                k,
113                num_units_in_tick,
114            };
115            (Some(timing), num_units_offset + 4)
116        } else {
117            (None, 1)
118        };
119
120        if body.len() < flags_offset + 1 {
121            return Err(Error::InvalidDescriptor {
122                tag: TAG,
123                reason: "AVC_timing_and_HRD_descriptor too short for trailing flags",
124            });
125        }
126        let flags_byte = body[flags_offset];
127        let fixed_frame_rate_flag = (flags_byte & 0x80) != 0;
128        let temporal_poc_flag = (flags_byte & 0x40) != 0;
129        let picture_to_display_conversion_flag = (flags_byte & 0x20) != 0;
130
131        Ok(Self {
132            hrd_management_valid_flag,
133            picture_timing,
134            fixed_frame_rate_flag,
135            temporal_poc_flag,
136            picture_to_display_conversion_flag,
137        })
138    }
139}
140
141impl Serialize for AvcTimingAndHrdDescriptor {
142    type Error = crate::error::Error;
143
144    fn serialized_len(&self) -> usize {
145        let body_len = 1u8
146            + if let Some(ref pt) = self.picture_timing {
147                if pt._90khz_flag {
148                    1u8 + 4 // 90kHz_flag byte + num_units_in_tick
149                } else {
150                    1u8 + 8 + 4 // 90kHz_flag byte + N(4) + K(4) + num_units_in_tick
151                }
152            } else {
153                0
154            };
155        HEADER_LEN + body_len as usize + 1 // +1 for the trailing flags byte
156    }
157
158    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
159        let len = self.serialized_len();
160        if buf.len() < len {
161            return Err(Error::OutputBufferTooSmall {
162                need: len,
163                have: buf.len(),
164            });
165        }
166        let body_len = (len - HEADER_LEN) as u8;
167        buf[0] = TAG;
168        buf[1] = body_len;
169
170        let picture_and_timing_info_present = self.picture_timing.is_some();
171        buf[HEADER_LEN] = ((self.hrd_management_valid_flag as u8) << 7)
172            | (if picture_and_timing_info_present {
173                1
174            } else {
175                0
176            });
177
178        let mut pos = HEADER_LEN + 1;
179        if let Some(ref pt) = self.picture_timing {
180            buf[pos] = if pt._90khz_flag { 0x80 } else { 0x00 };
181            pos += 1;
182            if !pt._90khz_flag {
183                let n = pt.n.unwrap_or(0);
184                let k = pt.k.unwrap_or(0);
185                buf[pos..pos + 4].copy_from_slice(&n.to_be_bytes());
186                buf[pos + 4..pos + 8].copy_from_slice(&k.to_be_bytes());
187                pos += 8;
188            }
189            buf[pos..pos + 4].copy_from_slice(&pt.num_units_in_tick.to_be_bytes());
190            pos += 4;
191        }
192
193        buf[pos] = ((self.fixed_frame_rate_flag as u8) << 7)
194            | ((self.temporal_poc_flag as u8) << 6)
195            | ((self.picture_to_display_conversion_flag as u8) << 5);
196        Ok(len)
197    }
198}
199
200impl<'a> crate::traits::DescriptorDef<'a> for AvcTimingAndHrdDescriptor {
201    const TAG: u8 = TAG;
202    const NAME: &'static str = "AVC_TIMING_AND_HRD";
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208
209    #[test]
210    fn round_trip_no_timing() {
211        let orig = AvcTimingAndHrdDescriptor {
212            hrd_management_valid_flag: true,
213            picture_timing: None,
214            fixed_frame_rate_flag: false,
215            temporal_poc_flag: true,
216            picture_to_display_conversion_flag: false,
217        };
218        let mut buf = vec![0u8; orig.serialized_len()];
219        orig.serialize_into(&mut buf).unwrap();
220        let reparsed = AvcTimingAndHrdDescriptor::parse(&buf).unwrap();
221        assert_eq!(orig, reparsed);
222    }
223
224    #[test]
225    fn round_trip_90khz() {
226        let orig = AvcTimingAndHrdDescriptor {
227            hrd_management_valid_flag: false,
228            picture_timing: Some(AvcPictureTiming {
229                _90khz_flag: true,
230                n: None,
231                k: None,
232                num_units_in_tick: 0xDEADBEEF,
233            }),
234            fixed_frame_rate_flag: true,
235            temporal_poc_flag: false,
236            picture_to_display_conversion_flag: true,
237        };
238        let mut buf = vec![0u8; orig.serialized_len()];
239        orig.serialize_into(&mut buf).unwrap();
240        let reparsed = AvcTimingAndHrdDescriptor::parse(&buf).unwrap();
241        assert_eq!(orig, reparsed);
242    }
243
244    #[test]
245    fn round_trip_nk() {
246        let orig = AvcTimingAndHrdDescriptor {
247            hrd_management_valid_flag: true,
248            picture_timing: Some(AvcPictureTiming {
249                _90khz_flag: false,
250                n: Some(0x12345678),
251                k: Some(0x9ABCDEF0),
252                num_units_in_tick: 0x0FEDCBA9,
253            }),
254            fixed_frame_rate_flag: false,
255            temporal_poc_flag: false,
256            picture_to_display_conversion_flag: false,
257        };
258        let mut buf = vec![0u8; orig.serialized_len()];
259        orig.serialize_into(&mut buf).unwrap();
260        let reparsed = AvcTimingAndHrdDescriptor::parse(&buf).unwrap();
261        assert_eq!(orig, reparsed);
262    }
263
264    #[test]
265    fn parse_rejects_wrong_tag() {
266        let err = AvcTimingAndHrdDescriptor::parse(&[0x02, 1, 0x00]).unwrap_err();
267        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
268    }
269
270    #[test]
271    fn parse_rejects_too_short() {
272        let err = AvcTimingAndHrdDescriptor::parse(&[TAG, 0]).unwrap_err();
273        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
274    }
275}