1#![cfg_attr(not(feature = "std"), no_std)]
20#![cfg_attr(feature = "backtrace", feature(error_generic_member_access))]
21#![deny(unsafe_code)]
22
23#[cfg(not(feature = "std"))]
24extern crate core as std;
25
26extern crate alloc;
27use alloc::{vec, vec::Vec};
28
29#[cfg(feature = "backtrace")]
30use std::backtrace::Backtrace;
31
32use bitvec::prelude::{BitVec, Msb0};
33
34mod golomb;
35use golomb::BitVecGolomb;
36
37pub mod ycbcr_image;
38use ycbcr_image::*;
39
40pub mod nal_unit;
41use nal_unit::*;
42
43pub mod sei;
44
45#[cfg(feature = "std")]
46mod writer;
47#[cfg(feature = "std")]
48pub use writer::H264Writer;
49
50mod encoder;
51pub use encoder::LessEncoder;
52
53#[derive(Debug)]
57pub enum Error {
58 DataShapeProblem {
59 msg: &'static str,
60 #[cfg(feature = "backtrace")]
61 backtrace: Backtrace,
62 },
63 UnsupportedFormat {
64 #[cfg(feature = "backtrace")]
65 backtrace: Backtrace,
66 },
67 UnsupportedImageSize {
68 #[cfg(feature = "backtrace")]
69 backtrace: Backtrace,
70 },
71 InconsistentState {
72 #[cfg(feature = "backtrace")]
73 backtrace: Backtrace,
74 },
75 #[cfg(feature = "std")]
76 IoError {
77 source: std::io::Error,
78 #[cfg(feature = "backtrace")]
79 backtrace: Backtrace,
80 },
81}
82type Result<T> = std::result::Result<T, Error>;
83
84#[cfg(feature = "std")]
85impl From<std::io::Error> for Error {
86 fn from(source: std::io::Error) -> Self {
87 Error::IoError {
88 source,
89 #[cfg(feature = "backtrace")]
90 backtrace: Backtrace::capture(),
91 }
92 }
93}
94
95#[cfg(feature = "std")]
96impl std::error::Error for Error {
97 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
98 match self {
99 Error::IoError {
100 source,
101 #[cfg(feature = "backtrace")]
102 backtrace: _,
103 } => Some(source),
104 _ => None,
105 }
106 }
107}
108
109impl std::fmt::Display for Error {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
111 match self {
112 Error::DataShapeProblem {
113 msg,
114 #[cfg(feature = "backtrace")]
115 backtrace: _,
116 } => {
117 write!(f, "Image data shape is problematic: {msg}")
118 }
119 Error::UnsupportedFormat {
120 #[cfg(feature = "backtrace")]
121 backtrace: _,
122 } => {
123 write!(f, "unsupported format")
124 }
125 Error::UnsupportedImageSize {
126 #[cfg(feature = "backtrace")]
127 backtrace: _,
128 } => {
129 write!(f, "unsupported image size: even width and height required")
130 }
131 Error::InconsistentState {
132 #[cfg(feature = "backtrace")]
133 backtrace: _,
134 } => {
135 write!(f, "internal error: inconsistent state")
136 }
137 #[cfg(feature = "std")]
138 Error::IoError {
139 source,
140 #[cfg(feature = "backtrace")]
141 backtrace: _,
142 } => {
143 write!(f, "IO error: {source}")
144 }
145 }
146 }
147}
148
149#[inline]
152fn div_ceil(a: u32, b: u32) -> u32 {
153 (a + b - 1) / b
155}
156
157#[inline]
158fn next_multiple(a: u32, b: u32) -> u32 {
159 div_ceil(a, b) * b
160}
161
162#[derive(Debug, PartialEq, Eq)]
165#[allow(dead_code, clippy::upper_case_acronyms)]
166enum VideoFormat {
167 Component,
168 PAL,
169 NTSC,
170 SECAM,
171 MAC,
172 Unspecified,
173 Reserved,
174}
175
176#[derive(Debug, PartialEq, Eq)]
177struct TimingInfo {
178 num_units_in_tick: u32,
182
183 time_scale: u32,
185
186 fixed_frame_rate_flag: bool,
189}
190
191#[derive(Debug, PartialEq, Eq)]
192struct Vui {
193 full_range: bool,
197 video_format: VideoFormat,
198 timing_info: Option<TimingInfo>,
199}
200
201impl Vui {
202 fn new(full_range: bool) -> Self {
203 Self {
204 full_range,
205 video_format: VideoFormat::Unspecified,
206 timing_info: None,
207 }
208 }
209
210 fn append_to_rbsp(&self, bv: &mut BitVec<u8, Msb0>) {
211 bv.push(false);
216
217 bv.push(false);
219
220 bv.push(true);
222
223 let video_format_arr = match &self.video_format {
225 VideoFormat::Component => [false, false, false],
226 VideoFormat::PAL => [false, false, true],
227 VideoFormat::NTSC => [false, true, false],
228 VideoFormat::SECAM => [false, true, true],
229 VideoFormat::MAC => [true, false, false],
230 VideoFormat::Unspecified => [true, false, true],
231 VideoFormat::Reserved => [true, true, true],
232 };
233 bv.extend(video_format_arr);
234
235 bv.push(self.full_range);
237
238 bv.push(false);
240
241 bv.push(false);
243
244 if let Some(_timing_info) = &self.timing_info {
246 todo!();
247 } else {
248 bv.push(false);
249 }
250
251 bv.push(false);
253
254 bv.push(false);
256
257 bv.push(false);
259
260 bv.push(false);
262 }
263}
264
265#[derive(Debug, PartialEq, Eq, Clone, Copy)]
267pub enum BitDepth {
268 Depth8,
270 Depth12,
272}
273
274impl BitDepth {
275 pub fn num_bits(&self) -> u8 {
277 match self {
278 Self::Depth8 => 8,
279 Self::Depth12 => 12,
280 }
281 }
282}
283
284#[derive(Debug, PartialEq, Eq)]
285enum ProfileIdc {
286 Bare(u8),
287 Extra((u8, ChromaFormatIdc)),
288}
289
290impl ProfileIdc {
291 fn baseline() -> Self {
292 Self::Bare(66)
293 }
294 fn high(chroma_format: ChromaFormatIdc) -> Self {
295 Self::Extra((100, chroma_format))
296 }
297 fn high444pp(chroma_format: ChromaFormatIdc) -> Self {
298 Self::Extra((244, chroma_format))
299 }
300 fn profile_idc_byte(&self) -> u8 {
301 match self {
302 Self::Bare(value) => *value,
303 Self::Extra((value, _)) => *value,
304 }
305 }
306 fn is_monochrome(&self) -> bool {
307 match self {
308 Self::Bare(_) | Self::Extra((_, ChromaFormatIdc::Chroma420(_))) => false,
309 Self::Extra((_, ChromaFormatIdc::Monochrome(_))) => true,
310 }
311 }
312 fn append_to_rbsp(&self, bv: &mut BitVec<u8, Msb0>) {
313 match self {
314 Self::Bare(_) => {}
315 Self::Extra((_, chroma_format_idc)) => {
316 let chroma_format_idc_value = chroma_format_idc.value();
317 bv.extend_exp_golomb(chroma_format_idc_value);
318 if chroma_format_idc_value == 3 {
319 bv.push(false);
321 }
322 let bit_depth = match chroma_format_idc {
323 ChromaFormatIdc::Monochrome(bit_depth)
324 | ChromaFormatIdc::Chroma420(bit_depth) => bit_depth,
325 };
326
327 let bit_depth_luma_minus8 = bit_depth.num_bits() - 8;
328 let bit_depth_chroma_minus8 = bit_depth.num_bits() - 8;
329 bv.extend_exp_golomb(bit_depth_luma_minus8.try_into().unwrap());
330 bv.extend_exp_golomb(bit_depth_chroma_minus8.try_into().unwrap());
331
332 bv.push(false);
334 bv.push(false);
336 }
337 }
338 }
339}
340
341#[derive(Debug, PartialEq, Eq)]
342#[allow(dead_code)]
343enum ChromaFormatIdc {
344 Monochrome(BitDepth),
345 Chroma420(BitDepth),
348 }
353
354impl ChromaFormatIdc {
355 fn value(&self) -> u32 {
356 match self {
357 Self::Monochrome(_) => 0,
358 Self::Chroma420(_) => 1,
359 }
362 }
363}
364
365#[derive(Debug, PartialEq, Eq)]
367struct Sps {
368 profile_idc: ProfileIdc,
369 pic_width_in_mbs_minus1: u32,
370 pic_height_in_map_units_minus1: u32,
371 frame_cropping: Option<[u32; 4]>,
372 log2_max_frame_num_minus4: u32,
373 pic_order_cnt_type: u32,
374 log2_max_pic_order_cnt_lsb_minus4: u32,
375 vui: Option<Vui>,
376 }
378
379impl Sps {
380 fn new(
381 profile_idc: ProfileIdc,
382 pic_width_in_mbs_minus1: u32,
383 pic_height_in_map_units_minus1: u32,
384 frame_cropping: Option<[u32; 4]>,
385 vui: Option<Vui>,
386 ) -> Self {
387 Self {
388 profile_idc,
389 pic_width_in_mbs_minus1,
390 pic_height_in_map_units_minus1,
391 frame_cropping,
392 log2_max_frame_num_minus4: 0,
393 pic_order_cnt_type: 0,
394 log2_max_pic_order_cnt_lsb_minus4: 0,
395 vui,
396 }
397 }
398
399 #[allow(dead_code)]
400 fn log2_max_frame_num(&self) -> u32 {
401 self.log2_max_frame_num_minus4 + 4
402 }
403
404 #[allow(dead_code)]
405 fn log2_max_pic_order_cnt_lsb(&self) -> u32 {
406 self.log2_max_pic_order_cnt_lsb_minus4 + 4
407 }
408 fn to_rbsp(&self) -> RbspData {
409 let profile_idc = self.profile_idc.profile_idc_byte();
412
413 let reserved = 0x00;
421
422 let level_idc = 10;
424
425 let start = vec![profile_idc, reserved, level_idc];
426 let mut bv: BitVec<u8, Msb0> = BitVec::from_vec(start);
427
428 bv.extend_exp_golomb(0);
430
431 self.profile_idc.append_to_rbsp(&mut bv);
433
434 bv.extend_exp_golomb(self.log2_max_frame_num_minus4);
435
436 bv.extend_exp_golomb(self.pic_order_cnt_type);
438
439 bv.extend_exp_golomb(self.log2_max_pic_order_cnt_lsb_minus4);
441
442 bv.extend_exp_golomb(0);
444
445 bv.push(false);
447
448 bv.extend_exp_golomb(self.pic_width_in_mbs_minus1);
450
451 bv.extend_exp_golomb(self.pic_height_in_map_units_minus1);
453
454 bv.push(true);
456
457 bv.push(false);
459
460 if let Some(lrtb) = &self.frame_cropping {
461 bv.push(true);
463 for frame_crop_offset in lrtb.iter() {
464 bv.extend_exp_golomb(*frame_crop_offset);
465 }
466 } else {
467 bv.push(false);
469 }
470
471 match &self.vui {
472 None => {
473 bv.push(false);
475 }
476 Some(vui) => {
477 bv.push(true);
478 vui.append_to_rbsp(&mut bv);
479 }
480 }
481
482 bv.push(true);
484
485 RbspData::new(bv.into_vec())
486 }
487}
488
489#[derive(PartialEq, Eq)]
491struct Pps {
492 pic_parameter_set_id: u32,
493 }
495
496impl Pps {
497 fn new(pic_parameter_set_id: u32) -> Self {
498 Self {
499 pic_parameter_set_id,
500 }
501 }
502
503 fn to_rbsp(&self) -> RbspData {
504 let mut bv: BitVec<u8, Msb0> = BitVec::with_capacity(20 * 8); bv.extend_exp_golomb(self.pic_parameter_set_id);
509
510 bv.extend_exp_golomb(0);
512
513 bv.push(false);
515
516 bv.push(false);
518
519 bv.extend_exp_golomb(0);
521
522 bv.extend_exp_golomb(0);
524
525 bv.extend_exp_golomb(0);
527
528 bv.push(false);
530
531 bv.push(false);
533 bv.push(false);
534
535 bv.extend_signed_exp_golomb(0);
537
538 bv.extend_signed_exp_golomb(0);
540
541 bv.extend_signed_exp_golomb(0);
543
544 bv.push(false);
546
547 bv.push(false);
549
550 bv.push(false);
552
553 bv.push(true);
555
556 RbspData::new(bv.into_vec())
557 }
558}
559
560struct SliceHeader {}
561
562impl SliceHeader {
563 fn new() -> Self {
564 Self {}
565 }
566
567 fn to_rbsp(&self, sps: &Sps, pps: &Pps) -> RbspData {
568 let mut bv: BitVec<u8, Msb0> = BitVec::with_capacity(20 * 8); bv.extend_exp_golomb(0);
578
579 bv.extend_exp_golomb(7);
581
582 bv.extend_exp_golomb(pps.pic_parameter_set_id);
583
584 let n_bits = sps.log2_max_frame_num();
588 for _ in 0..n_bits {
589 bv.push(false);
590 }
591
592 bv.extend_exp_golomb(0);
594
595 if sps.pic_order_cnt_type == 0 {
596 let n_bits = sps.log2_max_pic_order_cnt_lsb();
598 for _ in 0..n_bits {
599 bv.push(false);
600 }
601 } else {
602 todo!();
603 }
604
605 bv.push(true);
608
609 bv.push(false);
611
612 bv.extend_signed_exp_golomb(0);
614
615 bv.extend_exp_golomb(MacroblockType::I_PCM.mb_type());
619
620 RbspData::new(bv.into_vec())
621 }
622}
623
624#[allow(non_camel_case_types)]
625enum MacroblockType {
626 I_PCM,
628}
629
630impl MacroblockType {
631 #[inline]
632 fn mb_type(&self) -> u32 {
633 match self {
634 Self::I_PCM => 25,
636 }
637 }
638 #[inline]
640 const fn as_encoded_macroblock_header(&self) -> &'static [u8] {
641 match self {
642 Self::I_PCM => &[0x0D, 0x00],
643 }
644 }
645}
646
647#[test]
648fn test_macroblock_header() {
649 {
650 let typ = &MacroblockType::I_PCM;
651 let mut bv: BitVec<u8, Msb0> = BitVec::new();
652 bv.extend_exp_golomb(typ.mb_type());
653 let macroblock_header_dynamic = bv.as_raw_slice();
654 dbg!(macroblock_header_dynamic);
655 let macroblock_header_static = typ.as_encoded_macroblock_header();
656 assert_eq!(macroblock_header_static, macroblock_header_dynamic);
657 }
658}
659
660#[inline]
661fn copy_to_macroblock_8bit(
662 mbs_row: usize,
663 mbs_col: usize,
664 src_plane: &DataPlane,
665 dest: &mut Vec<u8>,
666 dest_sz: usize,
667) {
668 let src_data = src_plane.data;
671 let src_stride = src_plane.stride;
672 for src_row in (mbs_row * dest_sz)..((mbs_row + 1) * dest_sz) {
673 let row_chunk = &src_data[src_row * src_stride..(src_row + 1) * src_stride];
676 let chunk = &row_chunk[mbs_col * dest_sz..(mbs_col + 1) * dest_sz];
677 dest.extend(chunk);
678 }
679}
680
681#[inline]
682fn copy_to_macroblock_12bit(
683 mbs_row: usize,
684 mbs_col: usize,
685 src_plane: &DataPlane,
686 dest: &mut Vec<u8>,
687 dest_sz: usize,
688) {
689 let src_data = src_plane.data;
692 let src_stride = src_plane.stride;
693 let src_sz = dest_sz / 3 * 2;
694 for src_row in (mbs_row * src_sz)..((mbs_row + 1) * src_sz) {
695 let row_chunk = &src_data[src_row * src_stride..(src_row + 1) * src_stride];
698 let chunk = &row_chunk[mbs_col * dest_sz..(mbs_col + 1) * dest_sz];
699 dest.extend(chunk);
700 }
701}
702
703#[inline]
704fn macroblock(
705 mbs_row: usize,
706 mbs_col: usize,
707 result: &mut RbspData,
708 y4m_frame: &YCbCrImage,
709 luma_only: bool,
710) {
711 if !(mbs_row == 0 && mbs_col == 0) {
712 result
713 .data
714 .extend(MacroblockType::I_PCM.as_encoded_macroblock_header());
715 }
716
717 match &y4m_frame.planes {
718 Planes::Mono(y_plane) | Planes::YCbCr((y_plane, _, _)) => match y_plane.bit_depth {
719 BitDepth::Depth8 => {
720 copy_to_macroblock_8bit(mbs_row, mbs_col, y_plane, &mut result.data, 16);
721 }
722 BitDepth::Depth12 => {
723 copy_to_macroblock_12bit(mbs_row, mbs_col, y_plane, &mut result.data, 24);
724 }
725 },
726 }
727
728 if luma_only {
729 return;
730 }
731
732 match &y4m_frame.planes {
733 Planes::Mono(y_plane) => {
734 assert_eq!(y_plane.bit_depth, BitDepth::Depth8);
735 result.data.extend(vec![128u8; 2 * 8 * 8]);
737 }
738 Planes::YCbCr((_, u_plane, v_plane)) => {
739 assert_eq!(u_plane.bit_depth, v_plane.bit_depth);
740 match u_plane.bit_depth {
741 BitDepth::Depth8 => {
742 copy_to_macroblock_8bit(mbs_row, mbs_col, u_plane, &mut result.data, 8);
743 copy_to_macroblock_8bit(mbs_row, mbs_col, v_plane, &mut result.data, 8);
744 }
745 BitDepth::Depth12 => {
746 copy_to_macroblock_12bit(mbs_row, mbs_col, u_plane, &mut result.data, 12);
747 copy_to_macroblock_12bit(mbs_row, mbs_col, v_plane, &mut result.data, 12);
748 }
749 }
750 }
751 }
752}
753
754#[derive(Clone)]
759pub struct RbspData {
760 pub data: Vec<u8>,
762}
763
764impl RbspData {
765 fn new(data: Vec<u8>) -> Self {
766 Self { data }
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773
774 const HELLO_SPS: &[u8] = &[
776 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x0a, 0xf8, 0x41, 0xa2,
777 ];
778 const HELLO_PPS: &[u8] = &[0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x38, 0x80];
779 const FIXED_HELLO_SLICE_HEADER: &[u8] = &[0, 0, 0, 1, 37, 136, 132, 40, 104];
782 const _HELLO_SLICE_HEADER: &[u8] = &[0x00, 0x00, 0x00, 0x01, 0x05, 0x88, 0x84, 0x21, 0xa0];
784 const HELLO_MACROBLOCK_HEADER: &[u8] = &[0x0d, 0x00];
785
786 use h264_reader::{
787 nal::{pps::PicParameterSet, sps::SeqParameterSet, Nal, RefNal},
788 rbsp::BitReader,
789 Context,
790 };
791
792 #[test]
793 fn test_div_ceil() {
794 assert_eq!(div_ceil(10, 2), 5);
795 assert_eq!(div_ceil(11, 2), 6);
796 assert_eq!(div_ceil(15, 3), 5);
797 assert_eq!(div_ceil(16, 3), 6);
798 assert_eq!(div_ceil(18, 3), 6);
799 }
800
801 #[test]
802 fn test_next_multiple() {
803 assert_eq!(next_multiple(10, 16), 16);
804 assert_eq!(next_multiple(11, 16), 16);
805 assert_eq!(next_multiple(15, 16), 16);
806 assert_eq!(next_multiple(16, 16), 16);
807 assert_eq!(next_multiple(17, 16), 32);
808 }
809
810 #[test]
811 fn test_encode() {
812 use h264_reader::rbsp::decode_nal;
813 use std::ops::Deref;
814
815 let nal_with_escape = &b"\x67\x64\x00\x0A\xAC\x72\x84\x44\x26\x84\x00\x00\x03\x00\x04\x00\x00\x03\x00\xCA\x3C\x48\x96\x11\x80"[..];
816 let rbsp = &b"\x64\x00\x0a\xac\x72\x84\x44\x26\x84\x00\x00\x00\x04\x00\x00\x00\xca\x3c\x48\x96\x11\x80"[..];
817
818 assert_eq!(decode_nal(nal_with_escape).unwrap().deref(), rbsp);
820
821 let mut nal2 = vec![0u8; rbsp.len() * 3];
822 let sz = rbsp_to_ebsp(rbsp, &mut nal2);
823 nal2.truncate(sz);
824 nal2.insert(0, 0); assert_eq!(decode_nal(&nal2).unwrap().deref(), rbsp);
827 }
828
829 #[test]
830 fn test_sps() {
831 let width = 128;
832 let height = 96;
833
834 let pic_width_in_mbs_minus1 = div_ceil(width, 16) - 1;
835 let pic_height_in_map_units_minus1 = div_ceil(height, 16) - 1;
836
837 let payload = Sps::new(
838 ProfileIdc::baseline(),
839 pic_width_in_mbs_minus1,
840 pic_height_in_map_units_minus1,
841 None,
842 None,
843 )
844 .to_rbsp();
845 let encoded = NalUnit::new(
846 NalRefIdc::Three,
847 NalUnitType::SequenceParameterSet,
848 payload.clone(),
849 )
850 .to_annex_b_data();
851 assert_eq!(&encoded, HELLO_SPS);
852
853 let sps = SeqParameterSet::from_bits(BitReader::new(&payload.data[..])).unwrap();
854 let sps2 = RefNal::new(&encoded[4..], &[], true);
855 let sps2 = SeqParameterSet::from_bits(sps2.rbsp_bits()).unwrap();
856 assert_eq!(format!("{sps:?}"), format!("{sps2:?}")); assert_eq!(sps.pic_width_in_mbs_minus1, pic_width_in_mbs_minus1);
859 assert_eq!(
860 sps.pic_height_in_map_units_minus1,
861 pic_height_in_map_units_minus1
862 );
863 }
864
865 #[test]
866 fn test_pps() {
867 let payload = Pps::new(0).to_rbsp();
868 let encoded = NalUnit::new(
869 NalRefIdc::Three,
870 NalUnitType::PictureParameterSet,
871 payload.clone(),
872 )
873 .to_annex_b_data();
874
875 assert_eq!(&encoded, HELLO_PPS);
876
877 let sps = SeqParameterSet::from_bits(BitReader::new(&HELLO_SPS[5..])).unwrap();
878
879 let mut ctx = Context::default();
880 ctx.put_seq_param_set(sps);
881 let _pps = PicParameterSet::from_bits(&ctx, BitReader::new(&payload.data[..])).unwrap();
882 }
883
884 #[test]
885 fn test_slice_header() {
886 let sps = Sps::new(ProfileIdc::baseline(), 5, 5, None, None);
887 let pps = Pps::new(0);
888 let payload = SliceHeader::new().to_rbsp(&sps, &pps);
889
890 let sps = SeqParameterSet::from_bits(BitReader::new(&HELLO_SPS[5..])).unwrap();
891 let mut ctx = Context::default();
892 ctx.put_seq_param_set(sps);
893 let pps = PicParameterSet::from_bits(&ctx, BitReader::new(&HELLO_PPS[5..])).unwrap();
894 ctx.put_pic_param_set(pps);
895
896 fn dbg_hex(vals: &[u8]) -> &[u8] {
897 println!();
898 for v in vals.iter() {
899 println!("{:03} 0b{:08b} 0x{:02x}", v, v, v);
900 }
901 println!();
902 vals
903 }
904
905 let encoded = NalUnit::new(
906 NalRefIdc::One,
907 NalUnitType::CodedSliceOfAnIDRPicture,
908 payload,
909 )
910 .to_annex_b_data();
911
912 let hello_slice_header = {
913 let nal = RefNal::new(&FIXED_HELLO_SLICE_HEADER[4..], &[], true);
914 let hello_slice_header = h264_reader::nal::slice::SliceHeader::from_bits(
915 &ctx,
916 &mut nal.rbsp_bits(),
917 nal.header().unwrap(),
918 )
919 .unwrap()
920 .0;
921 hello_slice_header
922 };
923
924 let nal = RefNal::new(&encoded[4..], &[], true);
925 let slice_header = h264_reader::nal::slice::SliceHeader::from_bits(
926 &ctx,
927 &mut nal.rbsp_bits(),
928 nal.header().unwrap(),
929 )
930 .unwrap()
931 .0;
932
933 assert_eq!(
934 format!("{:?}", hello_slice_header),
935 format!("{:?}", slice_header)
936 );
937 assert_eq!(dbg_hex(&encoded), dbg_hex(FIXED_HELLO_SLICE_HEADER));
938 }
939
940 #[test]
941 fn test_macroblock() {
942 let mut bv: BitVec<u8, Msb0> = BitVec::new();
943 bv.extend_exp_golomb(MacroblockType::I_PCM.mb_type());
944 let macroblock_header = bv.as_raw_slice();
945
946 assert_eq!(macroblock_header, HELLO_MACROBLOCK_HEADER);
947 }
948}