rtc_rtp/codec/vp8/mod.rs
1#[cfg(test)]
2mod vp8_test;
3
4use crate::packetizer::{Depacketizer, Payloader};
5use shared::error::{Error, Result};
6
7use bytes::{Buf, BufMut, Bytes, BytesMut};
8
9/// Bytes of mandatory VP8 payload descriptor prefixed to each payload.
10pub const VP8_HEADER_SIZE: usize = 1;
11
12/// Vp8Payloader payloads VP8 packets
13#[derive(Default, Debug, Copy, Clone)]
14pub struct Vp8Payloader {
15 /// Whether to include the optional picture id in the payload descriptor.
16 ///
17 /// Lets a receiver detect loss within a frame, at one or two bytes per packet.
18 pub enable_picture_id: bool,
19 picture_id: u16,
20}
21
22impl Payloader for Vp8Payloader {
23 /// Payload fragments a VP8 packet across one or more byte arrays
24 fn payload(&mut self, mtu: usize, payload: &Bytes) -> Result<Vec<Bytes>> {
25 if payload.is_empty() || mtu == 0 {
26 return Ok(vec![]);
27 }
28
29 /*
30 * https://tools.ietf.org/html/rfc7741#section-4.2
31 *
32 * 0 1 2 3 4 5 6 7
33 * +-+-+-+-+-+-+-+-+
34 * |X|R|N|S|R| PID | (REQUIRED)
35 * +-+-+-+-+-+-+-+-+
36 * X: |I|L|T|K| RSV | (OPTIONAL)
37 * +-+-+-+-+-+-+-+-+
38 * I: |M| PictureID | (OPTIONAL)
39 * +-+-+-+-+-+-+-+-+
40 * L: | tl0picidx | (OPTIONAL)
41 * +-+-+-+-+-+-+-+-+
42 * T/K: |tid|Y| KEYIDX | (OPTIONAL)
43 * +-+-+-+-+-+-+-+-+
44 * S: Start of VP8 partition. SHOULD be set to 1 when the first payload
45 * octet of the RTP packet is the beginning of a new VP8 partition,
46 * and MUST NOT be 1 otherwise. The S bit MUST be set to 1 for the
47 * first packet of each encoded frame.
48 */
49 let using_header_size = if self.enable_picture_id {
50 if self.picture_id == 0 || self.picture_id < 128 {
51 VP8_HEADER_SIZE + 2
52 } else {
53 VP8_HEADER_SIZE + 3
54 }
55 } else {
56 VP8_HEADER_SIZE
57 };
58
59 let max_fragment_size = mtu as isize - using_header_size as isize;
60 let mut payload_data_remaining = payload.len() as isize;
61 let mut payload_data_index: usize = 0;
62 let mut payloads = vec![];
63
64 // Make sure the fragment/payload size is correct
65 if std::cmp::min(max_fragment_size, payload_data_remaining) <= 0 {
66 return Ok(payloads);
67 }
68
69 let mut first = true;
70 while payload_data_remaining > 0 {
71 let current_fragment_size =
72 std::cmp::min(max_fragment_size, payload_data_remaining) as usize;
73 let mut out = BytesMut::with_capacity(using_header_size + current_fragment_size);
74 let mut buf = [0u8; 4];
75 if first {
76 buf[0] = 0x10;
77 first = false;
78 }
79
80 if self.enable_picture_id {
81 if using_header_size == VP8_HEADER_SIZE + 2 {
82 buf[0] |= 0x80;
83 buf[1] |= 0x80;
84 buf[2] |= (self.picture_id & 0x7F) as u8;
85 } else if using_header_size == VP8_HEADER_SIZE + 3 {
86 buf[0] |= 0x80;
87 buf[1] |= 0x80;
88 buf[2] |= 0x80 | ((self.picture_id >> 8) & 0x7F) as u8;
89 buf[3] |= (self.picture_id & 0xFF) as u8;
90 }
91 }
92
93 out.put(&buf[..using_header_size]);
94
95 out.put(
96 &*payload.slice(payload_data_index..payload_data_index + current_fragment_size),
97 );
98 payloads.push(out.freeze());
99
100 payload_data_remaining -= current_fragment_size as isize;
101 payload_data_index += current_fragment_size;
102 }
103
104 self.picture_id += 1;
105 self.picture_id &= 0x7FFF;
106
107 Ok(payloads)
108 }
109
110 fn clone_to(&self) -> Box<dyn Payloader> {
111 Box::new(*self)
112 }
113}
114
115/// Vp8Packet represents the VP8 header that is stored in the payload of an RTP Packet
116#[derive(PartialEq, Eq, Debug, Default, Clone)]
117pub struct Vp8Packet {
118 /// Required Header
119 /// extended controlbits present
120 pub x: u8,
121 /// when set to 1 this frame can be discarded
122 pub n: u8,
123 /// start of VP8 partition
124 pub s: u8,
125 /// partition index
126 pub pid: u8,
127
128 /// Extended control bits
129 /// 1 if PictureID is present
130 pub i: u8,
131 /// 1 if tl0picidx is present
132 pub l: u8,
133 /// 1 if tid is present
134 pub t: u8,
135 /// 1 if KEYIDX is present
136 pub k: u8,
137
138 /// Optional extension
139 /// 8 or 16 bits, picture ID
140 pub picture_id: u16,
141 /// 8 bits temporal level zero index
142 pub tl0_pic_idx: u8,
143 /// 2 bits temporal layer index
144 pub tid: u8,
145 /// 1 bit layer sync bit
146 pub y: u8,
147 /// 5 bits temporal key frame index
148 pub key_idx: u8,
149}
150
151impl Depacketizer for Vp8Packet {
152 /// depacketize parses the passed byte slice and stores the result in the VP8Packet this method is called upon
153 fn depacketize(&mut self, packet: &Bytes) -> Result<Bytes> {
154 let payload_len = packet.len();
155 if payload_len < 4 {
156 return Err(Error::ErrShortPacket);
157 }
158 // 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
159 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
160 // |X|R|N|S|R| PID | (REQUIRED) |X|R|N|S|R| PID | (REQUIRED)
161 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
162 // X: |I|L|T|K| RSV | (OPTIONAL) X: |I|L|T|K| RSV | (OPTIONAL)
163 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
164 // I: |M| PictureID | (OPTIONAL) I: |M| PictureID | (OPTIONAL)
165 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
166 // L: | tl0picidx | (OPTIONAL) | PictureID |
167 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
168 //T/K:|tid|Y| KEYIDX | (OPTIONAL) L: | tl0picidx | (OPTIONAL)
169 // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
170 //T/K:|tid|Y| KEYIDX | (OPTIONAL)
171 // +-+-+-+-+-+-+-+-+
172
173 let reader = &mut packet.clone();
174 let mut payload_index = 0;
175
176 let mut b = reader.get_u8();
177 payload_index += 1;
178
179 self.x = (b & 0x80) >> 7;
180 self.n = (b & 0x20) >> 5;
181 self.s = (b & 0x10) >> 4;
182 self.pid = b & 0x07;
183
184 if self.x == 1 {
185 b = reader.get_u8();
186 payload_index += 1;
187 self.i = (b & 0x80) >> 7;
188 self.l = (b & 0x40) >> 6;
189 self.t = (b & 0x20) >> 5;
190 self.k = (b & 0x10) >> 4;
191 }
192
193 if self.i == 1 {
194 b = reader.get_u8();
195 payload_index += 1;
196 // PID present?
197 if b & 0x80 > 0 {
198 // M == 1, PID is 16bit
199 self.picture_id = (((b & 0x7f) as u16) << 8) | (reader.get_u8() as u16);
200 payload_index += 1;
201 } else {
202 self.picture_id = b as u16;
203 }
204 }
205
206 if payload_index >= payload_len {
207 return Err(Error::ErrShortPacket);
208 }
209
210 if self.l == 1 {
211 self.tl0_pic_idx = reader.get_u8();
212 payload_index += 1;
213 }
214
215 if payload_index >= payload_len {
216 return Err(Error::ErrShortPacket);
217 }
218
219 if self.t == 1 || self.k == 1 {
220 let b = reader.get_u8();
221 if self.t == 1 {
222 self.tid = b >> 6;
223 self.y = (b >> 5) & 0x1;
224 }
225 if self.k == 1 {
226 self.key_idx = b & 0x1F;
227 }
228 payload_index += 1;
229 }
230
231 if payload_index >= packet.len() {
232 return Err(Error::ErrShortPacket);
233 }
234
235 Ok(packet.slice(payload_index..))
236 }
237
238 /// is_partition_head checks whether if this is a head of the VP8 partition
239 fn is_partition_head(&self, payload: &Bytes) -> bool {
240 if payload.is_empty() {
241 false
242 } else {
243 (payload[0] & 0x10) != 0
244 }
245 }
246
247 fn is_partition_tail(&self, marker: bool, _payload: &Bytes) -> bool {
248 marker
249 }
250}