forked from mirrors/gstreamer-rs
manual changes post ObjectExt improvements
This commit is contained in:
parent
0d009bca31
commit
213020165a
43 changed files with 421 additions and 516 deletions
|
@ -81,7 +81,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
gst::ElementFactory::make("decodebin", None).map_err(|_| MissingElement("decodebin"))?;
|
gst::ElementFactory::make("decodebin", None).map_err(|_| MissingElement("decodebin"))?;
|
||||||
|
|
||||||
// Tell the filesrc what file to load
|
// Tell the filesrc what file to load
|
||||||
src.set_property("location", uri)?;
|
src.set_property("location", uri);
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &decodebin])?;
|
pipeline.add_many(&[&src, &decodebin])?;
|
||||||
gst::Element::link_many(&[&src, &decodebin])?;
|
gst::Element::link_many(&[&src, &decodebin])?;
|
||||||
|
|
|
@ -76,9 +76,7 @@ fn configure_encodebin(encodebin: &gst::Element) {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Finally, apply the EncodingProfile onto our encodebin element.
|
// Finally, apply the EncodingProfile onto our encodebin element.
|
||||||
encodebin
|
encodebin.set_property("profile", &container_profile);
|
||||||
.set_property("profile", &container_profile)
|
|
||||||
.expect("set profile property failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn example_main() -> Result<(), Error> {
|
fn example_main() -> Result<(), Error> {
|
||||||
|
@ -104,10 +102,8 @@ fn example_main() -> Result<(), Error> {
|
||||||
let sink =
|
let sink =
|
||||||
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?;
|
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?;
|
||||||
|
|
||||||
src.set_property("uri", uri)
|
src.set_property("uri", uri);
|
||||||
.expect("setting URI Property failed");
|
sink.set_property("location", output_file);
|
||||||
sink.set_property("location", output_file)
|
|
||||||
.expect("setting location property failed");
|
|
||||||
|
|
||||||
// Configure the encodebin.
|
// Configure the encodebin.
|
||||||
// Here we tell the bin what format we expect it to create at its output.
|
// Here we tell the bin what format we expect it to create at its output.
|
||||||
|
|
|
@ -33,11 +33,11 @@ fn create_ui(app: >k::Application) {
|
||||||
// video frames to our texture (if they are not already in the GPU). Now we tell the OpenGL-sink
|
// video frames to our texture (if they are not already in the GPU). Now we tell the OpenGL-sink
|
||||||
// about our gtkglsink element, form where it will retrieve the OpenGL texture to fill.
|
// about our gtkglsink element, form where it will retrieve the OpenGL texture to fill.
|
||||||
let glsinkbin = gst::ElementFactory::make("glsinkbin", None).unwrap();
|
let glsinkbin = gst::ElementFactory::make("glsinkbin", None).unwrap();
|
||||||
glsinkbin.set_property("sink", >kglsink).unwrap();
|
glsinkbin.set_property("sink", >kglsink);
|
||||||
// The gtkglsink creates the gtk widget for us. This is accessible through a property.
|
// The gtkglsink creates the gtk widget for us. This is accessible through a property.
|
||||||
// So we get it and use it later to add it to our gui.
|
// So we get it and use it later to add it to our gui.
|
||||||
let widget = gtkglsink.property("widget").unwrap();
|
let widget = gtkglsink.property::<gtk::Widget>("widget");
|
||||||
(glsinkbin, widget.get::<gtk::Widget>().unwrap())
|
(glsinkbin, widget)
|
||||||
} else {
|
} else {
|
||||||
// Unfortunately, using the OpenGL widget didn't work out, so we will have to render
|
// Unfortunately, using the OpenGL widget didn't work out, so we will have to render
|
||||||
// our frames manually, using the CPU. An example why this may fail is, when
|
// our frames manually, using the CPU. An example why this may fail is, when
|
||||||
|
@ -45,8 +45,8 @@ fn create_ui(app: >k::Application) {
|
||||||
let sink = gst::ElementFactory::make("gtksink", None).unwrap();
|
let sink = gst::ElementFactory::make("gtksink", None).unwrap();
|
||||||
// The gtksink creates the gtk widget for us. This is accessible through a property.
|
// The gtksink creates the gtk widget for us. This is accessible through a property.
|
||||||
// So we get it and use it later to add it to our gui.
|
// So we get it and use it later to add it to our gui.
|
||||||
let widget = sink.property("widget").unwrap();
|
let widget = sink.property::<gtk::Widget>("widget");
|
||||||
(sink, widget.get::<gtk::Widget>().unwrap())
|
(sink, widget)
|
||||||
};
|
};
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &sink]).unwrap();
|
pipeline.add_many(&[&src, &sink]).unwrap();
|
||||||
|
|
|
@ -85,7 +85,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
.field("height", 800i32)
|
.field("height", 800i32)
|
||||||
.field("framerate", gst::Fraction::new(15, 1))
|
.field("framerate", gst::Fraction::new(15, 1))
|
||||||
.build();
|
.build();
|
||||||
capsfilter.set_property("caps", &caps).unwrap();
|
capsfilter.set_property("caps", &caps);
|
||||||
|
|
||||||
// The videotestsrc supports multiple test patterns. In this example, we will use the
|
// The videotestsrc supports multiple test patterns. In this example, we will use the
|
||||||
// pattern with a white ball moving around the video's center point.
|
// pattern with a white ball moving around the video's center point.
|
||||||
|
@ -131,8 +131,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
//
|
//
|
||||||
// In this case, the signal passes the gst::Element and a gst::Sample with
|
// In this case, the signal passes the gst::Element and a gst::Sample with
|
||||||
// the current buffer
|
// the current buffer
|
||||||
overlay
|
overlay.connect("draw", false, move |args| {
|
||||||
.connect("draw", false, move |args| {
|
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
let drawer = &drawer_clone;
|
let drawer = &drawer_clone;
|
||||||
|
@ -147,9 +146,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
let info = drawer.info.as_ref().unwrap();
|
let info = drawer.info.as_ref().unwrap();
|
||||||
let layout = drawer.layout.borrow();
|
let layout = drawer.layout.borrow();
|
||||||
|
|
||||||
let angle = 2.0
|
let angle = 2.0 * PI * (timestamp % (10 * gst::ClockTime::SECOND)).nseconds() as f64
|
||||||
* PI
|
|
||||||
* (timestamp % (10 * gst::ClockTime::SECOND)).nseconds() as f64
|
|
||||||
/ (10.0 * gst::ClockTime::SECOND.nseconds() as f64);
|
/ (10.0 * gst::ClockTime::SECOND.nseconds() as f64);
|
||||||
|
|
||||||
/* Create a gst::Buffer for Cairo to draw into */
|
/* Create a gst::Buffer for Cairo to draw into */
|
||||||
|
@ -167,7 +164,8 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
gst_video::VideoFormat::Bgra,
|
gst_video::VideoFormat::Bgra,
|
||||||
frame_width as u32,
|
frame_width as u32,
|
||||||
frame_height as u32,
|
frame_height as u32,
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let buffer = buffer.into_mapped_buffer_writable().unwrap();
|
let buffer = buffer.into_mapped_buffer_writable().unwrap();
|
||||||
let buffer = {
|
let buffer = {
|
||||||
|
@ -259,13 +257,19 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
* here */
|
* here */
|
||||||
let rect = gst_video::VideoOverlayRectangle::new_raw(
|
let rect = gst_video::VideoOverlayRectangle::new_raw(
|
||||||
&buffer,
|
&buffer,
|
||||||
0, 0, frame_width as u32, frame_height as u32,
|
0,
|
||||||
|
0,
|
||||||
|
frame_width as u32,
|
||||||
|
frame_height as u32,
|
||||||
gst_video::VideoOverlayFormatFlags::PREMULTIPLIED_ALPHA,
|
gst_video::VideoOverlayFormatFlags::PREMULTIPLIED_ALPHA,
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(gst_video::VideoOverlayComposition::new(Some(&rect)).unwrap().to_value())
|
Some(
|
||||||
})
|
gst_video::VideoOverlayComposition::new(Some(&rect))
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.to_value(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
|
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
|
||||||
// be called when the sink that we render to does not support resizing the image
|
// be called when the sink that we render to does not support resizing the image
|
||||||
|
@ -274,8 +278,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
// resize our canvas's size.
|
// resize our canvas's size.
|
||||||
// Another possibility for when this might happen is, when our video is a network
|
// Another possibility for when this might happen is, when our video is a network
|
||||||
// stream that dynamically changes resolution when enough bandwith is available.
|
// stream that dynamically changes resolution when enough bandwith is available.
|
||||||
overlay
|
overlay.connect("caps-changed", false, move |args| {
|
||||||
.connect("caps-changed", false, move |args| {
|
|
||||||
let _overlay = args[0].get::<gst::Element>().unwrap();
|
let _overlay = args[0].get::<gst::Element>().unwrap();
|
||||||
let caps = args[1].get::<gst::Caps>().unwrap();
|
let caps = args[1].get::<gst::Caps>().unwrap();
|
||||||
|
|
||||||
|
@ -283,8 +286,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
|
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(pipeline)
|
Ok(pipeline)
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
.field("width", 800i32)
|
.field("width", 800i32)
|
||||||
.field("height", 800i32)
|
.field("height", 800i32)
|
||||||
.build();
|
.build();
|
||||||
capsfilter.set_property("caps", &caps).unwrap();
|
capsfilter.set_property("caps", &caps);
|
||||||
|
|
||||||
// The videotestsrc supports multiple test patterns. In this example, we will use the
|
// The videotestsrc supports multiple test patterns. In this example, we will use the
|
||||||
// pattern with a white ball moving around the video's center point.
|
// pattern with a white ball moving around the video's center point.
|
||||||
|
@ -125,8 +125,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
// passed as array of glib::Value. For a documentation about the actual arguments
|
// passed as array of glib::Value. For a documentation about the actual arguments
|
||||||
// it is always a good idea to either check the element's signals using either
|
// it is always a good idea to either check the element's signals using either
|
||||||
// gst-inspect, or the online documentation.
|
// gst-inspect, or the online documentation.
|
||||||
overlay
|
overlay.connect("draw", false, move |args| {
|
||||||
.connect("draw", false, move |args| {
|
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
let drawer = &drawer_clone;
|
let drawer = &drawer_clone;
|
||||||
|
@ -193,8 +192,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
|
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
|
||||||
// be called when the sink that we render to does not support resizing the image
|
// be called when the sink that we render to does not support resizing the image
|
||||||
|
@ -203,8 +201,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
// resize our canvas's size.
|
// resize our canvas's size.
|
||||||
// Another possibility for when this might happen is, when our video is a network
|
// Another possibility for when this might happen is, when our video is a network
|
||||||
// stream that dynamically changes resolution when enough bandwith is available.
|
// stream that dynamically changes resolution when enough bandwith is available.
|
||||||
overlay
|
overlay.connect("caps-changed", false, move |args| {
|
||||||
.connect("caps-changed", false, move |args| {
|
|
||||||
let _overlay = args[0].get::<gst::Element>().unwrap();
|
let _overlay = args[0].get::<gst::Element>().unwrap();
|
||||||
let caps = args[1].get::<gst::Caps>().unwrap();
|
let caps = args[1].get::<gst::Caps>().unwrap();
|
||||||
|
|
||||||
|
@ -212,8 +209,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
|
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(pipeline)
|
Ok(pipeline)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ fn example_main() {
|
||||||
|
|
||||||
// Create a new playbin element, and tell it what uri to play back.
|
// Create a new playbin element, and tell it what uri to play back.
|
||||||
let playbin = gst::ElementFactory::make("playbin", None).unwrap();
|
let playbin = gst::ElementFactory::make("playbin", None).unwrap();
|
||||||
playbin.set_property("uri", uri).unwrap();
|
playbin.set_property("uri", uri);
|
||||||
|
|
||||||
// For flags handling
|
// For flags handling
|
||||||
// With flags, one can configure playbin's behavior such as whether it
|
// With flags, one can configure playbin's behavior such as whether it
|
||||||
|
@ -50,8 +50,7 @@ fn example_main() {
|
||||||
// - Live streams (such as internet radios) update this metadata during the stream
|
// - Live streams (such as internet radios) update this metadata during the stream
|
||||||
// Note that this signal will be emitted from the streaming threads usually,
|
// Note that this signal will be emitted from the streaming threads usually,
|
||||||
// not the application's threads!
|
// not the application's threads!
|
||||||
playbin
|
playbin.connect("audio-tags-changed", false, |values| {
|
||||||
.connect("audio-tags-changed", false, |values| {
|
|
||||||
// The metadata of any of the contained audio streams changed
|
// The metadata of any of the contained audio streams changed
|
||||||
// In the case of a live-stream from an internet radio, this could for example
|
// In the case of a live-stream from an internet radio, this could for example
|
||||||
// mark the beginning of a new track, or a new DJ.
|
// mark the beginning of a new track, or a new DJ.
|
||||||
|
@ -76,10 +75,7 @@ fn example_main() {
|
||||||
// application is via properties, signals or action signals (or custom messages, events, queries).
|
// application is via properties, signals or action signals (or custom messages, events, queries).
|
||||||
// So what the following code does, is essentially asking playbin to tell us its already
|
// So what the following code does, is essentially asking playbin to tell us its already
|
||||||
// internally stored tag list for this stream index.
|
// internally stored tag list for this stream index.
|
||||||
let tags = playbin
|
let tags = playbin.emit_by_name("get-audio-tags", &[&idx]).unwrap();
|
||||||
.emit_by_name("get-audio-tags", &[&idx])
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let tags = tags.get::<gst::TagList>().expect("tags");
|
let tags = tags.get::<gst::TagList>().expect("tags");
|
||||||
|
|
||||||
if let Some(artist) = tags.get::<gst::tags::Artist>() {
|
if let Some(artist) = tags.get::<gst::tags::Artist>() {
|
||||||
|
@ -95,8 +91,7 @@ fn example_main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// The playbin element itself is a playbin, so it can be used as one, despite being
|
// The playbin element itself is a playbin, so it can be used as one, despite being
|
||||||
// created from an element factory.
|
// created from an element factory.
|
||||||
|
|
|
@ -87,12 +87,11 @@ fn make_fec_decoder(rtpbin: &gst::Element, sess_id: u32) -> Result<gst::Element,
|
||||||
let internal_storage = rtpbin
|
let internal_storage = rtpbin
|
||||||
.emit_by_name("get-internal-storage", &[&sess_id])
|
.emit_by_name("get-internal-storage", &[&sess_id])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
|
||||||
.get::<glib::Object>()
|
.get::<glib::Object>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
fecdec.set_property("storage", &internal_storage)?;
|
fecdec.set_property("storage", &internal_storage);
|
||||||
fecdec.set_property("pt", 100u32)?;
|
fecdec.set_property("pt", 100u32);
|
||||||
|
|
||||||
Ok(fecdec)
|
Ok(fecdec)
|
||||||
}
|
}
|
||||||
|
@ -134,7 +133,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
|
|
||||||
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])?;
|
||||||
sink.set_property("location", "out.mkv")?;
|
sink.set_property("location", "out.mkv");
|
||||||
enc.set_property_from_str("tune", "zerolatency")?;
|
enc.set_property_from_str("tune", "zerolatency")?;
|
||||||
eprintln!("Recording to out.mkv");
|
eprintln!("Recording to out.mkv");
|
||||||
}
|
}
|
||||||
|
@ -147,10 +146,10 @@ fn example_main() -> Result<(), Error> {
|
||||||
let storage = values[1]
|
let storage = values[1]
|
||||||
.get::<gst::Element>()
|
.get::<gst::Element>()
|
||||||
.expect("rtpbin \"new-storage\" signal values[1]");
|
.expect("rtpbin \"new-storage\" signal values[1]");
|
||||||
storage.set_property("size-time", 250_000_000u64).unwrap();
|
storage.set_property("size-time", 250_000_000u64);
|
||||||
|
|
||||||
None
|
None
|
||||||
})?;
|
});
|
||||||
|
|
||||||
rtpbin.connect("request-pt-map", false, |values| {
|
rtpbin.connect("request-pt-map", false, |values| {
|
||||||
let pt = values[2]
|
let pt = values[2]
|
||||||
|
@ -175,7 +174,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
),
|
),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
})?;
|
});
|
||||||
|
|
||||||
rtpbin.connect("request-fec-decoder", false, |values| {
|
rtpbin.connect("request-fec-decoder", false, |values| {
|
||||||
let rtpbin = values[0]
|
let rtpbin = values[0]
|
||||||
|
@ -197,7 +196,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})?;
|
});
|
||||||
|
|
||||||
let srcpad = static_pad(&netsim, "src")?;
|
let srcpad = static_pad(&netsim, "src")?;
|
||||||
let sinkpad = request_pad(&rtpbin, "recv_rtp_sink_0")?;
|
let sinkpad = request_pad(&rtpbin, "recv_rtp_sink_0")?;
|
||||||
|
@ -232,11 +231,11 @@ fn example_main() -> Result<(), Error> {
|
||||||
.field("height", 1080i32)
|
.field("height", 1080i32)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
src.set_property("address", "127.0.0.1")?;
|
src.set_property("address", "127.0.0.1");
|
||||||
src.set_property("caps", &rtp_caps)?;
|
src.set_property("caps", &rtp_caps);
|
||||||
netsim.set_property("drop-probability", drop_probability)?;
|
netsim.set_property("drop-probability", drop_probability);
|
||||||
rtpbin.set_property("do-lost", true)?;
|
rtpbin.set_property("do-lost", true);
|
||||||
filter.set_property("caps", &video_caps)?;
|
filter.set_property("caps", &video_caps);
|
||||||
|
|
||||||
let bus = pipeline
|
let bus = pipeline
|
||||||
.bus()
|
.bus()
|
||||||
|
|
|
@ -72,9 +72,9 @@ fn connect_decodebin_pad(src_pad: &gst::Pad, sink: &gst::Element) -> Result<(),
|
||||||
fn make_fec_encoder(fec_percentage: u32) -> Result<gst::Element, Error> {
|
fn make_fec_encoder(fec_percentage: u32) -> Result<gst::Element, Error> {
|
||||||
let fecenc = make_element("rtpulpfecenc", None)?;
|
let fecenc = make_element("rtpulpfecenc", None)?;
|
||||||
|
|
||||||
fecenc.set_property("pt", 100u32)?;
|
fecenc.set_property("pt", 100u32);
|
||||||
fecenc.set_property("multipacket", true)?;
|
fecenc.set_property("multipacket", true);
|
||||||
fecenc.set_property("percentage", fec_percentage)?;
|
fecenc.set_property("percentage", fec_percentage);
|
||||||
|
|
||||||
Ok(fecenc)
|
Ok(fecenc)
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})?;
|
});
|
||||||
|
|
||||||
let srcpad = static_pad(&q2, "src")?;
|
let srcpad = static_pad(&q2, "src")?;
|
||||||
let sinkpad = request_pad(&rtpbin, "send_rtp_sink_0")?;
|
let sinkpad = request_pad(&rtpbin, "send_rtp_sink_0")?;
|
||||||
|
@ -152,16 +152,16 @@ fn example_main() -> Result<(), Error> {
|
||||||
let video_caps = gst::Caps::builder("video/x-raw").build();
|
let video_caps = gst::Caps::builder("video/x-raw").build();
|
||||||
|
|
||||||
src.set_property_from_str("pattern", "ball")?;
|
src.set_property_from_str("pattern", "ball")?;
|
||||||
sink.set_property("host", "127.0.0.1")?;
|
sink.set_property("host", "127.0.0.1");
|
||||||
sink.set_property("sync", true)?;
|
sink.set_property("sync", true);
|
||||||
enc.set_property("keyframe-max-dist", 30i32)?;
|
enc.set_property("keyframe-max-dist", 30i32);
|
||||||
enc.set_property("threads", 12i32)?;
|
enc.set_property("threads", 12i32);
|
||||||
enc.set_property("cpu-used", -16i32)?;
|
enc.set_property("cpu-used", -16i32);
|
||||||
enc.set_property("deadline", 1i64)?;
|
enc.set_property("deadline", 1i64);
|
||||||
enc.set_property_from_str("error-resilient", "default")?;
|
enc.set_property_from_str("error-resilient", "default")?;
|
||||||
src.set_property("expose-all-streams", false)?;
|
src.set_property("expose-all-streams", false);
|
||||||
src.set_property("caps", video_caps)?;
|
src.set_property("caps", video_caps);
|
||||||
src.set_property("uri", uri)?;
|
src.set_property("uri", uri);
|
||||||
|
|
||||||
let bus = pipeline
|
let bus = pipeline
|
||||||
.bus()
|
.bus()
|
||||||
|
|
|
@ -130,10 +130,10 @@ mod media_factory {
|
||||||
let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap();
|
let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap();
|
||||||
|
|
||||||
// Configure the videotestsrc live
|
// Configure the videotestsrc live
|
||||||
src.set_property("is-live", true).unwrap();
|
src.set_property("is-live", true);
|
||||||
|
|
||||||
// Produce encoded data as fast as possible
|
// Produce encoded data as fast as possible
|
||||||
enc.set_property("deadline", 1i64).unwrap();
|
enc.set_property("deadline", 1i64);
|
||||||
|
|
||||||
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();
|
||||||
|
|
|
@ -47,7 +47,7 @@ fn create_pipeline(uri: String, out_path: std::path::PathBuf) -> Result<gst::Pip
|
||||||
.expect("Sink element is expected to be an appsink!");
|
.expect("Sink element is expected to be an appsink!");
|
||||||
|
|
||||||
// Don't synchronize on the clock, we only want a snapshot asap.
|
// Don't synchronize on the clock, we only want a snapshot asap.
|
||||||
appsink.set_property("sync", false).unwrap();
|
appsink.set_property("sync", false);
|
||||||
|
|
||||||
// Tell the appsink what format we want.
|
// Tell the appsink what format we want.
|
||||||
// This can be set after linking the two objects, because format negotiation between
|
// This can be set after linking the two objects, because format negotiation between
|
||||||
|
|
|
@ -31,7 +31,7 @@ fn example_main() {
|
||||||
let src = gst::ElementFactory::make("filesrc", None).unwrap();
|
let src = gst::ElementFactory::make("filesrc", None).unwrap();
|
||||||
let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();
|
let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();
|
||||||
|
|
||||||
src.set_property("location", uri).unwrap();
|
src.set_property("location", uri);
|
||||||
|
|
||||||
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();
|
||||||
|
|
|
@ -68,18 +68,11 @@ fn example_main() -> Result<(), Error> {
|
||||||
let sink =
|
let sink =
|
||||||
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?;
|
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?;
|
||||||
|
|
||||||
sink.set_property("location", output_file)
|
sink.set_property("location", output_file);
|
||||||
.expect("setting location property failed");
|
|
||||||
// Increase the queue capacity to 100MB to avoid a stalling pipeline
|
// Increase the queue capacity to 100MB to avoid a stalling pipeline
|
||||||
queue
|
queue.set_property("max-size-buffers", 0u32);
|
||||||
.set_property("max-size-buffers", 0u32)
|
queue.set_property("max-size-time", 0u64);
|
||||||
.expect("changing capacity of multiqueue failed");
|
queue.set_property("max-size-bytes", 1024u32 * 1024 * 100);
|
||||||
queue
|
|
||||||
.set_property("max-size-time", 0u64)
|
|
||||||
.expect("changing capacity of multiqueue failed");
|
|
||||||
queue
|
|
||||||
.set_property("max-size-bytes", 1024u32 * 1024 * 100)
|
|
||||||
.expect("changing capacity of multiqueue failed");
|
|
||||||
|
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[&src, &typefinder, &queue, &muxer, &sink])
|
.add_many(&[&src, &typefinder, &queue, &muxer, &sink])
|
||||||
|
@ -90,8 +83,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
|
|
||||||
let pipeline_clone = pipeline.clone();
|
let pipeline_clone = pipeline.clone();
|
||||||
let typefinder_clone = typefinder.clone();
|
let typefinder_clone = typefinder.clone();
|
||||||
typefinder
|
typefinder.connect("have-type", false, move |values| {
|
||||||
.connect("have-type", false, move |values| {
|
|
||||||
let (pipeline, typefinder) = (&pipeline_clone, &typefinder_clone);
|
let (pipeline, typefinder) = (&pipeline_clone, &typefinder_clone);
|
||||||
|
|
||||||
// Use the detected format to select between a small set of supported demuxers
|
// Use the detected format to select between a small set of supported demuxers
|
||||||
|
@ -137,8 +129,7 @@ fn example_main() -> Result<(), Error> {
|
||||||
.expect("Failed to build remux pipeline");
|
.expect("Failed to build remux pipeline");
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.expect("Failed to register have-type signal of typefind");
|
|
||||||
|
|
||||||
pipeline.set_state(gst::State::Playing)?;
|
pipeline.set_state(gst::State::Playing)?;
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,10 @@ fn example_main() {
|
||||||
/* Completely contrived example that takes the 4:3 input video, cuts out a 5:4 frame
|
/* Completely contrived example that takes the 4:3 input video, cuts out a 5:4 frame
|
||||||
* and then adds pillarbox borders to place it in a 16:9 target area */
|
* and then adds pillarbox borders to place it in a 16:9 target area */
|
||||||
/* The output will be the full frame: */
|
/* The output will be the full frame: */
|
||||||
sinkpad.set_property("xpos", 0i32).unwrap();
|
sinkpad.set_property("xpos", 0i32);
|
||||||
sinkpad.set_property("ypos", 0i32).unwrap();
|
sinkpad.set_property("ypos", 0i32);
|
||||||
sinkpad.set_property("width", 1280i32).unwrap();
|
sinkpad.set_property("width", 1280i32);
|
||||||
sinkpad.set_property("height", 720i32).unwrap();
|
sinkpad.set_property("height", 720i32);
|
||||||
|
|
||||||
let mut converter_config = gst_video::VideoConverterConfig::new();
|
let mut converter_config = gst_video::VideoConverterConfig::new();
|
||||||
/* Crop the input frame to 5:4: */
|
/* Crop the input frame to 5:4: */
|
||||||
|
@ -41,9 +41,7 @@ fn example_main() {
|
||||||
converter_config.set_dest_y(0);
|
converter_config.set_dest_y(0);
|
||||||
converter_config.set_dest_height(Some(720));
|
converter_config.set_dest_height(Some(720));
|
||||||
|
|
||||||
sinkpad
|
sinkpad.set_property("converter-config", &*converter_config);
|
||||||
.set_property("converter-config", &*converter_config)
|
|
||||||
.unwrap();
|
|
||||||
pipeline
|
pipeline
|
||||||
.set_state(gst::State::Playing)
|
.set_state(gst::State::Playing)
|
||||||
.expect("Unable to set the pipeline to the `Playing` state");
|
.expect("Unable to set the pipeline to the `Playing` state");
|
||||||
|
|
|
@ -543,9 +543,9 @@ impl App {
|
||||||
.dynamic_cast::<gst_app::AppSink>()
|
.dynamic_cast::<gst_app::AppSink>()
|
||||||
.expect("Sink element is expected to be an appsink!");
|
.expect("Sink element is expected to be an appsink!");
|
||||||
|
|
||||||
appsink.set_property("enable-last-sample", false)?;
|
appsink.set_property("enable-last-sample", false);
|
||||||
appsink.set_property("emit-signals", false)?;
|
appsink.set_property("emit-signals", false);
|
||||||
appsink.set_property("max-buffers", 1u32)?;
|
appsink.set_property("max-buffers", 1u32);
|
||||||
|
|
||||||
let caps = gst::Caps::builder("video/x-raw")
|
let caps = gst::Caps::builder("video/x-raw")
|
||||||
.features(&[&gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY])
|
.features(&[&gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY])
|
||||||
|
@ -571,7 +571,7 @@ impl App {
|
||||||
let sink = gst::ElementFactory::make("glsinkbin", None)
|
let sink = gst::ElementFactory::make("glsinkbin", None)
|
||||||
.map_err(|_| MissingElement("glsinkbin"))?;
|
.map_err(|_| MissingElement("glsinkbin"))?;
|
||||||
|
|
||||||
sink.set_property("sink", &appsink)?;
|
sink.set_property("sink", &appsink);
|
||||||
|
|
||||||
pipeline.add_many(&[&src, &sink])?;
|
pipeline.add_many(&[&src, &sink])?;
|
||||||
src.link(&sink)?;
|
src.link(&sink)?;
|
||||||
|
@ -680,11 +680,7 @@ pub(crate) fn main_loop(app: App) -> Result<(), Error> {
|
||||||
|
|
||||||
{
|
{
|
||||||
if gst_gl_context.is_none() {
|
if gst_gl_context.is_none() {
|
||||||
gst_gl_context = glupload
|
gst_gl_context = glupload.property::<Option<gst_gl::GLContext>>("context");
|
||||||
.property("context")
|
|
||||||
.unwrap()
|
|
||||||
.get::<Option<gst_gl::GLContext>>()
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sync_meta = buffer.meta::<gst_gl::GLSyncMeta>().unwrap();
|
let sync_meta = buffer.meta::<gst_gl::GLSyncMeta>().unwrap();
|
||||||
|
|
|
@ -1035,7 +1035,7 @@ mod tests {
|
||||||
let videotestsrc = gst::ElementFactory::make("videotestsrc", None).unwrap();
|
let videotestsrc = gst::ElementFactory::make("videotestsrc", None).unwrap();
|
||||||
let appsink = gst::ElementFactory::make("appsink", None).unwrap();
|
let appsink = gst::ElementFactory::make("appsink", None).unwrap();
|
||||||
|
|
||||||
videotestsrc.set_property("num-buffers", 5).unwrap();
|
videotestsrc.set_property("num-buffers", 5);
|
||||||
|
|
||||||
let pipeline = gst::Pipeline::new(None);
|
let pipeline = gst::Pipeline::new(None);
|
||||||
pipeline.add(&videotestsrc).unwrap();
|
pipeline.add(&videotestsrc).unwrap();
|
||||||
|
|
|
@ -454,7 +454,7 @@ mod tests {
|
||||||
let appsrc = gst::ElementFactory::make("appsrc", None).unwrap();
|
let appsrc = gst::ElementFactory::make("appsrc", None).unwrap();
|
||||||
let fakesink = gst::ElementFactory::make("fakesink", None).unwrap();
|
let fakesink = gst::ElementFactory::make("fakesink", None).unwrap();
|
||||||
|
|
||||||
fakesink.set_property("signal-handoffs", true).unwrap();
|
fakesink.set_property("signal-handoffs", true);
|
||||||
|
|
||||||
let pipeline = gst::Pipeline::new(None);
|
let pipeline = gst::Pipeline::new(None);
|
||||||
pipeline.add(&appsrc).unwrap();
|
pipeline.add(&appsrc).unwrap();
|
||||||
|
@ -475,8 +475,7 @@ mod tests {
|
||||||
|
|
||||||
let handoff_count_reference = Arc::new(AtomicUsize::new(0));
|
let handoff_count_reference = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
fakesink
|
fakesink.connect("handoff", false, {
|
||||||
.connect("handoff", false, {
|
|
||||||
let handoff_count_reference = Arc::clone(&handoff_count_reference);
|
let handoff_count_reference = Arc::clone(&handoff_count_reference);
|
||||||
|
|
||||||
move |_| {
|
move |_| {
|
||||||
|
@ -484,8 +483,7 @@ mod tests {
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
pipeline.set_state(gst::State::Playing).unwrap();
|
pipeline.set_state(gst::State::Playing).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
#[allow(clippy::needless_borrow)]
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,6 @@ use glib::signal::{connect_raw, SignalHandlerId};
|
||||||
use glib::translate::*;
|
use glib::translate::*;
|
||||||
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
||||||
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
|
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
|
||||||
use glib::Value;
|
|
||||||
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
|
||||||
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
|
|
||||||
use std::boxed::Box as Box_;
|
use std::boxed::Box as Box_;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
||||||
|
@ -129,29 +126,14 @@ impl<O: IsA<Aggregator>> AggregatorExtManual for O {
|
||||||
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
||||||
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
|
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
|
||||||
fn min_upstream_latency(&self) -> gst::ClockTime {
|
fn min_upstream_latency(&self) -> gst::ClockTime {
|
||||||
unsafe {
|
self.as_ref().property("min-upstream-latency")
|
||||||
let mut value = Value::from_type(<gst::ClockTime as StaticType>::static_type());
|
|
||||||
glib::gobject_ffi::g_object_get_property(
|
|
||||||
self.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
|
|
||||||
b"min-upstream-latency\0".as_ptr() as *const _,
|
|
||||||
value.to_glib_none_mut().0,
|
|
||||||
);
|
|
||||||
value
|
|
||||||
.get()
|
|
||||||
.expect("AggregatorExtManual::min_upstream_latency")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
||||||
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
|
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
|
||||||
fn set_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime) {
|
fn set_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime) {
|
||||||
unsafe {
|
self.as_ref()
|
||||||
glib::gobject_ffi::g_object_set_property(
|
.set_property("min-upstream-latency", &min_upstream_latency);
|
||||||
self.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
|
|
||||||
b"min-upstream-latency\0".as_ptr() as *const _,
|
|
||||||
Value::from(&min_upstream_latency).to_glib_none().0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
#[cfg(any(feature = "v1_16", feature = "dox"))]
|
||||||
|
|
|
@ -25,6 +25,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::functions::*;
|
pub use crate::auto::functions::*;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
|
@ -21,6 +21,7 @@ macro_rules! assert_initialized_main_thread {
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
mod control_point;
|
mod control_point;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
|
@ -57,6 +57,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
#[allow(clippy::needless_borrow)]
|
#[allow(clippy::needless_borrow)]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
mod net_client_clock;
|
mod net_client_clock;
|
||||||
|
|
|
@ -6,32 +6,17 @@ use glib::prelude::*;
|
||||||
use glib::signal::connect_raw;
|
use glib::signal::connect_raw;
|
||||||
use glib::signal::SignalHandlerId;
|
use glib::signal::SignalHandlerId;
|
||||||
use glib::translate::*;
|
use glib::translate::*;
|
||||||
use glib::Value;
|
|
||||||
|
|
||||||
use std::boxed::Box as Box_;
|
use std::boxed::Box as Box_;
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
|
|
||||||
impl Discoverer {
|
impl Discoverer {
|
||||||
pub fn set_timeout(&self, timeout: gst::ClockTime) {
|
pub fn set_timeout(&self, timeout: gst::ClockTime) {
|
||||||
unsafe {
|
self.set_property("timeout", &timeout);
|
||||||
glib::gobject_ffi::g_object_set_property(
|
|
||||||
self.as_ptr() as *mut _,
|
|
||||||
"timeout".to_glib_none().0,
|
|
||||||
Value::from(&timeout).to_glib_none().0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn timeout(&self) -> gst::ClockTime {
|
pub fn timeout(&self) -> gst::ClockTime {
|
||||||
let mut value = Value::from(&0u64);
|
self.property("timeout")
|
||||||
unsafe {
|
|
||||||
glib::gobject_ffi::g_object_get_property(
|
|
||||||
self.as_ptr() as *mut _,
|
|
||||||
"timeout".to_glib_none().0,
|
|
||||||
value.to_glib_none_mut().0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
value.get().expect("undefined timeout")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(alias = "timeout")]
|
#[doc(alias = "timeout")]
|
||||||
|
|
|
@ -32,6 +32,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::functions::*;
|
pub use crate::auto::functions::*;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
|
@ -23,6 +23,7 @@ macro_rules! assert_initialized_main_thread {
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
#[allow(clippy::needless_borrow)]
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
#[allow(clippy::needless_borrow)]
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
#[allow(clippy::needless_borrow)]
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl<O: IsA<glib::Object>> GObjectExtManualGst for O {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_property_from_value(name, &value)
|
self.try_set_property_from_value(name, &value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ mod tests {
|
||||||
fakesink
|
fakesink
|
||||||
.set_property_from_str("state-error", "ready-to-paused")
|
.set_property_from_str("state-error", "ready-to-paused")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let v = fakesink.property("state-error").unwrap();
|
let v = fakesink.property_value("state-error");
|
||||||
let e = glib::EnumValue::from_value(&v).unwrap();
|
let e = glib::EnumValue::from_value(&v).unwrap();
|
||||||
assert_eq!(e.nick(), "ready-to-paused");
|
assert_eq!(e.nick(), "ready-to-paused");
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ macro_rules! skip_assert_initialized {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
#[allow(clippy::use_self)]
|
#[allow(clippy::use_self)]
|
||||||
#[allow(clippy::needless_borrow)]
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
mod auto;
|
mod auto;
|
||||||
pub use crate::auto::functions::*;
|
pub use crate::auto::functions::*;
|
||||||
pub use crate::auto::*;
|
pub use crate::auto::*;
|
||||||
|
|
|
@ -74,7 +74,6 @@ impl<O: IsA<crate::Object>> GstObjectExtManual for O {
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_object_flags(&self, flags: ObjectFlags) {
|
fn set_object_flags(&self, flags: ObjectFlags) {
|
||||||
|
@ -145,7 +144,7 @@ mod tests {
|
||||||
*notify_clone.lock().unwrap() = Some((id.clone(), prop.name()));
|
*notify_clone.lock().unwrap() = Some((id.clone(), prop.name()));
|
||||||
});
|
});
|
||||||
|
|
||||||
identity.set_property("silent", false).unwrap();
|
identity.set_property("silent", false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*notify.lock().unwrap(),
|
*notify.lock().unwrap(),
|
||||||
Some((identity.upcast::<crate::Object>(), "silent"))
|
Some((identity.upcast::<crate::Object>(), "silent"))
|
||||||
|
|
|
@ -1643,11 +1643,7 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
|
||||||
// Since 1.14 templates can keep a pad GType with them, so we need to do some
|
// Since 1.14 templates can keep a pad GType with them, so we need to do some
|
||||||
// additional checks here now
|
// additional checks here now
|
||||||
if templ.has_property("gtype", Some(glib::Type::static_type())) {
|
if templ.has_property("gtype", Some(glib::Type::static_type())) {
|
||||||
let gtype = templ
|
let gtype = templ.property::<glib::Type>("gtype");
|
||||||
.property("gtype")
|
|
||||||
.unwrap()
|
|
||||||
.get::<glib::Type>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if gtype == glib::Type::UNIT {
|
if gtype == glib::Type::UNIT {
|
||||||
// Nothing to be done, we can create any kind of pad
|
// Nothing to be done, we can create any kind of pad
|
||||||
|
|
|
@ -806,7 +806,7 @@ mod tests {
|
||||||
let src = ElementFactory::make("fakesrc", None).unwrap();
|
let src = ElementFactory::make("fakesrc", None).unwrap();
|
||||||
let sink = ElementFactory::make("fakesink", None).unwrap();
|
let sink = ElementFactory::make("fakesink", None).unwrap();
|
||||||
|
|
||||||
src.set_property("num-buffers", 100i32).unwrap();
|
src.set_property("num-buffers", 100i32);
|
||||||
|
|
||||||
pipeline
|
pipeline
|
||||||
.add_many(&[&src, element.upcast_ref(), &sink])
|
.add_many(&[&src, element.upcast_ref(), &sink])
|
||||||
|
|
|
@ -56,11 +56,7 @@ fn send_seek_event(pipeline: &Element, rate: f64) -> bool {
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we have not done so, obtain the sink through which we will send the seek events
|
// If we have not done so, obtain the sink through which we will send the seek events
|
||||||
if let Ok(Some(video_sink)) = pipeline
|
if let Ok(Some(video_sink)) = pipeline.try_property::<Option<Element>>("video-sink") {
|
||||||
.property("video-sink")
|
|
||||||
.unwrap()
|
|
||||||
.get::<Option<Element>>()
|
|
||||||
{
|
|
||||||
println!("Current rate: {}\r", rate);
|
println!("Current rate: {}\r", rate);
|
||||||
// Send the event
|
// Send the event
|
||||||
video_sink.send_event(seek_event)
|
video_sink.send_event(seek_event)
|
||||||
|
@ -175,10 +171,7 @@ USAGE: Choose one of the following options, then press enter:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::NextFrame => {
|
Command::NextFrame => {
|
||||||
if let Ok(Some(video_sink)) = pipeline
|
if let Ok(Some(video_sink)) = pipeline.try_property::<Option<Element>>("video-sink")
|
||||||
.property("video-sink")
|
|
||||||
.unwrap()
|
|
||||||
.get::<Option<Element>>()
|
|
||||||
{
|
{
|
||||||
// Send the event
|
// Send the event
|
||||||
let step = Step::new(gst::format::Buffers(1), rate.abs(), true, false);
|
let step = Step::new(gst::format::Buffers(1), rate.abs(), true, false);
|
||||||
|
|
|
@ -30,9 +30,7 @@ fn tutorial_main() {
|
||||||
// Set the URI to play
|
// Set the URI to play
|
||||||
let uri =
|
let uri =
|
||||||
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
|
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
|
||||||
source
|
source.set_property("uri", uri);
|
||||||
.set_property("uri", uri)
|
|
||||||
.expect("Can't set uri property on uridecodebin");
|
|
||||||
|
|
||||||
// Connect the pad-added signal
|
// Connect the pad-added signal
|
||||||
source.connect_pad_added(move |src, src_pad| {
|
source.connect_pad_added(move |src, src_pad| {
|
||||||
|
|
|
@ -31,9 +31,7 @@ fn tutorial_main() {
|
||||||
// Set the URI to play
|
// Set the URI to play
|
||||||
let uri =
|
let uri =
|
||||||
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
|
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
|
||||||
playbin
|
playbin.set_property("uri", uri);
|
||||||
.set_property("uri", uri)
|
|
||||||
.expect("Can't set uri property on playbin");
|
|
||||||
|
|
||||||
// Start playing
|
// Start playing
|
||||||
playbin
|
playbin
|
||||||
|
|
|
@ -40,9 +40,9 @@ mod tutorial5 {
|
||||||
let propname: &str = &format!("n-{}", stype);
|
let propname: &str = &format!("n-{}", stype);
|
||||||
let signame: &str = &format!("get-{}-tags", stype);
|
let signame: &str = &format!("get-{}-tags", stype);
|
||||||
|
|
||||||
let x = playbin.property(propname).unwrap().get::<i32>().unwrap();
|
let x = playbin.property::<i32>(propname);
|
||||||
for i in 0..x {
|
for i in 0..x {
|
||||||
let tags = playbin.emit_by_name(signame, &[&i]).unwrap().unwrap();
|
let tags = playbin.emit_by_name(signame, &[&i]).unwrap();
|
||||||
|
|
||||||
if let Ok(Some(tags)) = tags.get::<Option<gst::TagList>>() {
|
if let Ok(Some(tags)) = tags.get::<Option<gst::TagList>>() {
|
||||||
textbuf.insert_at_cursor(&format!("{} stream {}:\n ", stype, i));
|
textbuf.insert_at_cursor(&format!("{} stream {}:\n ", stype, i));
|
||||||
|
@ -302,37 +302,31 @@ mod tutorial5 {
|
||||||
let uri = "https://www.freedesktop.org/software/gstreamer-sdk/\
|
let uri = "https://www.freedesktop.org/software/gstreamer-sdk/\
|
||||||
data/media/sintel_trailer-480p.webm";
|
data/media/sintel_trailer-480p.webm";
|
||||||
let playbin = gst::ElementFactory::make("playbin", None).unwrap();
|
let playbin = gst::ElementFactory::make("playbin", None).unwrap();
|
||||||
playbin.set_property("uri", uri).unwrap();
|
playbin.set_property("uri", uri);
|
||||||
|
|
||||||
playbin
|
playbin.connect("video-tags-changed", false, |args| {
|
||||||
.connect("video-tags-changed", false, |args| {
|
|
||||||
let pipeline = args[0]
|
let pipeline = args[0]
|
||||||
.get::<gst::Element>()
|
.get::<gst::Element>()
|
||||||
.expect("playbin \"video-tags-changed\" args[0]");
|
.expect("playbin \"video-tags-changed\" args[0]");
|
||||||
post_app_message(&pipeline);
|
post_app_message(&pipeline);
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
playbin
|
playbin.connect("audio-tags-changed", false, |args| {
|
||||||
.connect("audio-tags-changed", false, |args| {
|
|
||||||
let pipeline = args[0]
|
let pipeline = args[0]
|
||||||
.get::<gst::Element>()
|
.get::<gst::Element>()
|
||||||
.expect("playbin \"audio-tags-changed\" args[0]");
|
.expect("playbin \"audio-tags-changed\" args[0]");
|
||||||
post_app_message(&pipeline);
|
post_app_message(&pipeline);
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
playbin
|
playbin.connect("text-tags-changed", false, move |args| {
|
||||||
.connect("text-tags-changed", false, move |args| {
|
|
||||||
let pipeline = args[0]
|
let pipeline = args[0]
|
||||||
.get::<gst::Element>()
|
.get::<gst::Element>()
|
||||||
.expect("playbin \"text-tags-changed\" args[0]");
|
.expect("playbin \"text-tags-changed\" args[0]");
|
||||||
post_app_message(&pipeline);
|
post_app_message(&pipeline);
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let window = create_ui(&playbin);
|
let window = create_ui(&playbin);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ fn tutorial_main() {
|
||||||
|
|
||||||
let pipeline = gst::Pipeline::new(Some("test-pipeline"));
|
let pipeline = gst::Pipeline::new(Some("test-pipeline"));
|
||||||
|
|
||||||
audio_source.set_property("freq", 215.0).unwrap();
|
audio_source.set_property("freq", 215.0);
|
||||||
visual.set_property_from_str("shader", "none").unwrap();
|
visual.set_property_from_str("shader", "none").unwrap();
|
||||||
visual.set_property_from_str("style", "lines").unwrap();
|
visual.set_property_from_str("style", "lines").unwrap();
|
||||||
|
|
||||||
|
|
|
@ -12,19 +12,16 @@ use std::{thread, time};
|
||||||
mod tutorials_common;
|
mod tutorials_common;
|
||||||
|
|
||||||
fn analyze_streams(playbin: &gst::Element) {
|
fn analyze_streams(playbin: &gst::Element) {
|
||||||
let n_video = playbin.property("n-video").unwrap().get::<i32>().unwrap();
|
let n_video = playbin.property::<i32>("n-video");
|
||||||
let n_audio = playbin.property("n-audio").unwrap().get::<i32>().unwrap();
|
let n_audio = playbin.property::<i32>("n-audio");
|
||||||
let n_text = playbin.property("n-text").unwrap().get::<i32>().unwrap();
|
let n_text = playbin.property::<i32>("n-text");
|
||||||
println!(
|
println!(
|
||||||
"{} video stream(s), {} audio stream(s), {} text stream(s)",
|
"{} video stream(s), {} audio stream(s), {} text stream(s)",
|
||||||
n_video, n_audio, n_text
|
n_video, n_audio, n_text
|
||||||
);
|
);
|
||||||
|
|
||||||
for i in 0..n_video {
|
for i in 0..n_video {
|
||||||
let tags = playbin
|
let tags = playbin.emit_by_name("get-video-tags", &[&i]).unwrap();
|
||||||
.emit_by_name("get-video-tags", &[&i])
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Ok(tags) = tags.get::<gst::TagList>() {
|
if let Ok(tags) = tags.get::<gst::TagList>() {
|
||||||
println!("video stream {}:", i);
|
println!("video stream {}:", i);
|
||||||
|
@ -35,10 +32,7 @@ fn analyze_streams(playbin: &gst::Element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..n_audio {
|
for i in 0..n_audio {
|
||||||
let tags = playbin
|
let tags = playbin.emit_by_name("get-audio-tags", &[&i]).unwrap();
|
||||||
.emit_by_name("get-audio-tags", &[&i])
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Ok(tags) = tags.get::<gst::TagList>() {
|
if let Ok(tags) = tags.get::<gst::TagList>() {
|
||||||
println!("audio stream {}:", i);
|
println!("audio stream {}:", i);
|
||||||
|
@ -55,10 +49,7 @@ fn analyze_streams(playbin: &gst::Element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..n_text {
|
for i in 0..n_text {
|
||||||
let tags = playbin
|
let tags = playbin.emit_by_name("get-text-tags", &[&i]).unwrap();
|
||||||
.emit_by_name("get-text-tags", &[&i])
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Ok(tags) = tags.get::<gst::TagList>() {
|
if let Ok(tags) = tags.get::<gst::TagList>() {
|
||||||
println!("subtitle stream {}:", i);
|
println!("subtitle stream {}:", i);
|
||||||
|
@ -68,21 +59,9 @@ fn analyze_streams(playbin: &gst::Element) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_video = playbin
|
let current_video = playbin.property::<i32>("current-video");
|
||||||
.property("current-video")
|
let current_audio = playbin.property::<i32>("current-audio");
|
||||||
.unwrap()
|
let current_text = playbin.property::<i32>("current-text");
|
||||||
.get::<i32>()
|
|
||||||
.unwrap();
|
|
||||||
let current_audio = playbin
|
|
||||||
.property("current-audio")
|
|
||||||
.unwrap()
|
|
||||||
.get::<i32>()
|
|
||||||
.unwrap();
|
|
||||||
let current_text = playbin
|
|
||||||
.property("current-text")
|
|
||||||
.unwrap()
|
|
||||||
.get::<i32>()
|
|
||||||
.unwrap();
|
|
||||||
println!(
|
println!(
|
||||||
"Currently playing video stream {}, audio stream {}, text stream {}",
|
"Currently playing video stream {}, audio stream {}, text stream {}",
|
||||||
current_video, current_audio, current_text
|
current_video, current_audio, current_text
|
||||||
|
@ -100,11 +79,11 @@ fn handle_keyboard(playbin: &gst::Element, main_loop: &glib::MainLoop) {
|
||||||
if let Some(index) = index.to_digit(10) {
|
if let Some(index) = index.to_digit(10) {
|
||||||
// Here index can only be 0-9
|
// Here index can only be 0-9
|
||||||
let index = index as i32;
|
let index = index as i32;
|
||||||
let n_audio = playbin.property("n-audio").unwrap().get::<i32>().unwrap();
|
let n_audio = playbin.property::<i32>("n-audio");
|
||||||
|
|
||||||
if index < n_audio {
|
if index < n_audio {
|
||||||
println!("Setting current audio stream to {}", index);
|
println!("Setting current audio stream to {}", index);
|
||||||
playbin.set_property("current-audio", index).unwrap();
|
playbin.set_property("current-audio", index);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Index out of bounds");
|
eprintln!("Index out of bounds");
|
||||||
}
|
}
|
||||||
|
@ -134,10 +113,10 @@ fn tutorial_main() -> Result<(), Error> {
|
||||||
// Set URI to play
|
// Set URI to play
|
||||||
let uri =
|
let uri =
|
||||||
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm";
|
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm";
|
||||||
playbin.set_property("uri", uri)?;
|
playbin.set_property("uri", uri);
|
||||||
|
|
||||||
// Set flags to show Audio and Video but ignore Subtitles
|
// Set flags to show Audio and Video but ignore Subtitles
|
||||||
let flags = playbin.property("flags")?;
|
let flags = playbin.property_value("flags");
|
||||||
let flags_class = FlagsClass::new(flags.type_()).unwrap();
|
let flags_class = FlagsClass::new(flags.type_()).unwrap();
|
||||||
|
|
||||||
let flags = flags_class
|
let flags = flags_class
|
||||||
|
@ -148,10 +127,10 @@ fn tutorial_main() -> Result<(), Error> {
|
||||||
.unset_by_nick("text")
|
.unset_by_nick("text")
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
playbin.set_property_from_value("flags", &flags)?;
|
playbin.set_property_from_value("flags", &flags);
|
||||||
|
|
||||||
// Set connection speed. This will affect some internal decisions of playbin
|
// Set connection speed. This will affect some internal decisions of playbin
|
||||||
playbin.set_property("connection-speed", 56u64)?;
|
playbin.set_property("connection-speed", 56u64);
|
||||||
|
|
||||||
// Handle keyboard input
|
// Handle keyboard input
|
||||||
let playbin_clone = playbin.clone();
|
let playbin_clone = playbin.clone();
|
||||||
|
|
|
@ -20,10 +20,10 @@ fn tutorial_main() -> Result<(), Error> {
|
||||||
let uri =
|
let uri =
|
||||||
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
|
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
|
||||||
let pipeline = gst::ElementFactory::make("playbin", None)?;
|
let pipeline = gst::ElementFactory::make("playbin", None)?;
|
||||||
pipeline.set_property("uri", uri).unwrap();
|
pipeline.set_property("uri", uri);
|
||||||
|
|
||||||
// Set the download flag
|
// Set the download flag
|
||||||
let flags = pipeline.property("flags")?;
|
let flags = pipeline.property_value("flags");
|
||||||
let flags_class = FlagsClass::new(flags.type_()).unwrap();
|
let flags_class = FlagsClass::new(flags.type_()).unwrap();
|
||||||
let flags = flags_class
|
let flags = flags_class
|
||||||
.builder_with_value(flags)
|
.builder_with_value(flags)
|
||||||
|
@ -31,10 +31,10 @@ fn tutorial_main() -> Result<(), Error> {
|
||||||
.set_by_nick("download")
|
.set_by_nick("download")
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
pipeline.set_property_from_value("flags", &flags).unwrap();
|
pipeline.set_property_from_value("flags", &flags);
|
||||||
|
|
||||||
// Uncomment this line to limit the amount of downloaded data.
|
// Uncomment this line to limit the amount of downloaded data.
|
||||||
// pipeline.set_property("ring-buffer-max-size", 4_000_000u64)?;
|
// pipeline.set_property("ring-buffer-max-size", 4_000_000u64);
|
||||||
|
|
||||||
// Start playing
|
// Start playing
|
||||||
let mut is_live = false;
|
let mut is_live = false;
|
||||||
|
@ -103,16 +103,12 @@ fn tutorial_main() -> Result<(), Error> {
|
||||||
let download_buffer = args[1].get::<gst::Object>().unwrap();
|
let download_buffer = args[1].get::<gst::Object>().unwrap();
|
||||||
println!(
|
println!(
|
||||||
"Temporary file: {:?}",
|
"Temporary file: {:?}",
|
||||||
download_buffer
|
download_buffer.property::<Option<String>>("temp-location")
|
||||||
.property("temp-location")
|
|
||||||
.unwrap()
|
|
||||||
.get::<Option<String>>()
|
|
||||||
.unwrap()
|
|
||||||
);
|
);
|
||||||
// Uncomment this line to keep the temporary file after the program exists.
|
// Uncomment this line to keep the temporary file after the program exists.
|
||||||
// download_buffer.set_property("temp-remove", false).ok();
|
// download_buffer.set_property("temp-remove", false).ok();
|
||||||
None
|
None
|
||||||
})?;
|
});
|
||||||
|
|
||||||
let pipeline_weak_ = pipeline.downgrade();
|
let pipeline_weak_ = pipeline.downgrade();
|
||||||
let timeout_id = glib::timeout_add_seconds(1, move || {
|
let timeout_id = glib::timeout_add_seconds(1, move || {
|
||||||
|
|
Loading…
Reference in a new issue