mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git
synced 2024-11-25 19:11:06 +00:00
gstreamer: bin: more generic {add,remove}_many() API
No longer enforces to pass an array of references. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1230>
This commit is contained in:
parent
0bd6e07346
commit
c4a968a403
25 changed files with 49 additions and 33 deletions
|
@ -46,7 +46,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
pipeline.add_many(&[&src, appsink.upcast_ref()])?;
|
pipeline.add_many([&src, appsink.upcast_ref()])?;
|
||||||
src.link(&appsink)?;
|
src.link(&appsink)?;
|
||||||
|
|
||||||
// Getting data out of the appsink is done by setting callbacks on it.
|
// Getting data out of the appsink is done by setting callbacks on it.
|
||||||
|
|
|
@ -49,7 +49,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
|
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
|
||||||
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[appsrc.upcast_ref(), &videoconvert, &sink])?;
|
pipeline.add_many([appsrc.upcast_ref(), &videoconvert, &sink])?;
|
||||||
gst::Element::link_many(&[appsrc.upcast_ref(), &videoconvert, &sink])?;
|
gst::Element::link_many(&[appsrc.upcast_ref(), &videoconvert, &sink])?;
|
||||||
|
|
||||||
// Our frame counter, that is stored in the mutable environment
|
// Our frame counter, that is stored in the mutable environment
|
||||||
|
|
|
@ -576,7 +576,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
|
|
||||||
comp.set_property("background-color", 0xff_33_33_33u32);
|
comp.set_property("background-color", 0xff_33_33_33u32);
|
||||||
|
|
||||||
pipeline.add_many(&[&src1, &src2, comp.upcast_ref(), &conv, &sink])?;
|
pipeline.add_many([&src1, &src2, comp.upcast_ref(), &conv, &sink])?;
|
||||||
|
|
||||||
// Link everything together.
|
// Link everything together.
|
||||||
src1.link_filtered(
|
src1.link_filtered(
|
||||||
|
|
|
@ -70,7 +70,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
.build()?;
|
.build()?;
|
||||||
let decodebin = gst::ElementFactory::make("decodebin").build()?;
|
let decodebin = gst::ElementFactory::make("decodebin").build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &decodebin])?;
|
pipeline.add_many([&src, &decodebin])?;
|
||||||
gst::Element::link_many(&[&src, &decodebin])?;
|
gst::Element::link_many(&[&src, &decodebin])?;
|
||||||
|
|
||||||
// Need to move a new reference into the closure.
|
// Need to move a new reference into the closure.
|
||||||
|
|
|
@ -97,7 +97,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
configure_encodebin(&encodebin);
|
configure_encodebin(&encodebin);
|
||||||
|
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[&src, &encodebin, &sink])
|
.add_many([&src, &encodebin, &sink])
|
||||||
.expect("failed to add elements to pipeline");
|
.expect("failed to add elements to pipeline");
|
||||||
// It is clear from the start, that encodebin has only one src pad, so we can
|
// It is clear from the start, that encodebin has only one src pad, so we can
|
||||||
// directly link it to our filesink without problems.
|
// directly link it to our filesink without problems.
|
||||||
|
|
|
@ -45,7 +45,7 @@ fn create_receiver_pipeline(
|
||||||
let queue = gst::ElementFactory::make("queue").build()?;
|
let queue = gst::ElementFactory::make("queue").build()?;
|
||||||
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[src.upcast_ref(), &filter, &convert, &queue, &sink])?;
|
pipeline.add_many([src.upcast_ref(), &filter, &convert, &queue, &sink])?;
|
||||||
gst::Element::link_many(&[src.upcast_ref(), &filter, &convert, &queue, &sink])?;
|
gst::Element::link_many(&[src.upcast_ref(), &filter, &convert, &queue, &sink])?;
|
||||||
|
|
||||||
let fd_allocator = gst_allocators::FdAllocator::new();
|
let fd_allocator = gst_allocators::FdAllocator::new();
|
||||||
|
@ -115,7 +115,7 @@ fn create_sender_pipeline(
|
||||||
.ok_or_else(|| anyhow::anyhow!("is not a appsink"))?
|
.ok_or_else(|| anyhow::anyhow!("is not a appsink"))?
|
||||||
.set_caps(Some(&caps));
|
.set_caps(Some(&caps));
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &sink])?;
|
pipeline.add_many([&src, &sink])?;
|
||||||
gst::Element::link_many(&[&src, &sink])?;
|
gst::Element::link_many(&[&src, &sink])?;
|
||||||
|
|
||||||
let appsink = sink
|
let appsink = sink
|
||||||
|
|
|
@ -49,7 +49,7 @@ fn create_ui(app: >k::Application) {
|
||||||
(sink, widget)
|
(sink, widget)
|
||||||
};
|
};
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &sink]).unwrap();
|
pipeline.add_many([&src, &sink]).unwrap();
|
||||||
src.link(&sink).unwrap();
|
src.link(&sink).unwrap();
|
||||||
|
|
||||||
// Create a simple gtk gui window to place our widget into.
|
// Create a simple gtk gui window to place our widget into.
|
||||||
|
|
|
@ -103,7 +103,7 @@ fn create_ui(app: >k::Application) {
|
||||||
// specific. This example supports Linux and Mac (using X11 and Quartz).
|
// specific. This example supports Linux and Mac (using X11 and Quartz).
|
||||||
let sink = create_video_sink();
|
let sink = create_video_sink();
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &sink]).unwrap();
|
pipeline.add_many([&src, &sink]).unwrap();
|
||||||
src.link(&sink).unwrap();
|
src.link(&sink).unwrap();
|
||||||
|
|
||||||
// First, we create our gtk window - which will contain a region where
|
// First, we create our gtk window - which will contain a region where
|
||||||
|
|
|
@ -77,7 +77,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
|
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
|
||||||
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
pipeline.add_many([&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
||||||
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
||||||
|
|
||||||
// The PangoFontMap represents the set of fonts available for a particular rendering system.
|
// The PangoFontMap represents the set of fonts available for a particular rendering system.
|
||||||
|
|
|
@ -76,7 +76,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
|
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
|
||||||
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
let sink = gst::ElementFactory::make("autovideosink").build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
pipeline.add_many([&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
||||||
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
|
||||||
|
|
||||||
// The PangoFontMap represents the set of fonts available for a particular rendering system.
|
// The PangoFontMap represents the set of fonts available for a particular rendering system.
|
||||||
|
|
|
@ -114,7 +114,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
.property("caps", &video_caps)
|
.property("caps", &video_caps)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &netsim, &rtpbin, &depay, &dec, &conv, &scale, &filter])?;
|
pipeline.add_many([&src, &netsim, &rtpbin, &depay, &dec, &conv, &scale, &filter])?;
|
||||||
gst::Element::link_many(&[&depay, &dec, &conv, &scale, &filter])?;
|
gst::Element::link_many(&[&depay, &dec, &conv, &scale, &filter])?;
|
||||||
|
|
||||||
match args[1].as_str() {
|
match args[1].as_str() {
|
||||||
|
@ -132,7 +132,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
.property("location", "out.mkv")
|
.property("location", "out.mkv")
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&enc, &mux, &sink])?;
|
pipeline.add_many([&enc, &mux, &sink])?;
|
||||||
gst::Element::link_many(&[&filter, &enc, &mux, &sink])?;
|
gst::Element::link_many(&[&filter, &enc, &mux, &sink])?;
|
||||||
eprintln!("Recording to out.mkv");
|
eprintln!("Recording to out.mkv");
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
.property("sync", true)
|
.property("sync", true)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &conv, &q1, &enc, &q2, &pay, &rtpbin, &sink])?;
|
pipeline.add_many([&src, &conv, &q1, &enc, &q2, &pay, &rtpbin, &sink])?;
|
||||||
|
|
||||||
conv.link(&q1)?;
|
conv.link(&q1)?;
|
||||||
q1.link(&enc)?;
|
q1.link(&enc)?;
|
||||||
|
|
|
@ -137,7 +137,7 @@ mod media_factory {
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
bin.add_many(&[&src, &enc, &pay]).unwrap();
|
bin.add_many([&src, &enc, &pay]).unwrap();
|
||||||
gst::Element::link_many(&[&src, &enc, &pay]).unwrap();
|
gst::Element::link_many(&[&src, &enc, &pay]).unwrap();
|
||||||
|
|
||||||
Some(bin.upcast())
|
Some(bin.upcast())
|
||||||
|
|
|
@ -248,7 +248,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
let conv = gst::ElementFactory::make("audioconvert").build()?;
|
let conv = gst::ElementFactory::make("audioconvert").build()?;
|
||||||
let sink = gst::ElementFactory::make("autoaudiosink").build()?;
|
let sink = gst::ElementFactory::make("autoaudiosink").build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, filter.upcast_ref(), &conv, &sink])?;
|
pipeline.add_many([&src, filter.upcast_ref(), &conv, &sink])?;
|
||||||
src.link(&filter)?;
|
src.link(&filter)?;
|
||||||
filter.link(&conv)?;
|
filter.link(&conv)?;
|
||||||
conv.link(&sink)?;
|
conv.link(&sink)?;
|
||||||
|
|
|
@ -34,7 +34,7 @@ fn example_main() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let decodebin = gst::ElementFactory::make("decodebin").build().unwrap();
|
let decodebin = gst::ElementFactory::make("decodebin").build().unwrap();
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &decodebin]).unwrap();
|
pipeline.add_many([&src, &decodebin]).unwrap();
|
||||||
gst::Element::link_many(&[&src, &decodebin]).unwrap();
|
gst::Element::link_many(&[&src, &decodebin]).unwrap();
|
||||||
|
|
||||||
// Need to move a new reference into the closure.
|
// Need to move a new reference into the closure.
|
||||||
|
|
|
@ -66,7 +66,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
// Increase the queue capacity to 100MB to avoid a stalling pipeline
|
// Increase the queue capacity to 100MB to avoid a stalling pipeline
|
||||||
|
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[&src, &typefinder, &queue, &muxer, &sink])
|
.add_many([&src, &typefinder, &queue, &muxer, &sink])
|
||||||
.expect("failed to add elements to pipeline");
|
.expect("failed to add elements to pipeline");
|
||||||
|
|
||||||
src.link(&typefinder)?;
|
src.link(&typefinder)?;
|
||||||
|
|
|
@ -555,7 +555,7 @@ impl App {
|
||||||
if let Some(gl_element) = gl_element {
|
if let Some(gl_element) = gl_element {
|
||||||
let glupload = gst::ElementFactory::make("glupload").build()?;
|
let glupload = gst::ElementFactory::make("glupload").build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &glupload])?;
|
pipeline.add_many([&src, &glupload])?;
|
||||||
pipeline.add(gl_element)?;
|
pipeline.add(gl_element)?;
|
||||||
pipeline.add(&appsink)?;
|
pipeline.add(&appsink)?;
|
||||||
|
|
||||||
|
@ -569,7 +569,7 @@ impl App {
|
||||||
.property("sink", &appsink)
|
.property("sink", &appsink)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &sink])?;
|
pipeline.add_many([&src, &sink])?;
|
||||||
src.link(&sink)?;
|
src.link(&sink)?;
|
||||||
|
|
||||||
Ok((pipeline, appsink))
|
Ok((pipeline, appsink))
|
||||||
|
|
|
@ -23,9 +23,16 @@ impl Bin {
|
||||||
|
|
||||||
pub trait GstBinExtManual: 'static {
|
pub trait GstBinExtManual: 'static {
|
||||||
#[doc(alias = "gst_bin_add_many")]
|
#[doc(alias = "gst_bin_add_many")]
|
||||||
fn add_many<E: IsA<Element>>(&self, elements: &[&E]) -> Result<(), glib::BoolError>;
|
fn add_many(
|
||||||
|
&self,
|
||||||
|
elements: impl IntoIterator<Item = impl AsRef<Element>>,
|
||||||
|
) -> Result<(), glib::BoolError>;
|
||||||
|
|
||||||
#[doc(alias = "gst_bin_remove_many")]
|
#[doc(alias = "gst_bin_remove_many")]
|
||||||
fn remove_many<E: IsA<Element>>(&self, elements: &[&E]) -> Result<(), glib::BoolError>;
|
fn remove_many(
|
||||||
|
&self,
|
||||||
|
elements: impl IntoIterator<Item = impl AsRef<Element>>,
|
||||||
|
) -> Result<(), glib::BoolError>;
|
||||||
|
|
||||||
#[doc(alias = "do-latency")]
|
#[doc(alias = "do-latency")]
|
||||||
fn connect_do_latency<F: Fn(&Self) -> Result<(), LoggableError> + Send + Sync + 'static>(
|
fn connect_do_latency<F: Fn(&Self) -> Result<(), LoggableError> + Send + Sync + 'static>(
|
||||||
|
@ -78,7 +85,10 @@ pub trait GstBinExtManual: 'static {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: IsA<Bin>> GstBinExtManual for O {
|
impl<O: IsA<Bin>> GstBinExtManual for O {
|
||||||
fn add_many<E: IsA<Element>>(&self, elements: &[&E]) -> Result<(), glib::BoolError> {
|
fn add_many(
|
||||||
|
&self,
|
||||||
|
elements: impl IntoIterator<Item = impl AsRef<Element>>,
|
||||||
|
) -> Result<(), glib::BoolError> {
|
||||||
for e in elements {
|
for e in elements {
|
||||||
unsafe {
|
unsafe {
|
||||||
glib::result_from_gboolean!(
|
glib::result_from_gboolean!(
|
||||||
|
@ -91,7 +101,10 @@ impl<O: IsA<Bin>> GstBinExtManual for O {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_many<E: IsA<Element>>(&self, elements: &[&E]) -> Result<(), glib::BoolError> {
|
fn remove_many(
|
||||||
|
&self,
|
||||||
|
elements: impl IntoIterator<Item = impl AsRef<Element>>,
|
||||||
|
) -> Result<(), glib::BoolError> {
|
||||||
for e in elements {
|
for e in elements {
|
||||||
unsafe {
|
unsafe {
|
||||||
glib::result_from_gboolean!(
|
glib::result_from_gboolean!(
|
||||||
|
|
|
@ -733,7 +733,7 @@ mod tests {
|
||||||
let sink = ElementFactory::make("fakesink").build().unwrap();
|
let sink = ElementFactory::make("fakesink").build().unwrap();
|
||||||
|
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[&src, element.upcast_ref(), &sink])
|
.add_many([&src, element.upcast_ref(), &sink])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Element::link_many(&[&src, element.upcast_ref(), &sink]).unwrap();
|
Element::link_many(&[&src, element.upcast_ref(), &sink]).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ fn tutorial_main() {
|
||||||
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
||||||
|
|
||||||
// Build the pipeline
|
// Build the pipeline
|
||||||
pipeline.add_many(&[&source, &sink]).unwrap();
|
pipeline.add_many([&source, &sink]).unwrap();
|
||||||
source.link(&sink).expect("Elements could not be linked.");
|
source.link(&sink).expect("Elements could not be linked.");
|
||||||
|
|
||||||
// Start playing
|
// Start playing
|
||||||
|
|
|
@ -7,8 +7,7 @@ fn tutorial_main() {
|
||||||
// Initialize GStreamer
|
// Initialize GStreamer
|
||||||
gst::init().unwrap();
|
gst::init().unwrap();
|
||||||
|
|
||||||
let uri =
|
let uri = "http://desmottes.be/~cassidy/files/brol/test.mkv";
|
||||||
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
|
|
||||||
|
|
||||||
// Create the elements
|
// Create the elements
|
||||||
let source = gst::ElementFactory::make("uridecodebin")
|
let source = gst::ElementFactory::make("uridecodebin")
|
||||||
|
@ -36,7 +35,7 @@ fn tutorial_main() {
|
||||||
// Build the pipeline Note that we are NOT linking the source at this
|
// Build the pipeline Note that we are NOT linking the source at this
|
||||||
// point. We will do it later.
|
// point. We will do it later.
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[&source, &convert, &resample, &sink])
|
.add_many([&source, &convert, &resample, &sink])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
gst::Element::link_many(&[&convert, &resample, &sink]).expect("Elements could not be linked.");
|
gst::Element::link_many(&[&convert, &resample, &sink]).expect("Elements could not be linked.");
|
||||||
|
|
||||||
|
@ -44,6 +43,10 @@ fn tutorial_main() {
|
||||||
source.connect_pad_added(move |src, src_pad| {
|
source.connect_pad_added(move |src, src_pad| {
|
||||||
println!("Received new pad {} from {}", src_pad.name(), src.name());
|
println!("Received new pad {} from {}", src_pad.name(), src.name());
|
||||||
|
|
||||||
|
src.downcast_ref::<gst::Bin>()
|
||||||
|
.unwrap()
|
||||||
|
.debug_to_dot_file_with_ts(gst::DebugGraphDetails::all(), "pad-added");
|
||||||
|
|
||||||
let sink_pad = convert
|
let sink_pad = convert
|
||||||
.static_pad("sink")
|
.static_pad("sink")
|
||||||
.expect("Failed to get static sink pad from convert");
|
.expect("Failed to get static sink pad from convert");
|
||||||
|
|
|
@ -103,7 +103,7 @@ fn tutorial_main() {
|
||||||
// Create the empty pipeline
|
// Create the empty pipeline
|
||||||
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
||||||
|
|
||||||
pipeline.add_many(&[&source, &sink]).unwrap();
|
pipeline.add_many([&source, &sink]).unwrap();
|
||||||
source.link(&sink).expect("Elements could not be linked.");
|
source.link(&sink).expect("Elements could not be linked.");
|
||||||
|
|
||||||
// Print initial negotiated caps (in NULL state)
|
// Print initial negotiated caps (in NULL state)
|
||||||
|
|
|
@ -57,7 +57,7 @@ fn tutorial_main() {
|
||||||
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
||||||
|
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[
|
.add_many([
|
||||||
&audio_source,
|
&audio_source,
|
||||||
&tee,
|
&tee,
|
||||||
&audio_queue,
|
&audio_queue,
|
||||||
|
|
|
@ -110,7 +110,7 @@ fn main() {
|
||||||
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
let pipeline = gst::Pipeline::builder().name("test-pipeline").build();
|
||||||
|
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[
|
.add_many([
|
||||||
appsrc.upcast_ref(),
|
appsrc.upcast_ref(),
|
||||||
&tee,
|
&tee,
|
||||||
&audio_queue,
|
&audio_queue,
|
||||||
|
|
|
@ -28,7 +28,7 @@ fn tutorial_main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Create the sink bin, add the elements and link them
|
// Create the sink bin, add the elements and link them
|
||||||
let bin = gst::Bin::builder().name("audio_sink_bin").build();
|
let bin = gst::Bin::builder().name("audio_sink_bin").build();
|
||||||
bin.add_many(&[&equalizer, &convert, &sink]).unwrap();
|
bin.add_many([&equalizer, &convert, &sink]).unwrap();
|
||||||
gst::Element::link_many(&[&equalizer, &convert, &sink]).expect("Failed to link elements.");
|
gst::Element::link_many(&[&equalizer, &convert, &sink]).expect("Failed to link elements.");
|
||||||
|
|
||||||
let pad = equalizer
|
let pad = equalizer
|
||||||
|
|
Loading…
Reference in a new issue