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