gstreamer_video/
video_vbi_parser.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{ffi, VideoFormat};
4use glib::translate::*;
5
6use std::fmt;
7
8use crate::video_vbi::line_buffer_len;
9use crate::{VideoAncillaryDID, VideoAncillaryDID16, VideoVBIError};
10
11glib::wrapper! {
12    #[doc(alias = "GstVideoAncillary")]
13    pub struct VideoAncillary(BoxedInline<ffi::GstVideoAncillary>);
14}
15
16impl VideoAncillary {
17    pub fn did_u8(&self) -> u8 {
18        self.inner.DID
19    }
20
21    pub fn did(&self) -> VideoAncillaryDID {
22        unsafe { VideoAncillaryDID::from_glib(self.inner.DID as ffi::GstVideoAncillaryDID) }
23    }
24
25    pub fn sdid_block_number(&self) -> u8 {
26        self.inner.SDID_block_number
27    }
28
29    pub fn did16(&self) -> VideoAncillaryDID16 {
30        unsafe {
31            VideoAncillaryDID16::from_glib(
32                (((self.inner.DID as u16) << 8) + self.inner.SDID_block_number as u16)
33                    as ffi::GstVideoAncillaryDID16,
34            )
35        }
36    }
37
38    pub fn len(&self) -> usize {
39        self.inner.data_count as usize
40    }
41
42    pub fn is_empty(&self) -> bool {
43        self.inner.data_count == 0
44    }
45
46    pub fn data(&self) -> &[u8] {
47        &self.inner.data[0..(self.inner.data_count as usize)]
48    }
49}
50
51impl fmt::Debug for VideoAncillary {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        f.debug_struct("VideoAncillary")
54            .field("did", &self.did())
55            .field("sdid_block_number", &self.sdid_block_number())
56            .field("did16", &self.did16())
57            .field("data_count", &self.inner.data_count)
58            .finish()
59    }
60}
61
62glib::wrapper! {
63    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
64    struct VideoVBIParserInner(Boxed<ffi::GstVideoVBIParser>);
65
66    match fn {
67        copy => |ptr| ffi::gst_video_vbi_parser_copy(ptr),
68        free => |ptr| ffi::gst_video_vbi_parser_free(ptr),
69        type_ => || ffi::gst_video_vbi_parser_get_type(),
70    }
71}
72
73#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
74pub struct VideoVBIParser {
75    inner: VideoVBIParserInner,
76    line_buffer_len: usize,
77}
78
79impl VideoVBIParser {
80    #[doc(alias = "gst_video_vbi_parser_new")]
81    pub fn try_new(format: VideoFormat, pixel_width: u32) -> Result<VideoVBIParser, VideoVBIError> {
82        skip_assert_initialized!();
83        let res: Option<VideoVBIParserInner> = unsafe {
84            from_glib_full(ffi::gst_video_vbi_parser_new(
85                format.into_glib(),
86                pixel_width,
87            ))
88        };
89
90        Ok(VideoVBIParser {
91            inner: res.ok_or(VideoVBIError::Unsupported)?,
92            line_buffer_len: line_buffer_len(format, pixel_width),
93        })
94    }
95
96    // rustdoc-stripper-ignore-next
97    /// Returns the buffer length needed to store the line.
98    pub fn line_buffer_len(&self) -> usize {
99        self.line_buffer_len
100    }
101
102    #[doc(alias = "gst_video_vbi_parser_add_line")]
103    pub fn add_line(&mut self, data: &[u8]) -> Result<(), VideoVBIError> {
104        if data.len() < self.line_buffer_len {
105            return Err(VideoVBIError::InsufficientLineBufLen {
106                found: data.len(),
107                expected: self.line_buffer_len,
108            });
109        }
110        unsafe {
111            let data = data.as_ptr();
112            ffi::gst_video_vbi_parser_add_line(self.inner.to_glib_none_mut().0, data);
113        }
114
115        Ok(())
116    }
117
118    pub fn iter(&mut self) -> AncillaryIter<'_> {
119        AncillaryIter { parser: self }
120    }
121
122    #[doc(alias = "gst_video_vbi_parser_get_ancillary")]
123    pub fn next_ancillary(&mut self) -> Option<Result<VideoAncillary, VideoVBIError>> {
124        unsafe {
125            let mut video_anc = std::mem::MaybeUninit::uninit();
126            let res = ffi::gst_video_vbi_parser_get_ancillary(
127                self.inner.to_glib_none_mut().0,
128                video_anc.as_mut_ptr(),
129            );
130
131            match res {
132                ffi::GST_VIDEO_VBI_PARSER_RESULT_OK => Some(Ok(VideoAncillary {
133                    inner: video_anc.assume_init(),
134                })),
135                ffi::GST_VIDEO_VBI_PARSER_RESULT_DONE => None,
136                ffi::GST_VIDEO_VBI_PARSER_RESULT_ERROR => Some(Err(VideoVBIError::NotEnoughData)),
137                _ => unreachable!(),
138            }
139        }
140    }
141}
142
143unsafe impl Send for VideoVBIParser {}
144unsafe impl Sync for VideoVBIParser {}
145
146impl<'a> TryFrom<&'a crate::VideoInfo> for VideoVBIParser {
147    type Error = VideoVBIError;
148
149    fn try_from(info: &'a crate::VideoInfo) -> Result<VideoVBIParser, VideoVBIError> {
150        skip_assert_initialized!();
151        VideoVBIParser::try_new(info.format(), info.width())
152    }
153}
154
155#[must_use = "iterators are lazy and do nothing unless consumed"]
156#[derive(Debug)]
157pub struct AncillaryIter<'a> {
158    parser: &'a mut VideoVBIParser,
159}
160
161impl Iterator for AncillaryIter<'_> {
162    type Item = Result<VideoAncillary, VideoVBIError>;
163
164    fn next(&mut self) -> Option<Self::Item> {
165        self.parser.next_ancillary()
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172    use crate::VBI_HD_MIN_PIXEL_WIDTH;
173
174    fn init_line_buf(parser: &VideoVBIParser, anc_buf: &[u8]) -> Vec<u8> {
175        skip_assert_initialized!();
176        let mut line_buf = vec![0; parser.line_buffer_len()];
177        line_buf[0..anc_buf.len()].copy_from_slice(anc_buf);
178        line_buf
179    }
180
181    #[test]
182    fn cea608_component() {
183        let mut parser =
184            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
185        let line_buf = init_line_buf(
186            &parser,
187            &[
188                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
189                0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
190                0x00, 0x00, 0x00, 0x00,
191            ],
192        );
193        parser.add_line(&line_buf).unwrap();
194
195        let video_anc = parser.next_ancillary().unwrap().unwrap();
196        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia608);
197        assert_eq!(video_anc.data(), [0x80, 0x94, 0x2c]);
198
199        assert!(parser.next_ancillary().is_none());
200    }
201
202    #[test]
203    fn cea608_composite() {
204        let mut parser =
205            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
206        let line_buf = init_line_buf(
207            &parser,
208            &[
209                0x00, 0xf0, 0x0f, 0x00, 0x61, 0x01, 0x20, 0x10, 0x00, 0x0c, 0x08, 0x00, 0x15, 0x01,
210                0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0x3b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211                0x00, 0x00, 0x00, 0x00,
212            ],
213        );
214        parser.add_line(&line_buf).unwrap();
215
216        let video_anc = parser.next_ancillary().unwrap().unwrap();
217        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia608);
218        assert_eq!(video_anc.data(), [0x15, 0x94, 0x2c]);
219
220        assert!(parser.next_ancillary().is_none());
221    }
222
223    #[test]
224    fn cea608_can_not_parse() {
225        let mut parser =
226            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
227        let line_buf = init_line_buf(&parser, &[0x00, 0xf0, 0x0f, 0x00, 0x61, 0x01, 0x20, 0x10]);
228        parser.add_line(&line_buf).unwrap();
229
230        assert!(parser.next_ancillary().is_none());
231    }
232
233    #[test]
234    fn cea608_insufficient_line_buf_len() {
235        let mut parser =
236            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
237        let line_buf = vec![0; 10];
238
239        assert_eq!(
240            parser.add_line(&line_buf).unwrap_err(),
241            VideoVBIError::InsufficientLineBufLen {
242                found: 10,
243                expected: parser.line_buffer_len()
244            },
245        );
246    }
247
248    #[test]
249    fn cea708_component() {
250        let mut parser =
251            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
252        let line_buf = init_line_buf(
253            &parser,
254            &[
255                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x01, 0x01,
256                0x50, 0x25, 0x00, 0x58, 0x0a, 0x00, 0x69, 0x02, 0x50, 0x25, 0x00, 0xfc, 0x08, 0x00,
257                0x43, 0x01, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x72, 0x02, 0x80, 0x1f, 0x00, 0xf0,
258                0x0b, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0xe4, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
259                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
260                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
261                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
262                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
263                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
264                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
265                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
266                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
267                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
268                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
269                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
270                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
271                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x02,
272                0x00, 0x20, 0x00, 0x6c, 0x08, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
273                0x00, 0x00, 0x00, 0x00,
274            ],
275        );
276        parser.add_line(&line_buf).unwrap();
277
278        let video_anc = parser.next_ancillary().unwrap().unwrap();
279        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia708);
280        assert_eq!(
281            video_anc.data(),
282            [
283                0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9, 0x00,
284                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
285                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
286                0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
287                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
288                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x74, 0x00, 0x00,
289                0x1b,
290            ]
291        );
292
293        assert!(parser.next_ancillary().is_none());
294    }
295
296    #[test]
297    fn cea608_and_cea708_component() {
298        let mut parser =
299            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
300        let mut line_buf = vec![0; parser.line_buffer_len()];
301        let anc_buf = [
302            0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
303            0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
304            0x00, 0x00, 0xf0, 0x3f, 0x00, 0xfc, 0x0f, 0x00, 0x61, 0x01, 0x10, 0x10, 0x00, 0x54,
305            0x09, 0x00, 0x96, 0x02, 0x90, 0x26, 0x00, 0x54, 0x09, 0x00, 0x3f, 0x02, 0x30, 0x14,
306            0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x20, 0x27, 0x00, 0xe0, 0x07, 0x00, 0xfc, 0x02,
307            0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0xf9, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
308            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
309            0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
310            0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
311            0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
312            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
313            0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
314            0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
315            0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
316            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
317            0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
318            0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
319            0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
320            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x74, 0x02, 0x00, 0x20, 0x00, 0x00,
321            0x08, 0x00, 0x1b, 0x02, 0x70, 0x2b,
322        ];
323        line_buf[0..anc_buf.len()].copy_from_slice(&anc_buf);
324        parser.add_line(&line_buf).unwrap();
325
326        let mut anc_iter = parser.iter();
327
328        let video_anc = anc_iter.next().unwrap().unwrap();
329        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia608);
330        assert_eq!(video_anc.data(), [0x80, 0x94, 0x2c]);
331
332        let video_anc = anc_iter.next().unwrap().unwrap();
333        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia708);
334        assert_eq!(
335            video_anc.data(),
336            [
337                0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9, 0x00,
338                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
339                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
340                0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
341                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
342                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x74, 0x00, 0x00,
343                0x1b,
344            ]
345        );
346
347        assert!(anc_iter.next().is_none());
348    }
349}