1use super::descriptor_body;
7use crate::error::{Error, Result};
8use dvb_common::{Parse, Serialize};
9
10pub const TAG: u8 = 0x02;
12const HEADER_LEN: usize = 2;
13const BODY_LEN: u8 = 3;
14const BODY_MPEG1_LEN: u8 = 1;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23#[non_exhaustive]
24pub enum FrameRateCode {
25 Forbidden,
27 Frame23_976,
29 Frame24_0,
31 Frame25_0,
33 Frame29_97,
35 Frame30_0,
37 Frame50_0,
39 Frame59_94,
41 Frame60_0,
43 Reserved(u8),
46}
47
48impl FrameRateCode {
49 #[must_use]
52 pub fn from_u8(v: u8) -> Self {
53 match v {
54 0x0 => Self::Forbidden,
55 0x1 => Self::Frame23_976,
56 0x2 => Self::Frame24_0,
57 0x3 => Self::Frame25_0,
58 0x4 => Self::Frame29_97,
59 0x5 => Self::Frame30_0,
60 0x6 => Self::Frame50_0,
61 0x7 => Self::Frame59_94,
62 0x8 => Self::Frame60_0,
63 v => Self::Reserved(v),
64 }
65 }
66
67 #[must_use]
69 pub fn to_u8(self) -> u8 {
70 match self {
71 Self::Forbidden => 0x0,
72 Self::Frame23_976 => 0x1,
73 Self::Frame24_0 => 0x2,
74 Self::Frame25_0 => 0x3,
75 Self::Frame29_97 => 0x4,
76 Self::Frame30_0 => 0x5,
77 Self::Frame50_0 => 0x6,
78 Self::Frame59_94 => 0x7,
79 Self::Frame60_0 => 0x8,
80 Self::Reserved(v) => v,
81 }
82 }
83
84 #[must_use]
86 pub fn name(self) -> &'static str {
87 match self {
88 Self::Forbidden => "forbidden",
89 Self::Frame23_976 => "23.976",
90 Self::Frame24_0 => "24.0",
91 Self::Frame25_0 => "25.0",
92 Self::Frame29_97 => "29.97",
93 Self::Frame30_0 => "30.0",
94 Self::Frame50_0 => "50.0",
95 Self::Frame59_94 => "59.94",
96 Self::Frame60_0 => "60.0",
97 Self::Reserved(_) => "reserved",
98 }
99 }
100}
101dvb_common::impl_spec_display!(FrameRateCode, Reserved);
102
103#[derive(Debug, Clone, PartialEq, Eq)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize))]
110#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
111pub struct VideoStreamDescriptor {
112 pub multiple_frame_rate_flag: bool,
114 pub frame_rate_code: FrameRateCode,
116 pub mpeg_1_only_flag: bool,
118 pub constrained_parameter_flag: bool,
120 pub still_picture_flag: bool,
122 pub profile_and_level_indication: Option<u8>,
124 pub chroma_format: Option<u8>,
126 pub frame_rate_extension_flag: Option<bool>,
128}
129
130impl<'a> Parse<'a> for VideoStreamDescriptor {
131 type Error = crate::error::Error;
132
133 fn parse(bytes: &'a [u8]) -> Result<Self> {
134 let body = descriptor_body(
135 bytes,
136 TAG,
137 "VideoStreamDescriptor",
138 "unexpected tag for video_stream_descriptor",
139 )?;
140 if body.is_empty() {
141 return Err(Error::InvalidDescriptor {
142 tag: TAG,
143 reason: "video_stream_descriptor length must be at least 1",
144 });
145 }
146 let b0 = body[0];
147 let multiple_frame_rate_flag = (b0 & 0x80) != 0;
148 let frame_rate_code = FrameRateCode::from_u8((b0 >> 3) & 0x0F);
149 let mpeg_1_only_flag = (b0 & 0x04) != 0;
150 let constrained_parameter_flag = (b0 & 0x02) != 0;
151 let still_picture_flag = (b0 & 0x01) != 0;
152
153 if mpeg_1_only_flag {
154 Ok(Self {
155 multiple_frame_rate_flag,
156 frame_rate_code,
157 mpeg_1_only_flag,
158 constrained_parameter_flag,
159 still_picture_flag,
160 profile_and_level_indication: None,
161 chroma_format: None,
162 frame_rate_extension_flag: None,
163 })
164 } else {
165 if body.len() < (BODY_LEN as usize) {
166 return Err(Error::InvalidDescriptor {
167 tag: TAG,
168 reason: "video_stream_descriptor too short for MPEG-2 fields",
169 });
170 }
171 let b1 = body[1];
172 let b2 = body[2];
173 let profile_and_level_indication = b1;
174 let chroma_format = (b2 >> 6) & 0x03;
175 let frame_rate_extension_flag = (b2 & 0x20) != 0;
176 Ok(Self {
177 multiple_frame_rate_flag,
178 frame_rate_code,
179 mpeg_1_only_flag,
180 constrained_parameter_flag,
181 still_picture_flag,
182 profile_and_level_indication: Some(profile_and_level_indication),
183 chroma_format: Some(chroma_format),
184 frame_rate_extension_flag: Some(frame_rate_extension_flag),
185 })
186 }
187 }
188}
189
190impl Serialize for VideoStreamDescriptor {
191 type Error = crate::error::Error;
192
193 fn serialized_len(&self) -> usize {
194 if self.mpeg_1_only_flag {
195 HEADER_LEN + (BODY_MPEG1_LEN as usize)
196 } else {
197 HEADER_LEN + (BODY_LEN as usize)
198 }
199 }
200
201 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
202 let len = self.serialized_len();
203 if buf.len() < len {
204 return Err(Error::OutputBufferTooSmall {
205 need: len,
206 have: buf.len(),
207 });
208 }
209 buf[0] = TAG;
210 buf[1] = (len - HEADER_LEN) as u8;
211 let b0 = ((self.multiple_frame_rate_flag as u8) << 7)
212 | (self.frame_rate_code.to_u8() << 3)
213 | ((self.mpeg_1_only_flag as u8) << 2)
214 | ((self.constrained_parameter_flag as u8) << 1)
215 | (self.still_picture_flag as u8);
216 buf[HEADER_LEN] = b0;
217 if !self.mpeg_1_only_flag {
218 buf[HEADER_LEN + 1] = self.profile_and_level_indication.unwrap_or(0);
219 let chroma = self.chroma_format.unwrap_or(0) & 0x03;
220 let fre = self.frame_rate_extension_flag.unwrap_or(false) as u8;
221 buf[HEADER_LEN + 2] = (chroma << 6) | (fre << 5);
222 }
223 Ok(len)
224 }
225}
226impl<'a> crate::traits::DescriptorDef<'a> for VideoStreamDescriptor {
227 const TAG: u8 = TAG;
228 const NAME: &'static str = "VIDEO_STREAM";
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn parse_mpeg_2() {
237 let bytes = [
238 TAG,
239 3, 0b1010_0011, 0xDE, 0b1110_0000, ];
244 let d = VideoStreamDescriptor::parse(&bytes).unwrap();
245 assert!(d.multiple_frame_rate_flag);
246 assert_eq!(d.frame_rate_code, FrameRateCode::Frame29_97);
247 assert!(!d.mpeg_1_only_flag);
248 assert!(d.constrained_parameter_flag);
249 assert!(d.still_picture_flag);
250 assert_eq!(d.profile_and_level_indication, Some(0xDE));
251 assert_eq!(d.chroma_format, Some(3));
252 assert_eq!(d.frame_rate_extension_flag, Some(true));
253 }
254
255 #[test]
256 fn parse_mpeg_1() {
257 let bytes = [
258 TAG,
259 1,
260 0b0001_1101, ];
262 let d = VideoStreamDescriptor::parse(&bytes).unwrap();
263 assert!(!d.multiple_frame_rate_flag);
264 assert_eq!(d.frame_rate_code, FrameRateCode::Frame25_0);
265 assert!(d.mpeg_1_only_flag);
266 assert!(!d.constrained_parameter_flag);
267 assert!(d.still_picture_flag);
268 assert!(d.profile_and_level_indication.is_none());
269 assert!(d.chroma_format.is_none());
270 assert!(d.frame_rate_extension_flag.is_none());
271 }
272
273 #[test]
274 fn serialize_round_trip_mpeg_2() {
275 let d = VideoStreamDescriptor {
276 multiple_frame_rate_flag: true,
277 frame_rate_code: FrameRateCode::Frame60_0,
278 mpeg_1_only_flag: false,
279 constrained_parameter_flag: false,
280 still_picture_flag: true,
281 profile_and_level_indication: Some(0xAB),
282 chroma_format: Some(1),
283 frame_rate_extension_flag: Some(false),
284 };
285 let mut buf = vec![0u8; d.serialized_len()];
286 d.serialize_into(&mut buf).unwrap();
287 let reparsed = VideoStreamDescriptor::parse(&buf).unwrap();
288 assert_eq!(d, reparsed);
289 }
290
291 #[test]
292 fn serialize_round_trip_mpeg_1() {
293 let d = VideoStreamDescriptor {
294 multiple_frame_rate_flag: false,
295 frame_rate_code: FrameRateCode::Frame23_976,
296 mpeg_1_only_flag: true,
297 constrained_parameter_flag: true,
298 still_picture_flag: false,
299 profile_and_level_indication: None,
300 chroma_format: None,
301 frame_rate_extension_flag: None,
302 };
303 let mut buf = vec![0u8; d.serialized_len()];
304 d.serialize_into(&mut buf).unwrap();
305 let reparsed = VideoStreamDescriptor::parse(&buf).unwrap();
306 assert_eq!(d, reparsed);
307 }
308
309 #[test]
310 fn parse_rejects_wrong_tag() {
311 let err = VideoStreamDescriptor::parse(&[0x03, 1, 0x00]).unwrap_err();
312 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x03, .. }));
313 }
314
315 #[test]
316 fn parse_rejects_empty_body() {
317 let err = VideoStreamDescriptor::parse(&[TAG, 0]).unwrap_err();
318 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
319 }
320
321 #[test]
322 fn frame_rate_code_round_trip() {
323 for v in 0u8..=0x0F {
324 assert_eq!(
325 FrameRateCode::from_u8(v).to_u8(),
326 v,
327 "round-trip failed for {v:#04x}"
328 );
329 }
330 }
331
332 #[test]
333 fn frame_rate_code_name() {
334 assert_eq!(FrameRateCode::Frame23_976.name(), "23.976");
335 assert_eq!(FrameRateCode::Frame25_0.name(), "25.0");
336 assert_eq!(FrameRateCode::Reserved(0xA).name(), "reserved");
337 assert_eq!(FrameRateCode::Forbidden.name(), "forbidden");
338 }
339
340 #[test]
341 fn serialize_rejects_small_buffer() {
342 let d = VideoStreamDescriptor {
343 multiple_frame_rate_flag: false,
344 frame_rate_code: FrameRateCode::Frame25_0,
345 mpeg_1_only_flag: true,
346 constrained_parameter_flag: false,
347 still_picture_flag: false,
348 profile_and_level_indication: None,
349 chroma_format: None,
350 frame_rate_extension_flag: None,
351 };
352 let mut tiny = vec![0u8; 2];
353 let err = d.serialize_into(&mut tiny).unwrap_err();
354 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
355 }
356}