Skip to main content

rtc_rtp/codec/h265/
mod.rs

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