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