Skip to main content

gstreamer_video/
video_vbi_encoder.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{VideoFormat, ffi};
4use glib::translate::*;
5
6use crate::video_vbi::line_buffer_len;
7use crate::{VBI_HD_MIN_PIXEL_WIDTH, VideoAncillaryDID, VideoAncillaryDID16, VideoVBIError};
8
9glib::wrapper! {
10    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
11    struct VideoVBIEncoderInner(Boxed<ffi::GstVideoVBIEncoder>);
12
13    match fn {
14        copy => |ptr| ffi::gst_video_vbi_encoder_copy(ptr),
15        free => |ptr| ffi::gst_video_vbi_encoder_free(ptr),
16        type_ => || ffi::gst_video_vbi_encoder_get_type(),
17    }
18}
19
20/// An encoder for writing ancillary data to the
21/// Vertical Blanking Interval lines of component signals.
22#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct VideoVBIEncoder {
24    inner: VideoVBIEncoderInner,
25    format: VideoFormat,
26    pixel_width: u32,
27    line_buffer_len: usize,
28    anc_len: usize,
29}
30
31unsafe impl Send for VideoVBIEncoder {}
32unsafe impl Sync for VideoVBIEncoder {}
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub enum VideoAFDDescriptionMode {
36    Composite,
37    Component,
38}
39
40impl VideoAFDDescriptionMode {
41    pub fn is_composite(&self) -> bool {
42        matches!(self, VideoAFDDescriptionMode::Composite)
43    }
44
45    pub fn is_component(&self) -> bool {
46        matches!(self, VideoAFDDescriptionMode::Component)
47    }
48}
49
50impl VideoVBIEncoder {
51    #[doc(alias = "gst_video_vbi_encoder_new")]
52    pub fn try_new(
53        format: VideoFormat,
54        pixel_width: u32,
55    ) -> Result<VideoVBIEncoder, VideoVBIError> {
56        skip_assert_initialized!();
57        let res: Option<VideoVBIEncoderInner> = unsafe {
58            from_glib_full(ffi::gst_video_vbi_encoder_new(
59                format.into_glib(),
60                pixel_width,
61            ))
62        };
63
64        Ok(VideoVBIEncoder {
65            inner: res.ok_or(VideoVBIError::Unsupported)?,
66            format,
67            pixel_width,
68            line_buffer_len: line_buffer_len(format, pixel_width),
69            anc_len: 0,
70        })
71    }
72
73    // rustdoc-stripper-ignore-next
74    /// Adds the provided ancillary data as a DID and block number AFD.
75    pub fn add_did_ancillary(
76        &mut self,
77        adf_mode: VideoAFDDescriptionMode,
78        did: VideoAncillaryDID,
79        block_number: u8,
80        data: &[u8],
81    ) -> Result<(), VideoVBIError> {
82        self.add_ancillary(adf_mode, did.into_glib() as u8, block_number, data)
83    }
84
85    // rustdoc-stripper-ignore-next
86    /// Adds the provided ancillary data as a DID16 (DID & SDID) AFD.
87    pub fn add_did16_ancillary(
88        &mut self,
89        adf_mode: VideoAFDDescriptionMode,
90        did16: VideoAncillaryDID16,
91        data: &[u8],
92    ) -> Result<(), VideoVBIError> {
93        let did16 = did16.into_glib();
94
95        self.add_ancillary(
96            adf_mode,
97            ((did16 & 0xff00) >> 8) as u8,
98            (did16 & 0xff) as u8,
99            data,
100        )
101    }
102
103    /// Stores Video Ancillary data, according to SMPTE-291M specification.
104    ///
105    /// Note that the contents of the data are always read as 8bit data (i.e. do not contain
106    /// the parity check bits).
107    /// ## `composite`
108    /// [`true`] if composite ADF should be created, component otherwise
109    /// ## `DID`
110    /// The Data Identifier
111    /// ## `SDID_block_number`
112    /// The Secondary Data Identifier (if type 2) or the Data
113    ///  Block Number (if type 1)
114    /// ## `data`
115    /// The user data content of the Ancillary packet.
116    ///  Does not contain the ADF, DID, SDID nor CS.
117    ///
118    /// # Returns
119    ///
120    /// [`true`] if enough space was left in the current line, [`false`]
121    ///  otherwise.
122    #[doc(alias = "gst_video_vbi_encoder_add_ancillary")]
123    pub fn add_ancillary(
124        &mut self,
125        adf_mode: VideoAFDDescriptionMode,
126        did: u8,
127        sdid_block_number: u8,
128        data: &[u8],
129    ) -> Result<(), VideoVBIError> {
130        let data_count = data.len() as _;
131        let res: bool = unsafe {
132            from_glib(ffi::gst_video_vbi_encoder_add_ancillary(
133                self.inner.to_glib_none_mut().0,
134                adf_mode.is_composite().into_glib(),
135                did,
136                sdid_block_number,
137                data.to_glib_none().0,
138                data_count,
139            ))
140        };
141
142        if !res {
143            return Err(VideoVBIError::NotEnoughSpace);
144        }
145
146        // AFD: 1 byte (+2 if component)
147        // DID + SDID_block_number + Data Count: 3 bytes
148        // DATA: data_count bytes
149        // Checksum: 1 byte
150        let mut len = 1 + 3 + (data_count as usize) + 1;
151        if adf_mode.is_component() {
152            len += 2;
153        }
154
155        if matches!(self.format, VideoFormat::V210) {
156            // 10bits payload on 16bits for now: will be packed when writing the line
157            len *= 2;
158        }
159
160        self.anc_len += len;
161
162        Ok(())
163    }
164
165    // rustdoc-stripper-ignore-next
166    /// Returns the buffer length needed to store the line.
167    pub fn line_buffer_len(&self) -> usize {
168        self.line_buffer_len
169    }
170
171    // rustdoc-stripper-ignore-next
172    /// Writes the ancillaries encoded for VBI to the provided buffer.
173    ///
174    /// Use [`Self::line_buffer_len`] to get the expected buffer length.
175    ///
176    /// Resets the internal state, so this [`VideoVBIEncoder`] can be reused for
177    /// subsequent VBI encodings.
178    ///
179    /// # Returns
180    ///
181    /// - `Ok` with the written length in bytes in the line buffer containing the encoded
182    ///   ancilliaries previously added using [`VideoVBIEncoder::add_ancillary`],
183    ///   [`VideoVBIEncoder::add_did_ancillary`] or [`VideoVBIEncoder::add_did16_ancillary`].
184    /// - `Err` if the ancillary could not be added.
185    // rustdoc-stripper-ignore-next-stop
186    /// `data` needs to have the correct size and alignment for the configured video
187    /// format of `self`.
188    /// ## `data`
189    /// The line to write to
190    #[doc(alias = "gst_video_vbi_encoder_write_line")]
191    pub fn write_line(&mut self, data: &mut [u8]) -> Result<usize, VideoVBIError> {
192        if data.len() < self.line_buffer_len {
193            return Err(VideoVBIError::InsufficientLineBufLen {
194                found: data.len(),
195                expected: self.line_buffer_len,
196            });
197        }
198
199        unsafe {
200            let dest = data.as_mut_ptr();
201            ffi::gst_video_vbi_encoder_write_line(self.inner.to_glib_none_mut().0, dest);
202        }
203
204        let mut anc_len = std::mem::take(&mut self.anc_len);
205        match self.format {
206            VideoFormat::V210 => {
207                // Anc data consists in 10bits stored in 16bits word
208                let word_count = anc_len / 2;
209
210                if self.pixel_width < VBI_HD_MIN_PIXEL_WIDTH {
211                    // SD: Packs 12x 10bits data in 4x 32bits word
212                    anc_len = 4 * 4 * word_count.div_ceil(12);
213                } else {
214                    // HD: Packs 3x 10bits data in 1x 32bits word interleaving UV and Y components
215                    //     (where Y starts at buffer offset 0 and UV starts at buffer offset pixel_width)
216                    //     so we get 6 (uv,y) pairs every 4x 32bits word in the resulting line
217                    let pair_count = usize::min(word_count, self.pixel_width as usize);
218                    anc_len = 4 * 4 * pair_count.div_ceil(6);
219                }
220            }
221            VideoFormat::Uyvy => {
222                // Anc data stored as bytes
223
224                if self.pixel_width < VBI_HD_MIN_PIXEL_WIDTH {
225                    // SD: Stores 4x bytes in 4x bytes let's keep 32 bits alignment
226                    anc_len = 4 * anc_len.div_ceil(4);
227                } else {
228                    // HD: Stores 4x bytes in 4x bytes interleaving UV and Y components
229                    //     (where Y starts at buffer offset 0 and UV starts at buffer offset pixel_width)
230                    //     so we get 2 (uv,y) pairs every 4x bytes in the resulting line
231                    // let's keep 32 bits alignment
232                    let pair_count = usize::min(anc_len, self.pixel_width as usize);
233                    anc_len = 4 * pair_count.div_ceil(2);
234                }
235            }
236            _ => unreachable!(),
237        }
238
239        assert!(anc_len < self.line_buffer_len);
240
241        Ok(anc_len)
242    }
243}
244
245impl<'a> TryFrom<&'a crate::VideoInfo> for VideoVBIEncoder {
246    type Error = VideoVBIError;
247
248    fn try_from(info: &'a crate::VideoInfo) -> Result<VideoVBIEncoder, VideoVBIError> {
249        skip_assert_initialized!();
250        VideoVBIEncoder::try_new(info.format(), info.width())
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn cea608_component() {
260        let mut encoder =
261            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
262        encoder
263            .add_did16_ancillary(
264                VideoAFDDescriptionMode::Component,
265                VideoAncillaryDID16::S334Eia608,
266                &[0x80, 0x94, 0x2c],
267            )
268            .unwrap();
269
270        let mut buf = vec![0; encoder.line_buffer_len()];
271        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
272        assert_eq!(32, anc_len);
273        assert_eq!(
274            buf[0..anc_len],
275            [
276                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
277                0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
278                0x00, 0x00, 0x00, 0x00
279            ]
280        );
281    }
282
283    #[test]
284    fn cea608_component_sd() {
285        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::V210, 768).unwrap();
286        encoder
287            .add_did16_ancillary(
288                VideoAFDDescriptionMode::Component,
289                VideoAncillaryDID16::S334Eia608,
290                &[0x80, 0x94, 0x2c],
291            )
292            .unwrap();
293
294        let mut buf = vec![0; encoder.line_buffer_len()];
295        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
296        assert_eq!(16, anc_len);
297        assert_eq!(
298            buf[0..anc_len],
299            [
300                0x00, 0xfc, 0xff, 0x3f, 0x61, 0x09, 0x34, 0x20, 0x80, 0x51, 0xc6, 0x12, 0xa6, 0x02,
301                0x00, 0x00
302            ]
303        );
304    }
305
306    #[test]
307    fn cea608_composite() {
308        let mut encoder =
309            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
310        encoder
311            .add_did16_ancillary(
312                VideoAFDDescriptionMode::Composite,
313                VideoAncillaryDID16::S334Eia608,
314                &[0x15, 0x94, 0x2c],
315            )
316            .unwrap();
317
318        let mut buf = vec![0; encoder.line_buffer_len()];
319        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
320        assert_eq!(32, anc_len);
321        assert_eq!(
322            buf[0..anc_len],
323            [
324                0x00, 0xf0, 0x0f, 0x00, 0x61, 0x01, 0x20, 0x10, 0x00, 0x0c, 0x08, 0x00, 0x15, 0x01,
325                0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0x3b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326                0x00, 0x00, 0x00, 0x00
327            ]
328        );
329    }
330
331    #[test]
332    fn cea608_composite_sd() {
333        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::V210, 768).unwrap();
334        encoder
335            .add_did16_ancillary(
336                VideoAFDDescriptionMode::Composite,
337                VideoAncillaryDID16::S334Eia608,
338                &[0x15, 0x94, 0x2c],
339            )
340            .unwrap();
341
342        let mut buf = vec![0; encoder.line_buffer_len()];
343        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
344        assert_eq!(16, anc_len);
345        assert_eq!(
346            buf[0..anc_len],
347            [
348                0xfc, 0x87, 0x25, 0x10, 0x03, 0x56, 0x44, 0x19, 0x2c, 0xed, 0x08, 0x00, 0x00, 0x00,
349                0x00, 0x00
350            ]
351        );
352    }
353
354    #[test]
355    fn cea608_component_uyvy() {
356        let mut encoder =
357            VideoVBIEncoder::try_new(VideoFormat::Uyvy, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
358        encoder
359            .add_did16_ancillary(
360                VideoAFDDescriptionMode::Component,
361                VideoAncillaryDID16::S334Eia608,
362                &[0x80, 0x94, 0x2c],
363            )
364            .unwrap();
365
366        let mut buf = vec![0; encoder.line_buffer_len()];
367        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
368        assert_eq!(20, anc_len);
369        assert_eq!(
370            buf[0..anc_len],
371            [
372                0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x61, 0x00, 0x02, 0x00, 0x03, 0x00, 0x80,
373                0x00, 0x94, 0x00, 0x2c, 0x00, 0xa6
374            ]
375        );
376    }
377
378    #[test]
379    fn cea608_component_sd_uyvy() {
380        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::Uyvy, 768).unwrap();
381        encoder
382            .add_did16_ancillary(
383                VideoAFDDescriptionMode::Component,
384                VideoAncillaryDID16::S334Eia608,
385                &[0x80, 0x94, 0x2c],
386            )
387            .unwrap();
388
389        let mut buf = vec![0; encoder.line_buffer_len()];
390        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
391        assert_eq!(12, anc_len);
392        assert_eq!(
393            buf[0..anc_len],
394            [
395                0x00, 0xff, 0xff, 0x61, 0x02, 0x03, 0x80, 0x94, 0x2c, 0xa6, 0x00, 0x00
396            ]
397        );
398    }
399
400    #[test]
401    fn cea608_composite_uyvy() {
402        let mut encoder =
403            VideoVBIEncoder::try_new(VideoFormat::Uyvy, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
404        encoder
405            .add_did16_ancillary(
406                VideoAFDDescriptionMode::Composite,
407                VideoAncillaryDID16::S334Eia608,
408                &[0x15, 0x94, 0x2c],
409            )
410            .unwrap();
411
412        let mut buf = vec![0; encoder.line_buffer_len()];
413        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
414        assert_eq!(16, anc_len);
415        assert_eq!(
416            buf[0..anc_len],
417            [
418                0x00, 0xfc, 0x00, 0x61, 0x00, 0x02, 0x00, 0x03, 0x00, 0x15, 0x00, 0x94, 0x00, 0x2c,
419                0x00, 0x3b
420            ]
421        );
422    }
423
424    #[test]
425    fn cea608_composite_sd_uyvy() {
426        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::Uyvy, 768).unwrap();
427        encoder
428            .add_did16_ancillary(
429                VideoAFDDescriptionMode::Composite,
430                VideoAncillaryDID16::S334Eia608,
431                &[0x15, 0x94, 0x2c],
432            )
433            .unwrap();
434
435        let mut buf = vec![0; encoder.line_buffer_len()];
436        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
437        assert_eq!(8, anc_len);
438        assert_eq!(
439            buf[0..anc_len],
440            [0xfc, 0x61, 0x02, 0x03, 0x15, 0x94, 0x2c, 0x3b]
441        );
442    }
443
444    #[test]
445    fn insufficient_line_buf_len() {
446        let mut encoder =
447            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
448        encoder
449            .add_did16_ancillary(
450                VideoAFDDescriptionMode::Component,
451                VideoAncillaryDID16::S334Eia608,
452                &[0x80, 0x94, 0x2c],
453            )
454            .unwrap();
455        let mut buf = vec![0; 10];
456        assert_eq!(
457            encoder.write_line(buf.as_mut_slice()).unwrap_err(),
458            VideoVBIError::InsufficientLineBufLen {
459                found: 10,
460                expected: encoder.line_buffer_len()
461            },
462        );
463    }
464
465    #[test]
466    fn cea708_component() {
467        let mut encoder =
468            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
469        encoder
470            .add_did16_ancillary(
471                VideoAFDDescriptionMode::Component,
472                VideoAncillaryDID16::S334Eia708,
473                &[
474                    0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9,
475                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
476                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
477                    0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
478                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
479                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
480                    0xfa, 0x00, 0x00, 0x74, 0x00, 0x00, 0x1b,
481                ],
482            )
483            .unwrap();
484
485        let mut buf = vec![0; encoder.line_buffer_len()];
486        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
487        assert_eq!(256, anc_len);
488        assert_eq!(
489            buf[0..anc_len],
490            [
491                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x01, 0x01,
492                0x50, 0x25, 0x00, 0x58, 0x0a, 0x00, 0x69, 0x02, 0x50, 0x25, 0x00, 0xfc, 0x08, 0x00,
493                0x43, 0x01, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x72, 0x02, 0x80, 0x1f, 0x00, 0xf0,
494                0x0b, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0xe4, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
495                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
496                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
497                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
498                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
499                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
500                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
501                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
502                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
503                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
504                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
505                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
506                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
507                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x02,
508                0x00, 0x20, 0x00, 0x6c, 0x08, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
509                0x00, 0x00, 0x00, 0x00
510            ]
511        );
512    }
513
514    #[test]
515    fn cea608_and_cea708_component() {
516        let mut encoder =
517            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
518        encoder
519            .add_did16_ancillary(
520                VideoAFDDescriptionMode::Component,
521                VideoAncillaryDID16::S334Eia608,
522                &[0x80, 0x94, 0x2c],
523            )
524            .unwrap();
525
526        encoder
527            .add_did16_ancillary(
528                VideoAFDDescriptionMode::Component,
529                VideoAncillaryDID16::S334Eia708,
530                &[
531                    0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9,
532                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
533                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
534                    0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
535                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
536                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
537                    0xfa, 0x00, 0x00, 0x74, 0x00, 0x00, 0x1b,
538                ],
539            )
540            .unwrap();
541
542        let mut buf = vec![0; encoder.line_buffer_len()];
543        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
544        assert_eq!(272, anc_len);
545        assert_eq!(
546            buf[0..anc_len],
547            [
548                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
549                0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
550                0x00, 0x00, 0xf0, 0x3f, 0x00, 0xfc, 0x0f, 0x00, 0x61, 0x01, 0x10, 0x10, 0x00, 0x54,
551                0x09, 0x00, 0x96, 0x02, 0x90, 0x26, 0x00, 0x54, 0x09, 0x00, 0x3f, 0x02, 0x30, 0x14,
552                0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x20, 0x27, 0x00, 0xe0, 0x07, 0x00, 0xfc, 0x02,
553                0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0xf9, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
554                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
555                0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
556                0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
557                0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
558                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
559                0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
560                0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
561                0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
562                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
563                0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
564                0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
565                0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
566                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x74, 0x02, 0x00, 0x20, 0x00, 0x00,
567                0x08, 0x00, 0x1b, 0x02, 0x70, 0x2b
568            ]
569        );
570    }
571}