File
Blob: firmware/vendor/str0m/src/packet/av1.rs
| 1 | use super::{BitRead, CodecExtra, Depacketizer, PacketError, Packetizer, encode_leb_u63}; |
| 2 | |
| 3 | const OBU_EXTENSION_PRESENT_MASK: u8 = 0b0000_0100; |
| 4 | const OBU_SIZE_PRESENT_MASK: u8 = 0b0000_0010; |
| 5 | const OBU_TYPE_MASK: u8 = 0b0111_1000; |
| 6 | const AGGREGATION_HEADER_SIZE: usize = 1; |
| 7 | const MAX_NUM_OBUS_TO_OMTI_SIZE: usize = 3; |
| 8 | |
| 9 | /// Detect whether an AV1 RTP payload contains a keyframe. |
| 10 | /// |
| 11 | /// Checks the N bit (new coded video sequence) in the AV1 aggregation header. |
| 12 | /// N=1 indicates the first packet of a keyframe (random access point). |
| 13 | /// |
| 14 | /// AV1 aggregation header layout: `Z|Y|W W|N|reserved` |
| 15 | /// - N (bit 3): 1 = new coded video sequence starts |
| 16 | pub fn detect_av1_keyframe(payload: &[u8]) -> bool { |
| 17 | if payload.is_empty() { |
| 18 | return false; |
| 19 | } |
| 20 | // N bit is bit 3 of the aggregation header |
| 21 | payload[0] & 0x08 != 0 |
| 22 | } |
| 23 | |
| 24 | /// AV1 information describing the depacketized / packetized data |
| 25 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] |
| 26 | pub struct Av1CodecExtra { |
| 27 | /// Flag which indicates that within [`MediaData`], there is an individual frame |
| 28 | /// containing complete and independent visual information. This frame serves |
| 29 | /// as a reference point for other frames in the video sequence. |
| 30 | /// |
| 31 | /// [`MediaData`]: crate::media::MediaData |
| 32 | pub is_keyframe: bool, |
| 33 | } |
| 34 | |
| 35 | /// AV1 packetizer |
| 36 | #[derive(Default, Debug)] |
| 37 | pub struct Av1Packetizer { |
| 38 | packets: Vec<Packet>, |
| 39 | obus: Vec<Obu>, |
| 40 | } |
| 41 | |
| 42 | impl Av1Packetizer { |
| 43 | fn emit(&mut self, mtu: usize, payloads: &mut Vec<Vec<u8>>) { |
| 44 | if self.obus.is_empty() { |
| 45 | return; |
| 46 | } |
| 47 | |
| 48 | // Clear previous frame data |
| 49 | self.packets.clear(); |
| 50 | |
| 51 | // Reserve 1 byte for aggregation header |
| 52 | let max_payload_size = mtu - AGGREGATION_HEADER_SIZE; |
| 53 | let mut remaining_packet_size = max_payload_size; |
| 54 | |
| 55 | // Push as many obus as possible into each packet |
| 56 | let mut current_packet = Packet::new(0); |
| 57 | for (obu_idx, obu) in self.obus.iter().enumerate() { |
| 58 | let mut previous_obu_extra_size = self.extra_size_for_previous_obu(¤t_packet); |
| 59 | let min_required_size = if current_packet.num_obu_elements >= MAX_NUM_OBUS_TO_OMTI_SIZE |
| 60 | { |
| 61 | 2 |
| 62 | } else { |
| 63 | 1 |
| 64 | }; |
| 65 | |
| 66 | // Check if new packet is needed |
| 67 | if remaining_packet_size < previous_obu_extra_size + min_required_size { |
| 68 | self.packets.push(current_packet); |
| 69 | current_packet = Packet::new(obu_idx); |
| 70 | remaining_packet_size = max_payload_size; |
| 71 | previous_obu_extra_size = 0; |
| 72 | } |
| 73 | |
| 74 | current_packet.packet_size += previous_obu_extra_size; |
| 75 | current_packet.num_obu_elements += 1; |
| 76 | remaining_packet_size -= previous_obu_extra_size; |
| 77 | |
| 78 | let must_write_obu_element_size = |
| 79 | current_packet.num_obu_elements > MAX_NUM_OBUS_TO_OMTI_SIZE; |
| 80 | let mut required_bytes = obu.size; |
| 81 | if must_write_obu_element_size { |
| 82 | required_bytes += leb_128_size(obu.size); |
| 83 | } |
| 84 | |
| 85 | if required_bytes <= remaining_packet_size { |
| 86 | current_packet.last_obu_size = obu.size; |
| 87 | current_packet.packet_size += required_bytes; |
| 88 | remaining_packet_size -= required_bytes; |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | // Fragment the obu |
| 93 | let max_first_fragment_size = match must_write_obu_element_size { |
| 94 | true => self.max_fragment_size(remaining_packet_size), |
| 95 | false => remaining_packet_size, |
| 96 | }; |
| 97 | |
| 98 | if max_first_fragment_size == 0 { |
| 99 | current_packet.num_obu_elements -= 1; |
| 100 | current_packet.packet_size -= previous_obu_extra_size; |
| 101 | } else { |
| 102 | current_packet.packet_size += max_first_fragment_size; |
| 103 | if must_write_obu_element_size { |
| 104 | current_packet.packet_size += leb_128_size(max_first_fragment_size); |
| 105 | } |
| 106 | current_packet.last_obu_size = max_first_fragment_size; |
| 107 | } |
| 108 | |
| 109 | // Middle fragments |
| 110 | let mut obu_offset = max_first_fragment_size; |
| 111 | while obu_offset + max_payload_size < obu.size { |
| 112 | self.packets.push(current_packet); |
| 113 | current_packet = Packet::new(obu_idx); |
| 114 | current_packet.num_obu_elements = 1; |
| 115 | current_packet.first_obu_offset = obu_offset; |
| 116 | current_packet.last_obu_size = max_payload_size; |
| 117 | current_packet.packet_size = max_payload_size; |
| 118 | |
| 119 | obu_offset += max_payload_size |
| 120 | } |
| 121 | |
| 122 | // Last fragment |
| 123 | let last_fragment_size = obu.size - obu_offset; |
| 124 | self.packets.push(current_packet); |
| 125 | current_packet = Packet::new(obu_idx); |
| 126 | current_packet.num_obu_elements = 1; |
| 127 | current_packet.first_obu_offset = obu_offset; |
| 128 | current_packet.last_obu_size = last_fragment_size; |
| 129 | current_packet.packet_size = last_fragment_size; |
| 130 | |
| 131 | remaining_packet_size = max_payload_size - last_fragment_size; |
| 132 | } |
| 133 | |
| 134 | self.packets.push(current_packet); |
| 135 | self.write_rtp_payloads(payloads) |
| 136 | } |
| 137 | |
| 138 | fn write_rtp_payloads(&self, payloads: &mut Vec<Vec<u8>>) { |
| 139 | for (i, packet) in self.packets.iter().enumerate() { |
| 140 | let is_first_packet = i == 0; |
| 141 | let mut rtp_payload: Vec<u8> = vec![0u8; AGGREGATION_HEADER_SIZE + packet.packet_size]; |
| 142 | let mut pos = 0; |
| 143 | |
| 144 | let header = self.aggregation_header(packet, &self.obus, is_first_packet); |
| 145 | rtp_payload[pos] = header; |
| 146 | pos += 1; |
| 147 | |
| 148 | let mut obu_offset = packet.first_obu_offset; |
| 149 | // Write all obu elements except the last one |
| 150 | for obu_idx in 0..(packet.num_obu_elements - 1) { |
| 151 | let obu = &self.obus[packet.first_obu + obu_idx]; |
| 152 | let obu_fragment_size = obu.size - obu_offset; |
| 153 | pos += encode_leb_u63(obu_fragment_size as u64, &mut rtp_payload[pos..]); |
| 154 | |
| 155 | if obu_offset == 0 { |
| 156 | rtp_payload[pos] = obu.header & !OBU_SIZE_PRESENT_MASK; |
| 157 | pos += 1; |
| 158 | } |
| 159 | |
| 160 | if obu_offset <= 1 && obu.has_extension() { |
| 161 | rtp_payload[pos] = obu.ext_header; |
| 162 | pos += 1; |
| 163 | } |
| 164 | |
| 165 | let payload_offset = |
| 166 | obu_offset.saturating_sub(if obu.has_extension() { 2 } else { 1 }); |
| 167 | let payload_size = obu.payload.len() - payload_offset; |
| 168 | |
| 169 | if !obu.payload.is_empty() && payload_size > 0 { |
| 170 | rtp_payload[pos..pos + payload_size].copy_from_slice( |
| 171 | &obu.payload[payload_offset..payload_offset + payload_size], |
| 172 | ); |
| 173 | } |
| 174 | |
| 175 | pos += payload_size; |
| 176 | obu_offset = 0; |
| 177 | } |
| 178 | |
| 179 | let last_obu = &self.obus[packet.first_obu + packet.num_obu_elements - 1]; |
| 180 | let mut obu_fragment_size = packet.last_obu_size; |
| 181 | if packet.num_obu_elements > MAX_NUM_OBUS_TO_OMTI_SIZE { |
| 182 | pos += encode_leb_u63(obu_fragment_size as u64, &mut rtp_payload[pos..]); |
| 183 | } |
| 184 | |
| 185 | if obu_offset == 0 && obu_fragment_size > 0 { |
| 186 | rtp_payload[pos] = last_obu.header & !OBU_SIZE_PRESENT_MASK; |
| 187 | pos += 1; |
| 188 | obu_fragment_size -= 1; |
| 189 | } |
| 190 | |
| 191 | if obu_offset <= 1 && last_obu.has_extension() && obu_fragment_size > 0 { |
| 192 | rtp_payload[pos] = last_obu.ext_header; |
| 193 | pos += 1; |
| 194 | obu_fragment_size -= 1; |
| 195 | } |
| 196 | |
| 197 | let payload_offset = |
| 198 | obu_offset.saturating_sub(if last_obu.has_extension() { 2 } else { 1 }); |
| 199 | rtp_payload[pos..pos + obu_fragment_size].copy_from_slice( |
| 200 | &last_obu.payload[payload_offset..payload_offset + obu_fragment_size], |
| 201 | ); |
| 202 | |
| 203 | payloads.push(rtp_payload); |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | fn aggregation_header(&self, packet: &Packet, obus: &[Obu], is_first_packet: bool) -> u8 { |
| 208 | let mut agg_header = 0; |
| 209 | |
| 210 | // set Z flag: the first obu element is the continuation of the previous one |
| 211 | if packet.first_obu_offset > 0 { |
| 212 | agg_header |= 1 << 7; |
| 213 | } |
| 214 | |
| 215 | // set Y flag: the last obu element is continued in the next packet |
| 216 | let last_obu_offset = if packet.num_obu_elements == 1 { |
| 217 | packet.first_obu_offset |
| 218 | } else { |
| 219 | 0 |
| 220 | }; |
| 221 | let last_obu_is_fragment = (last_obu_offset + packet.last_obu_size) |
| 222 | < obus[packet.first_obu + packet.num_obu_elements - 1].size; |
| 223 | if last_obu_is_fragment { |
| 224 | agg_header |= 1 << 6; |
| 225 | } |
| 226 | |
| 227 | // set W field: small number of obu elements in the packet |
| 228 | if packet.num_obu_elements <= MAX_NUM_OBUS_TO_OMTI_SIZE { |
| 229 | agg_header |= packet.num_obu_elements << 4; |
| 230 | } |
| 231 | |
| 232 | // set N flag: |
| 233 | if let Some(ObuType::SequenceHeader) = obus[0].obu_type() { |
| 234 | if is_first_packet { |
| 235 | agg_header |= 1 << 3; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | agg_header as u8 |
| 240 | } |
| 241 | |
| 242 | /// Adding new OBU to the last packet would mean that the previous one is |
| 243 | /// no longer the final OBU element in that packet. Any OBU that is not the |
| 244 | /// last element must be prefixed with its length. `extra_size_for_previous_obu` |
| 245 | /// computes the number of bytes required to encode that length. |
| 246 | fn extra_size_for_previous_obu(&self, packet: &Packet) -> usize { |
| 247 | if packet.packet_size == 0 { |
| 248 | return 0; |
| 249 | } |
| 250 | |
| 251 | if packet.num_obu_elements > MAX_NUM_OBUS_TO_OMTI_SIZE { |
| 252 | return 0; |
| 253 | } |
| 254 | |
| 255 | leb_128_size(packet.last_obu_size) |
| 256 | } |
| 257 | |
| 258 | // Given the number of free bytes remaining in a packet, the function returns the largest |
| 259 | // OBU fragment size that will fit into the packet |
| 260 | // That is, FragmentSize + Leb128Size(FragmentSize) must not exceed remaining_bytes |
| 261 | fn max_fragment_size(&self, size: usize) -> usize { |
| 262 | if size <= 1 { |
| 263 | return 0; |
| 264 | } |
| 265 | |
| 266 | let mut idx = 1; |
| 267 | loop { |
| 268 | if size < (1 << 7 * idx) + 1 { |
| 269 | return size - idx; |
| 270 | } |
| 271 | idx += 1; |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | impl Packetizer for Av1Packetizer { |
| 277 | fn packetize(&mut self, mtu: usize, payload: &[u8]) -> Result<Vec<Vec<u8>>, PacketError> { |
| 278 | if payload.is_empty() || mtu <= AGGREGATION_HEADER_SIZE { |
| 279 | return Ok(vec![]); |
| 280 | } |
| 281 | |
| 282 | let mut payloads = vec![]; |
| 283 | parse_obus(payload, &mut self.obus)?; |
| 284 | self.emit(mtu, &mut payloads); |
| 285 | |
| 286 | Ok(payloads) |
| 287 | } |
| 288 | |
| 289 | fn is_marker(&mut self, _data: &[u8], _previous: Option<&[u8]>, last: bool) -> bool { |
| 290 | last |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | /// AV1 Depacketizer |
| 295 | #[derive(Default, Debug)] |
| 296 | pub struct Av1Depacketizer { |
| 297 | /// current obu payload |
| 298 | obu_buffer: Vec<u8>, |
| 299 | |
| 300 | /// reusable buffer for parsed OBUs |
| 301 | parsed_obus: Vec<Obu>, |
| 302 | |
| 303 | /// length of current obu |
| 304 | obu_length: usize, |
| 305 | |
| 306 | /// aggregation header Z bit |
| 307 | z: bool, |
| 308 | |
| 309 | /// aggregation header Y bit |
| 310 | y: bool, |
| 311 | |
| 312 | /// aggregation header N bit |
| 313 | n: bool, |
| 314 | |
| 315 | /// Number of obus in packet |
| 316 | obu_count: u8, |
| 317 | } |
| 318 | |
| 319 | impl Av1Depacketizer { |
| 320 | fn parse_aggregation_header(&mut self, agg_header: u8) { |
| 321 | // TODO: store these values as mask |
| 322 | self.z = agg_header & (1 << 7) != 0; |
| 323 | self.y = agg_header & (1 << 6) != 0; |
| 324 | self.obu_count = (agg_header & 0b0011_0000) >> 4; |
| 325 | self.n = agg_header & (1 << 3) != 0; |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | impl Depacketizer for Av1Depacketizer { |
| 330 | fn out_size_hint(&self, packets_size: usize) -> Option<usize> { |
| 331 | Some(packets_size) |
| 332 | } |
| 333 | |
| 334 | fn depacketize( |
| 335 | &mut self, |
| 336 | packet: &[u8], |
| 337 | out: &mut Vec<u8>, |
| 338 | codec_extra: &mut super::CodecExtra, |
| 339 | ) -> Result<(), PacketError> { |
| 340 | if packet.is_empty() { |
| 341 | return Err(PacketError::ErrShortPacket); |
| 342 | } |
| 343 | |
| 344 | let mut reader = (packet, 0); |
| 345 | |
| 346 | self.parse_aggregation_header(packet[0]); |
| 347 | reader.skip_bytes(1); |
| 348 | |
| 349 | // if packet does not start with a continuation of an obu fragment |
| 350 | // from the previous packet new obu starts |
| 351 | if !self.z { |
| 352 | self.obu_length = 0; |
| 353 | self.obu_buffer.clear(); |
| 354 | } |
| 355 | |
| 356 | // set the key frame flag if N bit is set and clear the obu buffer |
| 357 | // as it cannot start with a fragment of the previous packet |
| 358 | let mut is_keyframe = matches!( |
| 359 | *codec_extra, |
| 360 | CodecExtra::Av1(Av1CodecExtra { is_keyframe: true }) |
| 361 | ); |
| 362 | if self.n { |
| 363 | is_keyframe = true; |
| 364 | self.obu_length = 0; |
| 365 | self.obu_buffer.clear(); |
| 366 | } |
| 367 | *codec_extra = CodecExtra::Av1(Av1CodecExtra { is_keyframe }); |
| 368 | |
| 369 | let mut obu_idx: usize = 0; |
| 370 | while reader.remaining_bits() > 0 { |
| 371 | let is_first_obu = obu_idx == 0; |
| 372 | let mut is_last_obu = self.obu_count != 0 && obu_idx == (self.obu_count as usize - 1); |
| 373 | |
| 374 | // Read the length of obu |
| 375 | let fragment_obu_length = if self.obu_count == 0 || !is_last_obu { |
| 376 | let len = reader |
| 377 | .get_leb128() |
| 378 | .ok_or(PacketError::ErrAv1CorruptedPacket)?; |
| 379 | |
| 380 | if self.obu_count == 0 && len == reader.remaining_bytes() { |
| 381 | is_last_obu = true; |
| 382 | } |
| 383 | |
| 384 | len |
| 385 | } else { |
| 386 | reader.remaining_bytes() |
| 387 | }; |
| 388 | |
| 389 | // Safety checks |
| 390 | if fragment_obu_length == 0 { |
| 391 | return Err(PacketError::ErrAv1CorruptedPacket); |
| 392 | } |
| 393 | if reader.byte_offset() > packet.len() |
| 394 | || fragment_obu_length > packet.len() - reader.byte_offset() |
| 395 | { |
| 396 | return Err(PacketError::ErrAv1CorruptedPacket); |
| 397 | } |
| 398 | |
| 399 | if is_first_obu && self.z { |
| 400 | // the previous fragment is lost, drop the buffer |
| 401 | if self.obu_buffer.is_empty() { |
| 402 | reader.skip_bytes(fragment_obu_length); |
| 403 | obu_idx = 1; |
| 404 | continue; |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | let offset = reader.byte_offset(); |
| 409 | self.obu_buffer |
| 410 | .extend_from_slice(&packet[offset..offset + fragment_obu_length]); |
| 411 | reader.skip_bytes(fragment_obu_length); |
| 412 | |
| 413 | if is_last_obu && self.y { |
| 414 | self.obu_length += fragment_obu_length; |
| 415 | break; |
| 416 | } |
| 417 | |
| 418 | self.obu_length += fragment_obu_length; |
| 419 | |
| 420 | parse_obus(&self.obu_buffer, &mut self.parsed_obus)?; |
| 421 | let Some(obu) = self.parsed_obus.first_mut() else { |
| 422 | self.obu_length = 0; |
| 423 | self.obu_buffer.clear(); |
| 424 | obu_idx += 1; |
| 425 | continue; |
| 426 | }; |
| 427 | |
| 428 | // Write the obu payload to output |
| 429 | // set size flag |
| 430 | let size_flag_set = obu.header & OBU_SIZE_PRESENT_MASK != 0; |
| 431 | obu.header |= OBU_SIZE_PRESENT_MASK; |
| 432 | out.push(obu.header); |
| 433 | self.obu_length = self.obu_length.saturating_sub(1); |
| 434 | |
| 435 | // add extension if any |
| 436 | if obu.has_extension() { |
| 437 | out.push(obu.ext_header); |
| 438 | self.obu_length = self.obu_length.saturating_sub(1); |
| 439 | } |
| 440 | |
| 441 | // add size if not present |
| 442 | if !size_flag_set { |
| 443 | let mut temp_space = [0u8; 9]; |
| 444 | let bytes_written = encode_leb_u63(self.obu_length as u64, &mut temp_space[..]); |
| 445 | out.extend_from_slice(&temp_space[..bytes_written]); |
| 446 | } |
| 447 | |
| 448 | // finally payload |
| 449 | if self.obu_length > self.obu_buffer.len() { |
| 450 | return Err(PacketError::ErrAv1CorruptedPacket); |
| 451 | } |
| 452 | let start_idx = self.obu_buffer.len() - self.obu_length; |
| 453 | out.extend_from_slice(&self.obu_buffer[start_idx..start_idx + self.obu_length]); |
| 454 | |
| 455 | self.obu_length = 0; |
| 456 | self.obu_buffer.clear(); |
| 457 | |
| 458 | if is_last_obu { |
| 459 | self.obu_length = 0; |
| 460 | self.obu_buffer.clear(); |
| 461 | break; |
| 462 | } |
| 463 | |
| 464 | obu_idx += 1; |
| 465 | } |
| 466 | |
| 467 | Ok(()) |
| 468 | } |
| 469 | |
| 470 | fn is_partition_head(&self, packet: &[u8]) -> bool { |
| 471 | if packet.is_empty() { |
| 472 | return false; |
| 473 | } |
| 474 | |
| 475 | // Z bit in the aggregation header is set to 0 |
| 476 | packet[0] & (1 << 7) == 0 |
| 477 | } |
| 478 | |
| 479 | fn is_partition_tail(&self, marker: bool, _payload: &[u8]) -> bool { |
| 480 | marker |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | #[repr(u8)] |
| 485 | enum ObuType { |
| 486 | SequenceHeader = 1, |
| 487 | TemporalDelimiter = 2, |
| 488 | FrameHeader = 3, |
| 489 | TileGroup = 4, |
| 490 | MetaData = 5, |
| 491 | Frame = 6, |
| 492 | RedundantFrameHeader = 7, |
| 493 | TileList = 8, |
| 494 | Padding = 15, |
| 495 | } |
| 496 | |
| 497 | impl ObuType { |
| 498 | fn include_in_packetization(self) -> bool { |
| 499 | !matches!( |
| 500 | self, |
| 501 | Self::TemporalDelimiter | Self::TileList | Self::Padding |
| 502 | ) |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | /// Represents an OBU (Open Bitstream Unit) with its header, extension, payload, and size. |
| 507 | #[derive(Default, Debug)] |
| 508 | struct Obu { |
| 509 | header: u8, |
| 510 | ext_header: u8, |
| 511 | payload: Vec<u8>, |
| 512 | size: usize, |
| 513 | } |
| 514 | |
| 515 | impl Obu { |
| 516 | fn has_extension(&self) -> bool { |
| 517 | (self.header & OBU_EXTENSION_PRESENT_MASK) != 0 |
| 518 | } |
| 519 | |
| 520 | fn has_size(&self) -> bool { |
| 521 | (self.header & OBU_SIZE_PRESENT_MASK) != 0 |
| 522 | } |
| 523 | |
| 524 | fn obu_type(&self) -> Option<ObuType> { |
| 525 | match (self.header & OBU_TYPE_MASK) >> 3 { |
| 526 | 1 => Some(ObuType::SequenceHeader), |
| 527 | 2 => Some(ObuType::TemporalDelimiter), |
| 528 | 3 => Some(ObuType::FrameHeader), |
| 529 | 4 => Some(ObuType::TileGroup), |
| 530 | 5 => Some(ObuType::MetaData), |
| 531 | 6 => Some(ObuType::Frame), |
| 532 | 7 => Some(ObuType::RedundantFrameHeader), |
| 533 | 8 => Some(ObuType::TileList), |
| 534 | 15 => Some(ObuType::Padding), |
| 535 | _ => None, |
| 536 | } |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | /// The `Packet` struct holds information about the OBU (Open Bitstream Unit) and |
| 541 | /// related metadata necessary for constructing an RTP packet payload during packetization |
| 542 | #[derive(Debug)] |
| 543 | struct Packet { |
| 544 | first_obu: usize, |
| 545 | num_obu_elements: usize, |
| 546 | first_obu_offset: usize, |
| 547 | last_obu_size: usize, |
| 548 | packet_size: usize, |
| 549 | } |
| 550 | |
| 551 | impl Packet { |
| 552 | pub fn new(first_obu: usize) -> Self { |
| 553 | Packet { |
| 554 | first_obu, |
| 555 | num_obu_elements: 0, |
| 556 | first_obu_offset: 0, |
| 557 | last_obu_size: 0, |
| 558 | packet_size: 0, |
| 559 | } |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | /// Returns the number of bytes required to encode a value using unsigned LEB128 encoding. |
| 564 | /// LEB128 uses variable-length encoding with 7 bits per byte, and this function computes |
| 565 | /// how many bytes are needed to represent the given `value`. |
| 566 | pub fn leb_128_size(mut value: usize) -> usize { |
| 567 | let mut size = 0; |
| 568 | |
| 569 | while value >= 0x80 { |
| 570 | size += 1; |
| 571 | value >>= 7; |
| 572 | } |
| 573 | |
| 574 | size + 1 |
| 575 | } |
| 576 | |
| 577 | /// OBU Header Structure (8 bits total): |
| 578 | /// |
| 579 | /// 0 1 2 3 4 5 6 7 |
| 580 | /// +-+-+-+-+-+-+-+-+ |
| 581 | /// |F| T |S|E|R| |
| 582 | /// +-+-+-+-+-+-+-+-+ |
| 583 | /// |
| 584 | /// Explanation of each bit: |
| 585 | /// - F (1 bit) - OBU Forbidden Bit: must be set to 0. |
| 586 | /// - T (4 bits) - OBU Type: This field specifies the type of data structure contained in the OBU payload. |
| 587 | /// - S (1 bit) - OBU Has Size Field: A flag indicating whether the obu_size syntax element will be present. |
| 588 | /// - E (1 bit) - OBU Extension Flag: A flag indicatin if the optional obu_extension_header is present. |
| 589 | /// - R (1 bit) - OBU Reserved Bits: must be set to 0. The value is ignored by a decoder. |
| 590 | fn parse_obus(payload: &[u8], parsed_obus: &mut Vec<Obu>) -> Result<(), PacketError> { |
| 591 | let mut reader = (payload, 0); |
| 592 | let mut obu_idx = 0; |
| 593 | |
| 594 | while reader.remaining_bits() > 0 { |
| 595 | // Reuse existing Obu entry if available, otherwise grow the Vec |
| 596 | if obu_idx >= parsed_obus.len() { |
| 597 | parsed_obus.push(Obu::default()); |
| 598 | } |
| 599 | let obu = &mut parsed_obus[obu_idx]; |
| 600 | |
| 601 | obu.header = reader.get_u8().ok_or(PacketError::ErrAv1CorruptedPacket)?; |
| 602 | obu.ext_header = 0; |
| 603 | obu.size = 1; |
| 604 | |
| 605 | if obu.has_extension() { |
| 606 | obu.ext_header = reader.get_u8().ok_or(PacketError::ErrAv1CorruptedPacket)?; |
| 607 | obu.size += 1; |
| 608 | } |
| 609 | |
| 610 | // Reuse the existing payload Vec โ clear preserves capacity |
| 611 | obu.payload.clear(); |
| 612 | |
| 613 | if obu.has_size() { |
| 614 | let obu_size = reader |
| 615 | .get_leb128() |
| 616 | .ok_or(PacketError::ErrAv1CorruptedPacket)?; |
| 617 | if obu_size > reader.remaining_bytes() { |
| 618 | return Err(PacketError::ErrAv1CorruptedPacket); |
| 619 | } |
| 620 | let bytes = reader |
| 621 | .get_bytes(obu_size) |
| 622 | .ok_or(PacketError::ErrAv1CorruptedPacket)?; |
| 623 | obu.payload.extend_from_slice(bytes); |
| 624 | } else { |
| 625 | obu.payload.extend_from_slice(reader.get_remaining()); |
| 626 | } |
| 627 | obu.size += obu.payload.len(); |
| 628 | |
| 629 | if let Some(obu_type) = obu.obu_type() { |
| 630 | if obu_type.include_in_packetization() { |
| 631 | obu_idx += 1; |
| 632 | } |
| 633 | } |
| 634 | } |
| 635 | |
| 636 | // Trim any leftover entries from previous calls |
| 637 | parsed_obus.truncate(obu_idx); |
| 638 | Ok(()) |
| 639 | } |
| 640 | |
| 641 | #[cfg(test)] |
| 642 | mod test { |
| 643 | use super::*; |
| 644 | |
| 645 | /// AV1 Packetizer tets |
| 646 | #[test] |
| 647 | fn packetize_one_frame_type_obu_without_size() { |
| 648 | let payload = &[0x30, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; |
| 649 | let mut packetizer = Av1Packetizer::default(); |
| 650 | |
| 651 | let result = packetizer.packetize(1200, payload); |
| 652 | |
| 653 | assert!(result.is_ok()); |
| 654 | assert_eq!( |
| 655 | result.unwrap(), |
| 656 | [[0x10, 0x30, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]] |
| 657 | ); |
| 658 | } |
| 659 | |
| 660 | #[test] |
| 661 | fn packetize_one_frame_type_obu_without_size_with_extension() { |
| 662 | let payload = &[0x34, 0x28, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; |
| 663 | let mut packetizer = Av1Packetizer::default(); |
| 664 | |
| 665 | let result = packetizer.packetize(1200, payload); |
| 666 | |
| 667 | assert!(result.is_ok()); |
| 668 | assert_eq!( |
| 669 | result.unwrap(), |
| 670 | [[0x10, 0x34, 0x28, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]] |
| 671 | ); |
| 672 | } |
| 673 | |
| 674 | #[test] |
| 675 | fn packetize_one_frame_type_obu_remove_size_field_without_extension() { |
| 676 | let payload = &[0x32, 0x07, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11]; |
| 677 | let mut packetizer = Av1Packetizer::default(); |
| 678 | |
| 679 | let result = packetizer.packetize(1200, payload); |
| 680 | |
| 681 | assert!(result.is_ok()); |
| 682 | assert_eq!( |
| 683 | result.unwrap(), |
| 684 | [[0x10, 0x30, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11]] |
| 685 | ); |
| 686 | } |
| 687 | |
| 688 | #[test] |
| 689 | fn packetize_one_frame_type_obu_remove_size_field_with_extension() { |
| 690 | let payload = &[0x36, 0x28, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; |
| 691 | let mut packetizer = Av1Packetizer::default(); |
| 692 | |
| 693 | let result = packetizer.packetize(1200, payload); |
| 694 | |
| 695 | assert!(result.is_ok()); |
| 696 | assert_eq!( |
| 697 | result.unwrap(), |
| 698 | [[0x10, 0x34, 0x28, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]] |
| 699 | ); |
| 700 | } |
| 701 | |
| 702 | #[test] |
| 703 | fn omit_size_for_last_obu_when_three_obus_fits_into_the_packet() { |
| 704 | let payload = &[ |
| 705 | 0x0a, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // sequence header |
| 706 | 0x2a, 0x04, 0x0b, 0x0c, 0x0d, 0x0e, // metadata |
| 707 | 0x32, 0x06, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, // frame |
| 708 | ]; |
| 709 | let mut packetizer = Av1Packetizer::default(); |
| 710 | |
| 711 | let result = packetizer.packetize(1200, payload); |
| 712 | |
| 713 | assert!(result.is_ok()); |
| 714 | assert_eq!( |
| 715 | result.unwrap(), |
| 716 | [[ |
| 717 | 0x38, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x05, 0x28, 0x0b, 0x0c, 0x0d, |
| 718 | 0x0e, 0x30, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a |
| 719 | ]] |
| 720 | ); |
| 721 | } |
| 722 | |
| 723 | #[test] |
| 724 | fn use_size_for_all_obus_when_four_obus_fit_into_the_packet() { |
| 725 | let payload = &[ |
| 726 | 0x0a, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // sequence header |
| 727 | 0x2a, 0x04, 0x0b, 0x0c, 0x0d, 0x0e, // metadata |
| 728 | 0x1a, 0x03, 0x15, 0x16, 0x17, // frame |
| 729 | 0x22, 0x06, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, // tile group |
| 730 | ]; |
| 731 | let mut packetizer = Av1Packetizer::default(); |
| 732 | |
| 733 | let result = packetizer.packetize(1200, payload); |
| 734 | |
| 735 | assert!(result.is_ok()); |
| 736 | assert_eq!( |
| 737 | result.unwrap(), |
| 738 | [[ |
| 739 | 0x08, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x05, 0x28, 0x0b, 0x0c, 0x0d, |
| 740 | 0x0e, 0x04, 0x18, 0x15, 0x16, 0x17, 0x07, 0x20, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24 |
| 741 | ]] |
| 742 | ); |
| 743 | } |
| 744 | |
| 745 | #[test] |
| 746 | fn discards_temporal_delimiter_and_tile_list_obu() { |
| 747 | let payload = &[ |
| 748 | 0x12, 0x00, // temporal delimeter |
| 749 | 0x2a, 0x00, // metadata |
| 750 | 0x42, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // tile list |
| 751 | 0x1a, 0x03, 0x15, 0x16, 0x17, // frame |
| 752 | 0x22, 0x06, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, // tile group |
| 753 | ]; |
| 754 | let mut packetizer = Av1Packetizer::default(); |
| 755 | |
| 756 | let result = packetizer.packetize(1200, payload); |
| 757 | |
| 758 | assert!(result.is_ok()); |
| 759 | assert_eq!( |
| 760 | result.unwrap(), |
| 761 | [[ |
| 762 | 0x30, 0x01, 0x28, 0x04, 0x18, 0x15, 0x16, 0x17, 0x20, 0x1f, 0x20, 0x21, 0x22, 0x23, |
| 763 | 0x24 |
| 764 | ]] |
| 765 | ); |
| 766 | } |
| 767 | |
| 768 | #[test] |
| 769 | fn split_two_obus_into_two_packets_force_split_obu_header() { |
| 770 | let payload = &[ |
| 771 | 0x1e, 0x28, 0x01, 0x15, // frame |
| 772 | 0x26, 0x28, 0x04, 0x0b, 0x0c, 0x0d, 0x0e, // tile group |
| 773 | ]; |
| 774 | let mut packetizer = Av1Packetizer::default(); |
| 775 | |
| 776 | let result = packetizer.packetize(6, payload); |
| 777 | |
| 778 | assert!(result.is_ok()); |
| 779 | assert_eq!( |
| 780 | result.unwrap(), |
| 781 | vec![ |
| 782 | vec![0x60, 0x03, 0x1c, 0x28, 0x15, 0x24], |
| 783 | vec![0x90, 0x28, 0x0b, 0x0c, 0x0d, 0x0e] |
| 784 | ] |
| 785 | ); |
| 786 | } |
| 787 | |
| 788 | #[test] |
| 789 | fn set_nbit_at_the_first_packet_of_coded_video_sequence() {} |
| 790 | |
| 791 | #[test] |
| 792 | fn split_single_obu_into_two_packets() { |
| 793 | let payload = &[ |
| 794 | 0x32, 0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, |
| 795 | ]; |
| 796 | let mut packetizer = Av1Packetizer::default(); |
| 797 | |
| 798 | let result = packetizer.packetize(8, payload); |
| 799 | |
| 800 | assert!(result.is_ok()); |
| 801 | assert_eq!( |
| 802 | result.unwrap(), |
| 803 | vec![ |
| 804 | vec![0x50, 0x30, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10], |
| 805 | vec![0x90, 0x11, 0x12, 0x13] |
| 806 | ] |
| 807 | ); |
| 808 | } |
| 809 | |
| 810 | #[test] |
| 811 | fn split_single_obu_into_many_packets() { |
| 812 | let mut payload: Vec<u8> = vec![0x32, 0xB0, 0x09]; // obu header and leb128 encoded size |
| 813 | payload.extend(vec![27u8; 1200]); // obu payload |
| 814 | |
| 815 | let mut packetizer = Av1Packetizer::default(); |
| 816 | |
| 817 | let result = packetizer.packetize(100, &payload); |
| 818 | |
| 819 | assert!(result.is_ok()); |
| 820 | assert_eq!(result.unwrap().len(), 13 as usize); |
| 821 | } |
| 822 | |
| 823 | #[test] |
| 824 | fn split_two_obus_into_two_packets() { |
| 825 | let payload = &[ |
| 826 | 0x0a, 0x02, 0x0b, 0x0c, // sequence header |
| 827 | 0x32, 0x09, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, // frame |
| 828 | ]; |
| 829 | let mut packetizer = Av1Packetizer::default(); |
| 830 | |
| 831 | let result = packetizer.packetize(8, payload); |
| 832 | |
| 833 | assert!(result.is_ok()); |
| 834 | assert_eq!( |
| 835 | result.unwrap(), |
| 836 | vec![ |
| 837 | vec![0x68, 0x03, 0x08, 0x0b, 0x0c, 0x30, 0x01, 0x02], |
| 838 | vec![0x90, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09] |
| 839 | ] |
| 840 | ); |
| 841 | } |
| 842 | |
| 843 | #[test] |
| 844 | fn split_single_obu_into_two_packets_because_of_mtu_limit() { |
| 845 | let payload = &[ |
| 846 | 0x32, 0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, |
| 847 | ]; |
| 848 | let mut packetizer = Av1Packetizer::default(); |
| 849 | |
| 850 | let result = packetizer.packetize(10, payload); |
| 851 | |
| 852 | assert!(result.is_ok()); |
| 853 | assert_eq!( |
| 854 | result.unwrap(), |
| 855 | vec![ |
| 856 | vec![0x50, 0x30, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12], |
| 857 | vec![0x90, 0x13] |
| 858 | ] |
| 859 | ); |
| 860 | } |
| 861 | |
| 862 | /// AV1 Depacketizer tests |
| 863 | #[test] |
| 864 | fn obu_payload_size_set_when_absent() { |
| 865 | let paylod = &[0x10, 0x30, 0x14, 0x1e, 0x28]; |
| 866 | let mut out = Vec::new(); |
| 867 | let mut codec_extra = CodecExtra::None; |
| 868 | let mut depacketizer = Av1Depacketizer::default(); |
| 869 | |
| 870 | let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra); |
| 871 | |
| 872 | assert!(result.is_ok()); |
| 873 | assert_eq!(out, vec![0x32, 0x03, 0x14, 0x1e, 0x28]); |
| 874 | } |
| 875 | |
| 876 | #[test] |
| 877 | fn obu_payload_size_set_when_present() { |
| 878 | let paylod = &[0x10, 0x32, 0x03, 0x14, 0x1e, 0x28]; |
| 879 | let mut out = Vec::new(); |
| 880 | let mut codec_extra = CodecExtra::None; |
| 881 | let mut depacketizer = Av1Depacketizer::default(); |
| 882 | |
| 883 | let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra); |
| 884 | |
| 885 | assert!(result.is_ok()); |
| 886 | assert_eq!(out, vec![0x32, 0x03, 0x14, 0x1e, 0x28]); |
| 887 | } |
| 888 | |
| 889 | #[test] |
| 890 | fn obu_payload_size_set_after_extension_when_absent() { |
| 891 | let paylod = &[0x10, 0x34, 0x48, 0x14, 0x1e, 0x28]; |
| 892 | let mut out = Vec::new(); |
| 893 | let mut codec_extra = CodecExtra::None; |
| 894 | let mut depacketizer = Av1Depacketizer::default(); |
| 895 | |
| 896 | let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra); |
| 897 | |
| 898 | assert!(result.is_ok()); |
| 899 | assert_eq!(out, vec![0x36, 0x48, 0x03, 0x14, 0x1e, 0x28]); |
| 900 | } |
| 901 | |
| 902 | #[test] |
| 903 | fn obu_payload_size_set_after_extension_when_present() { |
| 904 | let paylod = &[0x10, 0x36, 0x48, 0x03, 0x14, 0x1e, 0x28]; |
| 905 | let mut out = Vec::new(); |
| 906 | let mut codec_extra = CodecExtra::None; |
| 907 | let mut depacketizer = Av1Depacketizer::default(); |
| 908 | |
| 909 | let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra); |
| 910 | |
| 911 | assert!(result.is_ok()); |
| 912 | assert_eq!(out, vec![0x36, 0x48, 0x03, 0x14, 0x1e, 0x28]); |
| 913 | } |
| 914 | |
| 915 | #[test] |
| 916 | fn one_packet_with_two_obus() { |
| 917 | let paylod = &[0x20, 0x02, 0x08, 0x0a, 0x30, 0x14]; |
| 918 | let mut out = Vec::new(); |
| 919 | let mut codec_extra = CodecExtra::None; |
| 920 | let mut depacketizer = Av1Depacketizer::default(); |
| 921 | |
| 922 | let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra); |
| 923 | |
| 924 | assert!(result.is_ok()); |
| 925 | assert_eq!(out, vec![0x0a, 0x01, 0x0a, 0x32, 0x01, 0x14]); |
| 926 | } |
| 927 | |
| 928 | #[test] |
| 929 | fn one_obu_from_two_packets() { |
| 930 | let payload1 = &[0x50, 0x30, 0x14, 0x1e]; |
| 931 | let payload2 = &[0x90, 0x28]; |
| 932 | let mut out = Vec::new(); |
| 933 | let mut codec_extra = CodecExtra::None; |
| 934 | let mut depacketizer = Av1Depacketizer::default(); |
| 935 | |
| 936 | let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra); |
| 937 | assert!(result.is_ok()); |
| 938 | |
| 939 | let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra); |
| 940 | assert!(result.is_ok()); |
| 941 | assert_eq!(out, vec![0x32, 0x03, 0x14, 0x1e, 0x28]); |
| 942 | } |
| 943 | |
| 944 | #[test] |
| 945 | fn two_packets_with_three_obus() { |
| 946 | let payload1 = &[0x60, 0x02, 0x08, 0x0a, 0x30, 0x14, 0x1e]; |
| 947 | let payload2 = &[0x90, 0x28]; |
| 948 | let mut out = Vec::new(); |
| 949 | let mut codec_extra = CodecExtra::None; |
| 950 | let mut depacketizer = Av1Depacketizer::default(); |
| 951 | |
| 952 | let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra); |
| 953 | assert!(result.is_ok()); |
| 954 | |
| 955 | let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra); |
| 956 | assert!(result.is_ok()); |
| 957 | assert_eq!(out, vec![0x0a, 0x01, 0x0a, 0x32, 0x03, 0x14, 0x1e, 0x28]); |
| 958 | } |
| 959 | |
| 960 | #[test] |
| 961 | fn two_packets_with_many_obus_some_with_extensions() { |
| 962 | let payload1 = &[ |
| 963 | 0x40, 0x02, 0x08, 0x0a, 0x02, 0x28, 0x14, 0x04, 0x2c, 0x30, 0x14, 0x1e, 0x05, 0x34, |
| 964 | 0x30, 0x28, 0x32, 0x3c, |
| 965 | ]; |
| 966 | let payload2 = &[0x90, 0x46, 0x50, 0x5a]; |
| 967 | let mut out = Vec::new(); |
| 968 | let mut codec_extra = CodecExtra::None; |
| 969 | let mut depacketizer = Av1Depacketizer::default(); |
| 970 | |
| 971 | let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra); |
| 972 | assert!(result.is_ok()); |
| 973 | |
| 974 | let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra); |
| 975 | assert!(result.is_ok()); |
| 976 | assert_eq!( |
| 977 | out, |
| 978 | vec![ |
| 979 | 0x0a, 0x01, 0x0a, 0x2a, 0x01, 0x14, 0x2e, 0x30, 0x02, 0x14, 0x1e, 0x36, 0x30, 0x06, |
| 980 | 0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a |
| 981 | ] |
| 982 | ); |
| 983 | } |
| 984 | |
| 985 | #[test] |
| 986 | fn one_obu_from_many_packets() { |
| 987 | let payload1 = &[0x50, 0x30, 0x0b, 0x0c]; |
| 988 | let payload2 = &[0xd0, 0x0d, 0x0e]; |
| 989 | let payload3 = &[0xd0, 0x0f, 0x10, 0x11]; |
| 990 | let payload4 = &[0x90, 0x12]; |
| 991 | let mut out = Vec::new(); |
| 992 | let mut codec_extra = CodecExtra::None; |
| 993 | let mut depacketizer = Av1Depacketizer::default(); |
| 994 | |
| 995 | let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra); |
| 996 | assert!(result.is_ok()); |
| 997 | let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra); |
| 998 | assert!(result.is_ok()); |
| 999 | let result = depacketizer.depacketize(payload3, &mut out, &mut codec_extra); |
| 1000 | assert!(result.is_ok()); |
| 1001 | let result = depacketizer.depacketize(payload4, &mut out, &mut codec_extra); |
| 1002 | assert!(result.is_ok()); |
| 1003 | assert_eq!( |
| 1004 | out, |
| 1005 | vec![0x32, 0x08, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12] |
| 1006 | ); |
| 1007 | } |
| 1008 | |
| 1009 | #[test] |
| 1010 | fn many_packets_with_border_aligned_obus() { |
| 1011 | let payload1 = &[0x60, 0x03, 0x18, 0x0b, 0x0c, 0x20, 0x15, 0x16, 0x17]; |
| 1012 | let payload2 = &[0x90, 0x18, 0x19, 0x1a, 0x1b]; |
| 1013 | let payload3 = &[0x60, 0x03, 0x38, 0x0b, 0x0c, 0x20, 0x1f, 0x20]; |
| 1014 | let payload4 = &[0x90, 0x21, 0x22, 0x23, 0x24]; |
| 1015 | let mut out = Vec::new(); |
| 1016 | let mut codec_extra = CodecExtra::None; |
| 1017 | let mut depacketizer = Av1Depacketizer::default(); |
| 1018 | |
| 1019 | let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra); |
| 1020 | assert!(result.is_ok()); |
| 1021 | let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra); |
| 1022 | assert!(result.is_ok()); |
| 1023 | let result = depacketizer.depacketize(payload3, &mut out, &mut codec_extra); |
| 1024 | assert!(result.is_ok()); |
| 1025 | let result = depacketizer.depacketize(payload4, &mut out, &mut codec_extra); |
| 1026 | assert!(result.is_ok()); |
| 1027 | assert_eq!( |
| 1028 | out, |
| 1029 | vec![ |
| 1030 | 0x1a, 0x02, 0x0b, 0x0c, 0x22, 0x07, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x3a, |
| 1031 | 0x02, 0x0b, 0x0c, 0x22, 0x06, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24 |
| 1032 | ] |
| 1033 | ); |
| 1034 | } |
| 1035 | |
| 1036 | #[test] |
| 1037 | fn one_packet_one_obu_with_payload_size_127_bytes() { |
| 1038 | let mut payload = [0; 131]; |
| 1039 | let mut out = Vec::new(); |
| 1040 | let mut codec_extra = CodecExtra::None; |
| 1041 | let mut depacketizer = Av1Depacketizer::default(); |
| 1042 | |
| 1043 | payload[0] = 0b0000_0000; // aggregation header |
| 1044 | payload[1] = 0x80; // leb128 encoded size of 128 bytes |
| 1045 | payload[2] = 0x01; // in two bytes |
| 1046 | payload[3] = 0b0011_0000; // obu_header with size and extension bits unset. |
| 1047 | payload[4 + 42] = 0x42; // random |
| 1048 | |
| 1049 | let result = depacketizer.depacketize(&payload, &mut out, &mut codec_extra); |
| 1050 | assert!(result.is_ok()); |
| 1051 | assert_eq!(out[0], 0b0011_0010); // size bit set |
| 1052 | assert_eq!(out[1], 127); // obu payloa size, 1 byte is enough |
| 1053 | assert_eq!(out[44], 0x42); // check random byte |
| 1054 | } |
| 1055 | |
| 1056 | #[test] |
| 1057 | fn two_packets_one_obu_with_payload_size_128_bytes() { |
| 1058 | let mut payload1 = [0; 35]; |
| 1059 | let mut payload2 = [0; 98]; |
| 1060 | let mut out = Vec::new(); |
| 1061 | let mut codec_extra = CodecExtra::None; |
| 1062 | let mut depacketizer = Av1Depacketizer::default(); |
| 1063 | |
| 1064 | payload1[0] = 0b0100_0000; // aggregation header |
| 1065 | payload1[1] = 33; // payload size |
| 1066 | payload1[2] = 0b0011_0000; // obu_header with size and extension bits unset. |
| 1067 | payload1[3 + 10] = 0x10; // random |
| 1068 | |
| 1069 | payload2[0] = 0b1000_0000; |
| 1070 | payload2[1] = 96; |
| 1071 | payload2[2 + 20] = 0x20; |
| 1072 | |
| 1073 | let result = depacketizer.depacketize(&payload1, &mut out, &mut codec_extra); |
| 1074 | assert!(result.is_ok()); |
| 1075 | let result = depacketizer.depacketize(&payload2, &mut out, &mut codec_extra); |
| 1076 | assert!(result.is_ok()); |
| 1077 | assert_eq!(out[0], 0b0011_0010); |
| 1078 | assert_eq!(out[1], 0x80); |
| 1079 | assert_eq!(out[2], 0x01); |
| 1080 | assert_eq!(out[3 + 10], 0x10); |
| 1081 | assert_eq!(out[3 + 32 + 20], 0x20); |
| 1082 | } |
| 1083 | |
| 1084 | // Regression tests for leb_128_size boundary values. |
| 1085 | // The bug was `value > 0x80` instead of `value >= 0x80`. |
| 1086 | // LEB128 encodes 7 bits per byte, so values >= 128 need 2 bytes, |
| 1087 | // values >= 16384 need 3 bytes, etc. |
| 1088 | |
| 1089 | #[test] |
| 1090 | fn leb128_size_boundary_at_128() { |
| 1091 | // 127 fits in 1 byte (7 bits), 128 needs 2 bytes |
| 1092 | assert_eq!(leb_128_size(0), 1); |
| 1093 | assert_eq!(leb_128_size(1), 1); |
| 1094 | assert_eq!(leb_128_size(127), 1); |
| 1095 | assert_eq!(leb_128_size(128), 2); // was incorrectly 1 before fix |
| 1096 | assert_eq!(leb_128_size(129), 2); |
| 1097 | assert_eq!(leb_128_size(255), 2); |
| 1098 | } |
| 1099 | |
| 1100 | #[test] |
| 1101 | fn leb128_size_boundary_at_16384() { |
| 1102 | // 16383 fits in 2 bytes (14 bits), 16384 needs 3 bytes |
| 1103 | assert_eq!(leb_128_size(16383), 2); |
| 1104 | assert_eq!(leb_128_size(16384), 3); // was incorrectly 2 before fix |
| 1105 | assert_eq!(leb_128_size(16385), 3); |
| 1106 | } |
| 1107 | |
| 1108 | #[test] |
| 1109 | fn leb128_size_boundary_at_2097152() { |
| 1110 | // 2097151 fits in 3 bytes (21 bits), 2097152 needs 4 bytes |
| 1111 | assert_eq!(leb_128_size(2097151), 3); |
| 1112 | assert_eq!(leb_128_size(2097152), 4); // was incorrectly 3 before fix |
| 1113 | } |
| 1114 | |
| 1115 | // Regression test: packetize an OBU with exactly 128 bytes of payload. |
| 1116 | // This triggers the leb_128_size boundary (128 needs 2-byte LEB128). |
| 1117 | // Before the fix, leb_128_size(128) returned 1 instead of 2, causing |
| 1118 | // the output buffer to be 1 byte too small โ panic on slice indexing. |
| 1119 | #[test] |
| 1120 | fn packetize_obu_with_128_byte_payload_no_panic() { |
| 1121 | // Build a raw OBU: header (0x32 = frame, size present) |
| 1122 | // + LEB128 size (0x80, 0x01 = 128) + 128 bytes payload |
| 1123 | let mut payload = Vec::with_capacity(3 + 128); |
| 1124 | payload.push(0x32); // OBU header: frame type, size present |
| 1125 | payload.push(0x80); // LEB128 size byte 1: 128 |
| 1126 | payload.push(0x01); // LEB128 size byte 2 |
| 1127 | payload.extend(vec![0xAB; 128]); // 128 bytes of payload |
| 1128 | |
| 1129 | let mut packetizer = Av1Packetizer::default(); |
| 1130 | let result = packetizer.packetize(1200, &payload); |
| 1131 | assert!(result.is_ok()); |
| 1132 | let packets = result.unwrap(); |
| 1133 | assert!(!packets.is_empty()); |
| 1134 | } |
| 1135 | |
| 1136 | // Regression test: packetize an OBU that requires fragmentation across |
| 1137 | // multiple RTP packets, with exactly 128 bytes of payload. |
| 1138 | // This exercises both the leb_128_size fix (buffer allocation) and |
| 1139 | // the payload_offset fix (correct data in continuation fragments). |
| 1140 | #[test] |
| 1141 | fn packetize_128_byte_obu_fragmented_correctly() { |
| 1142 | // OBU: header + LEB128(128) + 128 bytes payload |
| 1143 | let mut payload = Vec::with_capacity(3 + 128); |
| 1144 | payload.push(0x32); // frame type, size present |
| 1145 | payload.push(0x80); // LEB128: 128 |
| 1146 | payload.push(0x01); |
| 1147 | for i in 0u8..128 { |
| 1148 | payload.push(i); // distinguishable payload bytes |
| 1149 | } |
| 1150 | |
| 1151 | let mut packetizer = Av1Packetizer::default(); |
| 1152 | // MTU of 50 forces fragmentation into multiple packets |
| 1153 | let result = packetizer.packetize(50, &payload); |
| 1154 | assert!(result.is_ok()); |
| 1155 | let packets = result.unwrap(); |
| 1156 | assert!( |
| 1157 | packets.len() >= 3, |
| 1158 | "128-byte OBU with MTU 50 should produce at least 3 packets" |
| 1159 | ); |
| 1160 | |
| 1161 | // Verify round-trip: depacketize all packets and check payload integrity |
| 1162 | let mut out = Vec::new(); |
| 1163 | let mut codec_extra = CodecExtra::None; |
| 1164 | let mut depacketizer = Av1Depacketizer::default(); |
| 1165 | for pkt in &packets { |
| 1166 | let r = depacketizer.depacketize(pkt, &mut out, &mut codec_extra); |
| 1167 | assert!(r.is_ok(), "depacketize failed: {:?}", r); |
| 1168 | } |
| 1169 | // Output should be: OBU header (with size bit) + LEB128 size + payload |
| 1170 | // Find the payload portion and verify it matches |
| 1171 | assert!( |
| 1172 | out.len() >= 128, |
| 1173 | "round-tripped output too short: {} bytes", |
| 1174 | out.len() |
| 1175 | ); |
| 1176 | // The payload bytes should appear in order at the end |
| 1177 | let payload_start = out.len() - 128; |
| 1178 | for i in 0u8..128 { |
| 1179 | assert_eq!( |
| 1180 | out[payload_start + i as usize], |
| 1181 | i, |
| 1182 | "payload byte {} mismatch after round-trip", |
| 1183 | i |
| 1184 | ); |
| 1185 | } |
| 1186 | } |
| 1187 | |
| 1188 | // Regression test for the payload_offset bug in continuation fragments. |
| 1189 | // When an OBU with extension header spans multiple packets, the second |
| 1190 | // fragment used `obu_offset` instead of `payload_offset` to compute the |
| 1191 | // remaining payload size, causing incorrect data or panics. |
| 1192 | #[test] |
| 1193 | fn packetize_obu_with_extension_fragmented_round_trip() { |
| 1194 | // OBU with extension: header=0x36 (frame type + extension + size present), |
| 1195 | // ext_header=0x10, LEB128 size, then payload |
| 1196 | let mut raw = Vec::with_capacity(4 + 200); |
| 1197 | raw.push(0x36); // OBU header: frame, extension present, size present |
| 1198 | raw.push(0x10); // extension header |
| 1199 | raw.push(0xC8); // LEB128 size: 200 |
| 1200 | raw.push(0x01); |
| 1201 | for i in 0u8..200 { |
| 1202 | raw.push(i); |
| 1203 | } |
| 1204 | |
| 1205 | let mut packetizer = Av1Packetizer::default(); |
| 1206 | let result = packetizer.packetize(40, &raw); |
| 1207 | assert!(result.is_ok()); |
| 1208 | let packets = result.unwrap(); |
| 1209 | assert!( |
| 1210 | packets.len() >= 5, |
| 1211 | "200-byte OBU with MTU 40 should produce multiple packets" |
| 1212 | ); |
| 1213 | |
| 1214 | // Round-trip through depacketizer |
| 1215 | let mut out = Vec::new(); |
| 1216 | let mut codec_extra = CodecExtra::None; |
| 1217 | let mut depacketizer = Av1Depacketizer::default(); |
| 1218 | for pkt in &packets { |
| 1219 | depacketizer |
| 1220 | .depacketize(pkt, &mut out, &mut codec_extra) |
| 1221 | .unwrap(); |
| 1222 | } |
| 1223 | |
| 1224 | // Verify the 200-byte payload is intact |
| 1225 | let payload_start = out.len() - 200; |
| 1226 | for i in 0u8..200 { |
| 1227 | assert_eq!( |
| 1228 | out[payload_start + i as usize], |
| 1229 | i, |
| 1230 | "extension OBU payload byte {} corrupted after round-trip", |
| 1231 | i |
| 1232 | ); |
| 1233 | } |
| 1234 | } |
| 1235 | |
| 1236 | #[test] |
| 1237 | fn test_detect_av1_keyframe() { |
| 1238 | // Empty |
| 1239 | assert!(!detect_av1_keyframe(&[])); |
| 1240 | |
| 1241 | // AV1 aggregation header: Z|Y|W W|N|reserved |
| 1242 | // N bit is bit 3 (0x08) |
| 1243 | |
| 1244 | // N=1 โ keyframe |
| 1245 | assert!(detect_av1_keyframe(&[0x08])); |
| 1246 | assert!(detect_av1_keyframe(&[0x18])); // Z=0,Y=0,W=01,N=1 |
| 1247 | assert!(detect_av1_keyframe(&[0x78])); // Z=0,Y=1,W=11,N=1 |
| 1248 | assert!(detect_av1_keyframe(&[0x88])); // Z=1,Y=0,W=00,N=1 |
| 1249 | assert!(detect_av1_keyframe(&[0x0F])); // N=1, reserved bits set |
| 1250 | |
| 1251 | // N=0 โ not a keyframe |
| 1252 | assert!(!detect_av1_keyframe(&[0x00])); |
| 1253 | assert!(!detect_av1_keyframe(&[0x10])); // W=01, N=0 |
| 1254 | assert!(!detect_av1_keyframe(&[0x70])); // Y=1, W=11, N=0 |
| 1255 | assert!(!detect_av1_keyframe(&[0xF0])); // Z=1, Y=1, W=11, N=0 |
| 1256 | } |
| 1257 | |
| 1258 | #[test] |
| 1259 | fn packetize_respects_mtu() { |
| 1260 | // OBU header (frame-type, no size field) + payload bytes. |
| 1261 | let mut payload = vec![0x30u8]; |
| 1262 | payload.extend(std::iter::repeat(0xABu8).take(2000)); |
| 1263 | for &mtu in &[100usize, 300, 600, 1200] { |
| 1264 | let mut packetizer = Av1Packetizer::default(); |
| 1265 | let pkts = packetizer |
| 1266 | .packetize(mtu, &payload) |
| 1267 | .expect("AV1 packetize ok"); |
| 1268 | assert!(!pkts.is_empty(), "AV1 produced no packets at mtu {mtu}"); |
| 1269 | for (i, pkt) in pkts.iter().enumerate() { |
| 1270 | assert!( |
| 1271 | pkt.len() <= mtu, |
| 1272 | "AV1 packet {i} size {} > mtu {mtu}", |
| 1273 | pkt.len() |
| 1274 | ); |
| 1275 | } |
| 1276 | } |
| 1277 | } |
| 1278 | } |