2021-04-09 12:38:02 +00:00
|
|
|
use gst::prelude::*;
|
|
|
|
|
|
|
|
use gtk::prelude::*;
|
|
|
|
use gtk::{gdk, gio, glib};
|
|
|
|
|
|
|
|
use std::cell::RefCell;
|
|
|
|
|
|
|
|
fn create_ui(app: >k::Application) {
|
2021-10-19 06:45:07 +00:00
|
|
|
let window = gtk::ApplicationWindow::new(app);
|
|
|
|
window.set_default_size(640, 480);
|
|
|
|
|
|
|
|
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
|
|
|
let picture = gtk::Picture::new();
|
|
|
|
let label = gtk::Label::new(Some("Position: 00:00:00"));
|
|
|
|
|
2023-02-25 17:30:26 +00:00
|
|
|
let pipeline = gst::Pipeline::new();
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2022-10-19 16:18:43 +00:00
|
|
|
let overlay = gst::ElementFactory::make("clockoverlay")
|
|
|
|
.property("font-desc", "Monospace 42")
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
2021-10-17 17:23:43 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
let gtksink = gst::ElementFactory::make("gtk4paintablesink")
|
2022-10-19 16:18:43 +00:00
|
|
|
.build()
|
|
|
|
.unwrap();
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
let paintable = gtksink.property::<gdk::Paintable>("paintable");
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
// TODO: future plans to provide a bin-like element that works with less setup
|
|
|
|
let (src, sink) = if paintable
|
|
|
|
.property::<Option<gdk::GLContext>>("gl-context")
|
|
|
|
.is_some()
|
|
|
|
{
|
|
|
|
let src = gst::ElementFactory::make("gltestsrc").build().unwrap();
|
|
|
|
|
|
|
|
let sink = gst::ElementFactory::make("glsinkbin")
|
|
|
|
.property("sink", >ksink)
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
(src, sink)
|
|
|
|
} else {
|
|
|
|
let src = gst::ElementFactory::make("videotestsrc").build().unwrap();
|
2022-11-30 09:58:26 +00:00
|
|
|
|
|
|
|
let sink = gst::Bin::default();
|
|
|
|
let convert = gst::ElementFactory::make("videoconvert").build().unwrap();
|
|
|
|
|
|
|
|
sink.add(&convert).unwrap();
|
|
|
|
sink.add(>ksink).unwrap();
|
|
|
|
convert.link(>ksink).unwrap();
|
|
|
|
|
2023-05-10 15:02:08 +00:00
|
|
|
sink.add_pad(&gst::GhostPad::with_target(&convert.static_pad("sink").unwrap()).unwrap())
|
|
|
|
.unwrap();
|
2022-11-30 09:58:26 +00:00
|
|
|
|
|
|
|
(src, sink.upcast())
|
2021-10-19 06:45:07 +00:00
|
|
|
};
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2023-03-09 15:30:57 +00:00
|
|
|
pipeline.add_many([&src, &overlay, &sink]).unwrap();
|
2021-10-19 06:45:07 +00:00
|
|
|
let caps = gst_video::VideoCapsBuilder::new()
|
|
|
|
.width(640)
|
|
|
|
.height(480)
|
|
|
|
.any_features()
|
|
|
|
.build();
|
|
|
|
|
|
|
|
src.link_filtered(&overlay, &caps).unwrap();
|
|
|
|
overlay.link(&sink).unwrap();
|
2021-04-09 12:38:02 +00:00
|
|
|
|
|
|
|
picture.set_paintable(Some(&paintable));
|
|
|
|
vbox.append(&picture);
|
|
|
|
vbox.append(&label);
|
|
|
|
|
|
|
|
window.set_child(Some(&vbox));
|
|
|
|
window.show();
|
|
|
|
|
|
|
|
app.add_window(&window);
|
|
|
|
|
|
|
|
let pipeline_weak = pipeline.downgrade();
|
|
|
|
let timeout_id = glib::timeout_add_local(std::time::Duration::from_millis(500), move || {
|
2023-10-30 09:34:35 +00:00
|
|
|
let Some(pipeline) = pipeline_weak.upgrade() else {
|
|
|
|
return glib::ControlFlow::Break;
|
2021-04-09 12:38:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let position = pipeline.query_position::<gst::ClockTime>();
|
|
|
|
label.set_text(&format!("Position: {:.0}", position.display()));
|
2023-07-06 13:27:28 +00:00
|
|
|
glib::ControlFlow::Continue
|
2021-04-09 12:38:02 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let bus = pipeline.bus().unwrap();
|
|
|
|
|
|
|
|
pipeline
|
|
|
|
.set_state(gst::State::Playing)
|
|
|
|
.expect("Unable to set the pipeline to the `Playing` state");
|
|
|
|
|
|
|
|
let app_weak = app.downgrade();
|
2023-04-14 09:46:43 +00:00
|
|
|
let bus_watch = bus
|
|
|
|
.add_watch_local(move |_, msg| {
|
|
|
|
use gst::MessageView;
|
|
|
|
|
2023-10-30 09:34:35 +00:00
|
|
|
let Some(app) = app_weak.upgrade() else {
|
|
|
|
return glib::ControlFlow::Break;
|
2023-04-14 09:46:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
match msg.view() {
|
|
|
|
MessageView::Eos(..) => app.quit(),
|
|
|
|
MessageView::Error(err) => {
|
|
|
|
println!(
|
|
|
|
"Error from {:?}: {} ({:?})",
|
|
|
|
err.src().map(|s| s.path_string()),
|
|
|
|
err.error(),
|
|
|
|
err.debug()
|
|
|
|
);
|
|
|
|
app.quit();
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
|
2023-07-06 13:27:28 +00:00
|
|
|
glib::ControlFlow::Continue
|
2023-04-14 09:46:43 +00:00
|
|
|
})
|
|
|
|
.expect("Failed to add bus watch");
|
2021-04-09 12:38:02 +00:00
|
|
|
|
|
|
|
let timeout_id = RefCell::new(Some(timeout_id));
|
|
|
|
let pipeline = RefCell::new(Some(pipeline));
|
2023-04-14 09:46:43 +00:00
|
|
|
let bus_watch = RefCell::new(Some(bus_watch));
|
2021-04-09 12:38:02 +00:00
|
|
|
app.connect_shutdown(move |_| {
|
|
|
|
window.close();
|
|
|
|
|
2023-04-14 09:46:43 +00:00
|
|
|
drop(bus_watch.borrow_mut().take());
|
2021-04-09 12:38:02 +00:00
|
|
|
if let Some(pipeline) = pipeline.borrow_mut().take() {
|
|
|
|
pipeline
|
|
|
|
.set_state(gst::State::Null)
|
|
|
|
.expect("Unable to set the pipeline to the `Null` state");
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(timeout_id) = timeout_id.borrow_mut().take() {
|
|
|
|
timeout_id.remove();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-27 16:58:59 +00:00
|
|
|
fn main() -> glib::ExitCode {
|
2021-04-09 12:38:02 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
gtk::init().unwrap();
|
|
|
|
|
|
|
|
gstgtk4::plugin_register_static().expect("Failed to register gstgtk4 plugin");
|
|
|
|
|
2023-01-16 09:51:10 +00:00
|
|
|
let app = gtk::Application::new(None::<&str>, gio::ApplicationFlags::FLAGS_NONE);
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
app.connect_activate(create_ui);
|
2023-01-27 16:58:59 +00:00
|
|
|
let res = app.run();
|
2021-04-09 12:38:02 +00:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gst::deinit();
|
|
|
|
}
|
2023-01-27 16:58:59 +00:00
|
|
|
|
|
|
|
res
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|