Skip to main content

h264_reader/nal/sei/
mod.rs

1pub mod buffering_period;
2pub mod pic_timing;
3pub mod recovery_point;
4pub mod user_data_registered_itu_t_t35;
5pub mod user_data_unregistered;
6
7use crate::rbsp::BitReaderError;
8use hex_slice::AsHex;
9use std::fmt::{Debug, Formatter};
10use std::io::BufRead;
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13pub enum HeaderType {
14    BufferingPeriod,
15    PicTiming,
16    PanScanRect,
17    FillerPayload,
18    UserDataRegisteredItuTT35,
19    UserDataUnregistered,
20    RecoveryPoint,
21    DecRefPicMarkingRepetition,
22    SparePic,
23    SceneInfo,
24    SubSeqInfo,
25    SubSeqLayerCharacteristics,
26    SubSeqCharacteristics,
27    FullFrameFreeze,
28    FullFrameFreezeRelease,
29    FullFrameSnapshot,
30    ProgressiveRefinementSegmentStart,
31    ProgressiveRefinementSegmentEnd,
32    MotionConstrainedSliceGroupSet,
33    FilmGrainCharacteristics,
34    DeblockingFilterDisplayPreference,
35    StereoVideoInfo,
36    PostFilterHint,
37    ToneMappingInfo,
38    ScalabilityInfo,
39    SubPicScalableLayer,
40    NonRequiredLayerRep,
41    PriorityLayerInfo,
42    LayersNotPresent,
43    LayerDependencyChange,
44    ScalableNesting,
45    BaseLayerTemporalHrd,
46    QualityLayerIntegrityCheck,
47    RedundantPicProperty,
48    Tl0DepRepIndex,
49    TlSwitchingPoint,
50    ParallelDecodingInfo,
51    MvcScalableNesting,
52    ViewScalabilityInfo,
53    MultiviewSceneInfo,
54    MultiviewAcquisitionInfo,
55    NonRequiredViewComponent,
56    ViewDependencyChange,
57    OperationPointsNotPresent,
58    BaseViewTemporalHrd,
59    FramePackingArrangement,
60    MultiviewViewPosition,
61    DisplayOrientation,
62    MvcdScalableNesting,
63    MvcdViewScalabilityInfo,
64    DepthRepresentationInfo,
65    ThreeDimensionalReferenceDisplaysInfo,
66    DepthTiming,
67    DepthSamplingInfo,
68    ConstrainedDepthParameterSetIdentifier,
69    GreenMetadata,
70    MasteringDisplayColourVolume,
71    ColourRemappingInfo,
72    AlternativeTransferCharacteristics,
73    AlternativeDepthInfo,
74    ReservedSeiMessage(u32),
75}
76impl HeaderType {
77    fn from_id(id: u32) -> HeaderType {
78        match id {
79            0 => HeaderType::BufferingPeriod,
80            1 => HeaderType::PicTiming,
81            2 => HeaderType::PanScanRect,
82            3 => HeaderType::FillerPayload,
83            4 => HeaderType::UserDataRegisteredItuTT35,
84            5 => HeaderType::UserDataUnregistered,
85            6 => HeaderType::RecoveryPoint,
86            7 => HeaderType::DecRefPicMarkingRepetition,
87            8 => HeaderType::SparePic,
88            9 => HeaderType::SceneInfo,
89            10 => HeaderType::SubSeqInfo,
90            11 => HeaderType::SubSeqLayerCharacteristics,
91            12 => HeaderType::SubSeqCharacteristics,
92            13 => HeaderType::FullFrameFreeze,
93            14 => HeaderType::FullFrameFreezeRelease,
94            15 => HeaderType::FullFrameSnapshot,
95            16 => HeaderType::ProgressiveRefinementSegmentStart,
96            17 => HeaderType::ProgressiveRefinementSegmentEnd,
97            18 => HeaderType::MotionConstrainedSliceGroupSet,
98            19 => HeaderType::FilmGrainCharacteristics,
99            20 => HeaderType::DeblockingFilterDisplayPreference,
100            21 => HeaderType::StereoVideoInfo,
101            22 => HeaderType::PostFilterHint,
102            23 => HeaderType::ToneMappingInfo,
103            24 => HeaderType::ScalabilityInfo,
104            25 => HeaderType::SubPicScalableLayer,
105            26 => HeaderType::NonRequiredLayerRep,
106            27 => HeaderType::PriorityLayerInfo,
107            28 => HeaderType::LayersNotPresent,
108            29 => HeaderType::LayerDependencyChange,
109            30 => HeaderType::ScalableNesting,
110            31 => HeaderType::BaseLayerTemporalHrd,
111            32 => HeaderType::QualityLayerIntegrityCheck,
112            33 => HeaderType::RedundantPicProperty,
113            34 => HeaderType::Tl0DepRepIndex,
114            35 => HeaderType::TlSwitchingPoint,
115            36 => HeaderType::ParallelDecodingInfo,
116            37 => HeaderType::MvcScalableNesting,
117            38 => HeaderType::ViewScalabilityInfo,
118            39 => HeaderType::MultiviewSceneInfo,
119            40 => HeaderType::MultiviewAcquisitionInfo,
120            41 => HeaderType::NonRequiredViewComponent,
121            42 => HeaderType::ViewDependencyChange,
122            43 => HeaderType::OperationPointsNotPresent,
123            44 => HeaderType::BaseViewTemporalHrd,
124            45 => HeaderType::FramePackingArrangement,
125            46 => HeaderType::MultiviewViewPosition,
126            47 => HeaderType::DisplayOrientation,
127            48 => HeaderType::MvcdScalableNesting,
128            49 => HeaderType::MvcdViewScalabilityInfo,
129            50 => HeaderType::DepthRepresentationInfo,
130            51 => HeaderType::ThreeDimensionalReferenceDisplaysInfo,
131            52 => HeaderType::DepthTiming,
132            53 => HeaderType::DepthSamplingInfo,
133            54 => HeaderType::ConstrainedDepthParameterSetIdentifier,
134            56 => HeaderType::GreenMetadata,
135            137 => HeaderType::MasteringDisplayColourVolume,
136            142 => HeaderType::ColourRemappingInfo,
137            147 => HeaderType::AlternativeTransferCharacteristics,
138            188 => HeaderType::AlternativeDepthInfo,
139            _ => HeaderType::ReservedSeiMessage(id),
140        }
141    }
142}
143
144/// Maximum supported SEI payload size (1 MB). The H.264 spec does not mandate a limit, but
145/// real-world payloads are small (timing info, recovery points, user data). This cap prevents
146/// denial-of-service from crafted streams claiming enormous payload lengths.
147const MAX_SEI_PAYLOAD_LEN: u32 = 1024 * 1024;
148
149/// Reader of messages in an SEI NAL.
150pub struct SeiReader<'a, R: BufRead + Clone> {
151    reader: R,
152    scratch: &'a mut Vec<u8>,
153    payloads_seen: usize,
154    done: bool,
155}
156
157impl<'a, R: BufRead + Clone> SeiReader<'a, R> {
158    pub fn from_rbsp_bytes(reader: R, scratch: &'a mut Vec<u8>) -> Self {
159        Self {
160            reader,
161            scratch,
162            payloads_seen: 0,
163            done: false,
164        }
165    }
166
167    /// Returns the next payload.
168    ///
169    /// This is unfortunately not compatible with `std::iter::Iterator` because
170    /// of lifetime constraints.
171    pub fn next(&mut self) -> Result<Option<SeiMessage<'_>>, BitReaderError> {
172        if self.done {
173            return Ok(None);
174        }
175
176        // Fused iterator: once this returns `None` or `Err`, don't try to parse
177        // again and return a strange result. (Set done preemptively then clear
178        // it on success, rather than adjust each failure path.)
179        self.done = true;
180        let payload_type = read_u32(&mut self.reader, "payload_type")?;
181
182        // If this is not the first payload, the byte we just read may actually
183        // be a rbsp_trailing_bits (which is always byte-aligned). Check for EOF.
184        if payload_type == 0x80 && self.payloads_seen > 0 {
185            let buf = self
186                .reader
187                .fill_buf()
188                .map_err(|e| BitReaderError::ReaderError("payload_type", e))?;
189            if buf.is_empty() {
190                return Ok(None);
191            }
192        }
193        let payload_type = HeaderType::from_id(payload_type);
194        let payload_len = read_u32(&mut self.reader, "payload_len")?;
195        if payload_len > MAX_SEI_PAYLOAD_LEN {
196            return Err(BitReaderError::ReaderError(
197                "payload_len",
198                std::io::Error::new(
199                    std::io::ErrorKind::InvalidData,
200                    "SEI payload length exceeds 1 MB limit",
201                ),
202            ));
203        }
204        let payload_len = payload_len as usize;
205
206        // Read into scratch. We could instead directly use reader's buffer if
207        // the next chunk is long enough, or pass along a BufRead that uses
208        // something like std::io::Take, but it's probably not worth the
209        // complexity.
210        self.scratch.resize(payload_len, 0);
211        self.reader
212            .read_exact(&mut self.scratch)
213            .map_err(|e| BitReaderError::ReaderError("payload", e))?;
214
215        self.payloads_seen += 1;
216        self.done = false;
217        Ok(Some(SeiMessage {
218            payload_type,
219            payload: &self.scratch[..],
220        }))
221    }
222}
223
224#[derive(PartialEq, Eq)]
225pub struct SeiMessage<'a> {
226    pub payload_type: HeaderType,
227    pub payload: &'a [u8],
228}
229
230impl<'a> Debug for SeiMessage<'a> {
231    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
232        f.debug_struct("SeiMessage")
233            .field("payload_type", &self.payload_type)
234            .field("payload", &format!("{:02x}", self.payload.plain_hex(false)))
235            .finish()
236    }
237}
238
239/// Reads a u32 in the special `sei_message` format used for payload type and size.
240fn read_u32<R: BufRead>(reader: &mut R, name: &'static str) -> Result<u32, BitReaderError> {
241    let mut acc = 0u32;
242    loop {
243        let mut buf = [0];
244        reader
245            .read_exact(&mut buf[..])
246            .map_err(|e| BitReaderError::ReaderError(name, e))?;
247        let byte = buf[0];
248        acc = acc.checked_add(u32::from(byte)).ok_or_else(|| {
249            BitReaderError::ReaderError(
250                name,
251                std::io::Error::new(std::io::ErrorKind::InvalidData, "overflowed u32"),
252            )
253        })?;
254        if byte != 0xFF {
255            return Ok(acc);
256        }
257    }
258}
259
260#[cfg(test)]
261mod test {
262    use crate::nal::{Nal, RefNal};
263
264    use super::*;
265
266    #[test]
267    fn it_works() {
268        let data = [
269            0x06, // SEI
270            // header 1
271            0x01, // type
272            0x01, // len
273            0x01, // payload
274            // header 2
275            0x02, // type
276            0x02, // len
277            0x02, 0x02, // payload
278            0x80, // rbsp_trailing_bits
279        ];
280        let nal = RefNal::new(&data[..], &[], true);
281        let mut scratch = Vec::new();
282        let mut r = SeiReader::from_rbsp_bytes(nal.rbsp_bytes(), &mut scratch);
283        let m1 = r.next().unwrap().unwrap();
284        assert_eq!(m1.payload_type, HeaderType::PicTiming);
285        assert_eq!(m1.payload, &[0x01]);
286        let m2 = r.next().unwrap().unwrap();
287        assert_eq!(m2.payload_type, HeaderType::PanScanRect);
288        assert_eq!(m2.payload, &[0x02, 0x02]);
289        assert_eq!(r.next().unwrap(), None);
290        assert_eq!(r.next().unwrap(), None);
291    }
292}