2023-10-12 06:06:48 +00:00
|
|
|
//
|
|
|
|
// Copyright (C) 2023 Matthew Waters <matthew@centricular.com>
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
|
|
|
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
|
|
|
// <https://mozilla.org/MPL/2.0/>.
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2024-02-28 02:02:37 +00:00
|
|
|
use std::sync::{atomic::AtomicUsize, Arc, Mutex};
|
2023-10-12 06:06:48 +00:00
|
|
|
|
|
|
|
use gst::{prelude::*, Caps};
|
|
|
|
use gst_check::Harness;
|
|
|
|
use rtp_types::*;
|
|
|
|
|
2024-02-28 02:02:37 +00:00
|
|
|
static ELEMENT_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
|
|
|
|
fn next_element_counter() -> usize {
|
|
|
|
ELEMENT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
|
|
|
|
}
|
|
|
|
|
2023-10-12 06:06:48 +00:00
|
|
|
fn init() {
|
|
|
|
use std::sync::Once;
|
|
|
|
static INIT: Once = Once::new();
|
|
|
|
|
|
|
|
INIT.call_once(|| {
|
|
|
|
gst::init().unwrap();
|
|
|
|
gstrsrtp::plugin_register_static().expect("rtpbin2 test");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const TEST_SSRC: u32 = 0x12345678;
|
|
|
|
const TEST_PT: u8 = 96;
|
|
|
|
const TEST_CLOCK_RATE: u32 = 48000;
|
|
|
|
|
|
|
|
fn generate_rtp_buffer(seqno: u16, rtpts: u32, payload_len: usize) -> gst::Buffer {
|
|
|
|
let payload = vec![4; payload_len];
|
|
|
|
let packet = RtpPacketBuilder::new()
|
|
|
|
.ssrc(TEST_SSRC)
|
|
|
|
.payload_type(TEST_PT)
|
|
|
|
.sequence_number(seqno)
|
|
|
|
.timestamp(rtpts)
|
2024-02-28 02:02:37 +00:00
|
|
|
.payload(payload.as_slice());
|
2023-10-12 06:06:48 +00:00
|
|
|
let size = packet.calculate_size().unwrap();
|
|
|
|
let mut data = vec![0; size];
|
|
|
|
packet.write_into(&mut data).unwrap();
|
|
|
|
gst::Buffer::from_mut_slice(data)
|
|
|
|
}
|
|
|
|
|
2024-06-12 07:51:45 +00:00
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
struct PacketInfo {
|
|
|
|
seq_no: u16,
|
|
|
|
rtp_ts: u32,
|
|
|
|
payload_len: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PacketInfo {
|
2024-06-14 07:17:04 +00:00
|
|
|
fn generate_buffer(&self, dts: Option<gst::ClockTime>) -> gst::Buffer {
|
|
|
|
let mut buf = generate_rtp_buffer(self.seq_no, self.rtp_ts, self.payload_len);
|
|
|
|
let buf_mut = buf.make_mut();
|
|
|
|
buf_mut.set_dts(dts);
|
|
|
|
buf
|
2024-06-12 07:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send_init() -> gst_check::Harness {
|
2023-10-12 06:06:48 +00:00
|
|
|
init();
|
2024-06-12 07:51:45 +00:00
|
|
|
|
2024-02-28 02:02:37 +00:00
|
|
|
let id = next_element_counter();
|
2023-10-12 06:06:48 +00:00
|
|
|
|
2024-02-28 02:02:37 +00:00
|
|
|
let elem = gst::ElementFactory::make("rtpsend")
|
|
|
|
.property("rtp-id", id.to_string())
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
let mut h = Harness::with_element(&elem, Some("rtp_sink_0"), Some("rtp_src_0"));
|
2023-10-12 06:06:48 +00:00
|
|
|
h.play();
|
|
|
|
|
|
|
|
let caps = Caps::builder("application/x-rtp")
|
|
|
|
.field("media", "audio")
|
|
|
|
.field("payload", TEST_PT as i32)
|
|
|
|
.field("clock-rate", TEST_CLOCK_RATE as i32)
|
|
|
|
.field("encoding-name", "custom-test")
|
|
|
|
.build();
|
|
|
|
h.set_src_caps(caps);
|
|
|
|
|
2024-06-12 07:51:45 +00:00
|
|
|
h
|
|
|
|
}
|
2023-10-12 06:06:48 +00:00
|
|
|
|
2024-06-12 07:51:45 +00:00
|
|
|
fn send_push<I>(h: &mut gst_check::Harness, packets: I, buffer_list: bool)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = PacketInfo>,
|
|
|
|
{
|
|
|
|
if buffer_list {
|
|
|
|
let mut list = gst::BufferList::new();
|
|
|
|
let list_mut = list.make_mut();
|
|
|
|
for packet in packets {
|
2024-06-14 07:17:04 +00:00
|
|
|
list_mut.add(packet.generate_buffer(None));
|
2024-06-12 07:51:45 +00:00
|
|
|
}
|
|
|
|
let push_pad = h
|
|
|
|
.element()
|
|
|
|
.unwrap()
|
|
|
|
.static_pad("rtp_sink_0")
|
|
|
|
.unwrap()
|
|
|
|
.peer()
|
|
|
|
.unwrap();
|
|
|
|
push_pad.push_list(list).unwrap();
|
|
|
|
} else {
|
|
|
|
for packet in packets {
|
2024-06-14 07:17:04 +00:00
|
|
|
h.push(packet.generate_buffer(None)).unwrap();
|
2024-06-12 07:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-10-12 06:06:48 +00:00
|
|
|
|
2024-06-12 07:51:45 +00:00
|
|
|
fn send_pull<I>(h: &mut gst_check::Harness, packets: I)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = PacketInfo>,
|
|
|
|
{
|
|
|
|
for packet in packets {
|
|
|
|
let buffer = h.pull().unwrap();
|
|
|
|
let mapped = buffer.map_readable().unwrap();
|
|
|
|
let rtp = rtp_types::RtpPacket::parse(&mapped).unwrap();
|
|
|
|
assert_eq!(rtp.sequence_number(), packet.seq_no);
|
|
|
|
}
|
|
|
|
}
|
2023-10-12 06:06:48 +00:00
|
|
|
|
2024-06-12 07:51:45 +00:00
|
|
|
fn send_check_stats<I>(h: &mut gst_check::Harness, packets: I)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = PacketInfo>,
|
|
|
|
{
|
|
|
|
let mut n_packets = 0;
|
|
|
|
let mut n_bytes = 0;
|
|
|
|
for packet in packets {
|
|
|
|
n_packets += 1;
|
|
|
|
n_bytes += packet.payload_len;
|
|
|
|
}
|
2023-10-12 06:06:48 +00:00
|
|
|
let stats = h.element().unwrap().property::<gst::Structure>("stats");
|
2023-11-27 13:29:07 +00:00
|
|
|
|
2023-10-12 06:06:48 +00:00
|
|
|
let session_stats = stats.get::<gst::Structure>("0").unwrap();
|
|
|
|
let source_stats = session_stats
|
|
|
|
.get::<gst::Structure>(TEST_SSRC.to_string())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(source_stats.get::<u32>("ssrc").unwrap(), TEST_SSRC);
|
|
|
|
assert_eq!(
|
|
|
|
source_stats.get::<u32>("clock-rate").unwrap(),
|
|
|
|
TEST_CLOCK_RATE
|
|
|
|
);
|
|
|
|
assert!(source_stats.get::<bool>("sender").unwrap());
|
|
|
|
assert!(source_stats.get::<bool>("local").unwrap());
|
2024-06-12 07:51:45 +00:00
|
|
|
assert_eq!(source_stats.get::<u64>("packets-sent").unwrap(), n_packets);
|
|
|
|
assert_eq!(
|
|
|
|
source_stats.get::<u64>("octets-sent").unwrap(),
|
|
|
|
n_bytes as u64
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_send() {
|
|
|
|
init();
|
|
|
|
|
|
|
|
let mut h = send_init();
|
|
|
|
|
|
|
|
send_push(&mut h, PACKETS_TEST_1, false);
|
|
|
|
send_pull(&mut h, PACKETS_TEST_1);
|
|
|
|
send_check_stats(&mut h, PACKETS_TEST_1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_send_list() {
|
|
|
|
let mut h = send_init();
|
|
|
|
|
|
|
|
send_push(&mut h, PACKETS_TEST_1, true);
|
|
|
|
send_pull(&mut h, PACKETS_TEST_1);
|
|
|
|
send_check_stats(&mut h, PACKETS_TEST_1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_send_benchmark() {
|
|
|
|
init();
|
|
|
|
let clock = gst::SystemClock::obtain();
|
|
|
|
const N_PACKETS: usize = 2 * 1024 * 1024;
|
|
|
|
let mut packets = Vec::with_capacity(N_PACKETS);
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
packets.push(
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: (i % u16::MAX as usize) as u16,
|
|
|
|
rtp_ts: i as u32,
|
|
|
|
payload_len: 8,
|
|
|
|
}
|
2024-06-14 07:17:04 +00:00
|
|
|
.generate_buffer(None),
|
2024-06-12 07:51:45 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut h = send_init();
|
|
|
|
|
|
|
|
let start = clock.time();
|
|
|
|
|
|
|
|
for packet in packets {
|
|
|
|
h.push(packet).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let pushed = clock.time();
|
|
|
|
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
let buffer = h.pull().unwrap();
|
|
|
|
let mapped = buffer.map_readable().unwrap();
|
|
|
|
let rtp = rtp_types::RtpPacket::parse(&mapped).unwrap();
|
|
|
|
assert_eq!(rtp.sequence_number(), (i % u16::MAX as usize) as u16);
|
|
|
|
}
|
|
|
|
|
|
|
|
let end = clock.time();
|
|
|
|
|
|
|
|
let test_time = end.opt_sub(start);
|
|
|
|
let pull_time = end.opt_sub(pushed);
|
|
|
|
let push_time = pushed.opt_sub(start);
|
|
|
|
println!(
|
|
|
|
"test took {} (push {}, pull {})",
|
|
|
|
test_time.display(),
|
|
|
|
push_time.display(),
|
|
|
|
pull_time.display()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_send_list_benchmark() {
|
|
|
|
init();
|
|
|
|
let clock = gst::SystemClock::obtain();
|
|
|
|
const N_PACKETS: usize = 2 * 1024 * 1024;
|
|
|
|
let mut list = gst::BufferList::new();
|
|
|
|
let list_mut = list.make_mut();
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
list_mut.add(
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: (i % u16::MAX as usize) as u16,
|
|
|
|
rtp_ts: i as u32,
|
|
|
|
payload_len: 8,
|
|
|
|
}
|
2024-06-14 07:17:04 +00:00
|
|
|
.generate_buffer(None),
|
2024-06-12 07:51:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
let mut h = send_init();
|
|
|
|
|
|
|
|
let push_pad = h
|
|
|
|
.element()
|
|
|
|
.unwrap()
|
|
|
|
.static_pad("rtp_sink_0")
|
|
|
|
.unwrap()
|
|
|
|
.peer()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let start = clock.time();
|
|
|
|
|
|
|
|
push_pad.push_list(list).unwrap();
|
|
|
|
|
|
|
|
let pushed = clock.time();
|
|
|
|
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
let buffer = h.pull().unwrap();
|
|
|
|
let mapped = buffer.map_readable().unwrap();
|
|
|
|
let rtp = rtp_types::RtpPacket::parse(&mapped).unwrap();
|
|
|
|
assert_eq!(rtp.sequence_number(), (i % u16::MAX as usize) as u16);
|
|
|
|
}
|
|
|
|
|
|
|
|
let end = clock.time();
|
|
|
|
|
|
|
|
let test_time = end.opt_sub(start);
|
|
|
|
let pull_time = end.opt_sub(pushed);
|
|
|
|
let push_time = pushed.opt_sub(start);
|
|
|
|
println!(
|
|
|
|
"test took {} (push {}, pull {})",
|
|
|
|
test_time.display(),
|
|
|
|
push_time.display(),
|
|
|
|
pull_time.display()
|
|
|
|
);
|
2023-10-12 06:06:48 +00:00
|
|
|
}
|
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
fn receive_init() -> Arc<Mutex<gst_check::Harness>> {
|
2024-02-28 02:02:37 +00:00
|
|
|
let id = next_element_counter();
|
2023-10-12 06:06:48 +00:00
|
|
|
|
2024-02-28 02:02:37 +00:00
|
|
|
let elem = gst::ElementFactory::make("rtprecv")
|
|
|
|
.property("rtp-id", id.to_string())
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
let h = Arc::new(Mutex::new(Harness::with_element(
|
|
|
|
&elem,
|
|
|
|
Some("rtp_sink_0"),
|
2023-10-12 06:06:48 +00:00
|
|
|
None,
|
|
|
|
)));
|
|
|
|
let weak_h = Arc::downgrade(&h);
|
|
|
|
let mut inner = h.lock().unwrap();
|
|
|
|
inner
|
|
|
|
.element()
|
|
|
|
.unwrap()
|
|
|
|
.connect_pad_added(move |_elem, pad| {
|
|
|
|
weak_h
|
|
|
|
.upgrade()
|
|
|
|
.unwrap()
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.add_element_src_pad(pad)
|
|
|
|
});
|
|
|
|
inner.play();
|
|
|
|
let caps = Caps::builder("application/x-rtp")
|
|
|
|
.field("media", "audio")
|
|
|
|
.field("payload", TEST_PT as i32)
|
|
|
|
.field("clock-rate", TEST_CLOCK_RATE as i32)
|
|
|
|
.field("encoding-name", "custom-test")
|
|
|
|
.build();
|
|
|
|
inner.set_src_caps(caps);
|
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
drop(inner);
|
|
|
|
|
|
|
|
h
|
|
|
|
}
|
|
|
|
|
|
|
|
fn receive_push<I>(h: Arc<Mutex<gst_check::Harness>>, packets: I, buffer_list: bool)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = PacketInfo>,
|
|
|
|
{
|
|
|
|
let inner = h.lock().unwrap();
|
2023-10-12 06:06:48 +00:00
|
|
|
// Cannot push with harness lock as the 'pad-added' handler needs to add the newly created pad to
|
|
|
|
// the harness and needs to also take the harness lock. Workaround by pushing from the
|
|
|
|
// internal harness pad directly.
|
|
|
|
let push_pad = inner
|
|
|
|
.element()
|
|
|
|
.unwrap()
|
2024-02-28 02:02:37 +00:00
|
|
|
.static_pad("rtp_sink_0")
|
2023-10-12 06:06:48 +00:00
|
|
|
.unwrap()
|
|
|
|
.peer()
|
|
|
|
.unwrap();
|
|
|
|
drop(inner);
|
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
if buffer_list {
|
|
|
|
let mut list = gst::BufferList::new();
|
|
|
|
let list_mut = list.make_mut();
|
|
|
|
for packet in packets {
|
2024-06-14 07:17:04 +00:00
|
|
|
list_mut.add(packet.generate_buffer(None));
|
2024-06-12 07:52:41 +00:00
|
|
|
}
|
|
|
|
push_pad.push_list(list).unwrap();
|
|
|
|
} else {
|
|
|
|
for packet in packets {
|
2024-06-14 07:17:04 +00:00
|
|
|
push_pad.push(packet.generate_buffer(None)).unwrap();
|
2024-06-12 07:52:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn receive_pull<I>(h: Arc<Mutex<gst_check::Harness>>, packets: I)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = PacketInfo>,
|
|
|
|
{
|
|
|
|
let mut inner = h.lock().unwrap();
|
|
|
|
for packet in packets {
|
|
|
|
let buffer = inner.pull().unwrap();
|
|
|
|
let mapped = buffer.map_readable().unwrap();
|
|
|
|
let rtp = rtp_types::RtpPacket::parse(&mapped).unwrap();
|
|
|
|
assert_eq!(rtp.sequence_number(), packet.seq_no);
|
|
|
|
}
|
|
|
|
}
|
2023-10-12 06:06:48 +00:00
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
fn receive_check_stats<I>(h: Arc<Mutex<gst_check::Harness>>, packets: I)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = PacketInfo>,
|
|
|
|
{
|
|
|
|
let mut n_packets = 0;
|
|
|
|
let mut n_bytes = 0;
|
|
|
|
for packet in packets {
|
|
|
|
n_packets += 1;
|
|
|
|
n_bytes += packet.payload_len;
|
|
|
|
}
|
2023-10-12 06:06:48 +00:00
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
let inner = h.lock().unwrap();
|
2023-10-12 06:06:48 +00:00
|
|
|
let stats = inner.element().unwrap().property::<gst::Structure>("stats");
|
2024-06-12 07:52:41 +00:00
|
|
|
drop(inner);
|
2023-11-27 13:29:07 +00:00
|
|
|
|
2023-10-12 06:06:48 +00:00
|
|
|
let session_stats = stats.get::<gst::Structure>("0").unwrap();
|
|
|
|
let source_stats = session_stats
|
|
|
|
.get::<gst::Structure>(TEST_SSRC.to_string())
|
|
|
|
.unwrap();
|
2023-11-27 13:29:07 +00:00
|
|
|
let jitterbuffers_stats = session_stats
|
|
|
|
.get::<gst::List>("jitterbuffer-stats")
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(jitterbuffers_stats.len(), 1);
|
|
|
|
let jitterbuffer_stats = jitterbuffers_stats
|
|
|
|
.first()
|
|
|
|
.unwrap()
|
|
|
|
.get::<gst::Structure>()
|
|
|
|
.unwrap();
|
2023-10-12 06:06:48 +00:00
|
|
|
assert_eq!(source_stats.get::<u32>("ssrc").unwrap(), TEST_SSRC);
|
|
|
|
assert_eq!(
|
|
|
|
source_stats.get::<u32>("clock-rate").unwrap(),
|
|
|
|
TEST_CLOCK_RATE
|
|
|
|
);
|
|
|
|
assert!(source_stats.get::<bool>("sender").unwrap());
|
|
|
|
assert!(!source_stats.get::<bool>("local").unwrap());
|
2024-06-12 07:52:41 +00:00
|
|
|
assert_eq!(
|
|
|
|
source_stats.get::<u64>("packets-received").unwrap(),
|
|
|
|
n_packets
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
source_stats.get::<u64>("octets-received").unwrap(),
|
|
|
|
n_bytes as u64
|
|
|
|
);
|
2023-11-27 13:29:07 +00:00
|
|
|
assert_eq!(jitterbuffer_stats.get::<u64>("num-late").unwrap(), 0);
|
|
|
|
assert_eq!(jitterbuffer_stats.get::<u64>("num-lost").unwrap(), 0);
|
|
|
|
assert_eq!(jitterbuffer_stats.get::<u64>("num-duplicates").unwrap(), 0);
|
2024-06-12 07:52:41 +00:00
|
|
|
assert_eq!(
|
|
|
|
jitterbuffer_stats.get::<u64>("num-pushed").unwrap(),
|
|
|
|
n_packets
|
|
|
|
);
|
2023-11-27 13:29:07 +00:00
|
|
|
assert_eq!(jitterbuffer_stats.get::<i32>("pt").unwrap(), TEST_PT as i32);
|
|
|
|
assert_eq!(
|
|
|
|
jitterbuffer_stats.get::<i32>("ssrc").unwrap(),
|
|
|
|
TEST_SSRC as i32
|
|
|
|
);
|
2023-10-12 06:06:48 +00:00
|
|
|
}
|
2024-01-16 05:52:21 +00:00
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
static PACKETS_TEST_1: [PacketInfo; 2] = [
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: 500,
|
|
|
|
rtp_ts: 20,
|
|
|
|
payload_len: 13,
|
|
|
|
},
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: 501,
|
|
|
|
rtp_ts: 30,
|
|
|
|
payload_len: 7,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
2024-01-16 05:52:21 +00:00
|
|
|
#[test]
|
2024-06-12 07:52:41 +00:00
|
|
|
fn test_receive() {
|
2024-01-16 05:52:21 +00:00
|
|
|
init();
|
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
let h = receive_init();
|
|
|
|
receive_push(h.clone(), PACKETS_TEST_1, false);
|
|
|
|
receive_pull(h.clone(), PACKETS_TEST_1);
|
|
|
|
receive_check_stats(h, PACKETS_TEST_1);
|
|
|
|
}
|
2024-01-16 05:52:21 +00:00
|
|
|
|
2024-06-12 07:52:41 +00:00
|
|
|
#[test]
|
|
|
|
fn test_receive_list() {
|
|
|
|
init();
|
|
|
|
|
|
|
|
let h = receive_init();
|
|
|
|
receive_push(h.clone(), PACKETS_TEST_1, true);
|
|
|
|
receive_pull(h.clone(), PACKETS_TEST_1);
|
|
|
|
receive_check_stats(h, PACKETS_TEST_1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_receive_flush() {
|
|
|
|
init();
|
|
|
|
let h = receive_init();
|
|
|
|
|
|
|
|
receive_push(h.clone(), PACKETS_TEST_1, false);
|
2024-01-16 05:52:21 +00:00
|
|
|
|
|
|
|
let mut inner = h.lock().unwrap();
|
|
|
|
let seqnum = gst::Seqnum::next();
|
|
|
|
inner.push_event(gst::event::FlushStart::builder().seqnum(seqnum).build());
|
|
|
|
inner.push_event(gst::event::FlushStop::builder(false).seqnum(seqnum).build());
|
|
|
|
|
|
|
|
let event = inner.pull_event().unwrap();
|
|
|
|
let gst::EventView::FlushStart(fs) = event.view() else {
|
|
|
|
unreachable!();
|
|
|
|
};
|
|
|
|
assert_eq!(fs.seqnum(), seqnum);
|
|
|
|
let event = inner.pull_event().unwrap();
|
|
|
|
let gst::EventView::FlushStop(fs) = event.view() else {
|
|
|
|
unreachable!();
|
|
|
|
};
|
|
|
|
assert_eq!(fs.seqnum(), seqnum);
|
|
|
|
}
|
2024-06-12 07:52:41 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_receive_benchmark() {
|
|
|
|
init();
|
|
|
|
let clock = gst::SystemClock::obtain();
|
2024-06-14 07:17:04 +00:00
|
|
|
//const N_PACKETS: usize = 1024 * 1024;
|
|
|
|
const N_PACKETS: usize = 1024;
|
2024-06-12 07:52:41 +00:00
|
|
|
let mut packets = Vec::with_capacity(N_PACKETS);
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
packets.push(
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: (i % u16::MAX as usize) as u16,
|
|
|
|
rtp_ts: i as u32,
|
|
|
|
payload_len: 8,
|
|
|
|
}
|
2024-06-14 07:17:04 +00:00
|
|
|
.generate_buffer(None),
|
2024-06-12 07:52:41 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
let h = receive_init();
|
|
|
|
let inner = h.lock().unwrap();
|
|
|
|
let push_pad = inner
|
|
|
|
.element()
|
|
|
|
.unwrap()
|
|
|
|
.static_pad("rtp_sink_0")
|
|
|
|
.unwrap()
|
|
|
|
.peer()
|
|
|
|
.unwrap();
|
|
|
|
drop(inner);
|
|
|
|
|
|
|
|
let start = clock.time();
|
|
|
|
|
|
|
|
for packet in packets {
|
|
|
|
push_pad.push(packet).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let pushed = clock.time();
|
|
|
|
|
|
|
|
let mut inner = h.lock().unwrap();
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
let buffer = inner.pull().unwrap();
|
|
|
|
let mapped = buffer.map_readable().unwrap();
|
|
|
|
let rtp = rtp_types::RtpPacket::parse(&mapped).unwrap();
|
|
|
|
assert_eq!(rtp.sequence_number(), (i % u16::MAX as usize) as u16);
|
|
|
|
}
|
|
|
|
|
|
|
|
let end = clock.time();
|
|
|
|
drop(inner);
|
|
|
|
|
|
|
|
let test_time = end.opt_sub(start);
|
|
|
|
let pull_time = end.opt_sub(pushed);
|
|
|
|
let push_time = pushed.opt_sub(start);
|
|
|
|
println!(
|
|
|
|
"test took {} (push {}, pull {})",
|
|
|
|
test_time.display(),
|
|
|
|
push_time.display(),
|
|
|
|
pull_time.display()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_receive_list_benchmark() {
|
|
|
|
init();
|
|
|
|
let clock = gst::SystemClock::obtain();
|
|
|
|
const N_PACKETS: usize = 32 * 1024;
|
|
|
|
const N_PUSHES: usize = 1024 / 32;
|
|
|
|
let h = receive_init();
|
|
|
|
let inner = h.lock().unwrap();
|
|
|
|
|
|
|
|
let push_pad = inner
|
|
|
|
.element()
|
|
|
|
.unwrap()
|
|
|
|
.static_pad("rtp_sink_0")
|
|
|
|
.unwrap()
|
|
|
|
.peer()
|
|
|
|
.unwrap();
|
|
|
|
drop(inner);
|
|
|
|
|
|
|
|
let mut lists = Vec::with_capacity(N_PUSHES);
|
|
|
|
for p in 0..N_PUSHES {
|
|
|
|
let mut list = gst::BufferList::new();
|
|
|
|
let list_mut = list.make_mut();
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
list_mut.add(
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: ((p * N_PACKETS + i) % u16::MAX as usize) as u16,
|
|
|
|
rtp_ts: (p * N_PACKETS + i) as u32,
|
|
|
|
payload_len: 8,
|
|
|
|
}
|
2024-06-14 07:17:04 +00:00
|
|
|
.generate_buffer(None),
|
2024-06-12 07:52:41 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
lists.push(list);
|
|
|
|
}
|
|
|
|
|
|
|
|
let start = clock.time();
|
|
|
|
|
|
|
|
for list in lists {
|
|
|
|
push_pad.push_list(list).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let pushed = clock.time();
|
|
|
|
|
|
|
|
let mut inner = h.lock().unwrap();
|
|
|
|
for p in 0..N_PUSHES {
|
|
|
|
for i in 0..N_PACKETS {
|
|
|
|
let buffer = inner.pull().unwrap();
|
|
|
|
let mapped = buffer.map_readable().unwrap();
|
|
|
|
let rtp = rtp_types::RtpPacket::parse(&mapped).unwrap();
|
2024-06-14 07:17:04 +00:00
|
|
|
assert_eq!(
|
|
|
|
rtp.sequence_number(),
|
|
|
|
((p * N_PACKETS + i) % u16::MAX as usize) as u16
|
|
|
|
);
|
2024-06-12 07:52:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let end = clock.time();
|
|
|
|
drop(inner);
|
|
|
|
|
|
|
|
let test_time = end.opt_sub(start);
|
|
|
|
let pull_time = end.opt_sub(pushed);
|
|
|
|
let push_time = pushed.opt_sub(start);
|
|
|
|
println!(
|
|
|
|
"test took {} (push {}, pull {})",
|
|
|
|
test_time.display(),
|
|
|
|
push_time.display(),
|
|
|
|
pull_time.display()
|
|
|
|
);
|
|
|
|
}
|
2024-06-14 07:17:04 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn recv_release_sink_pad() {
|
|
|
|
init();
|
|
|
|
|
|
|
|
let id = next_element_counter();
|
|
|
|
|
|
|
|
let elem = gst::ElementFactory::make("rtprecv")
|
|
|
|
.property("rtp-id", id.to_string())
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
elem.set_state(gst::State::Playing).unwrap();
|
|
|
|
let sinkpad = elem.request_pad_simple("rtp_sink_0").unwrap();
|
|
|
|
let stream_start = gst::event::StreamStart::new("random");
|
|
|
|
sinkpad.send_event(stream_start);
|
|
|
|
let caps = Caps::builder("application/x-rtp")
|
|
|
|
.field("media", "audio")
|
|
|
|
.field("payload", TEST_PT as i32)
|
|
|
|
.field("clock-rate", TEST_CLOCK_RATE as i32)
|
|
|
|
.field("encoding-name", "custom-test")
|
|
|
|
.build();
|
|
|
|
sinkpad.send_event(gst::event::Caps::new(&caps));
|
|
|
|
let segment = gst::FormattedSegment::<gst::ClockTime>::new();
|
|
|
|
sinkpad.send_event(gst::event::Segment::new(&segment));
|
|
|
|
|
|
|
|
let (sender, recv) = std::sync::mpsc::sync_channel(1);
|
|
|
|
elem.connect_pad_added({
|
|
|
|
let sender = sender.clone();
|
|
|
|
move |_elem, pad| {
|
|
|
|
let other_pad = gst::Pad::builder(gst::PadDirection::Sink)
|
|
|
|
.chain_function(|_pad, _parent, _buffer| Ok(gst::FlowSuccess::Ok))
|
|
|
|
.build();
|
|
|
|
other_pad.set_active(true).unwrap();
|
|
|
|
pad.link(&other_pad).unwrap();
|
|
|
|
sender.send(other_pad).unwrap();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// push two buffers to get past the rtpsource validation
|
|
|
|
sinkpad
|
|
|
|
.chain(
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: 30,
|
|
|
|
rtp_ts: 10,
|
|
|
|
payload_len: 4,
|
|
|
|
}
|
|
|
|
.generate_buffer(Some(gst::ClockTime::from_mseconds(50))),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
sinkpad
|
|
|
|
.chain(
|
|
|
|
PacketInfo {
|
|
|
|
seq_no: 31,
|
|
|
|
rtp_ts: 10,
|
|
|
|
payload_len: 4,
|
|
|
|
}
|
|
|
|
.generate_buffer(Some(gst::ClockTime::from_mseconds(100))),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
let _other_pad = recv.recv().unwrap();
|
|
|
|
|
|
|
|
elem.release_request_pad(&sinkpad);
|
|
|
|
elem.set_state(gst::State::Null).unwrap();
|
|
|
|
}
|