From da1a0c31d8e95f5b50cc4e01b675c81b109709ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 29 Aug 2017 15:40:25 +0300 Subject: [PATCH] Add video overlay example, using GTK and only support Unix/X11 for now --- Cargo.lock | 1 + examples/Cargo.toml | 2 + examples/src/bin/gtkvideooverlay.rs | 202 ++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 examples/src/bin/gtkvideooverlay.rs diff --git a/Cargo.lock b/Cargo.lock index 43674a129..b9944d066 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,7 @@ version = "0.1.0" dependencies = [ "byte-slice-cast 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk 0.6.0 (git+https://github.com/gtk-rs/gdk)", "gio 0.2.0 (git+https://github.com/gtk-rs/gio)", "glib 0.3.0 (git+https://github.com/gtk-rs/glib)", "gstreamer 0.1.0", diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 2e73d8800..48e951f88 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -11,6 +11,7 @@ gstreamer-audio = { path = "../gstreamer-audio" } gstreamer-video = { path = "../gstreamer-video" } gstreamer-player = { path = "../gstreamer-player", optional = true } gtk = { version = "0.2", git = "https://github.com/gtk-rs/gtk", features = ["v3_6"], optional = true } +gdk = { version = "0.6", git = "https://github.com/gtk-rs/gdk", optional = true } gio = { version = "0.2", git = "https://github.com/gtk-rs/gio", optional = true } futures = { version = "0.1", optional = true } tokio-core = { version = "0.1", optional = true } @@ -20,6 +21,7 @@ byte-slice-cast = "0.1" [features] gst-player = ["gstreamer-player"] gtksink = ["gtk", "gio"] +gtkvideooverlay = ["gtk", "gdk", "gio"] tokio = ["gstreamer/futures", "futures", "tokio-core"] default-features = [] diff --git a/examples/src/bin/gtkvideooverlay.rs b/examples/src/bin/gtkvideooverlay.rs new file mode 100644 index 000000000..a8a87e8e9 --- /dev/null +++ b/examples/src/bin/gtkvideooverlay.rs @@ -0,0 +1,202 @@ +#[cfg(feature = "gtkvideooverlay")] +extern crate gstreamer as gst; +#[cfg(feature = "gtkvideooverlay")] +use gst::prelude::*; + +#[cfg(feature = "gtkvideooverlay")] +extern crate gstreamer_video as gst_video; +#[cfg(feature = "gtkvideooverlay")] +use gst_video::prelude::*; + +#[cfg(feature = "gtkvideooverlay")] +extern crate glib; +#[cfg(feature = "gtkvideooverlay")] +use glib::translate::ToGlibPtr; + +#[cfg(feature = "gtkvideooverlay")] +extern crate gio; +#[cfg(feature = "gtkvideooverlay")] +use gio::prelude::*; + +#[cfg(feature = "gtkvideooverlay")] +extern crate gtk; +#[cfg(feature = "gtkvideooverlay")] +use gtk::prelude::*; + +#[cfg(feature = "gtkvideooverlay")] +extern crate gdk; +#[cfg(feature = "gtkvideooverlay")] +use gdk::prelude::*; + +#[cfg(feature = "gtkvideooverlay")] +use std::env; + +#[cfg(feature = "gtkvideooverlay")] +use std::os::raw::c_void; + +#[cfg(feature = "gtkvideooverlay")] +extern crate send_cell; +#[cfg(feature = "gtkvideooverlay")] +use send_cell::SendCell; + +#[cfg(feature = "gtkvideooverlay")] +use std::process; + +#[cfg(feature = "gtkvideooverlay")] +fn create_ui(app: >k::Application) { + let pipeline = gst::Pipeline::new(None); + let src = gst::ElementFactory::make("videotestsrc", None).unwrap(); + + let sink = if cfg!(unix) { + gst::ElementFactory::make("xvimagesink", None).unwrap() + } else { + unreachable!() + }; + + pipeline.add_many(&[&src, &sink]).unwrap(); + src.link(&sink).unwrap(); + + let window = gtk::Window::new(gtk::WindowType::Toplevel); + window.set_default_size(320, 240); + + let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + + let video_window = gtk::DrawingArea::new(); + video_window.set_size_request(320, 240); + let video_overlay = sink.clone() + .dynamic_cast::() + .unwrap(); + video_window.connect_realize(move |video_window| { + let video_overlay = &video_overlay; + + let gdk_window = video_window.get_window().unwrap(); + + if !gdk_window.ensure_native() { + println!("Can't create native window for widget"); + process::exit(-1); + } + + #[cfg(unix)] + { + let display_type_name = gdk_window.get_display().get_type().name(); + + // Check if we're using X11 or ... + if display_type_name == "GdkX11Display" { + extern "C" { + pub fn gdk_x11_window_get_xid( + window: *mut glib::object::GObject, + ) -> *mut c_void; + } + + unsafe { + let xid = gdk_x11_window_get_xid(gdk_window.to_glib_none().0); + video_overlay.set_window_handle(xid as usize); + } + } else { + println!("Add support for display type '{}'", display_type_name); + process::exit(-1); + } + } + }); + + vbox.pack_start(&video_window, true, true, 0); + + let label = gtk::Label::new("Position: 00:00:00"); + vbox.pack_start(&label, true, true, 5); + window.add(&vbox); + + window.show_all(); + + app.add_window(&window); + + let pipeline_clone = pipeline.clone(); + gtk::timeout_add(500, move || { + let pipeline = &pipeline_clone; + let position = pipeline.query_position(gst::Format::Time); + + if let Some(position) = position { + let mut seconds = (position as gst::ClockTime) / gst::SECOND; + let mut minutes = seconds / 60; + let hours = minutes / 60; + + seconds %= 60; + minutes %= 60; + + label.set_text(&format!( + "Position: {:02}:{:02}:{:02}", + hours, + minutes, + seconds + )); + } else { + label.set_text("Position: 00:00:00"); + } + + glib::Continue(true) + }); + + let app_clone = app.clone(); + window.connect_delete_event(move |_, _| { + let app = &app_clone; + app.quit(); + Inhibit(false) + }); + + let bus = pipeline.get_bus().unwrap(); + + let ret = pipeline.set_state(gst::State::Playing); + assert_ne!(ret, gst::StateChangeReturn::Failure); + + let app_clone = SendCell::new(app.clone()); + bus.add_watch(move |_, msg| { + use gst::MessageView; + + let app = app_clone.borrow(); + match msg.view() { + MessageView::Eos(..) => gtk::main_quit(), + MessageView::Error(err) => { + println!( + "Error from {}: {} ({:?})", + msg.get_src().get_path_string(), + err.get_error(), + err.get_debug() + ); + app.quit(); + } + _ => (), + }; + + glib::Continue(true) + }); + + let pipeline_clone = pipeline.clone(); + app.connect_shutdown(move |_| { + let pipeline = &pipeline_clone; + let ret = pipeline.set_state(gst::State::Null); + assert_ne!(ret, gst::StateChangeReturn::Failure); + }); +} + +#[cfg(feature = "gtkvideooverlay")] +fn main() { + #[cfg(not(unix))] + { + println!("Add support for target platform"); + process::exit(-1); + } + + gst::init().unwrap(); + gtk::init().unwrap(); + + let app = gtk::Application::new(None, gio::APPLICATION_FLAGS_NONE).unwrap(); + + app.connect_activate(create_ui); + let args = env::args().collect::>(); + let args_ref = args.iter().map(|a| a.as_str()).collect::>(); + app.run(&args_ref); +} + +#[cfg(not(feature = "gtkvideooverlay"))] +fn main() { + println!("Please compile with --feature gtkvideooverlay"); +}