Skip to main content

h264_reader/nal/sei/
recovery_point.rs

1use super::SeiMessage;
2use crate::nal::sei::HeaderType;
3use crate::rbsp::BitRead;
4use crate::rbsp::BitReaderError;
5
6#[derive(Debug)]
7pub enum RecoveryPointError {
8    ReaderError(BitReaderError),
9}
10impl From<BitReaderError> for RecoveryPointError {
11    fn from(e: BitReaderError) -> Self {
12        RecoveryPointError::ReaderError(e)
13    }
14}
15
16/// Parsed `recovery_point()` SEI message (payloadType == 6, spec D.1.7).
17///
18/// Signals a recovery point in the bitstream, allowing decoders to identify
19/// open-GOP random access points where correct decoding can begin.
20#[derive(Debug, PartialEq, Eq)]
21pub struct RecoveryPoint {
22    pub recovery_frame_cnt: u32,
23    pub exact_match_flag: bool,
24    pub broken_link_flag: bool,
25    pub changing_slice_group_idc: u8,
26}
27
28impl RecoveryPoint {
29    pub fn read(msg: &SeiMessage<'_>) -> Result<Self, RecoveryPointError> {
30        assert_eq!(msg.payload_type, HeaderType::RecoveryPoint);
31        let mut r = crate::rbsp::BitReader::new(msg.payload);
32        let recovery_frame_cnt = r.read_ue("recovery_frame_cnt")?;
33        let exact_match_flag = r.read_bit("exact_match_flag")?;
34        let broken_link_flag = r.read_bit("broken_link_flag")?;
35        let changing_slice_group_idc = r.read::<2, u8>("changing_slice_group_idc")?;
36        r.finish_sei_payload()?;
37        Ok(RecoveryPoint {
38            recovery_frame_cnt,
39            exact_match_flag,
40            broken_link_flag,
41            changing_slice_group_idc,
42        })
43    }
44}
45
46#[cfg(test)]
47mod test {
48    use super::*;
49
50    #[test]
51    fn parse() {
52        // recovery_frame_cnt=0 (ue: 1)
53        // exact_match_flag=1
54        // broken_link_flag=0
55        // changing_slice_group_idc=0 (u(2): 00)
56        // trailing: 1_000 (stop bit + padding)
57        // Bits: 1_1_0_00_1_000 = 0b1100_0100 = 0xC4
58        let msg = SeiMessage {
59            payload_type: HeaderType::RecoveryPoint,
60            payload: &[0xC4],
61        };
62        let rp = RecoveryPoint::read(&msg).unwrap();
63        assert_eq!(rp.recovery_frame_cnt, 0);
64        assert!(rp.exact_match_flag);
65        assert!(!rp.broken_link_flag);
66        assert_eq!(rp.changing_slice_group_idc, 0);
67    }
68
69    #[test]
70    fn parse_nonzero_frame_cnt() {
71        // recovery_frame_cnt=5 (ue: 00110)
72        // exact_match_flag=0
73        // broken_link_flag=1
74        // changing_slice_group_idc=1 (u(2): 01)
75        // trailing: 1_0 (stop bit + padding)
76        // Bits: 00110_0_1_01_1_0 = 0b0011_0010_1100_0000 = 0x32, 0xC0
77        let msg = SeiMessage {
78            payload_type: HeaderType::RecoveryPoint,
79            payload: &[0x32, 0xC0],
80        };
81        let rp = RecoveryPoint::read(&msg).unwrap();
82        assert_eq!(rp.recovery_frame_cnt, 5);
83        assert!(!rp.exact_match_flag);
84        assert!(rp.broken_link_flag);
85        assert_eq!(rp.changing_slice_group_idc, 1);
86    }
87}