From 84c7771d428cdd4df1887a878153e94796b05847 Mon Sep 17 00:00:00 2001 From: Marco Melorio Date: Sat, 3 Dec 2022 16:51:45 +0100 Subject: [PATCH] message-video: Use gst paintable sink --- Cargo.lock | 316 +++++++++++++++++++++++ Cargo.toml | 3 + src/main.rs | 11 +- src/session/content/message_row/mod.rs | 2 +- src/session/content/message_row/video.rs | 135 +++++++++- 5 files changed, 452 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9ee73884..e44eee7f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +[[package]] +name = "atomic_refcell" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "857253367827bd9d0fd973f0ef15506a96e79e41b0ad7aa691203a4e3214f6c8" + [[package]] name = "atty" version = "0.2.14" @@ -422,6 +428,55 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gdk4-wayland" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d34175f400cfc1dc20443a97184d8366e78b16ae488f6ad208c44f710713d9a" +dependencies = [ + "gdk4", + "gdk4-wayland-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk4-wayland-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a7589f15d81c946598b31aa21113011b2465c5e4459f7b34d4895168f5a3e7" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk4-x11" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2cb4d8362649a35490098008235f242eedff719c6340178a80c3c03ea4dd5a" +dependencies = [ + "gdk4", + "gdk4-x11-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk4-x11-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "880be6ce14b75f173525d6e1dd62342d7189a01fab1686c0c6e1d58312acf3f1" +dependencies = [ + "gdk4-sys", + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "gettext-rs" version = "0.7.0" @@ -589,6 +644,237 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gst-plugin-gtk4" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06fc66e6bde2064b08efc79c6affcb2227a24da5677b1d643027c7d2d00afb23" +dependencies = [ + "gdk4-wayland", + "gdk4-x11", + "gst-plugin-version-helper", + "gstreamer", + "gstreamer-base", + "gstreamer-gl", + "gstreamer-gl-egl", + "gstreamer-gl-wayland", + "gstreamer-gl-x11", + "gstreamer-video", + "gtk4", + "once_cell", +] + +[[package]] +name = "gst-plugin-version-helper" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87921209945e5dc809848a100115fad65bd127671896f0206f45e272080cc4c9" +dependencies = [ + "chrono", +] + +[[package]] +name = "gstreamer" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4f183660dc65a607f9c97ce4a3f72a208bf888c0c34d6cd052c7c4b2e086a1" +dependencies = [ + "bitflags", + "cfg-if", + "futures-channel", + "futures-core", + "futures-util", + "glib", + "gstreamer-sys", + "libc", + "muldiv", + "num-integer", + "num-rational", + "once_cell", + "option-operations", + "paste", + "pretty-hex", + "smallvec", + "thiserror", +] + +[[package]] +name = "gstreamer-base" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5598bfedbff12675a6cddbe420b6a3ba5039c64aaf7df130db6339d09b634b0e" +dependencies = [ + "atomic_refcell", + "bitflags", + "cfg-if", + "glib", + "gstreamer", + "gstreamer-base-sys", + "libc", +] + +[[package]] +name = "gstreamer-base-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26114ed96f6668380f5a1554128159e98e06c3a7a8460f216d7cd6dce28f928c" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-gl" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b82b83d18ad1c4d890694b4bedde170c748462a11f51a68428671bc1bf93e71e" +dependencies = [ + "bitflags", + "glib", + "gstreamer", + "gstreamer-base", + "gstreamer-gl-sys", + "gstreamer-video", + "libc", + "once_cell", +] + +[[package]] +name = "gstreamer-gl-egl" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec342886c7037cf18e07cf00aa5b60498ef5f9841aa36078a51f28d93970f93" +dependencies = [ + "glib", + "gstreamer", + "gstreamer-gl", + "gstreamer-gl-egl-sys", + "libc", +] + +[[package]] +name = "gstreamer-gl-egl-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f6f24c488311f09198e41826634e5e693149f10c9da1f6aa1be9e4e81e414e" +dependencies = [ + "glib-sys", + "gstreamer-gl-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-gl-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08af9ae5ca5aa01c4875346bb7e61310b75a9afc3607b52a6b73470be93bbc7" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-base-sys", + "gstreamer-sys", + "gstreamer-video-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-gl-wayland" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e8229f920f1703cdd5415ed2a8caaa9dab7794be1a190bd7e3a3d91549f417" +dependencies = [ + "glib", + "gstreamer", + "gstreamer-gl", + "gstreamer-gl-wayland-sys", + "libc", +] + +[[package]] +name = "gstreamer-gl-wayland-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7812f3fd82eb23c3f7f564b4d5c3b175dcc1e1947a4dcb93dde9129d9197232f" +dependencies = [ + "glib-sys", + "gstreamer-gl-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-gl-x11" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a82db426e6789bcb36e093100c97f8586f3a118bb7f268f36da9421044f646" +dependencies = [ + "glib", + "gstreamer", + "gstreamer-gl", + "gstreamer-gl-x11-sys", + "libc", +] + +[[package]] +name = "gstreamer-gl-x11-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27467cb653e78d5d4b0b275f45b2bce6520f5b8b6a241867ae98f3c56f23f1" +dependencies = [ + "glib-sys", + "gstreamer-gl-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e56fe047adef7d47dbafa8bc1340fddb53c325e16574763063702fc94b5786d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-video" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef95ad7e9645ef1a96373f1ea50135aec88fd29407b1979af7bb933a2ab0075b" +dependencies = [ + "bitflags", + "cfg-if", + "futures-channel", + "glib", + "gstreamer", + "gstreamer-base", + "gstreamer-video-sys", + "libc", + "once_cell", +] + +[[package]] +name = "gstreamer-video-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66ddb6112d438aac0004d2db6053a572f92b1c5e0e9d6ff6c71d9245f7f73e46" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + [[package]] name = "gtk4" version = "0.6.2" @@ -868,6 +1154,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "muldiv" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0" + [[package]] name = "num-integer" version = "0.1.45" @@ -933,6 +1225,15 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "option-operations" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0" +dependencies = [ + "paste", +] + [[package]] name = "pango" version = "0.17.0" @@ -959,6 +1260,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -977,6 +1284,12 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + [[package]] name = "pretty_env_logger" version = "0.4.0" @@ -1249,6 +1562,9 @@ dependencies = [ "ellipse", "futures", "gettext-rs", + "gst-plugin-gtk4", + "gstreamer", + "gstreamer-video", "gtk4", "image", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index 98d31beab..c0f01f205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,9 @@ anyhow = "1.0" ellipse = "0.2" futures = { version = "0.3", default-features = false } gettext-rs = { version = "0.7", features = ["gettext-system"] } +gst = { version = "0.20", package = "gstreamer" } +gst_gtk = { version = "0.10", package = "gst-plugin-gtk4", features = ["wayland", "x11glx", "x11egl"] } +gst_video = { version = "0.20", package = "gstreamer-video" } gtk = { version = "0.6", package = "gtk4", features = ["gnome_43", "blueprint"] } image = { version = "0.24", default-features = false, features = ["jpeg", "webp"] } indexmap = "1.9" diff --git a/src/main.rs b/src/main.rs index fb8be97b8..aa7b89d23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,9 @@ pub(crate) static APPLICATION_OPTS: OnceCell = OnceCell::new pub(crate) static TEMP_DIR: OnceCell = OnceCell::new(); fn main() -> glib::ExitCode { + gst::init().unwrap(); + gst_gtk::plugin_register_static().expect("Failed to register gstgtk4 plugin"); + // Prepare i18n gettextrs::setlocale(LocaleCategory::LcAll, ""); gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain"); @@ -90,7 +93,13 @@ fn main() -> glib::ExitCode { } } - app.run() + let res = app.run(); + + unsafe { + gst::deinit(); + } + + res } /// Global options for the application diff --git a/src/session/content/message_row/mod.rs b/src/session/content/message_row/mod.rs index b193a8a36..ced573f22 100644 --- a/src/session/content/message_row/mod.rs +++ b/src/session/content/message_row/mod.rs @@ -328,7 +328,7 @@ impl MessageRow { match message_.content().0 { // FIXME: Re-enable MessageVideo when // https://github.com/melix99/telegrand/issues/410 is fixed - MessageContent::MessageAnimation(_) /*| MessageContent::MessageVideo(_)*/ => { + MessageContent::MessageAnimation(_) | MessageContent::MessageVideo(_) => { self.update_specific_content::<_, MessageVideo>(message_.clone()); } MessageContent::MessagePhoto(_) => { diff --git a/src/session/content/message_row/video.rs b/src/session/content/message_row/video.rs index ec61162f8..3fb4d3ca5 100644 --- a/src/session/content/message_row/video.rs +++ b/src/session/content/message_row/video.rs @@ -1,5 +1,5 @@ use glib::clone; -use gtk::prelude::*; +use gst::prelude::*; use gtk::subclass::prelude::*; use gtk::{gdk, glib, CompositeTemplate}; use tdlib::enums::MessageContent; @@ -16,6 +16,7 @@ use super::base::MessageBaseExt; mod imp { use super::*; use once_cell::sync::Lazy; + use once_cell::unsync::OnceCell; use std::cell::{Cell, RefCell}; #[derive(Debug, Default, CompositeTemplate)] @@ -42,6 +43,8 @@ mod imp { pub(super) handler_id: RefCell>, pub(super) message: RefCell>, pub(super) is_animation: Cell, + pub(super) pipeline: OnceCell, + pub(super) file_src: OnceCell, #[template_child] pub(super) message_bubble: TemplateChild, #[template_child] @@ -91,9 +94,45 @@ mod imp { _ => unimplemented!(), } } + + fn constructed(&self) { + self.parent_constructed(); + + self.obj().create_pipeline(); + } + + fn dispose(&self) { + if let Some(pipeline) = self.pipeline.get() { + pipeline + .set_state(gst::State::Null) + .expect("Unable to set the pipeline to the `Null` state"); + pipeline.bus().unwrap().remove_watch().unwrap(); + } + } + } + + impl WidgetImpl for MessageVideo { + fn map(&self) { + self.parent_map(); + + self.pipeline + .get() + .unwrap() + .set_state(gst::State::Playing) + .unwrap(); + } + + fn unmap(&self) { + self.parent_unmap(); + + self.pipeline + .get() + .unwrap() + .set_state(gst::State::Paused) + .unwrap(); + } } - impl WidgetImpl for MessageVideo {} impl MessageBaseImpl for MessageVideo {} } @@ -133,6 +172,78 @@ impl MessageBaseExt for MessageVideo { } impl MessageVideo { + fn create_pipeline(&self) { + let imp = self.imp(); + let pipeline = gst::Pipeline::new(None); + + let src = gst::ElementFactory::make("filesrc").build().unwrap(); + let decodebin = gst::ElementFactory::make("decodebin").build().unwrap(); + let gtksink = gst::ElementFactory::make("gtk4paintablesink") + .build() + .unwrap(); + + // Need to set state to Ready to get a GL context + gtksink.set_state(gst::State::Ready).unwrap(); + + let paintable = gtksink.property::("paintable"); + imp.picture.set_paintable(Some(&paintable)); + + let sink = if paintable + .property::>("gl-context") + .is_some() + { + gst::ElementFactory::make("glsinkbin") + .property("sink", >ksink) + .build() + .unwrap() + } else { + 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(); + + sink.add_pad( + &gst::GhostPad::with_target(Some("sink"), &convert.static_pad("sink").unwrap()) + .unwrap(), + ) + .unwrap(); + + sink.upcast() + }; + + decodebin.connect_pad_added(clone!(@weak sink => move |_, src_pad| { + let sink_pad = sink.static_pad("sink").unwrap(); + if !sink_pad.is_linked() { + src_pad.link(&sink_pad).unwrap(); + } + })); + + pipeline.add_many(&[&src, &decodebin, &sink]).unwrap(); + + src.link(&decodebin).unwrap(); + + let bus = pipeline.bus().unwrap(); + bus.add_watch_local( + clone!(@weak pipeline => @default-return glib::Continue(false), move |_, msg| { + use gst::MessageView; + + if let MessageView::Eos(_) = msg.view() { + pipeline + .seek_simple(gst::SeekFlags::FLUSH, gst::ClockTime::from_seconds(0)) + .unwrap(); + } + + glib::Continue(true) + }), + ) + .unwrap(); + + imp.pipeline.set(pipeline).unwrap(); + imp.file_src.set(src).unwrap(); + } + fn update_content(&self, content: MessageContent, session: &Session) { let imp = self.imp(); @@ -198,20 +309,18 @@ impl MessageVideo { fn load_video(&self, path: &str) { let imp = self.imp(); + let pipeline = imp.pipeline.get().unwrap(); - let media = gtk::MediaFile::for_filename(path); - media.set_muted(true); - media.set_loop(true); - media.play(); + imp.file_src.get().unwrap().set_property("location", path); - if !imp.is_animation.get() { - media.connect_timestamp_notify(clone!(@weak self as obj => move |media| { - let time = (media.duration() - media.timestamp()) / i64::pow(10, 6); - obj.update_remaining_time(time); - })); - } + pipeline.set_state(gst::State::Playing).unwrap(); - imp.picture.set_paintable(Some(&media)); + // if !imp.is_animation.get() { + // media.connect_timestamp_notify(clone!(@weak self as obj => move |media| { + // let time = (media.duration() - media.timestamp()) / i64::pow(10, 6); + // obj.update_remaining_time(time); + // })); + // } } fn update_remaining_time(&self, time: i64) {