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