rtc_rtp/codecs/h265/mod.rs
1use crate::packetizer::Depacketizer;
2use bytes::Bytes;
3use shared::error::{Error, Result};
4
5#[cfg(test)]
6mod h265_test;
7
8///
9/// Network Abstraction Unit Header implementation
10///
11
12const H265NALU_HEADER_SIZE: usize = 2;
13/// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
14const H265NALU_AGGREGATION_PACKET_TYPE: u8 = 48;
15/// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
16const H265NALU_FRAGMENTATION_UNIT_TYPE: u8 = 49;
17/// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
18const H265NALU_PACI_PACKET_TYPE: u8 = 50;
19
20/// H265NALUHeader is a H265 NAL Unit Header
21/// https://datatracker.ietf.org/doc/html/rfc7798#section-1.1.4
22/// +---------------+---------------+
23/// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
24/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25/// |F| Type | layer_id | tid |
26/// +-------------+-----------------+
27#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
28pub struct H265NALUHeader(pub u16);
29
30impl H265NALUHeader {
31 fn new(high_byte: u8, low_byte: u8) -> Self {
32 H265NALUHeader(((high_byte as u16) << 8) | low_byte as u16)
33 }
34
35 /// f is the forbidden bit, should always be 0.
36 pub fn f(&self) -> bool {
37 (self.0 >> 15) != 0
38 }
39
40 /// nalu_type of NAL Unit.
41 pub fn nalu_type(&self) -> u8 {
42 // 01111110 00000000
43 const MASK: u16 = 0b01111110 << 8;
44 ((self.0 & MASK) >> (8 + 1)) as u8
45 }
46
47 /// is_type_vcl_unit returns whether or not the NAL Unit type is a VCL NAL unit.
48 pub fn is_type_vcl_unit(&self) -> bool {
49 // Type is coded on 6 bits
50 const MSB_MASK: u8 = 0b00100000;
51 (self.nalu_type() & MSB_MASK) == 0
52 }
53
54 /// layer_id should always be 0 in non-3D HEVC context.
55 pub fn layer_id(&self) -> u8 {
56 // 00000001 11111000
57 const MASK: u16 = (0b00000001 << 8) | 0b11111000;
58 ((self.0 & MASK) >> 3) as u8
59 }
60
61 /// tid is the temporal identifier of the NAL unit +1.
62 pub fn tid(&self) -> u8 {
63 const MASK: u16 = 0b00000111;
64 (self.0 & MASK) as u8
65 }
66
67 /// is_aggregation_packet returns whether or not the packet is an Aggregation packet.
68 pub fn is_aggregation_packet(&self) -> bool {
69 self.nalu_type() == H265NALU_AGGREGATION_PACKET_TYPE
70 }
71
72 /// is_fragmentation_unit returns whether or not the packet is a Fragmentation Unit packet.
73 pub fn is_fragmentation_unit(&self) -> bool {
74 self.nalu_type() == H265NALU_FRAGMENTATION_UNIT_TYPE
75 }
76
77 /// is_paci_packet returns whether or not the packet is a PACI packet.
78 pub fn is_paci_packet(&self) -> bool {
79 self.nalu_type() == H265NALU_PACI_PACKET_TYPE
80 }
81}
82
83///
84/// Single NAL Unit Packet implementation
85///
86/// H265SingleNALUnitPacket represents a NALU packet, containing exactly one NAL unit.
87/// 0 1 2 3
88/// 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
89/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90/// | PayloadHdr | DONL (conditional) |
91/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
92/// | |
93/// | NAL unit payload data |
94/// | |
95/// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96/// | :...OPTIONAL RTP padding |
97/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98///
99/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.1
100#[derive(Default, Debug, Clone, PartialEq, Eq)]
101pub struct H265SingleNALUnitPacket {
102 /// payload_header is the header of the H265 packet.
103 payload_header: H265NALUHeader,
104 /// donl is a 16-bit field, that may or may not be present.
105 donl: Option<u16>,
106 /// payload of the fragmentation unit.
107 payload: Bytes,
108
109 might_need_donl: bool,
110}
111
112impl H265SingleNALUnitPacket {
113 /// with_donl can be called to specify whether or not DONL might be parsed.
114 /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
115 pub fn with_donl(&mut self, value: bool) {
116 self.might_need_donl = value;
117 }
118
119 /// depacketize parses the passed byte slice and stores the result in the H265SingleNALUnitPacket this method is called upon.
120 fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
121 if payload.len() <= H265NALU_HEADER_SIZE {
122 return Err(Error::ErrShortPacket);
123 }
124
125 let payload_header = H265NALUHeader::new(payload[0], payload[1]);
126 if payload_header.f() {
127 return Err(Error::ErrH265CorruptedPacket);
128 }
129 if payload_header.is_fragmentation_unit()
130 || payload_header.is_paci_packet()
131 || payload_header.is_aggregation_packet()
132 {
133 return Err(Error::ErrInvalidH265PacketType);
134 }
135
136 let mut payload = payload.slice(2..);
137
138 if self.might_need_donl {
139 // sizeof(uint16)
140 if payload.len() <= 2 {
141 return Err(Error::ErrShortPacket);
142 }
143
144 let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
145 self.donl = Some(donl);
146 payload = payload.slice(2..);
147 }
148
149 self.payload_header = payload_header;
150 self.payload = payload;
151
152 Ok(())
153 }
154
155 /// payload_header returns the NALU header of the packet.
156 pub fn payload_header(&self) -> H265NALUHeader {
157 self.payload_header
158 }
159
160 /// donl returns the DONL of the packet.
161 pub fn donl(&self) -> Option<u16> {
162 self.donl
163 }
164
165 /// payload returns the Fragmentation Unit packet payload.
166 pub fn payload(&self) -> Bytes {
167 self.payload.clone()
168 }
169}
170
171///
172/// Aggregation Packets implementation
173///
174/// H265AggregationUnitFirst represent the First Aggregation Unit in an AP.
175///
176/// 0 1 2 3
177/// 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
178/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
179/// : DONL (conditional) | NALU size |
180/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181/// | NALU size | |
182/// +-+-+-+-+-+-+-+-+ NAL unit |
183/// | |
184/// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
185/// | :
186/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
187///
188/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
189#[derive(Default, Debug, Clone, PartialEq, Eq)]
190pub struct H265AggregationUnitFirst {
191 donl: Option<u16>,
192 nal_unit_size: u16,
193 nal_unit: Bytes,
194}
195
196impl H265AggregationUnitFirst {
197 /// donl field, when present, specifies the value of the 16 least
198 /// significant bits of the decoding order number of the aggregated NAL
199 /// unit.
200 pub fn donl(&self) -> Option<u16> {
201 self.donl
202 }
203
204 /// nalu_size represents the size, in bytes, of the nal_unit.
205 pub fn nalu_size(&self) -> u16 {
206 self.nal_unit_size
207 }
208
209 /// nal_unit payload.
210 pub fn nal_unit(&self) -> Bytes {
211 self.nal_unit.clone()
212 }
213}
214
215/// H265AggregationUnit represent the an Aggregation Unit in an AP, which is not the first one.
216///
217/// 0 1 2 3
218/// 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
219/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
220/// : DOND (cond) | NALU size |
221/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
222/// | |
223/// | NAL unit |
224/// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
225/// | :
226/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
227///
228/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
229#[derive(Default, Debug, Clone, PartialEq, Eq)]
230pub struct H265AggregationUnit {
231 dond: Option<u8>,
232 nal_unit_size: u16,
233 nal_unit: Bytes,
234}
235
236impl H265AggregationUnit {
237 /// dond field plus 1 specifies the difference between
238 /// the decoding order number values of the current aggregated NAL unit
239 /// and the preceding aggregated NAL unit in the same AP.
240 pub fn dond(&self) -> Option<u8> {
241 self.dond
242 }
243
244 /// nalu_size represents the size, in bytes, of the nal_unit.
245 pub fn nalu_size(&self) -> u16 {
246 self.nal_unit_size
247 }
248
249 /// nal_unit payload.
250 pub fn nal_unit(&self) -> Bytes {
251 self.nal_unit.clone()
252 }
253}
254
255/// H265AggregationPacket represents an Aggregation packet.
256/// 0 1 2 3
257/// 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
258/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
259/// | PayloadHdr (Type=48) | |
260/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
261/// | |
262/// | two or more aggregation units |
263/// | |
264/// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
265/// | :...OPTIONAL RTP padding |
266/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
267///
268/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
269#[derive(Default, Debug, Clone, PartialEq, Eq)]
270pub struct H265AggregationPacket {
271 first_unit: Option<H265AggregationUnitFirst>,
272 other_units: Vec<H265AggregationUnit>,
273
274 might_need_donl: bool,
275}
276
277impl H265AggregationPacket {
278 /// with_donl can be called to specify whether or not DONL might be parsed.
279 /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
280 pub fn with_donl(&mut self, value: bool) {
281 self.might_need_donl = value;
282 }
283
284 /// depacketize parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.
285 fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
286 if payload.len() <= H265NALU_HEADER_SIZE {
287 return Err(Error::ErrShortPacket);
288 }
289
290 let payload_header = H265NALUHeader::new(payload[0], payload[1]);
291 if payload_header.f() {
292 return Err(Error::ErrH265CorruptedPacket);
293 }
294 if !payload_header.is_aggregation_packet() {
295 return Err(Error::ErrInvalidH265PacketType);
296 }
297
298 // First parse the first aggregation unit
299 let mut payload = payload.slice(2..);
300 let mut first_unit = H265AggregationUnitFirst::default();
301
302 if self.might_need_donl {
303 if payload.len() < 2 {
304 return Err(Error::ErrShortPacket);
305 }
306
307 let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
308 first_unit.donl = Some(donl);
309
310 payload = payload.slice(2..);
311 }
312 if payload.len() < 2 {
313 return Err(Error::ErrShortPacket);
314 }
315 first_unit.nal_unit_size = ((payload[0] as u16) << 8) | (payload[1] as u16);
316 payload = payload.slice(2..);
317
318 if payload.len() < first_unit.nal_unit_size as usize {
319 return Err(Error::ErrShortPacket);
320 }
321
322 first_unit.nal_unit = payload.slice(..first_unit.nal_unit_size as usize);
323 payload = payload.slice(first_unit.nal_unit_size as usize..);
324
325 // Parse remaining Aggregation Units
326 let mut units = vec![]; //H265AggregationUnit
327 loop {
328 let mut unit = H265AggregationUnit::default();
329
330 if self.might_need_donl {
331 if payload.is_empty() {
332 break;
333 }
334
335 let dond = payload[0];
336 unit.dond = Some(dond);
337
338 payload = payload.slice(1..);
339 }
340
341 if payload.len() < 2 {
342 break;
343 }
344 unit.nal_unit_size = ((payload[0] as u16) << 8) | (payload[1] as u16);
345 payload = payload.slice(2..);
346
347 if payload.len() < unit.nal_unit_size as usize {
348 break;
349 }
350
351 unit.nal_unit = payload.slice(..unit.nal_unit_size as usize);
352 payload = payload.slice(unit.nal_unit_size as usize..);
353
354 units.push(unit);
355 }
356
357 // There need to be **at least** two Aggregation Units (first + another one)
358 if units.is_empty() {
359 return Err(Error::ErrShortPacket);
360 }
361
362 self.first_unit = Some(first_unit);
363 self.other_units = units;
364
365 Ok(())
366 }
367
368 /// first_unit returns the first Aggregated Unit of the packet.
369 pub fn first_unit(&self) -> Option<&H265AggregationUnitFirst> {
370 self.first_unit.as_ref()
371 }
372
373 /// other_units returns the all the other Aggregated Unit of the packet (excluding the first one).
374 pub fn other_units(&self) -> &[H265AggregationUnit] {
375 self.other_units.as_slice()
376 }
377}
378
379///
380/// Fragmentation Unit implementation
381///
382
383const H265FRAGMENTATION_UNIT_HEADER_SIZE: usize = 1;
384
385/// H265FragmentationUnitHeader is a H265 FU Header
386/// +---------------+
387/// |0|1|2|3|4|5|6|7|
388/// +-+-+-+-+-+-+-+-+
389/// |S|E| fu_type |
390/// +---------------+
391#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
392pub struct H265FragmentationUnitHeader(pub u8);
393
394impl H265FragmentationUnitHeader {
395 /// s represents the start of a fragmented NAL unit.
396 pub fn s(&self) -> bool {
397 const MASK: u8 = 0b10000000;
398 ((self.0 & MASK) >> 7) != 0
399 }
400
401 /// e represents the end of a fragmented NAL unit.
402 pub fn e(&self) -> bool {
403 const MASK: u8 = 0b01000000;
404 ((self.0 & MASK) >> 6) != 0
405 }
406
407 /// fu_type MUST be equal to the field Type of the fragmented NAL unit.
408 pub fn fu_type(&self) -> u8 {
409 const MASK: u8 = 0b00111111;
410 self.0 & MASK
411 }
412}
413
414/// H265FragmentationUnitPacket represents a single Fragmentation Unit packet.
415///
416/// 0 1 2 3
417/// 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
418/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
419/// | PayloadHdr (Type=49) | FU header | DONL (cond) |
420/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
421/// | DONL (cond) | |
422/// |-+-+-+-+-+-+-+-+ |
423/// | FU payload |
424/// | |
425/// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
426/// | :...OPTIONAL RTP padding |
427/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428///
429/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
430#[derive(Default, Debug, Clone, PartialEq, Eq)]
431pub struct H265FragmentationUnitPacket {
432 /// payload_header is the header of the H265 packet.
433 payload_header: H265NALUHeader,
434 /// fu_header is the header of the fragmentation unit
435 fu_header: H265FragmentationUnitHeader,
436 /// donl is a 16-bit field, that may or may not be present.
437 donl: Option<u16>,
438 /// payload of the fragmentation unit.
439 payload: Bytes,
440
441 might_need_donl: bool,
442}
443
444impl H265FragmentationUnitPacket {
445 /// with_donl can be called to specify whether or not DONL might be parsed.
446 /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
447 pub fn with_donl(&mut self, value: bool) {
448 self.might_need_donl = value;
449 }
450
451 /// depacketize parses the passed byte slice and stores the result in the H265FragmentationUnitPacket this method is called upon.
452 fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
453 const TOTAL_HEADER_SIZE: usize = H265NALU_HEADER_SIZE + H265FRAGMENTATION_UNIT_HEADER_SIZE;
454 if payload.len() <= TOTAL_HEADER_SIZE {
455 return Err(Error::ErrShortPacket);
456 }
457
458 let payload_header = H265NALUHeader::new(payload[0], payload[1]);
459 if payload_header.f() {
460 return Err(Error::ErrH265CorruptedPacket);
461 }
462 if !payload_header.is_fragmentation_unit() {
463 return Err(Error::ErrInvalidH265PacketType);
464 }
465
466 let fu_header = H265FragmentationUnitHeader(payload[2]);
467 let mut payload = payload.slice(3..);
468
469 if fu_header.s() && self.might_need_donl {
470 if payload.len() <= 2 {
471 return Err(Error::ErrShortPacket);
472 }
473
474 let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
475 self.donl = Some(donl);
476 payload = payload.slice(2..);
477 }
478
479 self.payload_header = payload_header;
480 self.fu_header = fu_header;
481 self.payload = payload;
482
483 Ok(())
484 }
485
486 /// payload_header returns the NALU header of the packet.
487 pub fn payload_header(&self) -> H265NALUHeader {
488 self.payload_header
489 }
490
491 /// fu_header returns the Fragmentation Unit Header of the packet.
492 pub fn fu_header(&self) -> H265FragmentationUnitHeader {
493 self.fu_header
494 }
495
496 /// donl returns the DONL of the packet.
497 pub fn donl(&self) -> Option<u16> {
498 self.donl
499 }
500
501 /// payload returns the Fragmentation Unit packet payload.
502 pub fn payload(&self) -> Bytes {
503 self.payload.clone()
504 }
505}
506
507///
508/// PACI implementation
509///
510
511/// H265PACIPacket represents a single H265 PACI packet.
512///
513/// 0 1 2 3
514/// 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
515/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
516/// | PayloadHdr (Type=50) |A| cType | phssize |F0..2|Y|
517/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
518/// | payload Header Extension Structure (phes) |
519/// |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|
520/// | |
521/// | PACI payload: NAL unit |
522/// | . . . |
523/// | |
524/// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
525/// | :...OPTIONAL RTP padding |
526/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
527///
528/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
529#[derive(Default, Debug, Clone, PartialEq, Eq)]
530pub struct H265PACIPacket {
531 /// payload_header is the header of the H265 packet.
532 payload_header: H265NALUHeader,
533
534 /// Field which holds value for `A`, `cType`, `phssize`, `F0`, `F1`, `F2` and `Y` fields.
535 paci_header_fields: u16,
536
537 /// phes is a header extension, of byte length `phssize`
538 phes: Bytes,
539
540 /// payload contains NAL units & optional padding
541 payload: Bytes,
542}
543
544impl H265PACIPacket {
545 /// payload_header returns the NAL Unit Header.
546 pub fn payload_header(&self) -> H265NALUHeader {
547 self.payload_header
548 }
549
550 /// a copies the F bit of the PACI payload NALU.
551 pub fn a(&self) -> bool {
552 const MASK: u16 = 0b10000000 << 8;
553 (self.paci_header_fields & MASK) != 0
554 }
555
556 /// ctype copies the Type field of the PACI payload NALU.
557 pub fn ctype(&self) -> u8 {
558 const MASK: u16 = 0b01111110 << 8;
559 ((self.paci_header_fields & MASK) >> (8 + 1)) as u8
560 }
561
562 /// phs_size indicates the size of the phes field.
563 pub fn phs_size(&self) -> u8 {
564 const MASK: u16 = (0b00000001 << 8) | 0b11110000;
565 ((self.paci_header_fields & MASK) >> 4) as u8
566 }
567
568 /// f0 indicates the presence of a Temporal Scalability support extension in the phes.
569 pub fn f0(&self) -> bool {
570 const MASK: u16 = 0b00001000;
571 (self.paci_header_fields & MASK) != 0
572 }
573
574 /// f1 must be zero, reserved for future extensions.
575 pub fn f1(&self) -> bool {
576 const MASK: u16 = 0b00000100;
577 (self.paci_header_fields & MASK) != 0
578 }
579
580 /// f2 must be zero, reserved for future extensions.
581 pub fn f2(&self) -> bool {
582 const MASK: u16 = 0b00000010;
583 (self.paci_header_fields & MASK) != 0
584 }
585
586 /// y must be zero, reserved for future extensions.
587 pub fn y(&self) -> bool {
588 const MASK: u16 = 0b00000001;
589 (self.paci_header_fields & MASK) != 0
590 }
591
592 /// phes contains header extensions. Its size is indicated by phssize.
593 pub fn phes(&self) -> Bytes {
594 self.phes.clone()
595 }
596
597 /// payload is a single NALU or NALU-like struct, not including the first two octets (header).
598 pub fn payload(&self) -> Bytes {
599 self.payload.clone()
600 }
601
602 /// tsci returns the Temporal Scalability Control Information extension, if present.
603 pub fn tsci(&self) -> Option<H265TSCI> {
604 if !self.f0() || self.phs_size() < 3 {
605 return None;
606 }
607
608 Some(H265TSCI(
609 ((self.phes[0] as u32) << 16) | ((self.phes[1] as u32) << 8) | self.phes[0] as u32,
610 ))
611 }
612
613 /// depacketize parses the passed byte slice and stores the result in the H265PACIPacket this method is called upon.
614 fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
615 const TOTAL_HEADER_SIZE: usize = H265NALU_HEADER_SIZE + 2;
616 if payload.len() <= TOTAL_HEADER_SIZE {
617 return Err(Error::ErrShortPacket);
618 }
619
620 let payload_header = H265NALUHeader::new(payload[0], payload[1]);
621 if payload_header.f() {
622 return Err(Error::ErrH265CorruptedPacket);
623 }
624 if !payload_header.is_paci_packet() {
625 return Err(Error::ErrInvalidH265PacketType);
626 }
627
628 let paci_header_fields = ((payload[2] as u16) << 8) | (payload[3] as u16);
629 let mut payload = payload.slice(4..);
630
631 self.paci_header_fields = paci_header_fields;
632 let header_extension_size = self.phs_size();
633
634 if payload.len() < header_extension_size as usize + 1 {
635 self.paci_header_fields = 0;
636 return Err(Error::ErrShortPacket);
637 }
638
639 self.payload_header = payload_header;
640
641 if header_extension_size > 0 {
642 self.phes = payload.slice(..header_extension_size as usize);
643 }
644
645 payload = payload.slice(header_extension_size as usize..);
646 self.payload = payload;
647
648 Ok(())
649 }
650}
651
652///
653/// Temporal Scalability Control Information
654///
655
656/// H265TSCI is a Temporal Scalability Control Information header extension.
657/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.5
658#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
659pub struct H265TSCI(pub u32);
660
661impl H265TSCI {
662 /// tl0picidx see RFC7798 for more details.
663 pub fn tl0picidx(&self) -> u8 {
664 const M1: u32 = 0xFFFF0000;
665 const M2: u32 = 0xFF00;
666 ((((self.0 & M1) >> 16) & M2) >> 8) as u8
667 }
668
669 /// irap_pic_id see RFC7798 for more details.
670 pub fn irap_pic_id(&self) -> u8 {
671 const M1: u32 = 0xFFFF0000;
672 const M2: u32 = 0x00FF;
673 (((self.0 & M1) >> 16) & M2) as u8
674 }
675
676 /// s see RFC7798 for more details.
677 pub fn s(&self) -> bool {
678 const M1: u32 = 0xFF00;
679 const M2: u32 = 0b10000000;
680 (((self.0 & M1) >> 8) & M2) != 0
681 }
682
683 /// e see RFC7798 for more details.
684 pub fn e(&self) -> bool {
685 const M1: u32 = 0xFF00;
686 const M2: u32 = 0b01000000;
687 (((self.0 & M1) >> 8) & M2) != 0
688 }
689
690 /// res see RFC7798 for more details.
691 pub fn res(&self) -> u8 {
692 const M1: u32 = 0xFF00;
693 const M2: u32 = 0b00111111;
694 (((self.0 & M1) >> 8) & M2) as u8
695 }
696}
697
698///
699/// H265 Payload Enum
700///
701#[derive(Debug, Clone, PartialEq, Eq)]
702pub enum H265Payload {
703 H265SingleNALUnitPacket(H265SingleNALUnitPacket),
704 H265FragmentationUnitPacket(H265FragmentationUnitPacket),
705 H265AggregationPacket(H265AggregationPacket),
706 H265PACIPacket(H265PACIPacket),
707}
708
709impl Default for H265Payload {
710 fn default() -> Self {
711 H265Payload::H265SingleNALUnitPacket(H265SingleNALUnitPacket::default())
712 }
713}
714
715///
716/// Packet implementation
717///
718
719/// H265Packet represents a H265 packet, stored in the payload of an RTP packet.
720#[derive(Default, Debug, Clone, PartialEq, Eq)]
721pub struct H265Packet {
722 payload: H265Payload,
723 might_need_donl: bool,
724}
725
726impl H265Packet {
727 /// with_donl can be called to specify whether or not DONL might be parsed.
728 /// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
729 pub fn with_donl(&mut self, value: bool) {
730 self.might_need_donl = value;
731 }
732
733 /// payload returns the populated payload.
734 /// Must be casted to one of:
735 /// - H265SingleNALUnitPacket
736 /// - H265FragmentationUnitPacket
737 /// - H265AggregationPacket
738 /// - H265PACIPacket
739 pub fn payload(&self) -> &H265Payload {
740 &self.payload
741 }
742}
743
744impl Depacketizer for H265Packet {
745 /// depacketize parses the passed byte slice and stores the result in the H265Packet this method is called upon
746 fn depacketize(&mut self, payload: &Bytes) -> Result<Bytes> {
747 if payload.len() <= H265NALU_HEADER_SIZE {
748 return Err(Error::ErrShortPacket);
749 }
750
751 let payload_header = H265NALUHeader::new(payload[0], payload[1]);
752 if payload_header.f() {
753 return Err(Error::ErrH265CorruptedPacket);
754 }
755
756 if payload_header.is_paci_packet() {
757 let mut decoded = H265PACIPacket::default();
758 decoded.depacketize(payload)?;
759
760 self.payload = H265Payload::H265PACIPacket(decoded);
761 } else if payload_header.is_fragmentation_unit() {
762 let mut decoded = H265FragmentationUnitPacket::default();
763 decoded.with_donl(self.might_need_donl);
764
765 decoded.depacketize(payload)?;
766
767 self.payload = H265Payload::H265FragmentationUnitPacket(decoded);
768 } else if payload_header.is_aggregation_packet() {
769 let mut decoded = H265AggregationPacket::default();
770 decoded.with_donl(self.might_need_donl);
771
772 decoded.depacketize(payload)?;
773
774 self.payload = H265Payload::H265AggregationPacket(decoded);
775 } else {
776 let mut decoded = H265SingleNALUnitPacket::default();
777 decoded.with_donl(self.might_need_donl);
778
779 decoded.depacketize(payload)?;
780
781 self.payload = H265Payload::H265SingleNALUnitPacket(decoded);
782 }
783
784 Ok(payload.clone())
785 }
786
787 /// is_partition_head checks if this is the head of a packetized nalu stream.
788 fn is_partition_head(&self, _payload: &Bytes) -> bool {
789 //TODO:
790 true
791 }
792
793 fn is_partition_tail(&self, marker: bool, _payload: &Bytes) -> bool {
794 marker
795 }
796}