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#[derive(Debug)]
156pub struct AncillaryIter<'a> {
157    parser: &'a mut VideoVBIParser,
158}
159
160impl Iterator for AncillaryIter<'_> {
161    type Item = Result<VideoAncillary, VideoVBIError>;
162
163    fn next(&mut self) -> Option<Self::Item> {
164        self.parser.next_ancillary()
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use crate::VBI_HD_MIN_PIXEL_WIDTH;
172
173    fn init_line_buf(parser: &VideoVBIParser, anc_buf: &[u8]) -> Vec<u8> {
174        skip_assert_initialized!();
175        let mut line_buf = vec![0; parser.line_buffer_len()];
176        line_buf[0..anc_buf.len()].copy_from_slice(anc_buf);
177        line_buf
178    }
179
180    #[test]
181    fn cea608_component() {
182        let mut parser =
183            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
184        let line_buf = init_line_buf(
185            &parser,
186            &[
187                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
188                0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
189                0x00, 0x00, 0x00, 0x00,
190            ],
191        );
192        parser.add_line(&line_buf).unwrap();
193
194        let video_anc = parser.next_ancillary().unwrap().unwrap();
195        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia608);
196        assert_eq!(video_anc.data(), [0x80, 0x94, 0x2c]);
197
198        assert!(parser.next_ancillary().is_none());
199    }
200
201    #[test]
202    fn cea608_composite() {
203        let mut parser =
204            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
205        let line_buf = init_line_buf(
206            &parser,
207            &[
208                0x00, 0xf0, 0x0f, 0x00, 0x61, 0x01, 0x20, 0x10, 0x00, 0x0c, 0x08, 0x00, 0x15, 0x01,
209                0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0x3b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
210                0x00, 0x00, 0x00, 0x00,
211            ],
212        );
213        parser.add_line(&line_buf).unwrap();
214
215        let video_anc = parser.next_ancillary().unwrap().unwrap();
216        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia608);
217        assert_eq!(video_anc.data(), [0x15, 0x94, 0x2c]);
218
219        assert!(parser.next_ancillary().is_none());
220    }
221
222    #[test]
223    fn cea608_can_not_parse() {
224        let mut parser =
225            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
226        let line_buf = init_line_buf(&parser, &[0x00, 0xf0, 0x0f, 0x00, 0x61, 0x01, 0x20, 0x10]);
227        parser.add_line(&line_buf).unwrap();
228
229        assert!(parser.next_ancillary().is_none());
230    }
231
232    #[test]
233    fn cea608_insufficient_line_buf_len() {
234        let mut parser =
235            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
236        let line_buf = vec![0; 10];
237
238        assert_eq!(
239            parser.add_line(&line_buf).unwrap_err(),
240            VideoVBIError::InsufficientLineBufLen {
241                found: 10,
242                expected: parser.line_buffer_len()
243            },
244        );
245    }
246
247    #[test]
248    fn cea708_component() {
249        let mut parser =
250            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
251        let line_buf = init_line_buf(
252            &parser,
253            &[
254                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x01, 0x01,
255                0x50, 0x25, 0x00, 0x58, 0x0a, 0x00, 0x69, 0x02, 0x50, 0x25, 0x00, 0xfc, 0x08, 0x00,
256                0x43, 0x01, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x72, 0x02, 0x80, 0x1f, 0x00, 0xf0,
257                0x0b, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0xe4, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
258                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
259                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
260                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
261                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
262                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
263                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
264                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
265                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
266                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
267                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
268                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
269                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
270                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x02,
271                0x00, 0x20, 0x00, 0x6c, 0x08, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
272                0x00, 0x00, 0x00, 0x00,
273            ],
274        );
275        parser.add_line(&line_buf).unwrap();
276
277        let video_anc = parser.next_ancillary().unwrap().unwrap();
278        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia708);
279        assert_eq!(
280            video_anc.data(),
281            [
282                0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9, 0x00,
283                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
284                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
285                0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
286                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
287                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x74, 0x00, 0x00,
288                0x1b,
289            ]
290        );
291
292        assert!(parser.next_ancillary().is_none());
293    }
294
295    #[test]
296    fn cea608_and_cea708_component() {
297        let mut parser =
298            VideoVBIParser::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
299        let mut line_buf = vec![0; parser.line_buffer_len()];
300        let anc_buf = [
301            0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
302            0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
303            0x00, 0x00, 0xf0, 0x3f, 0x00, 0xfc, 0x0f, 0x00, 0x61, 0x01, 0x10, 0x10, 0x00, 0x54,
304            0x09, 0x00, 0x96, 0x02, 0x90, 0x26, 0x00, 0x54, 0x09, 0x00, 0x3f, 0x02, 0x30, 0x14,
305            0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x20, 0x27, 0x00, 0xe0, 0x07, 0x00, 0xfc, 0x02,
306            0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0xf9, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
307            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
308            0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
309            0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
310            0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
311            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
312            0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
313            0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
314            0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
315            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
316            0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
317            0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
318            0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
319            0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x74, 0x02, 0x00, 0x20, 0x00, 0x00,
320            0x08, 0x00, 0x1b, 0x02, 0x70, 0x2b,
321        ];
322        line_buf[0..anc_buf.len()].copy_from_slice(&anc_buf);
323        parser.add_line(&line_buf).unwrap();
324
325        let mut anc_iter = parser.iter();
326
327        let video_anc = anc_iter.next().unwrap().unwrap();
328        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia608);
329        assert_eq!(video_anc.data(), [0x80, 0x94, 0x2c]);
330
331        let video_anc = anc_iter.next().unwrap().unwrap();
332        assert_eq!(video_anc.did16(), VideoAncillaryDID16::S334Eia708);
333        assert_eq!(
334            video_anc.data(),
335            [
336                0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9, 0x00,
337                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
338                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
339                0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
340                0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
341                0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x74, 0x00, 0x00,
342                0x1b,
343            ]
344        );
345
346        assert!(anc_iter.next().is_none());
347    }
348}