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::{ffi, VideoFormat};
4use glib::translate::*;
5
6use crate::video_vbi::line_buffer_len;
7use crate::{VideoAncillaryDID, VideoAncillaryDID16, VideoVBIError, VBI_HD_MIN_PIXEL_WIDTH};
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    #[doc(alias = "gst_video_vbi_encoder_write_line")]
186    pub fn write_line(&mut self, data: &mut [u8]) -> Result<usize, VideoVBIError> {
187        if data.len() < self.line_buffer_len {
188            return Err(VideoVBIError::InsufficientLineBufLen {
189                found: data.len(),
190                expected: self.line_buffer_len,
191            });
192        }
193
194        unsafe {
195            let dest = data.as_mut_ptr();
196            ffi::gst_video_vbi_encoder_write_line(self.inner.to_glib_none_mut().0, dest);
197        }
198
199        let mut anc_len = std::mem::take(&mut self.anc_len);
200        match self.format {
201            VideoFormat::V210 => {
202                // Anc data consists in 10bits stored in 16bits word
203                let word_count = anc_len / 2;
204
205                if self.pixel_width < VBI_HD_MIN_PIXEL_WIDTH {
206                    // SD: Packs 12x 10bits data in 4x 32bits word
207                    anc_len = 4 * 4 * word_count.div_ceil(12);
208                } else {
209                    // HD: Packs 3x 10bits data in 1x 32bits word interleaving UV and Y components
210                    //     (where Y starts at buffer offset 0 and UV starts at buffer offset pixel_width)
211                    //     so we get 6 (uv,y) pairs every 4x 32bits word in the resulting line
212                    let pair_count = usize::min(word_count, self.pixel_width as usize);
213                    anc_len = 4 * 4 * pair_count.div_ceil(6);
214                }
215            }
216            VideoFormat::Uyvy => {
217                // Anc data stored as bytes
218
219                if self.pixel_width < VBI_HD_MIN_PIXEL_WIDTH {
220                    // SD: Stores 4x bytes in 4x bytes let's keep 32 bits alignment
221                    anc_len = 4 * anc_len.div_ceil(4);
222                } else {
223                    // HD: Stores 4x bytes in 4x bytes interleaving UV and Y components
224                    //     (where Y starts at buffer offset 0 and UV starts at buffer offset pixel_width)
225                    //     so we get 2 (uv,y) pairs every 4x bytes in the resulting line
226                    // let's keep 32 bits alignment
227                    let pair_count = usize::min(anc_len, self.pixel_width as usize);
228                    anc_len = 4 * pair_count.div_ceil(2);
229                }
230            }
231            _ => unreachable!(),
232        }
233
234        assert!(anc_len < self.line_buffer_len);
235
236        Ok(anc_len)
237    }
238}
239
240impl<'a> TryFrom<&'a crate::VideoInfo> for VideoVBIEncoder {
241    type Error = VideoVBIError;
242
243    fn try_from(info: &'a crate::VideoInfo) -> Result<VideoVBIEncoder, VideoVBIError> {
244        skip_assert_initialized!();
245        VideoVBIEncoder::try_new(info.format(), info.width())
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use super::*;
252
253    #[test]
254    fn cea608_component() {
255        let mut encoder =
256            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
257        encoder
258            .add_did16_ancillary(
259                VideoAFDDescriptionMode::Component,
260                VideoAncillaryDID16::S334Eia608,
261                &[0x80, 0x94, 0x2c],
262            )
263            .unwrap();
264
265        let mut buf = vec![0; encoder.line_buffer_len()];
266        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
267        assert_eq!(32, anc_len);
268        assert_eq!(
269            buf[0..anc_len],
270            [
271                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
272                0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
273                0x00, 0x00, 0x00, 0x00
274            ]
275        );
276    }
277
278    #[test]
279    fn cea608_component_sd() {
280        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::V210, 768).unwrap();
281        encoder
282            .add_did16_ancillary(
283                VideoAFDDescriptionMode::Component,
284                VideoAncillaryDID16::S334Eia608,
285                &[0x80, 0x94, 0x2c],
286            )
287            .unwrap();
288
289        let mut buf = vec![0; encoder.line_buffer_len()];
290        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
291        assert_eq!(16, anc_len);
292        assert_eq!(
293            buf[0..anc_len],
294            [
295                0x00, 0xfc, 0xff, 0x3f, 0x61, 0x09, 0x34, 0x20, 0x80, 0x51, 0xc6, 0x12, 0xa6, 0x02,
296                0x00, 0x00
297            ]
298        );
299    }
300
301    #[test]
302    fn cea608_composite() {
303        let mut encoder =
304            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
305        encoder
306            .add_did16_ancillary(
307                VideoAFDDescriptionMode::Composite,
308                VideoAncillaryDID16::S334Eia608,
309                &[0x15, 0x94, 0x2c],
310            )
311            .unwrap();
312
313        let mut buf = vec![0; encoder.line_buffer_len()];
314        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
315        assert_eq!(32, anc_len);
316        assert_eq!(
317            buf[0..anc_len],
318            [
319                0x00, 0xf0, 0x0f, 0x00, 0x61, 0x01, 0x20, 0x10, 0x00, 0x0c, 0x08, 0x00, 0x15, 0x01,
320                0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0x3b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321                0x00, 0x00, 0x00, 0x00
322            ]
323        );
324    }
325
326    #[test]
327    fn cea608_composite_sd() {
328        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::V210, 768).unwrap();
329        encoder
330            .add_did16_ancillary(
331                VideoAFDDescriptionMode::Composite,
332                VideoAncillaryDID16::S334Eia608,
333                &[0x15, 0x94, 0x2c],
334            )
335            .unwrap();
336
337        let mut buf = vec![0; encoder.line_buffer_len()];
338        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
339        assert_eq!(16, anc_len);
340        assert_eq!(
341            buf[0..anc_len],
342            [
343                0xfc, 0x87, 0x25, 0x10, 0x03, 0x56, 0x44, 0x19, 0x2c, 0xed, 0x08, 0x00, 0x00, 0x00,
344                0x00, 0x00
345            ]
346        );
347    }
348
349    #[test]
350    fn cea608_component_uyvy() {
351        let mut encoder =
352            VideoVBIEncoder::try_new(VideoFormat::Uyvy, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
353        encoder
354            .add_did16_ancillary(
355                VideoAFDDescriptionMode::Component,
356                VideoAncillaryDID16::S334Eia608,
357                &[0x80, 0x94, 0x2c],
358            )
359            .unwrap();
360
361        let mut buf = vec![0; encoder.line_buffer_len()];
362        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
363        assert_eq!(20, anc_len);
364        assert_eq!(
365            buf[0..anc_len],
366            [
367                0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x61, 0x00, 0x02, 0x00, 0x03, 0x00, 0x80,
368                0x00, 0x94, 0x00, 0x2c, 0x00, 0xa6
369            ]
370        );
371    }
372
373    #[test]
374    fn cea608_component_sd_uyvy() {
375        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::Uyvy, 768).unwrap();
376        encoder
377            .add_did16_ancillary(
378                VideoAFDDescriptionMode::Component,
379                VideoAncillaryDID16::S334Eia608,
380                &[0x80, 0x94, 0x2c],
381            )
382            .unwrap();
383
384        let mut buf = vec![0; encoder.line_buffer_len()];
385        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
386        assert_eq!(12, anc_len);
387        assert_eq!(
388            buf[0..anc_len],
389            [0x00, 0xff, 0xff, 0x61, 0x02, 0x03, 0x80, 0x94, 0x2c, 0xa6, 0x00, 0x00]
390        );
391    }
392
393    #[test]
394    fn cea608_composite_uyvy() {
395        let mut encoder =
396            VideoVBIEncoder::try_new(VideoFormat::Uyvy, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
397        encoder
398            .add_did16_ancillary(
399                VideoAFDDescriptionMode::Composite,
400                VideoAncillaryDID16::S334Eia608,
401                &[0x15, 0x94, 0x2c],
402            )
403            .unwrap();
404
405        let mut buf = vec![0; encoder.line_buffer_len()];
406        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
407        assert_eq!(16, anc_len);
408        assert_eq!(
409            buf[0..anc_len],
410            [
411                0x00, 0xfc, 0x00, 0x61, 0x00, 0x02, 0x00, 0x03, 0x00, 0x15, 0x00, 0x94, 0x00, 0x2c,
412                0x00, 0x3b
413            ]
414        );
415    }
416
417    #[test]
418    fn cea608_composite_sd_uyvy() {
419        let mut encoder = VideoVBIEncoder::try_new(VideoFormat::Uyvy, 768).unwrap();
420        encoder
421            .add_did16_ancillary(
422                VideoAFDDescriptionMode::Composite,
423                VideoAncillaryDID16::S334Eia608,
424                &[0x15, 0x94, 0x2c],
425            )
426            .unwrap();
427
428        let mut buf = vec![0; encoder.line_buffer_len()];
429        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
430        assert_eq!(8, anc_len);
431        assert_eq!(
432            buf[0..anc_len],
433            [0xfc, 0x61, 0x02, 0x03, 0x15, 0x94, 0x2c, 0x3b]
434        );
435    }
436
437    #[test]
438    fn insufficient_line_buf_len() {
439        let mut encoder =
440            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
441        encoder
442            .add_did16_ancillary(
443                VideoAFDDescriptionMode::Component,
444                VideoAncillaryDID16::S334Eia608,
445                &[0x80, 0x94, 0x2c],
446            )
447            .unwrap();
448        let mut buf = vec![0; 10];
449        assert_eq!(
450            encoder.write_line(buf.as_mut_slice()).unwrap_err(),
451            VideoVBIError::InsufficientLineBufLen {
452                found: 10,
453                expected: encoder.line_buffer_len()
454            },
455        );
456    }
457
458    #[test]
459    fn cea708_component() {
460        let mut encoder =
461            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
462        encoder
463            .add_did16_ancillary(
464                VideoAFDDescriptionMode::Component,
465                VideoAncillaryDID16::S334Eia708,
466                &[
467                    0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9,
468                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
469                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
470                    0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
471                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
472                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
473                    0xfa, 0x00, 0x00, 0x74, 0x00, 0x00, 0x1b,
474                ],
475            )
476            .unwrap();
477
478        let mut buf = vec![0; encoder.line_buffer_len()];
479        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
480        assert_eq!(256, anc_len);
481        assert_eq!(
482            buf[0..anc_len],
483            [
484                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x01, 0x01,
485                0x50, 0x25, 0x00, 0x58, 0x0a, 0x00, 0x69, 0x02, 0x50, 0x25, 0x00, 0xfc, 0x08, 0x00,
486                0x43, 0x01, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x72, 0x02, 0x80, 0x1f, 0x00, 0xf0,
487                0x0b, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0xe4, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
488                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
489                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
490                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
491                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
492                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
493                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
494                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
495                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
496                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02,
497                0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00,
498                0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8,
499                0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20,
500                0x00, 0xe8, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x02,
501                0x00, 0x20, 0x00, 0x6c, 0x08, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502                0x00, 0x00, 0x00, 0x00
503            ]
504        );
505    }
506
507    #[test]
508    fn cea608_and_cea708_component() {
509        let mut encoder =
510            VideoVBIEncoder::try_new(VideoFormat::V210, VBI_HD_MIN_PIXEL_WIDTH).unwrap();
511        encoder
512            .add_did16_ancillary(
513                VideoAFDDescriptionMode::Component,
514                VideoAncillaryDID16::S334Eia608,
515                &[0x80, 0x94, 0x2c],
516            )
517            .unwrap();
518
519        encoder
520            .add_did16_ancillary(
521                VideoAFDDescriptionMode::Component,
522                VideoAncillaryDID16::S334Eia708,
523                &[
524                    0x96, 0x69, 0x55, 0x3f, 0x43, 0x00, 0x00, 0x72, 0xf8, 0xfc, 0x94, 0x2c, 0xf9,
525                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
526                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
527                    0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
528                    0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
529                    0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
530                    0xfa, 0x00, 0x00, 0x74, 0x00, 0x00, 0x1b,
531                ],
532            )
533            .unwrap();
534
535        let mut buf = vec![0; encoder.line_buffer_len()];
536        let anc_len = encoder.write_line(buf.as_mut_slice()).unwrap();
537        assert_eq!(272, anc_len);
538        assert_eq!(
539            buf[0..anc_len],
540            [
541                0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xf0, 0x3f, 0x00, 0x84, 0x05, 0x00, 0x02, 0x01,
542                0x30, 0x20, 0x00, 0x00, 0x06, 0x00, 0x94, 0x01, 0xc0, 0x12, 0x00, 0x98, 0x0a, 0x00,
543                0x00, 0x00, 0xf0, 0x3f, 0x00, 0xfc, 0x0f, 0x00, 0x61, 0x01, 0x10, 0x10, 0x00, 0x54,
544                0x09, 0x00, 0x96, 0x02, 0x90, 0x26, 0x00, 0x54, 0x09, 0x00, 0x3f, 0x02, 0x30, 0x14,
545                0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x20, 0x27, 0x00, 0xe0, 0x07, 0x00, 0xfc, 0x02,
546                0x40, 0x19, 0x00, 0xb0, 0x04, 0x00, 0xf9, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
547                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
548                0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
549                0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
550                0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
551                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
552                0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
553                0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
554                0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
555                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00,
556                0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20,
557                0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02,
558                0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00,
559                0xfa, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x74, 0x02, 0x00, 0x20, 0x00, 0x00,
560                0x08, 0x00, 0x1b, 0x02, 0x70, 0x2b
561            ]
562        );
563    }
564}