2018-04-05 17:28:20 +00:00
|
|
|
// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
|
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Library General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Library General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Library General Public
|
|
|
|
// License along with this library; if not, write to the
|
|
|
|
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
|
|
|
// Boston, MA 02110-1335, USA.
|
2022-01-15 19:18:47 +00:00
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
2018-04-05 17:28:20 +00:00
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
use gst::glib;
|
|
|
|
use gst::prelude::*;
|
|
|
|
|
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
|
2018-04-05 17:28:20 +00:00
|
|
|
use std::net;
|
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
|
|
use std::{env, thread, time};
|
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|
|
|
gst::DebugCategory::new(
|
|
|
|
"ts-udpsrc-benchmark-sender",
|
|
|
|
gst::DebugColorFlags::empty(),
|
|
|
|
Some("Thread-sharing UDP src benchmark sender"),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
2018-04-05 17:28:20 +00:00
|
|
|
fn main() {
|
2022-11-03 11:33:39 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
gstthreadshare::plugin_register_static().unwrap();
|
|
|
|
|
2018-04-05 17:28:20 +00:00
|
|
|
let args = env::args().collect::<Vec<_>>();
|
2022-03-21 11:25:48 +00:00
|
|
|
assert!(args.len() > 1);
|
2018-04-05 17:28:20 +00:00
|
|
|
let n_streams: u16 = args[1].parse().unwrap();
|
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
let num_buffers: Option<i32> = if args.len() > 3 {
|
|
|
|
args[3].parse().ok()
|
2022-03-21 11:25:48 +00:00
|
|
|
} else {
|
2022-11-03 11:33:39 +00:00
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
if args.len() > 2 {
|
|
|
|
match args[2].as_str() {
|
|
|
|
"raw" => send_raw_buffers(n_streams),
|
|
|
|
"rtp" => send_rtp_buffers(n_streams, num_buffers),
|
|
|
|
_ => send_test_buffers(n_streams, num_buffers),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
send_test_buffers(n_streams, num_buffers);
|
2022-03-21 11:25:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send_raw_buffers(n_streams: u16) {
|
2018-04-05 17:28:20 +00:00
|
|
|
let buffer = [0; 160];
|
|
|
|
let socket = net::UdpSocket::bind("0.0.0.0:0").unwrap();
|
|
|
|
|
|
|
|
let ipaddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
2022-11-03 11:33:39 +00:00
|
|
|
let destinations = (5004..(5004 + n_streams))
|
2018-04-05 17:28:20 +00:00
|
|
|
.map(|port| SocketAddr::new(ipaddr, port))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let wait = time::Duration::from_millis(20);
|
|
|
|
|
|
|
|
thread::sleep(time::Duration::from_millis(1000));
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let now = time::Instant::now();
|
|
|
|
|
|
|
|
for dest in &destinations {
|
|
|
|
socket.send_to(&buffer, dest).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let elapsed = now.elapsed();
|
|
|
|
if elapsed < wait {
|
|
|
|
thread::sleep(wait - elapsed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-03-21 11:25:48 +00:00
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
fn send_test_buffers(n_streams: u16, num_buffers: Option<i32>) {
|
|
|
|
let pipeline = gst::Pipeline::default();
|
|
|
|
for i in 0..n_streams {
|
|
|
|
let src = gst::ElementFactory::make("ts-audiotestsrc")
|
2023-01-25 08:23:46 +00:00
|
|
|
.name(format!("ts-audiotestsrc-{i}").as_str())
|
2022-11-03 11:33:39 +00:00
|
|
|
.property("context-wait", 20u32)
|
|
|
|
.property("is-live", true)
|
|
|
|
.property("do-timestamp", true)
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
2022-03-21 11:25:48 +00:00
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
if let Some(num_buffers) = num_buffers {
|
|
|
|
src.set_property("num-buffers", num_buffers);
|
2022-03-21 11:25:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
#[cfg(feature = "tuning")]
|
|
|
|
if i == 0 {
|
|
|
|
src.set_property("main-elem", true);
|
2022-03-21 11:25:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
let sink = gst::ElementFactory::make("ts-udpsink")
|
2023-01-25 08:23:46 +00:00
|
|
|
.name(format!("udpsink-{i}").as_str())
|
2022-11-03 11:33:39 +00:00
|
|
|
.property("clients", format!("127.0.0.1:{}", i + 5004))
|
|
|
|
.property("context-wait", 20u32)
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let elements = &[&src, &sink];
|
|
|
|
pipeline.add_many(elements).unwrap();
|
|
|
|
gst::Element::link_many(elements).unwrap();
|
2022-03-21 11:25:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
run(pipeline);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send_rtp_buffers(n_streams: u16, num_buffers: Option<i32>) {
|
2022-10-22 16:06:29 +00:00
|
|
|
let pipeline = gst::Pipeline::default();
|
2022-03-21 11:25:48 +00:00
|
|
|
for i in 0..n_streams {
|
2022-11-03 11:33:39 +00:00
|
|
|
let src = gst::ElementFactory::make("ts-audiotestsrc")
|
2023-01-25 08:23:46 +00:00
|
|
|
.name(format!("ts-audiotestsrc-{i}").as_str())
|
2022-11-03 11:33:39 +00:00
|
|
|
.property("context-wait", 20u32)
|
|
|
|
.property("is-live", true)
|
|
|
|
.property("do-timestamp", true)
|
2022-10-19 16:18:43 +00:00
|
|
|
.build()
|
|
|
|
.unwrap();
|
2022-11-03 11:33:39 +00:00
|
|
|
|
|
|
|
if let Some(num_buffers) = num_buffers {
|
|
|
|
src.set_property("num-buffers", num_buffers);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "tuning")]
|
|
|
|
if i == 0 {
|
|
|
|
src.set_property("main-elem", true);
|
|
|
|
}
|
2022-03-21 11:25:48 +00:00
|
|
|
|
2022-10-19 16:18:43 +00:00
|
|
|
let enc = gst::ElementFactory::make("alawenc")
|
2023-01-25 08:23:46 +00:00
|
|
|
.name(format!("alawenc-{i}").as_str())
|
2022-10-19 16:18:43 +00:00
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
let pay = gst::ElementFactory::make("rtppcmapay")
|
2023-01-25 08:23:46 +00:00
|
|
|
.name(format!("rtppcmapay-{i}").as_str())
|
2022-10-19 16:18:43 +00:00
|
|
|
.build()
|
|
|
|
.unwrap();
|
2022-11-03 11:33:39 +00:00
|
|
|
|
2022-10-19 16:18:43 +00:00
|
|
|
let sink = gst::ElementFactory::make("ts-udpsink")
|
2023-01-25 08:23:46 +00:00
|
|
|
.name(format!("udpsink-{i}").as_str())
|
2022-10-19 16:18:43 +00:00
|
|
|
.property("context-wait", 20u32)
|
2022-11-03 11:33:39 +00:00
|
|
|
.property("clients", format!("127.0.0.1:{}", i + 5004))
|
2022-10-19 16:18:43 +00:00
|
|
|
.build()
|
2022-03-21 11:25:48 +00:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let elements = &[&src, &enc, &pay, &sink];
|
|
|
|
pipeline.add_many(elements).unwrap();
|
|
|
|
gst::Element::link_many(elements).unwrap();
|
|
|
|
}
|
|
|
|
|
2022-11-03 11:33:39 +00:00
|
|
|
run(pipeline);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run(pipeline: gst::Pipeline) {
|
|
|
|
let l = glib::MainLoop::new(None, false);
|
|
|
|
|
|
|
|
let bus = pipeline.bus().unwrap();
|
|
|
|
let l_clone = l.clone();
|
2023-04-14 09:46:43 +00:00
|
|
|
let _bus_watch = bus
|
|
|
|
.add_watch(move |_, msg| {
|
|
|
|
use gst::MessageView;
|
|
|
|
match msg.view() {
|
|
|
|
MessageView::Eos(_) => {
|
|
|
|
gst::info!(CAT, "Received eos");
|
|
|
|
l_clone.quit();
|
|
|
|
|
|
|
|
glib::Continue(false)
|
|
|
|
}
|
|
|
|
MessageView::Error(msg) => {
|
|
|
|
gst::error!(
|
|
|
|
CAT,
|
|
|
|
"Error from {:?}: {} ({:?})",
|
|
|
|
msg.src().map(|s| s.path_string()),
|
|
|
|
msg.error(),
|
|
|
|
msg.debug()
|
|
|
|
);
|
|
|
|
l_clone.quit();
|
|
|
|
|
|
|
|
glib::Continue(false)
|
|
|
|
}
|
|
|
|
_ => glib::Continue(true),
|
2022-11-03 11:33:39 +00:00
|
|
|
}
|
2023-04-14 09:46:43 +00:00
|
|
|
})
|
|
|
|
.expect("Failed to add bus watch");
|
2022-11-03 11:33:39 +00:00
|
|
|
|
2022-03-21 11:25:48 +00:00
|
|
|
pipeline.set_state(gst::State::Playing).unwrap();
|
|
|
|
l.run();
|
2022-11-03 11:33:39 +00:00
|
|
|
|
|
|
|
pipeline.set_state(gst::State::Null).unwrap();
|
2022-03-21 11:25:48 +00:00
|
|
|
}
|