2022-04-29 02:15:13 +04:00
|
|
|
// Copyright (C) 2022 Scott Lamb <slamb@slamb.org>
|
2021-06-26 03:39:48 +04:00
|
|
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
|
2022-04-29 02:15:13 +04:00
|
|
|
//! Tests a roundtrip through the H.264 packetizer and depacketizer with an arbitrary
|
2021-06-26 03:39:48 +04:00
|
|
|
//! input packet size and frame. Ensures the following:
|
|
|
|
//! * there are no crashes.
|
2022-04-29 02:15:13 +04:00
|
|
|
//! * the round trip produces an error, nothing (see note about `can_end_au`), or identical data.
|
2021-06-26 03:39:48 +04:00
|
|
|
|
|
|
|
#![no_main]
|
|
|
|
use bytes::Bytes;
|
|
|
|
use libfuzzer_sys::fuzz_target;
|
|
|
|
use std::num::NonZeroU32;
|
|
|
|
|
|
|
|
fuzz_target!(|data: &[u8]| {
|
|
|
|
if data.len() < 2 {
|
|
|
|
return;
|
|
|
|
}
|
2021-08-20 00:16:16 +04:00
|
|
|
let conn_ctx = retina::ConnectionContext::dummy();
|
2022-04-27 00:02:10 +04:00
|
|
|
let stream_ctx = retina::StreamContextRef::dummy();
|
2021-06-26 03:39:48 +04:00
|
|
|
let max_payload_size = u16::from_be_bytes([data[0], data[1]]);
|
2022-04-29 02:00:36 +04:00
|
|
|
let mut p = match retina::codec::h264::Packetizer::new(max_payload_size, 0, 0, 0, 0) {
|
2021-06-26 03:39:48 +04:00
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return,
|
|
|
|
};
|
|
|
|
let mut d = retina::codec::Depacketizer::new(
|
2021-08-20 00:16:16 +04:00
|
|
|
"video",
|
|
|
|
"h264",
|
|
|
|
90_000,
|
|
|
|
None,
|
2021-06-26 03:39:48 +04:00
|
|
|
Some("packetization-mode=1;sprop-parameter-sets=J01AHqkYGwe83gDUBAQG2wrXvfAQ,KN4JXGM4"),
|
2021-08-20 00:16:16 +04:00
|
|
|
)
|
|
|
|
.unwrap();
|
2021-06-26 03:39:48 +04:00
|
|
|
let timestamp = retina::Timestamp::new(0, NonZeroU32::new(90_000).unwrap(), 0).unwrap();
|
2021-08-20 00:16:16 +04:00
|
|
|
if p.push(timestamp, Bytes::copy_from_slice(&data[2..]))
|
|
|
|
.is_err()
|
|
|
|
{
|
2021-06-26 03:39:48 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let frame = loop {
|
|
|
|
match p.pull() {
|
|
|
|
Ok(Some(pkt)) => {
|
2022-04-29 02:00:36 +04:00
|
|
|
let mark = pkt.mark();
|
2021-06-26 03:39:48 +04:00
|
|
|
if d.push(pkt).is_err() {
|
|
|
|
return;
|
|
|
|
}
|
2022-04-27 00:02:10 +04:00
|
|
|
match d.pull(&conn_ctx, stream_ctx) {
|
2021-06-26 03:39:48 +04:00
|
|
|
Err(_) => return,
|
|
|
|
Ok(Some(retina::codec::CodecItem::VideoFrame(f))) => {
|
|
|
|
assert!(mark);
|
2021-08-20 00:16:16 +04:00
|
|
|
break f;
|
|
|
|
}
|
2021-06-26 03:39:48 +04:00
|
|
|
Ok(Some(_)) => panic!(),
|
2022-04-29 02:15:13 +04:00
|
|
|
Ok(None) => {
|
|
|
|
// XXX: assert!(!mark)
|
|
|
|
//
|
|
|
|
// One would expect that a frame would be produced if the packet has mark
|
|
|
|
// set. This used to be true, but Retina now has a workaround for some
|
|
|
|
// RTSP servers that spuriously set the mark bit. See
|
|
|
|
// `retina::codec::h264::can_end_au`. In this case, just exit.
|
|
|
|
if mark {
|
|
|
|
assert!(matches!(p.pull(), Ok(None)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-06-26 03:39:48 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(None) => panic!("packetizer ran out of packets before depacketizer produced frame"),
|
|
|
|
Err(_) => return,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
assert_eq!(&data[2..], &frame.data()[..]);
|
2022-04-27 00:02:10 +04:00
|
|
|
assert!(matches!(d.pull(&conn_ctx, stream_ctx), Ok(None)));
|
2021-06-26 03:39:48 +04:00
|
|
|
assert!(matches!(p.pull(), Ok(None)));
|
|
|
|
});
|