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")]
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 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}