[go: up one dir, main page]

rtp 0.4.1

A pure Rust implementation of RTP
Documentation
#[cfg(test)]
mod vp8_test;

use crate::{
    error::Error,
    packetizer::{Depacketizer, Payloader},
};

use anyhow::Result;
use bytes::{Buf, BufMut, Bytes, BytesMut};

pub const VP8_HEADER_SIZE: usize = 1;

/// Vp8Payloader payloads VP8 packets
#[derive(Default, Debug, Copy, Clone)]
pub struct Vp8Payloader {
    picture_id: u16,
}

impl Payloader for Vp8Payloader {
    /// Payload fragments a VP8 packet across one or more byte arrays
    fn payload(&mut self, mtu: usize, payload: &Bytes) -> Result<Vec<Bytes>> {
        if payload.is_empty() || mtu == 0 {
            return Ok(vec![]);
        }

        /*
         * https://tools.ietf.org/html/rfc7741#section-4.2
         *
         *       0 1 2 3 4 5 6 7
         *      +-+-+-+-+-+-+-+-+
         *      |X|R|N|S|R| PID | (REQUIRED)
         *      +-+-+-+-+-+-+-+-+
         * X:   |I|L|T|K| RSV   | (OPTIONAL)
         *      +-+-+-+-+-+-+-+-+
         * I:   |M| PictureID   | (OPTIONAL)
         *      +-+-+-+-+-+-+-+-+
         * L:   |   TL0PICIDX   | (OPTIONAL)
         *      +-+-+-+-+-+-+-+-+
         * T/K: |TID|Y| KEYIDX  | (OPTIONAL)
         *      +-+-+-+-+-+-+-+-+
         *  S: Start of VP8 partition.  SHOULD be set to 1 when the first payload
         *     octet of the RTP packet is the beginning of a new VP8 partition,
         *     and MUST NOT be 1 otherwise.  The S bit MUST be set to 1 for the
         *     first packet of each encoded frame.
         */
        let using_header_size = if self.picture_id == 0 || self.picture_id < 128 {
            VP8_HEADER_SIZE + 2
        } else {
            VP8_HEADER_SIZE + 3
        };

        let max_fragment_size = mtu as isize - VP8_HEADER_SIZE as isize;
        let mut payload_data_remaining = payload.len() as isize;
        let mut payload_data_index: usize = 0;
        let mut payloads = vec![];

        // Make sure the fragment/payload size is correct
        if std::cmp::min(max_fragment_size, payload_data_remaining) <= 0 {
            return Ok(payloads);
        }

        let mut first = true;
        while payload_data_remaining > 0 {
            let current_fragment_size =
                std::cmp::min(max_fragment_size, payload_data_remaining) as usize;
            let mut out = BytesMut::with_capacity(using_header_size + current_fragment_size);
            let mut buf = vec![0u8; 4];
            if first {
                buf[0] = 0x10;
                first = false;
            }

            if using_header_size == VP8_HEADER_SIZE + 2 {
                buf[0] |= 0x80;
                buf[1] |= 0x80;
                buf[2] |= (self.picture_id & 0x7F) as u8;
            } else if using_header_size == VP8_HEADER_SIZE + 3 {
                buf[0] |= 0x80;
                buf[1] |= 0x80;
                buf[2] |= 0x80 | ((self.picture_id >> 8) & 0x7F) as u8;
                buf[3] |= (self.picture_id & 0xFF) as u8;
            }

            out.put(&buf[..using_header_size]);

            out.put(
                &*payload.slice(payload_data_index..payload_data_index + current_fragment_size),
            );
            payloads.push(out.freeze());

            payload_data_remaining -= current_fragment_size as isize;
            payload_data_index += current_fragment_size;
        }

        self.picture_id += 1;
        self.picture_id &= 0x7FFF;

        Ok(payloads)
    }

    fn clone_to(&self) -> Box<dyn Payloader + Send + Sync> {
        Box::new(*self)
    }
}

/// Vp8Packet represents the VP8 header that is stored in the payload of an RTP Packet
#[derive(PartialEq, Debug, Default, Clone)]
pub struct Vp8Packet {
    /// Required Header
    /// extended controlbits present
    pub x: u8,
    /// (non-reference frame)  when set to 1 this frame can be discarded
    pub n: u8,
    /// start of VP8 partition
    pub s: u8,
    /// partition index
    pub pid: u8,

    /// Optional Header
    /// 1 if PictureID is present
    pub i: u8,
    /// 1 if TL0PICIDX is present
    pub l: u8,
    /// 1 if tid is present
    pub t: u8,
    /// 1 if KEYIDX is present
    pub k: u8,

    /// 8 or 16 bits, picture ID
    pub picture_id: u16,
    /// 8 bits temporal level zero index
    pub tl0_pic_idx: u8,

    pub payload: Bytes,
}

impl Depacketizer for Vp8Packet {
    /// depacketize parses the passed byte slice and stores the result in the VP8Packet this method is called upon
    fn depacketize(&mut self, packet: &Bytes) -> Result<()> {
        if packet.len() < 4 {
            return Err(Error::ErrShortPacket.into());
        }
        //    0 1 2 3 4 5 6 7                      0 1 2 3 4 5 6 7
        //    +-+-+-+-+-+-+-+-+                   +-+-+-+-+-+-+-+-+
        //    |X|R|N|S|R| PID | (REQUIRED)        |X|R|N|S|R| PID | (REQUIRED)
        //    +-+-+-+-+-+-+-+-+                   +-+-+-+-+-+-+-+-+
        // X: |I|L|T|K| RSV   | (OPTIONAL)   X:   |I|L|T|K| RSV   | (OPTIONAL)
        //    +-+-+-+-+-+-+-+-+                   +-+-+-+-+-+-+-+-+
        // I: |M| PictureID   | (OPTIONAL)   I:   |M| PictureID   | (OPTIONAL)
        //    +-+-+-+-+-+-+-+-+                   +-+-+-+-+-+-+-+-+
        // L: |   TL0PICIDX   | (OPTIONAL)        |   PictureID   |
        //    +-+-+-+-+-+-+-+-+                   +-+-+-+-+-+-+-+-+
        //T/K:|TID|Y| KEYIDX  | (OPTIONAL)   L:   |   TL0PICIDX   | (OPTIONAL)
        //    +-+-+-+-+-+-+-+-+                   +-+-+-+-+-+-+-+-+
        //T/K:|TID|Y| KEYIDX  | (OPTIONAL)
        //    +-+-+-+-+-+-+-+-+

        let reader = &mut packet.clone();
        let mut payload_index = 0;

        let mut b = reader.get_u8();
        payload_index += 1;

        self.x = (b & 0x80) >> 7;
        self.n = (b & 0x20) >> 5;
        self.s = (b & 0x10) >> 4;
        self.pid = b & 0x07;

        if self.x == 1 {
            b = reader.get_u8();
            payload_index += 1;
            self.i = (b & 0x80) >> 7;
            self.l = (b & 0x40) >> 6;
            self.t = (b & 0x20) >> 5;
            self.k = (b & 0x10) >> 4;
        }

        if self.i == 1 {
            b = reader.get_u8();
            payload_index += 1;
            // PID present?
            if b & 0x80 > 0 {
                // M == 1, PID is 16bit
                self.picture_id = (((b & 0x7f) as u16) << 8) | (reader.get_u8() as u16);
                payload_index += 1;
            } else {
                self.picture_id = b as u16;
            }
        }

        if self.l == 1 {
            reader.get_u8();
            payload_index += 1;
        }

        if self.t == 1 || self.k == 1 {
            payload_index += 1;
        }

        if payload_index >= packet.len() {
            return Err(Error::ErrShortPacket.into());
        }

        self.payload = packet.slice(payload_index..);

        Ok(())
    }
}