rtcp_types/
compound.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use crate::{
4    prelude::*,
5    utils::{parser::*, writer::*},
6    RtcpPacket, RtcpParseError, RtcpWriteError,
7};
8
9/// A (currently) unknown RTCP packet type.  Can also be used as a way to parse a custom RTCP packet
10/// type.
11#[derive(Debug, PartialEq, Eq)]
12pub struct Unknown<'a> {
13    data: &'a [u8],
14}
15
16impl<'a> RtcpPacket for Unknown<'a> {
17    const MIN_PACKET_LEN: usize = 4;
18    const PACKET_TYPE: u8 = 255; // Not used
19}
20
21impl<'a> RtcpPacketParser<'a> for Unknown<'a> {
22    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
23        if data.len() < Self::MIN_PACKET_LEN {
24            return Err(RtcpParseError::Truncated {
25                expected: Self::MIN_PACKET_LEN,
26                actual: data.len(),
27            });
28        }
29
30        let version = parse_version(data);
31        if parse_version(data) != Self::VERSION {
32            return Err(RtcpParseError::UnsupportedVersion(version));
33        }
34
35        let length = parse_length(data);
36        if data.len() < length {
37            return Err(RtcpParseError::Truncated {
38                expected: length,
39                actual: data.len(),
40            });
41        }
42        if data.len() > length {
43            return Err(RtcpParseError::TooLarge {
44                expected: length,
45                actual: data.len(),
46            });
47        }
48
49        Ok(Self { data })
50    }
51
52    #[inline(always)]
53    fn header_data(&self) -> [u8; 4] {
54        self.data[..4].try_into().unwrap()
55    }
56}
57
58impl<'a> Unknown<'a> {
59    /// The data of this RTCP packet
60    pub fn data(&self) -> &[u8] {
61        self.data
62    }
63
64    /// Try to parse this unknown RTCP packet as a different RTCP packet.  Can be used with an
65    /// external implementation of [`RtcpPacket`] to parse a custom RTCP packet.
66    pub fn try_as<P>(&'a self) -> Result<P, RtcpParseError>
67    where
68        P: RtcpPacket,
69        P: TryFrom<&'a Self, Error = RtcpParseError>,
70    {
71        TryFrom::try_from(self)
72    }
73
74    /// The builder for an [`Unknown`] RTCP packet.  The data does not include the 4 byte RTCP
75    /// header.
76    pub fn builder(type_: u8, data: &'a [u8]) -> UnknownBuilder<'a> {
77        UnknownBuilder::new(type_, data)
78    }
79}
80
81/// Unknown RTCP packet builder
82#[derive(Debug)]
83#[must_use = "The builder must be built to be used"]
84pub struct UnknownBuilder<'a> {
85    padding: u8,
86    type_: u8,
87    count: u8,
88    data: &'a [u8],
89}
90
91impl<'a> UnknownBuilder<'a> {
92    /// Create a new builder for an [`Unknown`] RTCP packet.  The data does not include the 4 byte RTCP
93    /// header.
94    pub fn new(type_: u8, data: &'a [u8]) -> UnknownBuilder<'a> {
95        UnknownBuilder {
96            padding: 0,
97            type_,
98            count: 0,
99            data,
100        }
101    }
102
103    /// Sets the number of padding bytes to use for this Unknown packet
104    pub fn padding(mut self, padding: u8) -> Self {
105        self.padding = padding;
106        self
107    }
108
109    /// Set the count (or possibly type) field in the RTCP header.  The exact interpretation of
110    /// this value is RTCP packet specific.
111    pub fn count(mut self, count: u8) -> Self {
112        self.count = count;
113        self
114    }
115}
116
117impl<'a> RtcpPacketWriter for UnknownBuilder<'a> {
118    /// Calculates the size required to write this Unknown packet.
119    ///
120    /// Returns an error if:
121    ///
122    /// * The count is out of range.
123    /// * The padding is not a multiple of 4.
124    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
125        if self.count > Unknown::MAX_COUNT {
126            return Err(RtcpWriteError::CountOutOfRange {
127                count: self.count,
128                max: Unknown::MAX_COUNT,
129            });
130        }
131
132        check_padding(self.padding)?;
133
134        Ok(Unknown::MIN_PACKET_LEN + self.data.len())
135    }
136
137    /// Write this Unknown packet data into `buf` without any validity checks.
138    ///
139    /// Returns the number of bytes written.
140    ///
141    /// # Panic
142    ///
143    /// Panics if the buf is not large enough.
144    #[inline]
145    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
146        write_header_unchecked::<Unknown>(self.padding, self.count, buf);
147        buf[1] = self.type_;
148
149        let mut end = 4 + self.data.len();
150        buf[4..end].copy_from_slice(self.data);
151
152        end += write_padding_unchecked(self.padding, &mut buf[end..]);
153
154        end
155    }
156
157    fn get_padding(&self) -> Option<u8> {
158        if self.padding == 0 {
159            return None;
160        }
161
162        Some(self.padding)
163    }
164}
165
166/// A (closed) enum of all currently known RTCP packet types.  The Unknown variant can be used to
167/// parse a custom RTCP packet.
168#[derive(Debug)]
169pub enum Packet<'a> {
170    App(crate::App<'a>),
171    Bye(crate::Bye<'a>),
172    Rr(crate::ReceiverReport<'a>),
173    Sdes(crate::Sdes<'a>),
174    Sr(crate::SenderReport<'a>),
175    TransportFeedback(crate::TransportFeedback<'a>),
176    PayloadFeedback(crate::PayloadFeedback<'a>),
177    Unknown(Unknown<'a>),
178}
179
180impl<'a> Packet<'a> {
181    pub fn is_unknown(&self) -> bool {
182        matches!(self, Packet::Unknown(_))
183    }
184}
185
186impl<'a> RtcpPacket for Packet<'a> {
187    const MIN_PACKET_LEN: usize = 4;
188    const PACKET_TYPE: u8 = 255; // Not used
189}
190
191impl<'a> RtcpPacketParser<'a> for Packet<'a> {
192    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
193        if data.len() < Self::MIN_PACKET_LEN {
194            return Err(RtcpParseError::Truncated {
195                expected: Self::MIN_PACKET_LEN,
196                actual: data.len(),
197            });
198        }
199
200        match parse_packet_type(data) {
201            crate::App::PACKET_TYPE => crate::App::parse(data).map(Packet::App),
202            crate::Bye::PACKET_TYPE => crate::Bye::parse(data).map(Packet::Bye),
203            crate::ReceiverReport::PACKET_TYPE => {
204                crate::ReceiverReport::parse(data).map(Packet::Rr)
205            }
206            crate::Sdes::PACKET_TYPE => crate::Sdes::parse(data).map(Packet::Sdes),
207            crate::SenderReport::PACKET_TYPE => crate::SenderReport::parse(data).map(Packet::Sr),
208            crate::PayloadFeedback::PACKET_TYPE => {
209                crate::PayloadFeedback::parse(data).map(Packet::PayloadFeedback)
210            }
211            crate::TransportFeedback::PACKET_TYPE => {
212                crate::TransportFeedback::parse(data).map(Packet::TransportFeedback)
213            }
214            _ => Ok(Packet::Unknown(Unknown::parse(data)?)),
215        }
216    }
217
218    #[inline(always)]
219    fn header_data(&self) -> [u8; 4] {
220        use Packet::*;
221        match self {
222            App(this) => this.header_data(),
223            Bye(this) => this.header_data(),
224            Rr(this) => this.header_data(),
225            Sdes(this) => this.header_data(),
226            Sr(this) => this.header_data(),
227            TransportFeedback(this) => this.header_data(),
228            PayloadFeedback(this) => this.header_data(),
229            Unknown(this) => this.header_data(),
230        }
231    }
232}
233
234impl<'a> Packet<'a> {
235    /// Try parsing this [`Packet`] as a particular [`RtcpPacket`] implementation.
236    pub fn try_as<P>(&'a self) -> Result<P, RtcpParseError>
237    where
238        P: RtcpPacket,
239        P: TryFrom<&'a Self, Error = RtcpParseError>,
240    {
241        TryFrom::try_from(self)
242    }
243}
244
245/// A compound RTCP packet consisting of multiple RTCP packets one after the other
246#[derive(Debug)]
247pub struct Compound<'a> {
248    data: &'a [u8],
249    offset: usize,
250    is_over: bool,
251}
252
253impl<'a> Compound<'a> {
254    /// Parse data into a [`Compound`] RTCP packet.
255    ///
256    /// This will validate that the length of each individual RTCP packet is valid upfront.
257    pub fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
258        let mut offset = 0;
259        let mut packet_length;
260
261        if data.is_empty() {
262            return Err(RtcpParseError::Truncated {
263                expected: 4,
264                actual: 0,
265            });
266        }
267
268        while offset < data.len() {
269            if data.len() < offset + Unknown::MIN_PACKET_LEN {
270                return Err(RtcpParseError::Truncated {
271                    expected: offset + Unknown::MIN_PACKET_LEN,
272                    actual: data.len(),
273                });
274            }
275
276            packet_length = parse_length(&data[offset..]);
277            if data.len() < offset + packet_length {
278                return Err(RtcpParseError::Truncated {
279                    expected: offset + packet_length,
280                    actual: data.len(),
281                });
282            }
283
284            offset += packet_length;
285        }
286
287        Ok(Self {
288            data,
289            offset: 0,
290            is_over: false,
291        })
292    }
293
294    /// Create a new [`CompoundBuilder`]
295    pub fn builder() -> CompoundBuilder<'a> {
296        CompoundBuilder::default()
297    }
298}
299
300impl<'a> Iterator for Compound<'a> {
301    type Item = Result<Packet<'a>, RtcpParseError>;
302
303    fn next(&mut self) -> Option<Self::Item> {
304        if self.is_over {
305            return None;
306        }
307
308        // Length conformity checked in `Self::parse`
309
310        let packet_length = parse_length(&self.data[self.offset..]);
311        let res = Packet::parse(&self.data[self.offset..self.offset + packet_length]);
312
313        self.is_over = res.is_err();
314
315        self.offset += packet_length;
316        if self.offset >= self.data.len() {
317            self.is_over = true;
318        }
319
320        Some(res)
321    }
322}
323
324/// A builder for a RTCP packet
325#[derive(Debug)]
326#[must_use = "The builder must be built to be used"]
327pub enum PacketBuilder<'a> {
328    App(crate::app::AppBuilder<'a>),
329    Bye(crate::bye::ByeBuilder<'a>),
330    Rr(crate::receiver::ReceiverReportBuilder),
331    Sdes(crate::sdes::SdesBuilder<'a>),
332    Sr(crate::sender::SenderReportBuilder),
333    TransportFeedback(crate::feedback::TransportFeedbackBuilder<'a>),
334    PayloadFeedback(crate::feedback::PayloadFeedbackBuilder<'a>),
335    Unknown(UnknownBuilder<'a>),
336}
337
338impl<'a> RtcpPacketWriter for PacketBuilder<'a> {
339    fn get_padding(&self) -> Option<u8> {
340        use PacketBuilder::*;
341        match self {
342            App(this) => this.get_padding(),
343            Bye(this) => this.get_padding(),
344            Rr(this) => this.get_padding(),
345            Sdes(this) => this.get_padding(),
346            Sr(this) => this.get_padding(),
347            TransportFeedback(this) => this.get_padding(),
348            PayloadFeedback(this) => this.get_padding(),
349            Unknown(this) => this.get_padding(),
350        }
351    }
352
353    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
354        use PacketBuilder::*;
355        match self {
356            App(this) => this.calculate_size(),
357            Bye(this) => this.calculate_size(),
358            Rr(this) => this.calculate_size(),
359            Sdes(this) => this.calculate_size(),
360            Sr(this) => this.calculate_size(),
361            TransportFeedback(this) => this.calculate_size(),
362            PayloadFeedback(this) => this.calculate_size(),
363            Unknown(this) => this.calculate_size(),
364        }
365    }
366
367    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
368        use PacketBuilder::*;
369        match self {
370            App(this) => this.write_into_unchecked(buf),
371            Bye(this) => this.write_into_unchecked(buf),
372            Rr(this) => this.write_into_unchecked(buf),
373            Sdes(this) => this.write_into_unchecked(buf),
374            Sr(this) => this.write_into_unchecked(buf),
375            TransportFeedback(this) => this.write_into_unchecked(buf),
376            PayloadFeedback(this) => this.write_into_unchecked(buf),
377            Unknown(this) => this.write_into_unchecked(buf),
378        }
379    }
380}
381
382/// A builder for a [`Compound`] RTCP packet
383#[derive(Default, Debug)]
384#[must_use = "The builder must be built to be used"]
385pub struct CompoundBuilder<'a> {
386    packets: Vec<Box<dyn RtcpPacketWriter + 'a>>,
387}
388
389impl<'a> CompoundBuilder<'a> {
390    /// Add a packet to the compound rtcp packet
391    pub fn add_packet(mut self, packet: impl RtcpPacketWriter + 'a) -> Self {
392        self.packets.push(Box::new(packet));
393        self
394    }
395}
396
397impl<'a> RtcpPacketWriter for CompoundBuilder<'a> {
398    /// Calculates the size required to write this Receiver Report packet.
399    ///
400    /// Returns an error if:
401    ///
402    /// * A Packet is erroneous.
403    /// * A Packet defined a padding
404    ///   while it's not the last packet in the Compound.
405    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
406        let mut size = 0;
407        let last = self.packets.len().saturating_sub(1);
408        for (idx, packet) in self.packets.iter().enumerate() {
409            size += packet.calculate_size()?;
410
411            if packet.get_padding().unwrap_or(0) > 0 && idx != last {
412                return Err(RtcpWriteError::NonLastCompoundPacketPadding);
413            }
414        }
415
416        Ok(size)
417    }
418
419    /// Writes this Compound packet into `buf` without prior length checks.
420    ///
421    /// Uses the length of the buffer for the length field.
422    ///
423    /// Returns the number of bytes written.
424    ///
425    /// # Panic
426    ///
427    /// Panics if the buf is not large enough or if a packet is invalid.
428    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
429        let mut offset = 0;
430        for packet in self.packets.iter() {
431            let req_size = packet.calculate_size().unwrap();
432            offset += packet.write_into_unchecked(&mut buf[offset..offset + req_size]);
433        }
434
435        offset
436    }
437
438    fn get_padding(&self) -> Option<u8> {
439        self.packets.last()?.get_padding()
440    }
441}
442
443macro_rules! impl_try_from {
444    ($parser:ty, $builder:ty, $variant:ident) => {
445        impl<'a> TryFrom<Unknown<'a>> for $parser {
446            type Error = RtcpParseError;
447
448            fn try_from(p: Unknown<'a>) -> Result<Self, Self::Error> {
449                <$parser>::parse(p.data)
450            }
451        }
452
453        impl<'a> TryFrom<&'a Unknown<'a>> for $parser {
454            type Error = RtcpParseError;
455
456            fn try_from(p: &'a Unknown<'a>) -> Result<Self, Self::Error> {
457                <$parser>::parse(p.data)
458            }
459        }
460
461        impl<'a> TryFrom<Packet<'a>> for $parser {
462            type Error = RtcpParseError;
463
464            fn try_from(p: Packet<'a>) -> Result<Self, Self::Error> {
465                match p {
466                    Packet::$variant(this) => Ok(this),
467                    Packet::Unknown(p) => Self::try_from(p),
468                    _ => Err(RtcpParseError::PacketTypeMismatch {
469                        actual: p.type_(),
470                        requested: <$parser>::PACKET_TYPE,
471                    }),
472                }
473            }
474        }
475
476        impl<'a> TryFrom<&'a Packet<'a>> for $parser {
477            type Error = RtcpParseError;
478
479            fn try_from(p: &'a Packet<'a>) -> Result<Self, Self::Error> {
480                match p {
481                    Packet::$variant(this) => Ok(this.clone()),
482                    Packet::Unknown(p) => Self::try_from(p),
483                    _ => Err(RtcpParseError::PacketTypeMismatch {
484                        actual: p.type_(),
485                        requested: <$parser>::PACKET_TYPE,
486                    }),
487                }
488            }
489        }
490
491        impl<'a> From<$parser> for Packet<'a> {
492            fn from(p: $parser) -> Self {
493                Packet::$variant(p)
494            }
495        }
496
497        impl<'a> From<$builder> for PacketBuilder<'a> {
498            fn from(pb: $builder) -> Self {
499                Self::$variant(pb)
500            }
501        }
502    };
503}
504
505impl_try_from!(crate::app::App<'a>, crate::app::AppBuilder<'a>, App);
506impl_try_from!(crate::bye::Bye<'a>, crate::bye::ByeBuilder<'a>, Bye);
507impl_try_from!(crate::sdes::Sdes<'a>, crate::sdes::SdesBuilder<'a>, Sdes);
508impl_try_from!(
509    crate::receiver::ReceiverReport<'a>,
510    crate::receiver::ReceiverReportBuilder,
511    Rr
512);
513impl_try_from!(
514    crate::sender::SenderReport<'a>,
515    crate::sender::SenderReportBuilder,
516    Sr
517);
518impl_try_from!(
519    crate::feedback::TransportFeedback<'a>,
520    crate::feedback::TransportFeedbackBuilder<'a>,
521    TransportFeedback
522);
523impl_try_from!(
524    crate::feedback::PayloadFeedback<'a>,
525    crate::feedback::PayloadFeedbackBuilder<'a>,
526    PayloadFeedback
527);
528
529impl<'a> From<Unknown<'a>> for Packet<'a> {
530    fn from(p: Unknown<'a>) -> Self {
531        Packet::Unknown(p)
532    }
533}
534
535impl<'a> From<UnknownBuilder<'a>> for PacketBuilder<'a> {
536    fn from(pb: UnknownBuilder<'a>) -> Self {
537        Self::Unknown(pb)
538    }
539}
540
541#[cfg(test)]
542mod tests {
543    use super::*;
544    use crate::{App, Bye, ReceiverReport, SenderReport};
545
546    #[test]
547    fn parse_rr_bye() {
548        let data = [
549            0x80, 0xc9, 0x00, 0x01, 0x91, 0x82, 0x73, 0x64, 0x80, 0xcb, 0x00, 0x00,
550        ];
551        let mut compound = Compound::parse(&data).unwrap();
552        let packet = compound.next().unwrap().unwrap();
553        matches!(packet, Packet::Rr(_));
554
555        let packet = compound.next().unwrap().unwrap();
556        matches!(packet, Packet::Bye(_));
557
558        assert!(compound.next().is_none());
559    }
560
561    #[test]
562    fn build_rr_bye() {
563        const REQ_LEN: usize = ReceiverReport::MIN_PACKET_LEN + Bye::MIN_PACKET_LEN;
564
565        let b = Compound::builder()
566            .add_packet(ReceiverReport::builder(0x1234567))
567            .add_packet(Bye::builder());
568
569        let mut data = [0; REQ_LEN];
570        let len = b.write_into(&mut data).unwrap();
571        assert_eq!(len, REQ_LEN);
572        assert_eq!(
573            data,
574            [0x80, 0xc9, 0x00, 0x01, 0x01, 0x23, 0x45, 0x67, 0x80, 0xcb, 0x00, 0x00]
575        );
576    }
577
578    #[test]
579    fn parse_sr_bye() {
580        let data = [
581            0x80, 0xc8, 0x00, 0x06, 0x91, 0x82, 0x73, 0x64, 0x89, 0xab, 0xcd, 0xef, 0x02, 0x24,
582            0x46, 0x68, 0x8a, 0xac, 0xce, 0xe0, 0xf1, 0xe2, 0xd3, 0xc4, 0xb5, 0xa6, 0x97, 0x88,
583            0x80, 0xcb, 0x00, 0x00,
584        ];
585        let mut compound = Compound::parse(&data).unwrap();
586        let packet = compound.next().unwrap().unwrap();
587        matches!(packet, Packet::Sr(_));
588
589        let packet = compound.next().unwrap().unwrap();
590        matches!(packet, Packet::Bye(_));
591
592        assert!(compound.next().is_none());
593    }
594
595    #[test]
596    fn build_sr_bye() {
597        const REQ_LEN: usize = SenderReport::MIN_PACKET_LEN + Bye::MIN_PACKET_LEN;
598
599        let b = Compound::builder()
600            .add_packet(SenderReport::builder(0x1234567))
601            .add_packet(Bye::builder());
602
603        let mut data = [0; REQ_LEN];
604        let len = b.write_into(&mut data).unwrap();
605        assert_eq!(len, REQ_LEN);
606        assert_eq!(
607            data,
608            [
609                0x80, 0xc8, 0x00, 0x06, 0x01, 0x23, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
610                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
611                0x80, 0xcb, 0x00, 0x00,
612            ]
613        );
614    }
615
616    #[test]
617    fn build_rr_bye_padding() {
618        const REQ_LEN: usize = ReceiverReport::MIN_PACKET_LEN + Bye::MIN_PACKET_LEN + 4;
619
620        let b = Compound::builder()
621            .add_packet(ReceiverReport::builder(0x1234567))
622            .add_packet(Bye::builder().padding(4));
623
624        let mut data = [0; REQ_LEN];
625        let len = b.write_into(&mut data).unwrap();
626        assert_eq!(len, REQ_LEN);
627        assert_eq!(
628            data,
629            [
630                0x80, 0xc9, 0x00, 0x01, 0x01, 0x23, 0x45, 0x67, 0xa0, 0xcb, 0x00, 0x01, 0x00, 0x00,
631                0x00, 0x04,
632            ]
633        );
634    }
635
636    #[test]
637    fn parse_unknown() {
638        let data = [
639            0x80, 0xf2, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00,
640        ];
641        let p = Packet::parse(&data).unwrap();
642        assert!(p.is_unknown());
643        assert_eq!(p.type_(), 242);
644    }
645
646    #[test]
647    fn build_app_padding_bye() {
648        let b = Compound::builder()
649            .add_packet(App::builder(0x91827364, "name").padding(4))
650            .add_packet(Bye::builder());
651
652        let err = b.calculate_size().unwrap_err();
653        assert_eq!(err, RtcpWriteError::NonLastCompoundPacketPadding);
654    }
655
656    #[test]
657    fn parse_rr_bye_wrong_first_len() {
658        let data = [
659            0x80, 0xc9, 0x00, 0x03, 0x91, 0x82, 0x73, 0x64, 0x80, 0xcb, 0x00, 0x00,
660        ];
661        let err = Compound::parse(&data).unwrap_err();
662        assert_eq!(
663            err,
664            RtcpParseError::Truncated {
665                expected: 16,
666                actual: 12
667            }
668        );
669    }
670
671    #[test]
672    fn parse_rr_truncated_bye() {
673        let data = [
674            0x80, 0xc9, 0x00, 0x01, 0x91, 0x82, 0x73, 0x64, 0x80, 0xcb, 0x00,
675        ];
676        let err = Compound::parse(&data).unwrap_err();
677        assert_eq!(
678            err,
679            RtcpParseError::Truncated {
680                expected: 12,
681                actual: 11
682            }
683        );
684    }
685
686    #[test]
687    fn parsing_failure_rr_bye() {
688        let data = [
689            0x81, 0xc9, 0x00, 0x01, 0x91, 0x82, 0x73, 0x64, 0x80, 0xcb, 0x00, 0x00,
690        ];
691        let mut compound = Compound::parse(&data).unwrap();
692
693        // RR count is 1 when the actual packet contains no reports.
694        let err = compound.next().unwrap().unwrap_err();
695        assert_eq!(
696            err,
697            RtcpParseError::Truncated {
698                expected: 32,
699                actual: 8
700            }
701        );
702
703        assert!(compound.next().is_none());
704    }
705
706    #[test]
707    fn parse_packet_try_as_app() {
708        let data = [
709            0x80, 0xcc, 0x00, 0x02, 0x91, 0x82, 0x73, 0x64, 0x6e, 0x61, 0x6d, 0x65,
710        ];
711        let packet = Packet::parse(&data).unwrap();
712
713        let app = packet.try_as::<crate::App>().unwrap();
714        assert_eq!(app.name(), "name".as_bytes());
715
716        matches!(packet, Packet::App(_));
717    }
718
719    #[test]
720    fn parse_unknown_try_as_bye() {
721        let data = [0x81, 0xcb, 0x00, 0x01, 0x12, 0x34, 0x56, 0x78];
722        let unknown = Unknown::parse(&data).unwrap();
723
724        let bye = unknown.try_as::<crate::Bye>().unwrap();
725        let mut ssrcs = bye.ssrcs();
726        let ssrc = ssrcs.next().unwrap();
727        assert_eq!(ssrc, 0x12345678);
728    }
729}