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#[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}