Skip to main content

rtc_rtp/codec/h265/
mod.rs

1//! H.265/HEVC RTP payload format ([RFC 7798]).
2//!
3//! HEVC differs from H.264 in ways that matter to packetization: NAL headers are two bytes
4//! rather than one, and there is an extra aggregation form (PACI) that can carry payload
5//! content information ahead of the NAL unit.
6//!
7//! This module provides the three packet shapes the RFC defines — single NAL unit,
8//! aggregation, and fragmentation unit — plus [`HevcPayloader`](crate::codec::h265::HevcPayloader), which picks between them
9//! based on the MTU, and the depacketizer that reverses the choice.
10//!
11//! [RFC 7798]: https://datatracker.ietf.org/doc/html/rfc7798
12
13use bytes::{BufMut, Bytes, BytesMut};
14
15use super::h264::ANNEXB_NALUSTART_CODE;
16use crate::packetizer::{Depacketizer, Payloader};
17use shared::error::{Error, Result};
18
19#[cfg(test)]
20mod h265_test;
21
22/// The three-byte Annex B start code (`00 00 01`).
23pub static ANNEXB_3_NALUSTART_CODE: Bytes = Bytes::from_static(&[0x00, 0x00, 0x01]);
24/// Payload header for a single NAL unit packet.
25pub static SING_PAYLOAD_HDR: Bytes = Bytes::from_static(&[0x1C, 0x01]);
26/// Payload header for an aggregation packet.
27pub static AGGR_PAYLOAD_HDR: Bytes = Bytes::from_static(&[0x60, 0x01]);
28/// Payload header for a fragmentation unit.
29pub static FRAG_PAYLOAD_HDR: Bytes = Bytes::from_static(&[0x62, 0x01]);
30/// FU header for the first fragment of an IDR frame.
31pub static FU_HDR_IDR_S: u8 = 0x93;
32/// FU header for a middle fragment of an IDR frame.
33pub static FU_HDR_IDR_M: u8 = 0x13;
34/// FU header for the last fragment of an IDR frame.
35pub static FU_HDR_IDR_E: u8 = 0x53;
36/// FU header for the first fragment of a P frame.
37pub static FU_HDR_P_S: u8 = 0x81;
38/// FU header for a middle fragment of a P frame.
39pub static FU_HDR_P_M: u8 = 0x01;
40/// FU header for the last fragment of a P frame.
41pub static FU_HDR_P_E: u8 = 0x41;
42/// FU header for the first fragment of a B frame.
43pub static FU_HDR_B_S: u8 = 0x80;
44/// FU header for a middle fragment of a B frame.
45pub static FU_HDR_B_M: u8 = 0x00;
46/// FU header for the last fragment of a B frame.
47pub static FU_HDR_B_E: u8 = 0x40;
48/// The payload MTU this payloader targets, chosen to survive typical paths without IP
49/// fragmentation.
50pub const RTP_OUTBOUND_MTU: usize = 1200;
51/// Bytes of FU header following the payload header in a fragmentation unit.
52pub const H265FRAGMENTATION_UNIT_HEADER_SIZE: usize = 1;
53/// Bytes in an H.265 NAL header — two, unlike H.264's one.
54pub const NAL_HEADER_SIZE: usize = 2;
55
56#[derive(PartialEq, Hash, Debug, Copy, Clone)]
57/// The H.265 NAL unit types this payloader distinguishes.
58#[non_exhaustive]
59pub enum UnitType {
60    /// Video parameter set.
61    VPS = 32,
62    /// Sequence parameter set.
63    SPS = 33,
64    /// Picture parameter set.
65    PPS = 34,
66    /// Clean random access picture — a keyframe that allows mid-stream tune-in.
67    CRA = 21,
68    /// Supplemental enhancement information.
69    SEI = 39,
70    /// Instantaneous decoder refresh picture — a keyframe.
71    IDR = 19,
72    /// A predicted (P) frame.
73    PFR = 1,
74    /// A bidirectionally predicted (B) frame.
75    BFR = 0,
76    /// A unit type this payloader skips.
77    IGNORE = -1,
78}
79impl UnitType {
80    /// Maps a raw NAL type id to a [`UnitType`].
81    ///
82    /// # Errors
83    ///
84    /// Fails if the id is not one this payloader handles.
85    pub fn for_id(id: u8) -> Result<UnitType> {
86        if id > 64 {
87            Err(Error::ErrUnhandledNaluType)
88        } else {
89            let t = match id {
90                32 => UnitType::VPS,
91                33 => UnitType::SPS,
92                34 => UnitType::PPS,
93                21 => UnitType::CRA,
94                39 => UnitType::SEI,
95                19 => UnitType::IDR,
96                1 => UnitType::PFR,
97                0 => UnitType::BFR,
98                _ => UnitType::IGNORE, // shouldn't happen
99            };
100            Ok(t)
101        }
102    }
103}
104
105#[derive(Default, Debug, Clone)]
106/// Packetizes H.265/HEVC NAL units into RTP payloads, fragmenting when needed.
107pub struct HevcPayloader;
108
109impl HevcPayloader {
110    fn aggregation_payload_header(nalus: &[Bytes]) -> [u8; 2] {
111        let mut f = false;
112        let mut layer_id = u8::MAX;
113        let mut tid = u8::MAX;
114
115        for nalu in nalus {
116            let header = H265NALUHeader::new(nalu[0], nalu[1]);
117            f |= header.f();
118            layer_id = layer_id.min(header.layer_id());
119            tid = tid.min(header.tid());
120        }
121
122        let mut raw = (H265NALU_AGGREGATION_PACKET_TYPE as u16) << 9;
123        raw |= (layer_id as u16) << 3;
124        raw |= tid as u16;
125        if f {
126            raw |= 1 << 15;
127        }
128
129        raw.to_be_bytes()
130    }
131
132    fn fragmentation_payload_header(payload_header: H265NALUHeader) -> [u8; 2] {
133        let mut raw = (H265NALU_FRAGMENTATION_UNIT_TYPE as u16) << 9;
134        raw |= (payload_header.layer_id() as u16) << 3;
135        raw |= payload_header.tid() as u16;
136        if payload_header.f() {
137            raw |= 1 << 15;
138        }
139
140        raw.to_be_bytes()
141    }
142
143    fn fu_header(nalu_type: u8, is_first: bool, is_last: bool) -> u8 {
144        let mut header = nalu_type & 0b0011_1111;
145        if is_first {
146            header |= 0b1000_0000;
147        } else if is_last {
148            header |= 0b0100_0000;
149        }
150        header
151    }
152
153    /// Locates the NAL unit boundaries in an Annex B buffer.
154    ///
155    /// Returns the offset of each start code and the length of the code that was matched.
156    pub fn parse(nalu: &Bytes) -> (Vec<usize>, usize) {
157        let finder = memchr::memmem::Finder::new(&ANNEXB_NALUSTART_CODE);
158        let nals = finder.find_iter(nalu).collect::<Vec<usize>>();
159        if nals.is_empty() {
160            let finder = memchr::memmem::Finder::new(&ANNEXB_3_NALUSTART_CODE);
161            return (finder.find_iter(nalu).collect::<Vec<usize>>(), 3);
162        }
163        (nals, 4)
164    }
165
166    fn flush_aggregation_buffer(nalus: &mut Vec<Bytes>, mtu: usize, payloads: &mut Vec<Bytes>) {
167        match nalus.len() {
168            0 => {}
169            1 => {
170                payloads.push(nalus.pop().expect("single buffered NAL exists"));
171            }
172            _ => {
173                let header = Self::aggregation_payload_header(nalus);
174                let mut aggr_nalu = BytesMut::with_capacity(
175                    NAL_HEADER_SIZE + nalus.iter().map(|nalu| 2 + nalu.len()).sum::<usize>(),
176                );
177                aggr_nalu.extend_from_slice(&header);
178                for nalu in nalus.drain(..) {
179                    aggr_nalu.extend_from_slice(&(nalu.len() as u16).to_be_bytes());
180                    aggr_nalu.extend_from_slice(&nalu);
181                }
182                if aggr_nalu.len() <= mtu {
183                    payloads.push(aggr_nalu.freeze());
184                }
185            }
186        }
187    }
188
189    fn emit(nalu: &Bytes, mtu: usize, payloads: &mut Vec<Bytes>) {
190        if nalu.is_empty() {
191            return;
192        }
193        let payload_header = H265NALUHeader::new(nalu[0], nalu[1]);
194        let payload_nalu_type = payload_header.nalu_type();
195
196        if payload_nalu_type >= H265NALU_AGGREGATION_PACKET_TYPE {
197            return;
198        }
199
200        // Single NALU
201        if nalu.len() <= mtu {
202            payloads.push(nalu.clone());
203            return;
204        }
205        let max_fragment_size =
206            mtu as isize - NAL_HEADER_SIZE as isize - H265FRAGMENTATION_UNIT_HEADER_SIZE as isize;
207        let nalu_data = nalu;
208        let mut nalu_data_index = 2;
209        let nalu_data_length = nalu.len() as isize - nalu_data_index;
210        let mut nalu_data_remaining = nalu_data_length;
211        if std::cmp::min(max_fragment_size, nalu_data_remaining) <= 0 {
212            return;
213        }
214        while nalu_data_remaining > 0 {
215            let current_fragment_size = std::cmp::min(max_fragment_size, nalu_data_remaining);
216            let mut out = BytesMut::with_capacity(
217                NAL_HEADER_SIZE
218                    + H265FRAGMENTATION_UNIT_HEADER_SIZE
219                    + current_fragment_size as usize,
220            );
221            out.extend_from_slice(&Self::fragmentation_payload_header(payload_header));
222            let is_first = nalu_data_index == 2;
223            let is_last = nalu_data_remaining == current_fragment_size;
224            out.put_u8(Self::fu_header(payload_nalu_type, is_first, is_last));
225
226            out.extend_from_slice(
227                &nalu_data
228                    [nalu_data_index as usize..(nalu_data_index + current_fragment_size) as usize],
229            );
230            payloads.push(out.freeze());
231
232            nalu_data_remaining -= current_fragment_size;
233            nalu_data_index += current_fragment_size;
234        }
235    }
236}
237
238impl Payloader for HevcPayloader {
239    /// Payload fragments a H264 packet across one or more byte arrays
240    fn payload(&mut self, mtu: usize, payload: &Bytes) -> Result<Vec<Bytes>> {
241        if payload.is_empty() || mtu == 0 {
242            return Ok(vec![]);
243        }
244
245        let mut payloads = vec![];
246        let mut aggregation_buffer = vec![];
247
248        let (nal_idxs, offset) = HevcPayloader::parse(payload);
249        if nal_idxs.is_empty() {
250            Self::emit(payload, mtu, &mut payloads);
251            return Ok(payloads);
252        }
253        let nal_len = nal_idxs.len();
254        for (i, start) in nal_idxs.iter().enumerate() {
255            let end = if (i + 1) < nal_len {
256                nal_idxs[i + 1]
257            } else {
258                payload.len()
259            };
260            let nalu = payload.slice((start + offset)..end);
261            if nalu.len() < NAL_HEADER_SIZE {
262                continue;
263            }
264
265            let payload_header = H265NALUHeader::new(nalu[0], nalu[1]);
266            if payload_header.is_aggregation_packet()
267                || payload_header.is_fragmentation_unit()
268                || payload_header.is_paci_packet()
269            {
270                continue;
271            }
272
273            if nalu.len() > mtu {
274                Self::flush_aggregation_buffer(&mut aggregation_buffer, mtu, &mut payloads);
275                Self::emit(&nalu, mtu, &mut payloads);
276                continue;
277            }
278
279            let aggregated_size = NAL_HEADER_SIZE
280                + aggregation_buffer
281                    .iter()
282                    .map(|nalu| 2 + nalu.len())
283                    .sum::<usize>()
284                + 2
285                + nalu.len();
286            if !aggregation_buffer.is_empty() && aggregated_size > mtu {
287                Self::flush_aggregation_buffer(&mut aggregation_buffer, mtu, &mut payloads);
288            }
289
290            aggregation_buffer.push(nalu);
291        }
292        Self::flush_aggregation_buffer(&mut aggregation_buffer, mtu, &mut payloads);
293
294        Ok(payloads)
295    }
296
297    fn clone_to(&self) -> Box<dyn Payloader> {
298        Box::new(self.clone())
299    }
300}
301
302///
303/// Network Abstraction Unit Header implementation
304///
305const H265NALU_HEADER_SIZE: usize = 2;
306/// <https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2>
307const H265NALU_AGGREGATION_PACKET_TYPE: u8 = 48;
308/// <https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3>
309const H265NALU_FRAGMENTATION_UNIT_TYPE: u8 = 49;
310/// <https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4>
311const H265NALU_PACI_PACKET_TYPE: u8 = 50;
312
313/// H265NALUHeader is a H265 NAL Unit Header
314///
315/// ```text
316/// +---------------+---------------+
317/// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
318/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
319/// |F|   Type    |  layer_id  | tid|
320/// +-------------+-----------------+
321/// ```
322///
323/// ## Specifications
324///
325/// * [RFC 7798 §1.1.4]
326///
327/// [RFC 7798 §1.1.4]: https://tools.ietf.org/html/rfc7798#section-1.1.4
328#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
329pub struct H265NALUHeader(pub u16);
330
331impl H265NALUHeader {
332    /// Parses an H.265 NAL header from its two bytes.
333    pub fn new(high_byte: u8, low_byte: u8) -> Self {
334        H265NALUHeader(((high_byte as u16) << 8) | low_byte as u16)
335    }
336
337    /// f is the forbidden bit, should always be 0.
338    pub fn f(&self) -> bool {
339        (self.0 >> 15) != 0
340    }
341
342    /// nalu_type of NAL Unit.
343    pub fn nalu_type(&self) -> u8 {
344        // 01111110 00000000
345        const MASK: u16 = 0b01111110 << 8;
346        ((self.0 & MASK) >> (8 + 1)) as u8
347    }
348
349    /// is_type_vcl_unit returns whether or not the NAL Unit type is a VCL NAL unit.
350    pub fn is_type_vcl_unit(&self) -> bool {
351        // Type is coded on 6 bits
352        const MSB_MASK: u8 = 0b00100000;
353        (self.nalu_type() & MSB_MASK) == 0
354    }
355
356    /// layer_id should always be 0 in non-3D HEVC context.
357    pub fn layer_id(&self) -> u8 {
358        // 00000001 11111000
359        const MASK: u16 = (0b00000001 << 8) | 0b11111000;
360        ((self.0 & MASK) >> 3) as u8
361    }
362
363    /// tid is the temporal identifier of the NAL unit +1.
364    pub fn tid(&self) -> u8 {
365        const MASK: u16 = 0b00000111;
366        (self.0 & MASK) as u8
367    }
368
369    /// is_aggregation_packet returns whether or not the packet is an Aggregation packet.
370    pub fn is_aggregation_packet(&self) -> bool {
371        self.nalu_type() == H265NALU_AGGREGATION_PACKET_TYPE
372    }
373
374    /// is_fragmentation_unit returns whether or not the packet is a Fragmentation Unit packet.
375    pub fn is_fragmentation_unit(&self) -> bool {
376        self.nalu_type() == H265NALU_FRAGMENTATION_UNIT_TYPE
377    }
378
379    /// is_paci_packet returns whether or not the packet is a PACI packet.
380    pub fn is_paci_packet(&self) -> bool {
381        self.nalu_type() == H265NALU_PACI_PACKET_TYPE
382    }
383}
384
385///
386/// Single NAL Unit Packet implementation
387///
388/// H265SingleNALUnitPacket represents a NALU packet, containing exactly one NAL unit.
389///     0                   1                   2                   3
390///    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
391///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
392///   |           PayloadHdr          |      DONL (conditional)       |
393///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394///   |                                                               |
395///   |                  NAL unit payload data                        |
396///   |                                                               |
397///   |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
398///   |                               :...OPTIONAL RTP padding        |
399///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
400///
401/// ## Specifications
402///
403/// * [RFC 7798 §4.4.1]
404///
405/// [RFC 7798 §4.4.1]: https://tools.ietf.org/html/rfc7798#section-4.4.1
406#[derive(Default, Debug, Clone, PartialEq, Eq)]
407pub struct H265SingleNALUnitPacket {
408    /// payload_header is the header of the H265 packet.
409    payload_header: H265NALUHeader,
410    /// donl is a 16-bit field, that may or may not be present.
411    donl: Option<u16>,
412    /// payload of the fragmentation unit.
413    payload: Bytes,
414
415    might_need_donl: bool,
416}
417
418impl H265SingleNALUnitPacket {
419    /// with_donl can be called to specify whether or not DONL might be parsed.
420    /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
421    pub fn with_donl(&mut self, value: bool) {
422        self.might_need_donl = value;
423    }
424
425    /// depacketize parses the passed byte slice and stores the result in the H265SingleNALUnitPacket this method is called upon.
426    fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
427        if payload.len() <= H265NALU_HEADER_SIZE {
428            return Err(Error::ErrShortPacket);
429        }
430
431        let payload_header = H265NALUHeader::new(payload[0], payload[1]);
432        if payload_header.f() {
433            return Err(Error::ErrH265CorruptedPacket);
434        }
435        if payload_header.is_fragmentation_unit()
436            || payload_header.is_paci_packet()
437            || payload_header.is_aggregation_packet()
438        {
439            return Err(Error::ErrInvalidH265PacketType);
440        }
441
442        let mut payload = payload.slice(2..);
443
444        if self.might_need_donl {
445            // sizeof(uint16)
446            if payload.len() <= 2 {
447                return Err(Error::ErrShortPacket);
448            }
449
450            let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
451            self.donl = Some(donl);
452            payload = payload.slice(2..);
453        }
454
455        self.payload_header = payload_header;
456        self.payload = payload;
457
458        Ok(())
459    }
460
461    /// payload_header returns the NALU header of the packet.
462    pub fn payload_header(&self) -> H265NALUHeader {
463        self.payload_header
464    }
465
466    /// donl returns the DONL of the packet.
467    pub fn donl(&self) -> Option<u16> {
468        self.donl
469    }
470
471    /// payload returns the Fragmentation Unit packet payload.
472    pub fn payload(&self) -> Bytes {
473        self.payload.clone()
474    }
475}
476
477///
478/// Aggregation Packets implementation
479///
480/// H265AggregationUnitFirst represent the First Aggregation Unit in an AP.
481///
482///    0                   1                   2                   3
483///    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
484///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
485///                   :       DONL (conditional)      |   NALU size   |
486///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
487///   |   NALU size   |                                               |
488///   +-+-+-+-+-+-+-+-+         NAL unit                              |
489///   |                                                               |
490///   |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
491///   |                               :
492///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
493///
494/// ## Specifications
495///
496/// * [RFC 7798 §4.4.2]
497///
498/// [RFC 7798 §4.4.2]: https://tools.ietf.org/html/rfc7798#section-4.4.2
499#[derive(Default, Debug, Clone, PartialEq, Eq)]
500pub struct H265AggregationUnitFirst {
501    donl: Option<u16>,
502    nal_unit_size: u16,
503    nal_unit: Bytes,
504}
505
506impl H265AggregationUnitFirst {
507    /// donl field, when present, specifies the value of the 16 least
508    /// significant bits of the decoding order number of the aggregated NAL
509    /// unit.
510    pub fn donl(&self) -> Option<u16> {
511        self.donl
512    }
513
514    /// nalu_size represents the size, in bytes, of the nal_unit.
515    pub fn nalu_size(&self) -> u16 {
516        self.nal_unit_size
517    }
518
519    /// nal_unit payload.
520    pub fn nal_unit(&self) -> Bytes {
521        self.nal_unit.clone()
522    }
523}
524
525/// H265AggregationUnit represent the an Aggregation Unit in an AP, which is not the first one.
526///
527///    0                   1                   2                   3
528///    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
529///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
530///                   : DOND (cond)   |          NALU size            |
531///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
532///   |                                                               |
533///   |                       NAL unit                                |
534///   |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
535///   |                               :
536///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
537///
538/// ## Specifications
539///
540/// * [RFC 7798 §4.4.2]
541///
542/// [RFC 7798 §4.4.2]: https://tools.ietf.org/html/rfc7798#section-4.4.2
543#[derive(Default, Debug, Clone, PartialEq, Eq)]
544pub struct H265AggregationUnit {
545    dond: Option<u8>,
546    nal_unit_size: u16,
547    nal_unit: Bytes,
548}
549
550impl H265AggregationUnit {
551    /// dond field plus 1 specifies the difference between
552    /// the decoding order number values of the current aggregated NAL unit
553    /// and the preceding aggregated NAL unit in the same AP.
554    pub fn dond(&self) -> Option<u8> {
555        self.dond
556    }
557
558    /// nalu_size represents the size, in bytes, of the nal_unit.
559    pub fn nalu_size(&self) -> u16 {
560        self.nal_unit_size
561    }
562
563    /// nal_unit payload.
564    pub fn nal_unit(&self) -> Bytes {
565        self.nal_unit.clone()
566    }
567}
568
569/// H265AggregationPacket represents an Aggregation packet.
570///   0                   1                   2                   3
571///    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
572///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
573///   |    PayloadHdr (Type=48)       |                               |
574///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
575///   |                                                               |
576///   |             two or more aggregation units                     |
577///   |                                                               |
578///   |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
579///   |                               :...OPTIONAL RTP padding        |
580///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
581///
582/// ## Specifications
583///
584/// * [RFC 7798 §4.4.2]
585///
586/// [RFC 7798 §4.4.2]: https://tools.ietf.org/html/rfc7798#section-4.4.2
587#[derive(Default, Debug, Clone, PartialEq, Eq)]
588pub struct H265AggregationPacket {
589    first_unit: Option<H265AggregationUnitFirst>,
590    other_units: Vec<H265AggregationUnit>,
591
592    might_need_donl: bool,
593}
594
595impl H265AggregationPacket {
596    /// with_donl can be called to specify whether or not DONL might be parsed.
597    /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
598    pub fn with_donl(&mut self, value: bool) {
599        self.might_need_donl = value;
600    }
601
602    /// depacketize parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.
603    fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
604        if payload.len() <= H265NALU_HEADER_SIZE {
605            return Err(Error::ErrShortPacket);
606        }
607
608        let payload_header = H265NALUHeader::new(payload[0], payload[1]);
609        if payload_header.f() {
610            return Err(Error::ErrH265CorruptedPacket);
611        }
612        if !payload_header.is_aggregation_packet() {
613            return Err(Error::ErrInvalidH265PacketType);
614        }
615
616        // First parse the first aggregation unit
617        let mut payload = payload.slice(2..);
618        let mut first_unit = H265AggregationUnitFirst::default();
619
620        if self.might_need_donl {
621            if payload.len() < 2 {
622                return Err(Error::ErrShortPacket);
623            }
624
625            let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
626            first_unit.donl = Some(donl);
627
628            payload = payload.slice(2..);
629        }
630        if payload.len() < 2 {
631            return Err(Error::ErrShortPacket);
632        }
633        first_unit.nal_unit_size = ((payload[0] as u16) << 8) | (payload[1] as u16);
634        payload = payload.slice(2..);
635
636        if payload.len() < first_unit.nal_unit_size as usize {
637            return Err(Error::ErrShortPacket);
638        }
639
640        first_unit.nal_unit = payload.slice(..first_unit.nal_unit_size as usize);
641        payload = payload.slice(first_unit.nal_unit_size as usize..);
642
643        // Parse remaining Aggregation Units
644        let mut units = vec![]; //H265AggregationUnit
645        loop {
646            let mut unit = H265AggregationUnit::default();
647
648            if self.might_need_donl {
649                if payload.is_empty() {
650                    break;
651                }
652
653                let dond = payload[0];
654                unit.dond = Some(dond);
655
656                payload = payload.slice(1..);
657            }
658
659            if payload.len() < 2 {
660                break;
661            }
662            unit.nal_unit_size = ((payload[0] as u16) << 8) | (payload[1] as u16);
663            payload = payload.slice(2..);
664
665            if payload.len() < unit.nal_unit_size as usize {
666                break;
667            }
668
669            unit.nal_unit = payload.slice(..unit.nal_unit_size as usize);
670            payload = payload.slice(unit.nal_unit_size as usize..);
671
672            units.push(unit);
673        }
674
675        // There need to be **at least** two Aggregation Units (first + another one)
676        if units.is_empty() {
677            return Err(Error::ErrShortPacket);
678        }
679
680        self.first_unit = Some(first_unit);
681        self.other_units = units;
682
683        Ok(())
684    }
685
686    /// first_unit returns the first Aggregated Unit of the packet.
687    pub fn first_unit(&self) -> Option<&H265AggregationUnitFirst> {
688        self.first_unit.as_ref()
689    }
690
691    /// other_units returns the all the other Aggregated Unit of the packet (excluding the first one).
692    pub fn other_units(&self) -> &[H265AggregationUnit] {
693        self.other_units.as_slice()
694    }
695}
696
697/// Fragmentation Unit implementation
698///
699/// H265FragmentationUnitHeader is a H265 FU Header
700/// +---------------+
701/// |0|1|2|3|4|5|6|7|
702/// +-+-+-+-+-+-+-+-+
703/// |S|E|  fu_type   |
704/// +---------------+
705#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
706pub struct H265FragmentationUnitHeader(pub u8);
707
708impl H265FragmentationUnitHeader {
709    /// s represents the start of a fragmented NAL unit.
710    pub fn s(&self) -> bool {
711        const MASK: u8 = 0b10000000;
712        ((self.0 & MASK) >> 7) != 0
713    }
714
715    /// e represents the end of a fragmented NAL unit.
716    pub fn e(&self) -> bool {
717        const MASK: u8 = 0b01000000;
718        ((self.0 & MASK) >> 6) != 0
719    }
720
721    /// fu_type MUST be equal to the field Type of the fragmented NAL unit.
722    pub fn fu_type(&self) -> u8 {
723        const MASK: u8 = 0b00111111;
724        self.0 & MASK
725    }
726}
727
728/// H265FragmentationUnitPacket represents a single Fragmentation Unit packet.
729///
730///  0                   1                   2                   3
731/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
732/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
733/// |    PayloadHdr (Type=49)       |   FU header   | DONL (cond)   |
734/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
735/// | DONL (cond)   |                                               |
736/// |-+-+-+-+-+-+-+-+                                               |
737/// |                         FU payload                            |
738/// |                                                               |
739/// |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
740/// |                               :...OPTIONAL RTP padding        |
741/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
742///
743/// ## Specifications
744///
745/// * [RFC 7798 §4.4.3]
746///
747/// [RFC 7798 §4.4.3]: https://tools.ietf.org/html/rfc7798#section-4.4.3
748#[derive(Default, Debug, Clone, PartialEq, Eq)]
749pub struct H265FragmentationUnitPacket {
750    /// payload_header is the header of the H265 packet.
751    payload_header: H265NALUHeader,
752    /// fu_header is the header of the fragmentation unit
753    fu_header: H265FragmentationUnitHeader,
754    /// donl is a 16-bit field, that may or may not be present.
755    donl: Option<u16>,
756    /// payload of the fragmentation unit.
757    payload: Bytes,
758
759    might_need_donl: bool,
760}
761
762impl H265FragmentationUnitPacket {
763    /// with_donl can be called to specify whether or not DONL might be parsed.
764    /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
765    pub fn with_donl(&mut self, value: bool) {
766        self.might_need_donl = value;
767    }
768
769    /// depacketize parses the passed byte slice and stores the result in the H265FragmentationUnitPacket this method is called upon.
770    fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
771        const TOTAL_HEADER_SIZE: usize = H265NALU_HEADER_SIZE + H265FRAGMENTATION_UNIT_HEADER_SIZE;
772        if payload.len() <= TOTAL_HEADER_SIZE {
773            return Err(Error::ErrShortPacket);
774        }
775
776        let payload_header = H265NALUHeader::new(payload[0], payload[1]);
777        if payload_header.f() {
778            return Err(Error::ErrH265CorruptedPacket);
779        }
780        if !payload_header.is_fragmentation_unit() {
781            return Err(Error::ErrInvalidH265PacketType);
782        }
783
784        let fu_header = H265FragmentationUnitHeader(payload[2]);
785        let mut payload = payload.slice(3..);
786
787        if fu_header.s() && self.might_need_donl {
788            if payload.len() <= 2 {
789                return Err(Error::ErrShortPacket);
790            }
791
792            let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
793            self.donl = Some(donl);
794            payload = payload.slice(2..);
795        }
796
797        self.payload_header = payload_header;
798        self.fu_header = fu_header;
799        self.payload = payload;
800
801        Ok(())
802    }
803
804    /// payload_header returns the NALU header of the packet.
805    pub fn payload_header(&self) -> H265NALUHeader {
806        self.payload_header
807    }
808
809    /// fu_header returns the Fragmentation Unit Header of the packet.
810    pub fn fu_header(&self) -> H265FragmentationUnitHeader {
811        self.fu_header
812    }
813
814    /// donl returns the DONL of the packet.
815    pub fn donl(&self) -> Option<u16> {
816        self.donl
817    }
818
819    /// payload returns the Fragmentation Unit packet payload.
820    pub fn payload(&self) -> Bytes {
821        self.payload.clone()
822    }
823}
824
825///
826/// PACI implementation
827///
828/// H265PACIPacket represents a single H265 PACI packet.
829///
830///  0                   1                   2                   3
831/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
832/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
833/// |    PayloadHdr (Type=50)       |A|   cType   | phssize |F0..2|Y|
834/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
835/// |        payload Header Extension Structure (phes)              |
836/// |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|
837/// |                                                               |
838/// |                  PACI payload: NAL unit                       |
839/// |                   . . .                                       |
840/// |                                                               |
841/// |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
842/// |                               :...OPTIONAL RTP padding        |
843/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
844///
845/// ## Specifications
846///
847/// * [RFC 7798 §4.4.4]
848///
849/// [RFC 7798 §4.4.4]: https://tools.ietf.org/html/rfc7798#section-4.4.4
850#[derive(Default, Debug, Clone, PartialEq, Eq)]
851pub struct H265PACIPacket {
852    /// payload_header is the header of the H265 packet.
853    payload_header: H265NALUHeader,
854
855    /// Field which holds value for `A`, `cType`, `phssize`, `F0`, `F1`, `F2` and `Y` fields.
856    paci_header_fields: u16,
857
858    /// phes is a header extension, of byte length `phssize`
859    phes: Bytes,
860
861    /// payload contains NAL units & optional padding
862    payload: Bytes,
863}
864
865impl H265PACIPacket {
866    /// payload_header returns the NAL Unit Header.
867    pub fn payload_header(&self) -> H265NALUHeader {
868        self.payload_header
869    }
870
871    /// a copies the F bit of the PACI payload NALU.
872    pub fn a(&self) -> bool {
873        const MASK: u16 = 0b10000000 << 8;
874        (self.paci_header_fields & MASK) != 0
875    }
876
877    /// ctype copies the Type field of the PACI payload NALU.
878    pub fn ctype(&self) -> u8 {
879        const MASK: u16 = 0b01111110 << 8;
880        ((self.paci_header_fields & MASK) >> (8 + 1)) as u8
881    }
882
883    /// phs_size indicates the size of the phes field.
884    pub fn phs_size(&self) -> u8 {
885        const MASK: u16 = (0b00000001 << 8) | 0b11110000;
886        ((self.paci_header_fields & MASK) >> 4) as u8
887    }
888
889    /// f0 indicates the presence of a Temporal Scalability support extension in the phes.
890    pub fn f0(&self) -> bool {
891        const MASK: u16 = 0b00001000;
892        (self.paci_header_fields & MASK) != 0
893    }
894
895    /// f1 must be zero, reserved for future extensions.
896    pub fn f1(&self) -> bool {
897        const MASK: u16 = 0b00000100;
898        (self.paci_header_fields & MASK) != 0
899    }
900
901    /// f2 must be zero, reserved for future extensions.
902    pub fn f2(&self) -> bool {
903        const MASK: u16 = 0b00000010;
904        (self.paci_header_fields & MASK) != 0
905    }
906
907    /// y must be zero, reserved for future extensions.
908    pub fn y(&self) -> bool {
909        const MASK: u16 = 0b00000001;
910        (self.paci_header_fields & MASK) != 0
911    }
912
913    /// phes contains header extensions. Its size is indicated by phssize.
914    pub fn phes(&self) -> Bytes {
915        self.phes.clone()
916    }
917
918    /// payload is a single NALU or NALU-like struct, not including the first two octets (header).
919    pub fn payload(&self) -> Bytes {
920        self.payload.clone()
921    }
922
923    /// tsci returns the Temporal Scalability Control Information extension, if present.
924    pub fn tsci(&self) -> Option<H265TSCI> {
925        if !self.f0() || self.phs_size() < 3 {
926            return None;
927        }
928
929        Some(H265TSCI(
930            ((self.phes[0] as u32) << 16) | ((self.phes[1] as u32) << 8) | self.phes[0] as u32,
931        ))
932    }
933
934    /// depacketize parses the passed byte slice and stores the result in the H265PACIPacket this method is called upon.
935    fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
936        const TOTAL_HEADER_SIZE: usize = H265NALU_HEADER_SIZE + 2;
937        if payload.len() <= TOTAL_HEADER_SIZE {
938            return Err(Error::ErrShortPacket);
939        }
940
941        let payload_header = H265NALUHeader::new(payload[0], payload[1]);
942        if payload_header.f() {
943            return Err(Error::ErrH265CorruptedPacket);
944        }
945        if !payload_header.is_paci_packet() {
946            return Err(Error::ErrInvalidH265PacketType);
947        }
948
949        let paci_header_fields = ((payload[2] as u16) << 8) | (payload[3] as u16);
950        let mut payload = payload.slice(4..);
951
952        self.paci_header_fields = paci_header_fields;
953        let header_extension_size = self.phs_size();
954
955        if payload.len() < header_extension_size as usize + 1 {
956            self.paci_header_fields = 0;
957            return Err(Error::ErrShortPacket);
958        }
959
960        self.payload_header = payload_header;
961
962        if header_extension_size > 0 {
963            self.phes = payload.slice(..header_extension_size as usize);
964        }
965
966        payload = payload.slice(header_extension_size as usize..);
967        self.payload = payload;
968
969        Ok(())
970    }
971}
972
973///
974/// Temporal Scalability Control Information
975///
976/// H265TSCI is a Temporal Scalability Control Information header extension.
977///
978/// ## Specifications
979///
980/// * [RFC 7798 §4.5]
981///
982/// [RFC 7798 §4.5]: https://tools.ietf.org/html/rfc7798#section-4.5
983#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
984pub struct H265TSCI(pub u32);
985
986impl H265TSCI {
987    /// tl0picidx see RFC7798 for more details.
988    pub fn tl0picidx(&self) -> u8 {
989        const M1: u32 = 0xFFFF0000;
990        const M2: u32 = 0xFF00;
991        ((((self.0 & M1) >> 16) & M2) >> 8) as u8
992    }
993
994    /// irap_pic_id see RFC7798 for more details.
995    pub fn irap_pic_id(&self) -> u8 {
996        const M1: u32 = 0xFFFF0000;
997        const M2: u32 = 0x00FF;
998        (((self.0 & M1) >> 16) & M2) as u8
999    }
1000
1001    /// s see RFC7798 for more details.
1002    pub fn s(&self) -> bool {
1003        const M1: u32 = 0xFF00;
1004        const M2: u32 = 0b10000000;
1005        (((self.0 & M1) >> 8) & M2) != 0
1006    }
1007
1008    /// e see RFC7798 for more details.
1009    pub fn e(&self) -> bool {
1010        const M1: u32 = 0xFF00;
1011        const M2: u32 = 0b01000000;
1012        (((self.0 & M1) >> 8) & M2) != 0
1013    }
1014
1015    /// res see RFC7798 for more details.
1016    pub fn res(&self) -> u8 {
1017        const M1: u32 = 0xFF00;
1018        const M2: u32 = 0b00111111;
1019        (((self.0 & M1) >> 8) & M2) as u8
1020    }
1021}
1022
1023///
1024/// H265 Payload Enum
1025///
1026#[derive(Debug, Clone, PartialEq, Eq)]
1027#[non_exhaustive]
1028pub enum H265Payload {
1029    /// One NAL unit carried whole in a single packet.
1030    H265SingleNALUnitPacket(H265SingleNALUnitPacket),
1031    /// One fragment of a NAL unit too large for the MTU.
1032    H265FragmentationUnitPacket(H265FragmentationUnitPacket),
1033    /// Several small NAL units aggregated into one packet.
1034    H265AggregationPacket(H265AggregationPacket),
1035    /// A PACI packet, which carries payload content information ahead of the NAL unit.
1036    H265PACIPacket(H265PACIPacket),
1037}
1038
1039impl Default for H265Payload {
1040    fn default() -> Self {
1041        H265Payload::H265SingleNALUnitPacket(H265SingleNALUnitPacket::default())
1042    }
1043}
1044
1045///
1046/// Packet implementation
1047///
1048/// H265Packet represents a H265 packet, stored in the payload of an RTP packet.
1049#[derive(Default, Debug, Clone, PartialEq, Eq)]
1050pub struct H265Packet {
1051    payload: H265Payload,
1052    might_need_donl: bool,
1053}
1054
1055impl H265Packet {
1056    /// with_donl can be called to specify whether or not DONL might be parsed.
1057    /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
1058    pub fn with_donl(&mut self, value: bool) {
1059        self.might_need_donl = value;
1060    }
1061
1062    /// payload returns the populated payload.
1063    /// Must be casted to one of:
1064    /// - H265SingleNALUnitPacket
1065    /// - H265FragmentationUnitPacket
1066    /// - H265AggregationPacket
1067    /// - H265PACIPacket
1068    pub fn payload(&self) -> &H265Payload {
1069        &self.payload
1070    }
1071}
1072
1073impl Depacketizer for H265Packet {
1074    /// depacketize parses the passed byte slice and stores the result in the H265Packet this method is called upon
1075    fn depacketize(&mut self, payload: &Bytes) -> Result<Bytes> {
1076        if payload.len() <= H265NALU_HEADER_SIZE {
1077            return Err(Error::ErrShortPacket);
1078        }
1079
1080        let payload_header = H265NALUHeader::new(payload[0], payload[1]);
1081        if payload_header.f() {
1082            return Err(Error::ErrH265CorruptedPacket);
1083        }
1084
1085        if payload_header.is_paci_packet() {
1086            let mut decoded = H265PACIPacket::default();
1087            decoded.depacketize(payload)?;
1088
1089            self.payload = H265Payload::H265PACIPacket(decoded);
1090        } else if payload_header.is_fragmentation_unit() {
1091            let mut decoded = H265FragmentationUnitPacket::default();
1092            decoded.with_donl(self.might_need_donl);
1093
1094            decoded.depacketize(payload)?;
1095
1096            self.payload = H265Payload::H265FragmentationUnitPacket(decoded);
1097        } else if payload_header.is_aggregation_packet() {
1098            let mut decoded = H265AggregationPacket::default();
1099            decoded.with_donl(self.might_need_donl);
1100
1101            decoded.depacketize(payload)?;
1102
1103            self.payload = H265Payload::H265AggregationPacket(decoded);
1104        } else {
1105            let mut decoded = H265SingleNALUnitPacket::default();
1106            decoded.with_donl(self.might_need_donl);
1107
1108            decoded.depacketize(payload)?;
1109
1110            self.payload = H265Payload::H265SingleNALUnitPacket(decoded);
1111        }
1112
1113        Ok(payload.clone())
1114    }
1115
1116    /// is_partition_head checks if this is the head of a packetized nalu stream.
1117    fn is_partition_head(&self, _payload: &Bytes) -> bool {
1118        //TODO:
1119        true
1120    }
1121
1122    fn is_partition_tail(&self, marker: bool, _payload: &Bytes) -> bool {
1123        marker
1124    }
1125}