gst-plugin-gtk4-0.12.3/.cargo_vcs_info.json0000644000000001500000000000100140530ustar { "git": { "sha1": "5f6ed181c2a30c63e8895e42ee65789de5a50d16" }, "path_in_vcs": "video/gtk4" }gst-plugin-gtk4-0.12.3/Cargo.lock0000644000001023570000000000100120420ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anyhow" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "async-channel" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "atomic_refcell" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "cairo-rs" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2650f66005301bd33cc486dec076e1293c4cecf768bc7ba9bf5d2b1be339b99c" dependencies = [ "bitflags", "cairo-sys-rs", "glib", "libc", "thiserror", ] [[package]] name = "cairo-sys-rs" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "cc" version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-expr" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "windows-targets", ] [[package]] name = "concurrent-queue" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "either" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "event-listener" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" dependencies = [ "event-listener", "pin-project-lite", ] [[package]] name = "field-offset" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", "rustc_version", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gdk-pixbuf" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6a23f8a0b5090494fd04924662d463f8386cc678dd3915015a838c1a3679b92" dependencies = [ "gdk-pixbuf-sys", "gio", "glib", "libc", ] [[package]] name = "gdk-pixbuf-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcbd04c1b2c4834cc008b4828bc917d062483b88d26effde6342e5622028f96" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "gdk4" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9100b25604183f2fd97f55ef087fae96ab4934d7215118a35303e422688e6e4b" dependencies = [ "cairo-rs", "gdk-pixbuf", "gdk4-sys", "gio", "glib", "libc", "pango", ] [[package]] name = "gdk4-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0b76874c40bb8d1c7d03a7231e23ac75fa577a456cd53af32ec17ec8f121626" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gio-sys", "glib-sys", "gobject-sys", "libc", "pango-sys", "pkg-config", "system-deps", ] [[package]] name = "gdk4-wayland" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13fc91be838be766ff038221e068e05e4083f3b6cf48ef1f5251ba28f98f80bf" dependencies = [ "gdk4", "gdk4-wayland-sys", "gio", "glib", "libc", ] [[package]] name = "gdk4-wayland-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5cdc6c5cb3f64ba9b810782077939a0fc8d82e809f0147054bbe41910ac2b51" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "gdk4-win32" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab6181b6e5c91ee292dca0032b00d48dee8e61358253742c9752537a88486b3f" dependencies = [ "gdk4", "gdk4-win32-sys", "gio", "glib", "khronos-egl", "libc", ] [[package]] name = "gdk4-win32-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa8530d6619cf43f007f3efd993a356e1ca4e643c4d0bd2a99832a08af2e402" dependencies = [ "gdk4-sys", "glib-sys", "libc", "system-deps", ] [[package]] name = "gdk4-x11" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bff98d3e61807ecc1ddd15cf746b9de14fb6499fc259ca9e8d87d92e8901c7db" dependencies = [ "gdk4", "gdk4-x11-sys", "gio", "glib", "libc", ] [[package]] name = "gdk4-x11-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30406b31a7c355c73c504c9b31d35806397944165730eca7db46f0409abf8f8f" dependencies = [ "gdk4-sys", "glib-sys", "libc", "system-deps", ] [[package]] name = "gio" version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c64947d08d7fbb03bf8ad1f25a8ac6cf4329bc772c9b7e5abe7bf9493c81194f" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-util", "gio-sys", "glib", "libc", "pin-project-lite", "smallvec", "thiserror", ] [[package]] name = "gio-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf8e1d9219bb294636753d307b030c1e8a032062cba74f493c431a5c8b81ce4" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", "windows-sys", ] [[package]] name = "glib" version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01e191cc1af1f35b9699213107068cd3fe05d9816275ac118dc785a0dd8faebf" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-executor", "futures-task", "futures-util", "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", "memchr", "smallvec", "thiserror", ] [[package]] name = "glib-macros" version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9972bb91643d589c889654693a4f1d07697fdcb5d104b5c44fb68649ba1bf68d" dependencies = [ "heck", "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "glib-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630f097773d7c7a0bb3258df4e8157b47dc98bbfa0e60ad9ab56174813feced4" dependencies = [ "libc", "system-deps", ] [[package]] name = "gobject-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e2b1080b9418dd0c58b498da3a5c826030343e0ef07bde6a955d28de54979" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "graphene-rs" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e4d388e96c5f29e2b2f67045d229ddf826d0a8d6d282f94ed3b34452222c91" dependencies = [ "glib", "graphene-sys", "libc", ] [[package]] name = "graphene-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "236ed66cc9b18d8adf233716f75de803d0bf6fc806f60d14d948974a12e240d0" dependencies = [ "glib-sys", "libc", "pkg-config", "system-deps", ] [[package]] name = "gsk4" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c65036fc8f99579e8cb37b12487969b707ab23ec8ab953682ff347cbd15d396e" dependencies = [ "cairo-rs", "gdk4", "glib", "graphene-rs", "gsk4-sys", "libc", "pango", ] [[package]] name = "gsk4-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd24c814379f9c3199dc53e52253ee8d0f657eae389ab282c330505289d24738" dependencies = [ "cairo-sys-rs", "gdk4-sys", "glib-sys", "gobject-sys", "graphene-sys", "libc", "pango-sys", "system-deps", ] [[package]] name = "gst-plugin-gtk4" version = "0.12.3" dependencies = [ "async-channel", "gdk4-wayland", "gdk4-win32", "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", "windows-sys", ] [[package]] name = "gst-plugin-version-helper" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e5e874f1660252fd2ec81c602066df3633b3a6fcbe2b196f7f93c27cf069b2a" dependencies = [ "chrono", "toml_edit 0.22.8", ] [[package]] name = "gstreamer" version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "066f3c354c526792a3deb8b6d349eba8ffcc13bae4d0bf05d4adf4bf1b91e459" dependencies = [ "cfg-if", "futures-channel", "futures-core", "futures-util", "glib", "gstreamer-sys", "itertools", "libc", "muldiv", "num-integer", "num-rational", "once_cell", "option-operations", "paste", "pin-project-lite", "smallvec", "thiserror", ] [[package]] name = "gstreamer-base" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514c71195b53c7eced4842b66ca9149833e41cf6a1d949e45e2ca4a4fa929850" dependencies = [ "atomic_refcell", "cfg-if", "glib", "gstreamer", "gstreamer-base-sys", "libc", ] [[package]] name = "gstreamer-base-sys" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "286591e0f85bbda1adf9bab6f21d015acd9ca0a4d4acb61da65e3d0487e23c4e" dependencies = [ "glib-sys", "gobject-sys", "gstreamer-sys", "libc", "system-deps", ] [[package]] name = "gstreamer-gl" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d21c0c5fbf74018a0254b3ab77bca0a5b2c0f002bcfd910c09113ae90a95d98" dependencies = [ "glib", "gstreamer", "gstreamer-base", "gstreamer-gl-sys", "gstreamer-video", "libc", "once_cell", ] [[package]] name = "gstreamer-gl-egl" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfde7bf67f5f7c87e1ff29cdeea4918530d677b51e3f4847121ada44f1fab139" dependencies = [ "glib", "gstreamer", "gstreamer-gl", "gstreamer-gl-egl-sys", "libc", ] [[package]] name = "gstreamer-gl-egl-sys" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c9ec3c03af5d4ed3e58ddbca4eea13e90e01b88e37f6c0689b26e05168eb7bf" dependencies = [ "glib-sys", "gstreamer-gl-sys", "libc", "system-deps", ] [[package]] name = "gstreamer-gl-sys" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61d1e3b9b02abc23835e9d770f2bd705b67a50406ea37e963b4526a77c6a7cd8" dependencies = [ "glib-sys", "gobject-sys", "gstreamer-base-sys", "gstreamer-sys", "gstreamer-video-sys", "libc", "system-deps", ] [[package]] name = "gstreamer-gl-wayland" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbfe3d0c2469023b73df8408a4b19daaf7bd30141e9fc67e4ab63d41db5ee2" dependencies = [ "glib", "gstreamer", "gstreamer-gl", "gstreamer-gl-wayland-sys", "libc", ] [[package]] name = "gstreamer-gl-wayland-sys" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83bc79debd1ef92795a3bd411986b19dbfe2527424f396e460aacc59d5fab4f1" dependencies = [ "glib-sys", "gstreamer-gl-sys", "libc", "system-deps", ] [[package]] name = "gstreamer-gl-x11" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ed82941c84668d89dbf81f220083422268c22ec6ab4991806649ed6758cec8" dependencies = [ "glib", "gstreamer", "gstreamer-gl", "gstreamer-gl-x11-sys", "libc", ] [[package]] name = "gstreamer-gl-x11-sys" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b59f2782f5e71e3ef5fd534598938966a4dc3911f2540807f7d13b586e4ed1" dependencies = [ "glib-sys", "gstreamer-gl-sys", "libc", "system-deps", ] [[package]] name = "gstreamer-sys" version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5ddf526b3bf90ea627224c804f00b8bcb0452e3b447978b4d5092f8e8ff5918" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "gstreamer-video" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ab3f4045ddb92bf2b469f5db8825d4f5eb46e4beff661fc97f50bb4e2b2c626" dependencies = [ "cfg-if", "futures-channel", "glib", "gstreamer", "gstreamer-base", "gstreamer-video-sys", "libc", "once_cell", "thiserror", ] [[package]] name = "gstreamer-video-sys" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ea7996ba44fbbf563aeeda96e24259efc9f06b407854d837ee58e260d7ba78" dependencies = [ "glib-sys", "gobject-sys", "gstreamer-base-sys", "gstreamer-sys", "libc", "system-deps", ] [[package]] name = "gtk4" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa82753b8c26277e4af1446c70e35b19aad4fb794a7b143859e7eeb9a4025d83" dependencies = [ "cairo-rs", "field-offset", "futures-channel", "gdk-pixbuf", "gdk4", "gio", "glib", "graphene-rs", "gsk4", "gtk4-macros", "gtk4-sys", "libc", "pango", ] [[package]] name = "gtk4-macros" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40300bf071d2fcd4c94eacc09e84ec6fe73129d2ceb635cf7e55b026b5443567" dependencies = [ "anyhow", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "gtk4-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0db1b104138f087ccdc81d2c332de5dd049b89de3d384437cc1093b17cd2da18" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gdk4-sys", "gio-sys", "glib-sys", "gobject-sys", "graphene-sys", "gsk4-sys", "libc", "pango-sys", "system-deps", ] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "iana-time-zone" version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "indexmap" version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "itertools" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "khronos-egl" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", ] [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 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.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-rational" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[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.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1264d13deb823cc652f26cfe59afb1ec4b9db2a5bd27c41b738c879cc1bfaa1" dependencies = [ "gio", "glib", "libc", "pango-sys", ] [[package]] name = "pango-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52ef6a881c19fbfe3b1484df5cad411acaaba29dbec843941c3110d19f340ea" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro-crate" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ "toml_edit 0.21.1", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn 1.0.109", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "semver" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "serde_spanned" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "system-deps" version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", "heck", "pkg-config", "toml", "version-compare", ] [[package]] name = "target-lexicon" version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "thiserror" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "toml" version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit 0.22.8", ] [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" version = "0.22.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c12219811e0c1ba077867254e5ad62ee2c9c190b0d957110750ac0cda1ae96cd" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow 0.6.5", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "version-compare" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.53", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "winnow" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] gst-plugin-gtk4-0.12.3/Cargo.toml0000644000000071210000000000100120560ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70" name = "gst-plugin-gtk4" version = "0.12.3" authors = [ "Bilal Elmoussaoui ", "Jordan Petridis ", "Sebastian Dröge ", ] description = "GStreamer GTK 4 Sink element and Paintable widget" readme = "README.md" license = "MPL-2.0" repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs" [package.metadata.capi] min_version = "0.9.21" [package.metadata.capi.header] enabled = false [package.metadata.capi.library] import_library = false install_subdir = "gstreamer-1.0" versioning = false [package.metadata.capi.pkg_config] requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-video-1.0, gtk4, gobject-2.0, glib-2.0, gmodule-2.0" [lib] name = "gstgtk4" crate-type = [ "cdylib", "rlib", ] path = "src/lib.rs" [dependencies.async-channel] version = "2.0.0" [dependencies.gdk-wayland] version = "0.8" features = ["v4_4"] optional = true package = "gdk4-wayland" [dependencies.gdk-x11] version = "0.8" features = ["v4_4"] optional = true package = "gdk4-x11" [dependencies.gst] version = "0.22" features = ["v1_16"] package = "gstreamer" [dependencies.gst-base] version = "0.22" package = "gstreamer-base" [dependencies.gst-gl] version = "0.22" features = ["v1_16"] optional = true package = "gstreamer-gl" [dependencies.gst-gl-egl] version = "0.22" features = ["v1_16"] optional = true package = "gstreamer-gl-egl" [dependencies.gst-gl-wayland] version = "0.22" features = ["v1_16"] optional = true package = "gstreamer-gl-wayland" [dependencies.gst-gl-x11] version = "0.22" features = ["v1_16"] optional = true package = "gstreamer-gl-x11" [dependencies.gst-video] version = "0.22" package = "gstreamer-video" [dependencies.gtk] version = "0.8" package = "gtk4" [dependencies.once_cell] version = "1" [build-dependencies.gst-plugin-version-helper] version = "0.8" [features] capi = [] default = [] doc = ["gst/v1_18"] gtk_v4_10 = ["gtk/v4_10"] gtk_v4_12 = [ "gtk/v4_12", "gtk_v4_10", ] gtk_v4_14 = [ "gtk/v4_14", "gtk_v4_12", ] static = [] wayland = [ "gtk/v4_6", "gdk-wayland", "gst-gl", "gst-gl-wayland", ] winegl = [ "gdk-win32/egl", "gst-gl-egl", ] x11egl = [ "gtk/v4_6", "gdk-x11", "gst-gl", "gst-gl-egl", ] x11glx = [ "gtk/v4_6", "gdk-x11", "gst-gl", "gst-gl-x11", ] [target."cfg(target_os = \"macos\")".dependencies.gst-gl] version = "0.22" features = ["v1_16"] package = "gstreamer-gl" [target."cfg(target_os = \"macos\")".dependencies.gtk] version = "0.8" features = ["v4_6"] package = "gtk4" [target."cfg(target_os = \"windows\")".dependencies.gdk-win32] version = "0.8" features = ["v4_4"] package = "gdk4-win32" [target."cfg(target_os = \"windows\")".dependencies.gst-gl] version = "0.22" features = ["v1_20"] package = "gstreamer-gl" [target."cfg(target_os = \"windows\")".dependencies.gtk] version = "0.8" features = ["v4_6"] package = "gtk4" [target."cfg(target_os = \"windows\")".dependencies.windows-sys] version = "0.52" features = [ "Win32_Graphics_OpenGL", "Win32_Foundation", "Win32_Graphics_Gdi", ] gst-plugin-gtk4-0.12.3/Cargo.toml.orig000064400000000000000000000044771046102023000155520ustar 00000000000000[package] name = "gst-plugin-gtk4" version.workspace = true authors = ["Bilal Elmoussaoui ", "Jordan Petridis ", "Sebastian Dröge "] repository.workspace = true license = "MPL-2.0" edition.workspace = true rust-version.workspace = true description = "GStreamer GTK 4 Sink element and Paintable widget" [dependencies] gtk.workspace = true gdk-wayland = { workspace = true, features = ["v4_4"], optional = true} gdk-x11 = { workspace = true, features = ["v4_4"], optional = true} gst = { workspace = true, features = ["v1_16"] } gst-base.workspace = true gst-video.workspace = true gst-gl = { workspace = true, features = ["v1_16"], optional = true } gst-gl-wayland = { workspace = true, features = ["v1_16"], optional = true } gst-gl-x11 = { workspace = true, features = ["v1_16"], optional = true } gst-gl-egl = { workspace = true, features = ["v1_16"], optional = true } async-channel = "2.0.0" once_cell.workspace = true [target.'cfg(target_os = "macos")'.dependencies] gtk = { workspace = true, features = ["v4_6"] } gst-gl = { workspace = true, features = ["v1_16"] } [target.'cfg(target_os = "windows")'.dependencies] gtk = { workspace = true, features = ["v4_6"] } gst-gl = { workspace = true, features = ["v1_20"] } gdk-win32 = { workspace = true, features = ["v4_4"]} windows-sys = { version = "0.52", features = ["Win32_Graphics_OpenGL", "Win32_Foundation", "Win32_Graphics_Gdi"] } [lib] name = "gstgtk4" crate-type = ["cdylib", "rlib"] path = "src/lib.rs" [build-dependencies] gst-plugin-version-helper.workspace = true [features] default = [] static = [] wayland = ["gtk/v4_6", "gdk-wayland", "gst-gl", "gst-gl-wayland"] x11glx = ["gtk/v4_6", "gdk-x11", "gst-gl", "gst-gl-x11"] x11egl = ["gtk/v4_6", "gdk-x11", "gst-gl", "gst-gl-egl"] winegl = ["gdk-win32/egl", "gst-gl-egl"] capi = [] doc = ["gst/v1_18"] gtk_v4_10 = ["gtk/v4_10"] gtk_v4_12 = ["gtk/v4_12", "gtk_v4_10"] gtk_v4_14 = ["gtk/v4_14", "gtk_v4_12"] [package.metadata.capi] min_version = "0.9.21" [package.metadata.capi.header] enabled = false [package.metadata.capi.library] install_subdir = "gstreamer-1.0" versioning = false import_library = false [package.metadata.capi.pkg_config] requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-video-1.0, gtk4, gobject-2.0, glib-2.0, gmodule-2.0" gst-plugin-gtk4-0.12.3/LICENSE-MPL-2.0000064400000000000000000000405261046102023000146060ustar 00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. gst-plugin-gtk4-0.12.3/README.md000064400000000000000000000035071046102023000141330ustar 00000000000000# Gtk 4 Sink & Paintable GTK 4 provides `gtk::Video` & `gtk::Picture` for rendering media such as videos. As the default `gtk::Video` widget doesn't offer the possibility to use a custom `gst::Pipeline`. The plugin provides a `gst_video::VideoSink` along with a `gdk::Paintable` that's capable of rendering the sink's frames. The Sink can generate GL Textures if the system is capable of it, but it needs to be compiled with either `wayland`, `x11glx` or `x11egl` cargo features. # Flatpak Integration To build and include the plugin in a Flatpak manifest, you can add the following snippet to your json manifest: ```json { "sdk-extensions": [ "org.freedesktop.Sdk.Extension.rust-stable" ], "build-options": { "env": { "CARGO_HOME": "/run/build/cargo-c/cargo" }, "append-path": "/usr/lib/sdk/rust-stable/bin", }, "modules": [ { "name": "cargo-c", "buildsystem": "simple", "build-commands": [ "cargo install cargo-c --root /app" ], "build-options": { "build-args": [ "--share=network" ] }, "cleanup": [ "*" ] }, { "name": "gst-plugins-rs", "buildsystem": "simple", "sources": [ { "type": "git", "url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs", "branch": "0.10" } ], "build-options": { "build-args": [ "--share=network" ] }, "build-commands": [ "cargo cinstall -p gst-plugin-gtk4 --prefix=/app" ] } ] } ``` gst-plugin-gtk4-0.12.3/build.rs000064400000000000000000000000641046102023000143140ustar 00000000000000fn main() { gst_plugin_version_helper::info() } gst-plugin-gtk4-0.12.3/examples/gtksink.py000064400000000000000000000027051046102023000165150ustar 00000000000000import gi import sys gi.require_version("Gtk", "4.0") gi.require_version('Gst', '1.0') from gi.repository import Gtk from gi.repository import Gst Gst.init(sys.argv[1:]) gtksink = Gst.ElementFactory.make("gtk4paintablesink", "sink") # Get the paintable from the sink paintable = gtksink.props.paintable # Use GL if available if paintable.props.gl_context: print("Using GL") source = Gst.ElementFactory.make("gltestsrc", "source") glsink = Gst.ElementFactory.make("glsinkbin", "sink") glsink.props.sink = gtksink sink = glsink else: source = Gst.ElementFactory.make("videotestsrc", "source") sink = gtksink pipeline = Gst.Pipeline.new() if not pipeline or not source or not sink: print("Not all elements could be created.") exit(-1) pipeline.add(source) pipeline.add(sink) source.link(sink) def on_activate(app): win = Gtk.ApplicationWindow(application=app) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) picture = Gtk.Picture.new() picture.set_size_request(640, 480) # Set the paintable on the picture picture.set_paintable(paintable) box.append(picture) btn = Gtk.Button(label="▶/⏸") box.append(btn) btn.connect('clicked', lambda _: pipeline.set_state(Gst.State.PAUSED) if pipeline.get_state(1)[1]==Gst.State.PLAYING else pipeline.set_state(Gst.State.PLAYING)) win.set_child(box) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) gst-plugin-gtk4-0.12.3/examples/gtksink.rs000064400000000000000000000106421046102023000165100ustar 00000000000000use gst::prelude::*; use gtk::prelude::*; use gtk::{gdk, gio, glib}; use std::cell::RefCell; fn create_ui(app: >k::Application) { 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")); let pipeline = gst::Pipeline::new(); let overlay = gst::ElementFactory::make("clockoverlay") .property("font-desc", "Monospace 42") .build() .unwrap(); let gtksink = gst::ElementFactory::make("gtk4paintablesink") .build() .unwrap(); let paintable = gtksink.property::("paintable"); // TODO: future plans to provide a bin-like element that works with less setup let (src, sink) = if paintable .property::>("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(); 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(&convert.static_pad("sink").unwrap()).unwrap()) .unwrap(); (src, sink.upcast()) }; pipeline.add_many([&src, &overlay, &sink]).unwrap(); let caps = gst_video::VideoCapsBuilder::new() .width(640) .height(480) .any_features() .build(); src.link_filtered(&overlay, &caps).unwrap(); overlay.link(&sink).unwrap(); 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 || { let Some(pipeline) = pipeline_weak.upgrade() else { return glib::ControlFlow::Break; }; let position = pipeline.query_position::(); label.set_text(&format!("Position: {:.0}", position.display())); glib::ControlFlow::Continue }); 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(); let bus_watch = bus .add_watch_local(move |_, msg| { use gst::MessageView; let Some(app) = app_weak.upgrade() else { return glib::ControlFlow::Break; }; 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(); } _ => (), }; glib::ControlFlow::Continue }) .expect("Failed to add bus watch"); let timeout_id = RefCell::new(Some(timeout_id)); let pipeline = RefCell::new(Some(pipeline)); let bus_watch = RefCell::new(Some(bus_watch)); app.connect_shutdown(move |_| { window.close(); drop(bus_watch.borrow_mut().take()); 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(); } }); } fn main() -> glib::ExitCode { gst::init().unwrap(); gtk::init().unwrap(); gstgtk4::plugin_register_static().expect("Failed to register gstgtk4 plugin"); let app = gtk::Application::new(None::<&str>, gio::ApplicationFlags::FLAGS_NONE); app.connect_activate(create_ui); let res = app.run(); unsafe { gst::deinit(); } res } gst-plugin-gtk4-0.12.3/src/lib.rs000064400000000000000000000025661046102023000145630ustar 00000000000000// // Copyright (C) 2021 Bilal Elmoussaoui // Copyright (C) 2021 Jordan Petridis // Copyright (C) 2021 Sebastian Dröge // // This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at // . // // SPDX-License-Identifier: MPL-2.0 #![allow(clippy::non_send_fields_in_send_ty, unused_doc_comments)] /** * plugin-gtk4: * * Since: plugins-rs-0.8.0 */ use gst::glib; mod sink; mod utils; pub use sink::PaintableSink; fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { #[cfg(not(feature = "gtk_v4_10"))] { if gtk::micro_version() >= 13 { gst::warning!(sink::imp::CAT, obj: plugin, "GTK 4.13 or newer detected but plugin not compiled with support for this version. Rendering of video frames with alpha will likely be wrong"); } } sink::register(plugin) } gst::plugin_define!( gtk4, env!("CARGO_PKG_DESCRIPTION"), plugin_init, concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")), // FIXME: MPL-2.0 is only allowed since 1.18.3 (as unknown) and 1.20 (as known) "MPL", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_NAME"), env!("CARGO_PKG_REPOSITORY"), env!("BUILD_REL_DATE") ); gst-plugin-gtk4-0.12.3/src/sink/frame.rs000064400000000000000000000402041046102023000160420ustar 00000000000000// // Copyright (C) 2021 Bilal Elmoussaoui // Copyright (C) 2021 Jordan Petridis // Copyright (C) 2021 Sebastian Dröge // // This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at // . // // SPDX-License-Identifier: MPL-2.0 use gst_video::prelude::*; #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] use gst_gl::prelude::*; use gtk::{gdk, glib}; use std::collections::{HashMap, HashSet}; #[derive(Debug)] enum MappedFrame { SysMem(gst_video::VideoFrame), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] GL { frame: gst_gl::GLVideoFrame, wrapped_context: gst_gl::GLContext, }, } impl MappedFrame { fn buffer(&self) -> &gst::BufferRef { match self { MappedFrame::SysMem(frame) => frame.buffer(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.buffer(), } } fn width(&self) -> u32 { match self { MappedFrame::SysMem(frame) => frame.width(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.width(), } } fn height(&self) -> u32 { match self { MappedFrame::SysMem(frame) => frame.height(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.height(), } } fn format_info(&self) -> gst_video::VideoFormatInfo { match self { MappedFrame::SysMem(frame) => frame.format_info(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.format_info(), } } } #[derive(Debug)] pub(crate) struct Frame { frame: MappedFrame, overlays: Vec, } #[derive(Debug)] struct Overlay { frame: gst_video::VideoFrame, x: i32, y: i32, width: u32, height: u32, global_alpha: f32, } #[derive(Debug)] pub(crate) struct Texture { pub texture: gdk::Texture, pub x: f32, pub y: f32, pub width: f32, pub height: f32, pub global_alpha: f32, pub has_alpha: bool, } struct FrameWrapper(gst_video::VideoFrame); impl AsRef<[u8]> for FrameWrapper { fn as_ref(&self) -> &[u8] { self.0.plane_data(0).unwrap() } } fn video_format_to_memory_format(f: gst_video::VideoFormat) -> gdk::MemoryFormat { match f { gst_video::VideoFormat::Bgra => gdk::MemoryFormat::B8g8r8a8, gst_video::VideoFormat::Argb => gdk::MemoryFormat::A8r8g8b8, gst_video::VideoFormat::Rgba => gdk::MemoryFormat::R8g8b8a8, gst_video::VideoFormat::Abgr => gdk::MemoryFormat::A8b8g8r8, gst_video::VideoFormat::Rgb => gdk::MemoryFormat::R8g8b8, gst_video::VideoFormat::Bgr => gdk::MemoryFormat::B8g8r8, _ => unreachable!(), } } fn video_frame_to_memory_texture( frame: gst_video::VideoFrame, cached_textures: &mut HashMap, used_textures: &mut HashSet, ) -> (gdk::Texture, f64) { let texture_id = frame.plane_data(0).unwrap().as_ptr() as usize; let pixel_aspect_ratio = (frame.info().par().numer() as f64) / (frame.info().par().denom() as f64); if let Some(texture) = cached_textures.get(&texture_id) { used_textures.insert(texture_id); return (texture.clone(), pixel_aspect_ratio); } let format = video_format_to_memory_format(frame.format()); let width = frame.width(); let height = frame.height(); let rowstride = frame.plane_stride()[0] as usize; let texture = gdk::MemoryTexture::new( width as i32, height as i32, format, &glib::Bytes::from_owned(FrameWrapper(frame)), rowstride, ) .upcast::(); cached_textures.insert(texture_id, texture.clone()); used_textures.insert(texture_id); (texture, pixel_aspect_ratio) } #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] fn video_frame_to_gl_texture( frame: gst_gl::GLVideoFrame, cached_textures: &mut HashMap, used_textures: &mut HashSet, gdk_context: &gdk::GLContext, #[allow(unused)] wrapped_context: &gst_gl::GLContext, ) -> (gdk::Texture, f64) { let texture_id = frame.texture_id(0).expect("Invalid texture id") as usize; let pixel_aspect_ratio = (frame.info().par().numer() as f64) / (frame.info().par().denom() as f64); if let Some(texture) = cached_textures.get(&(texture_id)) { used_textures.insert(texture_id); return (texture.clone(), pixel_aspect_ratio); } let width = frame.width(); let height = frame.height(); let sync_meta = frame.buffer().meta::().unwrap(); let texture = unsafe { #[cfg(feature = "gtk_v4_12")] { let format = { let format = video_format_to_memory_format(frame.format()); #[cfg(feature = "gtk_v4_14")] { use gtk::prelude::*; if gdk_context.api() != gdk::GLAPI::GLES || gdk_context.version().0 < 3 { // Map alpha formats to the pre-multiplied versions because we pre-multiply // ourselves if not GLES3 with the new GL renderer is used as the GTK GL // backend does not natively support non-premultiplied formats. match format { gdk::MemoryFormat::B8g8r8a8 => gdk::MemoryFormat::B8g8r8a8Premultiplied, gdk::MemoryFormat::A8r8g8b8 => gdk::MemoryFormat::A8r8g8b8Premultiplied, gdk::MemoryFormat::R8g8b8a8 => gdk::MemoryFormat::R8g8b8a8Premultiplied, gdk::MemoryFormat::A8b8g8r8 => gdk::MemoryFormat::A8r8g8b8Premultiplied, gdk::MemoryFormat::R8g8b8 | gdk::MemoryFormat::B8g8r8 => format, _ => unreachable!(), } } else { format } } #[cfg(not(feature = "gtk_v4_14"))] { // Map alpha formats to the pre-multiplied versions because we pre-multiply // ourselves in pre-4.14 versions as the GTK GL backend does not natively // support non-premultiplied formats match format { gdk::MemoryFormat::B8g8r8a8 => gdk::MemoryFormat::B8g8r8a8Premultiplied, gdk::MemoryFormat::A8r8g8b8 => gdk::MemoryFormat::A8r8g8b8Premultiplied, gdk::MemoryFormat::R8g8b8a8 => gdk::MemoryFormat::R8g8b8a8Premultiplied, gdk::MemoryFormat::A8b8g8r8 => gdk::MemoryFormat::A8r8g8b8Premultiplied, gdk::MemoryFormat::R8g8b8 | gdk::MemoryFormat::B8g8r8 => format, _ => unreachable!(), } } }; let sync_point = (*sync_meta.as_ptr()).data; gdk::GLTextureBuilder::new() .set_context(Some(gdk_context)) .set_id(texture_id as u32) .set_width(width as i32) .set_height(height as i32) .set_format(format) .set_sync(Some(sync_point)) .build_with_release_func(move || { // Unmap and drop the GStreamer GL texture once GTK is done with it and not earlier drop(frame); }) } #[cfg(not(feature = "gtk_v4_12"))] { sync_meta.wait(wrapped_context); gdk::GLTexture::with_release_func( gdk_context, texture_id as u32, width as i32, height as i32, move || { // Unmap and drop the GStreamer GL texture once GTK is done with it and not earlier drop(frame); }, ) } .upcast::() }; cached_textures.insert(texture_id, texture.clone()); used_textures.insert(texture_id); (texture, pixel_aspect_ratio) } impl Frame { pub(crate) fn into_textures( self, #[allow(unused_variables)] gdk_context: Option<&gdk::GLContext>, cached_textures: &mut HashMap, ) -> Vec { let mut textures = Vec::with_capacity(1 + self.overlays.len()); let mut used_textures = HashSet::with_capacity(1 + self.overlays.len()); let width = self.frame.width(); let height = self.frame.height(); let has_alpha = self.frame.format_info().has_alpha(); let (texture, pixel_aspect_ratio) = match self.frame { MappedFrame::SysMem(frame) => { video_frame_to_memory_texture(frame, cached_textures, &mut used_textures) } #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, wrapped_context, } => { let Some(gdk_context) = gdk_context else { // This will fail badly if the video frame was actually mapped as GL texture // but this case can't really happen as we only do that if we actually have a // GDK GL context. unreachable!(); }; video_frame_to_gl_texture( frame, cached_textures, &mut used_textures, gdk_context, &wrapped_context, ) } }; textures.push(Texture { texture, x: 0.0, y: 0.0, width: width as f32 * pixel_aspect_ratio as f32, height: height as f32, global_alpha: 1.0, has_alpha, }); for overlay in self.overlays { let has_alpha = overlay.frame.format_info().has_alpha(); let (texture, _pixel_aspect_ratio) = video_frame_to_memory_texture(overlay.frame, cached_textures, &mut used_textures); textures.push(Texture { texture, x: overlay.x as f32, y: overlay.y as f32, width: overlay.width as f32, height: overlay.height as f32, global_alpha: overlay.global_alpha, has_alpha, }); } // Remove textures that were not used this time cached_textures.retain(|id, _| used_textures.contains(id)); textures } } impl Frame { pub(crate) fn new( buffer: &gst::Buffer, info: &gst_video::VideoInfo, #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] wrapped_context: Option< &gst_gl::GLContext, >, #[allow(unused_variables)] #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst-gl")))] wrapped_context: Option<&()>, ) -> Result { // Empty buffers get filtered out in show_frame debug_assert!(buffer.n_memory() > 0); let mut frame; #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst-gl")))] { frame = Self { frame: MappedFrame::SysMem( gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info) .map_err(|_| gst::FlowError::Error)?, ), overlays: vec![], }; } #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { // Check we received a buffer with GL memory and if the context of that memory // can share with the wrapped context around the GDK GL context. // // If not it has to be uploaded to the GPU. let memory_ctx = buffer .peek_memory(0) .downcast_memory_ref::() .and_then(|m| { let ctx = m.context(); if wrapped_context .map_or(false, |wrapped_context| wrapped_context.can_share(ctx)) { Some(ctx) } else { None } }); if let Some(memory_ctx) = memory_ctx { // If there is no GLSyncMeta yet then we need to add one here now, which requires // obtaining a writable buffer. let mapped_frame = if buffer.meta::().is_some() { gst_gl::GLVideoFrame::from_buffer_readable(buffer.clone(), info) .map_err(|_| gst::FlowError::Error)? } else { let mut buffer = buffer.clone(); { let buffer = buffer.make_mut(); gst_gl::GLSyncMeta::add(buffer, memory_ctx); } gst_gl::GLVideoFrame::from_buffer_readable(buffer, info) .map_err(|_| gst::FlowError::Error)? }; // Now that it's guaranteed that there is a sync meta and the frame is mapped, set // a sync point so we can ensure that the texture is ready later when making use of // it as gdk::GLTexture. let meta = mapped_frame.buffer().meta::().unwrap(); meta.set_sync_point(memory_ctx); frame = Self { frame: MappedFrame::GL { frame: mapped_frame, wrapped_context: wrapped_context.unwrap().clone(), }, overlays: vec![], }; } else { frame = Self { frame: MappedFrame::SysMem( gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info) .map_err(|_| gst::FlowError::Error)?, ), overlays: vec![], }; } } frame.overlays = frame .frame .buffer() .iter_meta::() .flat_map(|meta| { meta.overlay() .iter() .filter_map(|rect| { let buffer = rect .pixels_unscaled_argb(gst_video::VideoOverlayFormatFlags::GLOBAL_ALPHA); let (x, y, width, height) = rect.render_rectangle(); let global_alpha = rect.global_alpha(); let vmeta = buffer.meta::().unwrap(); let info = gst_video::VideoInfo::builder( vmeta.format(), vmeta.width(), vmeta.height(), ) .build() .unwrap(); let frame = gst_video::VideoFrame::from_buffer_readable(buffer, &info).ok()?; Some(Overlay { frame, x, y, width, height, global_alpha, }) }) .collect::>() }) .collect(); Ok(frame) } } gst-plugin-gtk4-0.12.3/src/sink/imp.rs000064400000000000000000001135101046102023000155360ustar 00000000000000// // Copyright (C) 2021 Bilal Elmoussaoui // Copyright (C) 2021 Jordan Petridis // Copyright (C) 2021 Sebastian Dröge // // This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at // . // // SPDX-License-Identifier: MPL-2.0 use super::SinkEvent; use crate::sink::frame::Frame; use crate::sink::paintable::Paintable; use glib::thread_guard::ThreadGuard; use gtk::prelude::*; use gtk::{gdk, glib}; #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] use gst_gl::prelude::GLContextExt as GstGLContextExt; #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] use gst_gl::prelude::*; #[allow(unused_imports)] use gst::prelude::*; use gst::subclass::prelude::*; use gst_base::subclass::prelude::*; use gst_video::subclass::prelude::*; use once_cell::sync::Lazy; use std::sync::{Mutex, MutexGuard}; use crate::utils; // Global GL context that is created by the first sink and kept around until the end of the // process. This is provided to other elements in the pipeline to make sure they create GL contexts // that are sharing with the GTK GL context. #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] enum GLContext { Uninitialized, Unsupported, Initialized { display: gst_gl::GLDisplay, wrapped_context: gst_gl::GLContext, gdk_context: ThreadGuard, }, } #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] static GL_CONTEXT: Mutex = Mutex::new(GLContext::Uninitialized); pub(crate) static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new( "gtk4paintablesink", gst::DebugColorFlags::empty(), Some("GTK4 Paintable sink"), ) }); #[derive(Default)] pub struct PaintableSink { paintable: Mutex>>, window: Mutex>>, info: Mutex>, sender: Mutex>>, pending_frame: Mutex>, cached_caps: Mutex>, } impl Drop for PaintableSink { fn drop(&mut self) { let mut paintable = self.paintable.lock().unwrap(); if let Some(paintable) = paintable.take() { glib::MainContext::default().invoke(|| drop(paintable)) } } } #[glib::object_subclass] impl ObjectSubclass for PaintableSink { const NAME: &'static str = "GstGtk4PaintableSink"; type Type = super::PaintableSink; type ParentType = gst_video::VideoSink; } impl ObjectImpl for PaintableSink { fn properties() -> &'static [glib::ParamSpec] { static PROPERTIES: Lazy> = Lazy::new(|| { vec![ glib::ParamSpecObject::builder::("paintable") .nick("Paintable") .blurb("The Paintable the sink renders to") .read_only() .build(), ] }); PROPERTIES.as_ref() } fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { match pspec.name() { "paintable" => { // Fix segfault when GTK3 and GTK4 are loaded (e.g. `gst-inspect-1.0 -a`) // checking if GtkBin is registered to know if libgtk3.so is already present // GtkBin was dropped for GTK4 https://gitlab.gnome.org/GNOME/gtk/-/commit/3c165b3b77 if glib::types::Type::from_name("GtkBin").is_some() { gst::error!(CAT, imp: self, "Skipping the creation of paintable to avoid segfault between GTK3 and GTK4"); return None::<&gdk::Paintable>.to_value(); } let mut paintable = self.paintable.lock().unwrap(); if paintable.is_none() { self.create_paintable(&mut paintable); } let paintable = match &*paintable { Some(ref paintable) => paintable, None => { gst::error!(CAT, imp: self, "Failed to create paintable"); return None::<&gdk::Paintable>.to_value(); } }; // Getter must be called from the main thread if paintable.is_owner() { paintable.get_ref().to_value() } else { gst::error!( CAT, imp: self, "Can't retrieve Paintable from non-main thread" ); None::<&gdk::Paintable>.to_value() } } _ => unimplemented!(), } } } impl GstObjectImpl for PaintableSink {} impl ElementImpl for PaintableSink { fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { static ELEMENT_METADATA: Lazy = Lazy::new(|| { gst::subclass::ElementMetadata::new( "GTK 4 Paintable Sink", "Sink/Video", "A GTK 4 Paintable sink", "Bilal Elmoussaoui , Jordan Petridis , Sebastian Dröge ", ) }); Some(&*ELEMENT_METADATA) } fn pad_templates() -> &'static [gst::PadTemplate] { static PAD_TEMPLATES: Lazy> = Lazy::new(|| { // Those are the supported formats by a gdk::Texture let mut caps = gst::Caps::new_empty(); { let caps = caps.get_mut().unwrap(); for features in [ None, #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] Some(gst::CapsFeatures::new([ "memory:GLMemory", "meta:GstVideoOverlayComposition", ])), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] Some(gst::CapsFeatures::new(["memory:GLMemory"])), Some(gst::CapsFeatures::new([ "memory:SystemMemory", "meta:GstVideoOverlayComposition", ])), Some(gst::CapsFeatures::new(["meta:GstVideoOverlayComposition"])), ] { const GL_FORMATS: &[gst_video::VideoFormat] = &[gst_video::VideoFormat::Rgba, gst_video::VideoFormat::Rgb]; const NON_GL_FORMATS: &[gst_video::VideoFormat] = &[ gst_video::VideoFormat::Bgra, gst_video::VideoFormat::Argb, gst_video::VideoFormat::Rgba, gst_video::VideoFormat::Abgr, gst_video::VideoFormat::Rgb, gst_video::VideoFormat::Bgr, ]; let formats = if features .as_ref() .is_some_and(|features| features.contains("memory:GLMemory")) { GL_FORMATS } else { NON_GL_FORMATS }; let mut c = gst_video::video_make_raw_caps(formats).build(); if let Some(features) = features { let c = c.get_mut().unwrap(); if features.contains("memory:GLMemory") { c.set("texture-target", "2D") } c.set_features_simple(Some(features)); } caps.append(c); } } vec![gst::PadTemplate::new( "sink", gst::PadDirection::Sink, gst::PadPresence::Always, &caps, ) .unwrap()] }); PAD_TEMPLATES.as_ref() } #[allow(clippy::single_match)] fn change_state( &self, transition: gst::StateChange, ) -> Result { match transition { gst::StateChange::NullToReady => { let create_window = glib::program_name().as_deref() == Some("gst-launch-1.0") || std::env::var("GST_GTK4_WINDOW").as_deref() == Ok("1"); if create_window { let res = utils::invoke_on_main_thread(gtk::init); if let Err(err) = res { gst::error!(CAT, imp: self, "Failed to create initialize GTK: {err}"); return Err(gst::StateChangeError); } } let mut paintable = self.paintable.lock().unwrap(); if paintable.is_none() { self.create_paintable(&mut paintable); } if paintable.is_none() { gst::error!(CAT, imp: self, "Failed to create paintable"); return Err(gst::StateChangeError); } drop(paintable); // Notify the pipeline about the GL display and wrapped context so that any other // elements in the pipeline ideally use the same / create GL contexts that are // sharing with this one. #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { let gl_context = GL_CONTEXT.lock().unwrap(); if let GLContext::Initialized { display, wrapped_context, .. } = &*gl_context { let display = display.clone(); let wrapped_context = wrapped_context.clone(); drop(gl_context); gst_gl::gl_element_propagate_display_context(&*self.obj(), &display); let mut ctx = gst::Context::new("gst.gl.app_context", true); { let ctx = ctx.get_mut().unwrap(); ctx.structure_mut().set("context", &wrapped_context); } let _ = self.obj().post_message( gst::message::HaveContext::builder(ctx) .src(&*self.obj()) .build(), ); } } if create_window { self.create_window(); } } _ => (), } let res = self.parent_change_state(transition); match transition { gst::StateChange::PausedToReady => { let _ = self.info.lock().unwrap().take(); let _ = self.pending_frame.lock().unwrap().take(); // Flush frames from the GDK paintable but don't wait // for this to finish as this can other deadlock. let self_ = self.to_owned(); glib::MainContext::default().invoke(move || { let paintable = self_.paintable.lock().unwrap(); if let Some(paintable) = &*paintable { paintable.get_ref().handle_flush_frames(); } }); } gst::StateChange::ReadyToNull => { let mut window_guard = self.window.lock().unwrap(); if let Some(window) = window_guard.take() { drop(window_guard); glib::MainContext::default().invoke(move || { let window = window.get_ref(); window.close(); }); } } _ => (), } res } } impl BaseSinkImpl for PaintableSink { fn caps(&self, filter: Option<&gst::Caps>) -> Option { let cached_caps = self .cached_caps .lock() .expect("Failed to lock cached caps mutex") .clone(); let mut tmp_caps = cached_caps.unwrap_or_else(|| { let templ = Self::pad_templates(); templ[0].caps().clone() }); gst::debug!(CAT, imp: self, "Advertising our own caps: {tmp_caps:?}"); if let Some(filter_caps) = filter { gst::debug!( CAT, imp: self, "Intersecting with filter caps: {filter_caps:?}", ); tmp_caps = filter_caps.intersect_with_mode(&tmp_caps, gst::CapsIntersectMode::First); }; gst::debug!(CAT, imp: self, "Returning caps: {tmp_caps:?}"); Some(tmp_caps) } fn set_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> { gst::debug!(CAT, imp: self, "Setting caps {caps:?}"); let video_info = gst_video::VideoInfo::from_caps(caps) .map_err(|_| gst::loggable_error!(CAT, "Invalid caps"))?; self.info.lock().unwrap().replace(video_info); Ok(()) } fn propose_allocation( &self, query: &mut gst::query::Allocation, ) -> Result<(), gst::LoggableError> { gst::debug!(CAT, imp: self, "Proposing Allocation query"); self.parent_propose_allocation(query)?; query.add_allocation_meta::(None); // TODO: Provide a preferred "window size" here for higher-resolution rendering query.add_allocation_meta::(None); #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { if let GLContext::Initialized { wrapped_context, .. } = &*GL_CONTEXT.lock().unwrap() { if wrapped_context.check_feature("GL_ARB_sync") || wrapped_context.check_feature("GL_EXT_EGL_sync") { query.add_allocation_meta::(None) } } } Ok(()) } fn query(&self, query: &mut gst::QueryRef) -> bool { gst::log!(CAT, imp: self, "Handling query {:?}", query); match query.view_mut() { #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] gst::QueryViewMut::Context(q) => { // Avoid holding the locks while we respond to the query // The objects are ref-counted anyway. let mut display_clone = None; let mut wrapped_context_clone = None; if let GLContext::Initialized { display, wrapped_context, .. } = &*GL_CONTEXT.lock().unwrap() { display_clone = Some(display.clone()); wrapped_context_clone = Some(wrapped_context.clone()); } if let (Some(display), Some(wrapped_context)) = (display_clone, wrapped_context_clone) { return gst_gl::functions::gl_handle_context_query( &*self.obj(), q, Some(&display), None::<&gst_gl::GLContext>, Some(&wrapped_context), ); } BaseSinkImplExt::parent_query(self, query) } _ => BaseSinkImplExt::parent_query(self, query), } } } impl VideoSinkImpl for PaintableSink { fn show_frame(&self, buffer: &gst::Buffer) -> Result { gst::trace!(CAT, imp: self, "Rendering buffer {:?}", buffer); // Empty buffer, nothing to render if buffer.n_memory() == 0 { gst::trace!( CAT, imp: self, "Empty buffer, nothing to render. Returning." ); return Ok(gst::FlowSuccess::Ok); }; let info = self.info.lock().unwrap(); let info = info.as_ref().ok_or_else(|| { gst::error!(CAT, imp: self, "Received no caps yet"); gst::FlowError::NotNegotiated })?; let wrapped_context = { #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst-gl")))] { None } #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { let gl_context = GL_CONTEXT.lock().unwrap(); if let GLContext::Initialized { wrapped_context, .. } = &*gl_context { Some(wrapped_context.clone()) } else { None } } }; let frame = Frame::new(buffer, info, wrapped_context.as_ref()).map_err(|err| { gst::error!(CAT, imp: self, "Failed to map video frame"); err })?; self.pending_frame.lock().unwrap().replace(frame); let sender = self.sender.lock().unwrap(); let sender = sender.as_ref().ok_or_else(|| { gst::error!(CAT, imp: self, "Have no main thread sender"); gst::FlowError::Flushing })?; match sender.try_send(SinkEvent::FrameChanged) { Ok(_) => (), Err(async_channel::TrySendError::Full(_)) => { gst::warning!(CAT, imp: self, "Have too many pending frames"); } Err(async_channel::TrySendError::Closed(_)) => { gst::error!(CAT, imp: self, "Have main thread receiver shut down"); return Err(gst::FlowError::Flushing); } } Ok(gst::FlowSuccess::Ok) } } impl PaintableSink { fn pending_frame(&self) -> Option { self.pending_frame.lock().unwrap().take() } fn do_action(&self, action: SinkEvent) -> glib::ControlFlow { let paintable = self.paintable.lock().unwrap(); let paintable = match &*paintable { Some(paintable) => paintable, None => return glib::ControlFlow::Break, }; match action { SinkEvent::FrameChanged => { gst::trace!(CAT, imp: self, "Frame changed"); paintable .get_ref() .handle_frame_changed(self.pending_frame()) } } glib::ControlFlow::Continue } fn configure_caps(&self) { #[allow(unused_mut)] let mut tmp_caps = Self::pad_templates()[0].caps().clone(); #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { // Filter out GL caps from the template pads if we have no context if !matches!(&*GL_CONTEXT.lock().unwrap(), GLContext::Initialized { .. }) { tmp_caps = tmp_caps .iter_with_features() .filter(|(_, features)| !features.contains("memory:GLMemory")) .map(|(s, c)| (s.to_owned(), c.to_owned())) .collect::(); } } self.cached_caps .lock() .expect("Failed to lock Mutex") .replace(tmp_caps); } fn create_window(&self) { let self_ = self.to_owned(); glib::MainContext::default().invoke(move || { let mut window_guard = self_.window.lock().unwrap(); if window_guard.is_some() { return; } let paintable = match &*self_.paintable.lock().unwrap() { Some(paintable) => paintable.get_ref().clone(), None => return, }; let window = gtk::Window::new(); let picture = gtk::Picture::new(); picture.set_paintable(Some(&paintable)); window.set_child(Some(&picture)); window.set_default_size(640, 480); window.connect_close_request({ let self_ = self_.clone(); move |_window| { if self_.window.lock().unwrap().is_some() { gst::element_imp_error!( self_, gst::ResourceError::NotFound, ("Output window was closed") ); } glib::Propagation::Proceed } }); window.show(); *window_guard = Some(ThreadGuard::new(window)); }); } fn create_paintable(&self, paintable_storage: &mut MutexGuard>>) { #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { self.initialize_gl_context(); } self.configure_caps(); self.initialize_paintable(paintable_storage); } fn initialize_paintable( &self, paintable_storage: &mut MutexGuard>>, ) { gst::debug!(CAT, imp: self, "Initializing paintable"); // The channel for the SinkEvents let (sender, receiver) = async_channel::bounded(3); // Spawn an async task on the main context to handle the channel messages let main_context = glib::MainContext::default(); let self_ = self.downgrade(); main_context.spawn(async move { while let Ok(action) = receiver.recv().await { let Some(self_) = self_.upgrade() else { break; }; self_.do_action(action); } }); // Create the paintable from the main thread let paintable = utils::invoke_on_main_thread(move || { #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { let gdk_context = if let GLContext::Initialized { gdk_context, .. } = &*GL_CONTEXT.lock().unwrap() { Some(gdk_context.get_ref().clone()) } else { None }; ThreadGuard::new(Paintable::new(gdk_context)) } #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst-gl")))] { ThreadGuard::new(Paintable::new(None)) } }); **paintable_storage = Some(paintable); *self.sender.lock().unwrap() = Some(sender); } #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] fn initialize_gl_context(&self) { gst::debug!(CAT, imp: self, "Realizing GDK GL Context"); let self_ = self.to_owned(); utils::invoke_on_main_thread(move || { self_.initialize_gl_context_main(); }); } #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] fn initialize_gl_context_main(&self) { gst::debug!(CAT, imp: self, "Realizing GDK GL Context from main thread"); let mut gl_context_guard = GL_CONTEXT.lock().unwrap(); if !matches!(&*gl_context_guard, GLContext::Uninitialized) { gst::debug!(CAT, imp: self, "Already initialized GL context before"); return; } *gl_context_guard = GLContext::Unsupported; // This can return NULL but only happens in 2 situations: // * If the function is called before gtk_init // * If the function is called after gdk_display_close(default_display) // Both of which are treated as programming errors. // // However, when we are building the docs, gtk_init doesn't get called // and this would cause the documentation generation to error. // Thus its okayish to return None here and fallback to software // rendering, since this path isn't going to be used by applications // anyway. // // FIXME: add a couple more gtk_init checks across the codebase where // applicable since this is no longer going to panic. let gdk_display = match gdk::Display::default() { Some(display) => display, None => { gst::warning!(CAT, imp: self, "Failed to retrieve GDK display"); return; } }; let gdk_context = match gdk_display.create_gl_context() { Ok(gdk_context) => gdk_context, Err(err) => { gst::warning!(CAT, imp: self, "Failed to create GDK GL Context: {err}"); return; } }; match gdk_context.type_().name() { #[cfg(feature = "x11egl")] "GdkX11GLContextEGL" => (), #[cfg(feature = "x11glx")] "GdkX11GLContextGLX" => (), #[cfg(feature = "wayland")] "GdkWaylandGLContext" => (), #[cfg(target_os = "macos")] "GdkMacosGLContext" => (), #[cfg(target_os = "windows")] "GdkWin32GLContextWGL" => (), #[cfg(all(windows, feature = "winegl"))] "GdkWin32GLContextEGL" => (), display => { gst::error!(CAT, imp: self, "Unsupported GDK display {display} for GL"); return; } } gst::info!(CAT, imp: self, "Realizing GDK GL Context",); if let Err(err) = gdk_context.realize() { gst::warning!(CAT, imp: self, "Failed to realize GDK GL Context: {err}"); return; } gst::info!(CAT, imp: self, "Successfully realized GDK GL Context"); gdk_context.make_current(); let res = match gdk_context.type_().name() { #[cfg(feature = "x11egl")] "GdkX11GLContextEGL" => self.initialize_x11egl(gdk_display), #[cfg(feature = "x11glx")] "GdkX11GLContextGLX" => self.initialize_x11glx(gdk_display), #[cfg(feature = "wayland")] "GdkWaylandGLContext" => self.initialize_waylandegl(gdk_display), #[cfg(target_os = "macos")] "GdkMacosGLContext" => self.initialize_macosgl(gdk_display), #[cfg(target_os = "windows")] "GdkWin32GLContextWGL" => self.initialize_wgl(gdk_display, &gdk_context), #[cfg(all(target_os = "windows", feature = "winegl"))] "GdkWin32GLContextEGL" => self.initialize_winegl(gdk_display), display_type => { unreachable!("Unsupported GDK display {display_type} for GL"); } }; let (display, wrapped_context) = res.unwrap(); match wrapped_context.activate(true) { Ok(_) => gst::info!(CAT, imp: self, "Successfully activated GL Context"), Err(_) => { gst::error!(CAT, imp: self, "Failed to activate GL context",); return; } }; if let Err(err) = wrapped_context.fill_info() { gst::error!( CAT, imp: self, "Failed to fill info on the GL Context: {err}", ); // Deactivate the context upon failure if wrapped_context.activate(false).is_err() { gst::error!( CAT, imp: self, "Failed to deactivate the context after failing fill info", ); } return; } gst::info!(CAT, imp: self, "Successfully initialized GL Context"); *gl_context_guard = GLContext::Initialized { display, wrapped_context, gdk_context: ThreadGuard::new(gdk_context), }; } #[cfg(feature = "x11egl")] fn initialize_x11egl( &self, display: gdk::Display, ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, "Initializing GL for x11 EGL backend and display" ); let platform = gst_gl::GLPlatform::EGL; let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform); let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); return None; } // FIXME: bindings unsafe { use glib::translate::*; let display = display.downcast::().unwrap(); let x11_display = gdk_x11::ffi::gdk_x11_display_get_egl_display(display.to_glib_none().0); if x11_display.is_null() { gst::error!(CAT, imp: self, "Failed to get EGL display"); return None; } let gst_display = gst_gl_egl::ffi::gst_gl_display_egl_new_with_egl_display(x11_display); let gst_display = gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay); let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); return None; } Some(wrapped_context) => wrapped_context, }; Some((gst_display, wrapped_context)) } } #[cfg(feature = "x11glx")] fn initialize_x11glx( &self, display: gdk::Display, ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, "Initializing GL for x11 GLX backend and display" ); let platform = gst_gl::GLPlatform::GLX; let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform); let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); return None; } // FIXME: bindings unsafe { use glib::translate::*; let display = display.downcast::().unwrap(); let x11_display = gdk_x11::ffi::gdk_x11_display_get_xdisplay(display.to_glib_none().0); if x11_display.is_null() { gst::error!(CAT, imp: self, "Failed to get X11 display"); return None; } let gst_display = gst_gl_x11::ffi::gst_gl_display_x11_new_with_display(x11_display); let gst_display = gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay); let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); return None; } Some(wrapped_context) => wrapped_context, }; Some((gst_display, wrapped_context)) } } #[cfg(feature = "wayland")] fn initialize_waylandegl( &self, display: gdk::Display, ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, "Initializing GL for Wayland EGL backend and display" ); let platform = gst_gl::GLPlatform::EGL; let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform); let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); return None; } // FIXME: bindings unsafe { use glib::translate::*; // let wayland_display = gdk_wayland::WaylandDisplay::wl_display(display.downcast()); // get the ptr directly since we are going to use it raw let display = display.downcast::().unwrap(); let wayland_display = gdk_wayland::ffi::gdk_wayland_display_get_wl_display(display.to_glib_none().0); if wayland_display.is_null() { gst::error!(CAT, imp: self, "Failed to get Wayland display"); return None; } let gst_display = gst_gl_wayland::ffi::gst_gl_display_wayland_new_with_display(wayland_display); let gst_display = gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay); let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); return None; } Some(wrapped_context) => wrapped_context, }; Some((gst_display, wrapped_context)) } } #[cfg(target_os = "macos")] fn initialize_macosgl( &self, display: gdk::Display, ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, "Initializing GL for macOS backend and display" ); let platform = gst_gl::GLPlatform::CGL; let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform); let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); return None; } let gst_display = gst_gl::GLDisplay::new(); unsafe { let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); return None; } Some(wrapped_context) => wrapped_context, }; Some((gst_display, wrapped_context)) } } #[cfg(target_os = "windows")] fn initialize_wgl( &self, _display: gdk::Display, context: &gdk::GLContext, ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, "Initializing GL with for Windows WGL backend and display." ); let platform = gst_gl::GLPlatform::WGL; let gl_api = if context.is_legacy() { gst_gl::GLAPI::OPENGL } else { gst_gl::GLAPI::OPENGL3 }; let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",); return None; } unsafe { let gst_display = if let Some(display) = gst_gl::GLDisplay::with_type(gst_gl::GLDisplayType::WIN32) { display } else { gst::error!(CAT, imp: self, "Failed to get GL display"); return None; }; gst_display.filter_gl_api(gl_api); let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); return None; } Some(wrapped_context) => wrapped_context, }; Some((gst_display, wrapped_context)) } } #[cfg(all(target_os = "windows", feature = "winegl"))] fn initialize_winegl( &self, display: gdk::Display, ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, "Initializing GL with for Windows EGL backend and display." ); let platform = gst_gl::GLPlatform::EGL; let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform); let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",); return None; } // FIXME: bindings unsafe { use gdk_win32::prelude::*; use glib::translate::*; let d = display.downcast::().unwrap(); let egl_display = d.egl_display().unwrap().as_ptr(); // TODO: On the binary distribution of GStreamer for Windows, this symbol is not there let gst_display = gst_gl_egl::ffi::gst_gl_display_egl_from_gl_display(egl_display.cast()); if gst_display.is_null() { gst::error!(CAT, imp: self, "Failed to get EGL display"); return None; } let gst_display = gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay); gst_display.filter_gl_api(gl_api); let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); return None; } Some(wrapped_context) => wrapped_context, }; Some((gst_display, wrapped_context)) } } } gst-plugin-gtk4-0.12.3/src/sink/mod.rs000064400000000000000000000021211046102023000155230ustar 00000000000000// // Copyright (C) 2021 Bilal Elmoussaoui // Copyright (C) 2021 Jordan Petridis // Copyright (C) 2021 Sebastian Dröge // // This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at // . // // SPDX-License-Identifier: MPL-2.0 use gtk::glib; use gtk::glib::prelude::*; mod frame; pub(super) mod imp; mod paintable; enum SinkEvent { FrameChanged, } glib::wrapper! { pub struct PaintableSink(ObjectSubclass) @extends gst_video::VideoSink, gst_base::BaseSink, gst::Element, gst::Object; } impl PaintableSink { pub fn new(name: Option<&str>) -> Self { glib::Object::builder().property("name", name).build() } } pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { gst::Element::register( Some(plugin), "gtk4paintablesink", gst::Rank::NONE, PaintableSink::static_type(), ) } gst-plugin-gtk4-0.12.3/src/sink/paintable/imp.rs000064400000000000000000000341341046102023000175010ustar 00000000000000// // Copyright (C) 2021 Bilal Elmoussaoui // Copyright (C) 2021 Jordan Petridis // Copyright (C) 2021 Sebastian Dröge // // This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at // . // // SPDX-License-Identifier: MPL-2.0 use gtk::prelude::*; use gtk::subclass::prelude::*; use gtk::{gdk, glib, graphene, gsk}; use crate::sink::frame::{Frame, Texture}; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use once_cell::sync::Lazy; static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new( "gtk4paintable", gst::DebugColorFlags::empty(), Some("GTK4 Paintable Sink Paintable"), ) }); #[derive(Debug)] pub struct Paintable { paintables: RefCell>, cached_textures: RefCell>, gl_context: RefCell>, background_color: Cell, #[cfg(feature = "gtk_v4_10")] scaling_filter: Cell, use_scaling_filter: Cell, #[cfg(not(feature = "gtk_v4_10"))] premult_shader: gsk::GLShader, } impl Default for Paintable { fn default() -> Self { Self { paintables: Default::default(), cached_textures: Default::default(), gl_context: Default::default(), background_color: Cell::new(gdk::RGBA::BLACK), #[cfg(feature = "gtk_v4_10")] scaling_filter: Cell::new(gsk::ScalingFilter::Linear), use_scaling_filter: Cell::new(false), #[cfg(not(feature = "gtk_v4_10"))] premult_shader: gsk::GLShader::from_bytes(&glib::Bytes::from_static(include_bytes!( "premult.glsl" ))), } } } #[glib::object_subclass] impl ObjectSubclass for Paintable { const NAME: &'static str = "GstGtk4Paintable"; type Type = super::Paintable; type Interfaces = (gdk::Paintable,); } impl ObjectImpl for Paintable { fn properties() -> &'static [glib::ParamSpec] { static PROPERTIES: Lazy> = Lazy::new(|| { vec![ glib::ParamSpecObject::builder::("gl-context") .nick("GL Context") .blurb("GL context to use for rendering") .construct_only() .build(), glib::ParamSpecUInt::builder("background-color") .nick("Background Color") .blurb("Background color to render behind the video frame and in the borders") .default_value(0) .build(), #[cfg(feature = "gtk_v4_10")] glib::ParamSpecEnum::builder_with_default::( "scaling-filter", gsk::ScalingFilter::Linear, ) .nick("Scaling Filter") .blurb("Scaling filter to use for rendering") .build(), #[cfg(feature = "gtk_v4_10")] glib::ParamSpecBoolean::builder("use-scaling-filter") .nick("Use Scaling Filter") .blurb("Use selected scaling filter or GTK default for rendering") .default_value(false) .build(), ] }); PROPERTIES.as_ref() } fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { match pspec.name() { "gl-context" => self.gl_context.borrow().to_value(), "background-color" => { let color = self.background_color.get(); let v = (f32::clamp(color.red() * 255.0, 0.0, 255.0) as u32) << 24 | (f32::clamp(color.green() * 255.0, 0.0, 255.0) as u32) << 16 | (f32::clamp(color.blue() * 255.0, 0.0, 255.0) as u32) << 8 | (f32::clamp(color.alpha() * 255.0, 0.0, 255.0) as u32); v.to_value() } #[cfg(feature = "gtk_v4_10")] "scaling-filter" => self.scaling_filter.get().to_value(), #[cfg(feature = "gtk_v4_10")] "use-scaling-filter" => self.use_scaling_filter.get().to_value(), _ => unimplemented!(), } } fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { match pspec.name() { "gl-context" => { *self.gl_context.borrow_mut() = value.get::>().unwrap(); } "background-color" => { let v = value.get::().unwrap(); let red = ((v & 0xff_00_00_00) >> 24) as f32 / 255.0; let green = ((v & 0x00_ff_00_00) >> 16) as f32 / 255.0; let blue = ((v & 0x00_00_ff_00) >> 8) as f32 / 255.0; let alpha = (v & 0x00_00_00_ff) as f32 / 255.0; self.background_color .set(gdk::RGBA::new(red, green, blue, alpha)) } #[cfg(feature = "gtk_v4_10")] "scaling-filter" => self.scaling_filter.set(value.get().unwrap()), #[cfg(feature = "gtk_v4_10")] "use-scaling-filter" => self.use_scaling_filter.set(value.get().unwrap()), _ => unimplemented!(), } } } impl PaintableImpl for Paintable { fn intrinsic_height(&self) -> i32 { if let Some(paintable) = self.paintables.borrow().first() { f32::round(paintable.height) as i32 } else { 0 } } fn intrinsic_width(&self) -> i32 { if let Some(paintable) = self.paintables.borrow().first() { f32::round(paintable.width) as i32 } else { 0 } } fn intrinsic_aspect_ratio(&self) -> f64 { if let Some(paintable) = self.paintables.borrow().first() { paintable.width as f64 / paintable.height as f64 } else { 0.0 } } fn snapshot(&self, snapshot: &gdk::Snapshot, width: f64, height: f64) { let snapshot = snapshot.downcast_ref::().unwrap(); let background_color = self.background_color.get(); let paintables = self.paintables.borrow(); if !paintables.is_empty() { gst::trace!(CAT, imp: self, "Snapshotting frame"); let (frame_width, frame_height) = paintables.first().map(|p| (p.width, p.height)).unwrap(); let mut scale_x = width / frame_width as f64; let mut scale_y = height / frame_height as f64; let mut trans_x = 0.0; let mut trans_y = 0.0; // TODO: Property for keeping aspect ratio or not if (scale_x - scale_y).abs() > f64::EPSILON { if scale_x > scale_y { trans_x = ((frame_width as f64 * scale_x) - (frame_width as f64 * scale_y)) / 2.0; scale_x = scale_y; } else { trans_y = ((frame_height as f64 * scale_y) - (frame_height as f64 * scale_x)) / 2.0; scale_y = scale_x; } } snapshot.append_color( &background_color, &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), ); snapshot.translate(&graphene::Point::new(trans_x as f32, trans_y as f32)); for Texture { texture, x, y, width: paintable_width, height: paintable_height, global_alpha, has_alpha, } in &*paintables { snapshot.push_opacity(*global_alpha as f64); let texture_width = *paintable_width * scale_x as f32; let texture_height = *paintable_height * scale_y as f32; let x = *x * scale_x as f32; let y = *y * scale_y as f32; let bounds = graphene::Rect::new(x, y, texture_width, texture_height); // Only premultiply GL textures that expect to be in premultiplied RGBA format. // // For GTK 4.14 or newer we use the correct format directly when building the // texture, but only if a GLES3+ context is used. In that case the NGL renderer is // used by GTK, which supports non-premultiplied formats correctly and fast. // // For GTK 4.10-4.12, or 4.14 and newer if a GLES2 context is used, we use a // self-mask to pre-multiply the alpha. // // For GTK before 4.10, we use a GL shader and hope that it works. #[cfg(feature = "gtk_v4_10")] { let context_requires_premult = { #[cfg(feature = "gtk_v4_14")] { self.gl_context.borrow().as_ref().map_or(false, |context| { context.api() != gdk::GLAPI::GLES || context.version().0 < 3 }) } #[cfg(not(feature = "gtk_v4_14"))] { true } }; let do_premult = context_requires_premult && texture.is::() && *has_alpha; if do_premult { snapshot.push_mask(gsk::MaskMode::Alpha); if self.use_scaling_filter.get() { #[cfg(feature = "gtk_v4_10")] snapshot.append_scaled_texture( texture, self.scaling_filter.get(), &bounds, ); } else { snapshot.append_texture(texture, &bounds); } snapshot.pop(); // pop mask // color matrix to set alpha of the source to 1.0 as it was // already applied via the mask just above. snapshot.push_color_matrix( &graphene::Matrix::from_float({ [ 1.0, 0.0, 0.0, 0.0, // 0.0, 1.0, 0.0, 0.0, // 0.0, 0.0, 1.0, 0.0, // 0.0, 0.0, 0.0, 0.0, ] }), &graphene::Vec4::new(0.0, 0.0, 0.0, 1.0), ); } if self.use_scaling_filter.get() { #[cfg(feature = "gtk_v4_10")] snapshot.append_scaled_texture(texture, self.scaling_filter.get(), &bounds); } else { snapshot.append_texture(texture, &bounds); } if do_premult { snapshot.pop(); // pop color matrix snapshot.pop(); // pop mask 2 } } #[cfg(not(feature = "gtk_v4_10"))] { let do_premult = texture.is::() && *has_alpha && gtk::micro_version() < 13; if do_premult { snapshot.push_gl_shader( &self.premult_shader, &bounds, gsk::ShaderArgsBuilder::new(&self.premult_shader, None).to_args(), ); } if self.use_scaling_filter.get() { #[cfg(feature = "gtk_v4_10")] snapshot.append_scaled_texture(texture, self.scaling_filter.get(), &bounds); } else { snapshot.append_texture(texture, &bounds); } if do_premult { snapshot.gl_shader_pop_texture(); // pop texture appended above from the shader snapshot.pop(); // pop shader } } snapshot.pop(); // pop opacity } } else { gst::trace!(CAT, imp: self, "Snapshotting black frame"); snapshot.append_color( &background_color, &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), ); } } } impl Paintable { pub(super) fn handle_frame_changed(&self, frame: Option) { let context = self.gl_context.borrow(); if let Some(frame) = frame { gst::trace!(CAT, imp: self, "Received new frame"); let new_paintables = frame.into_textures(context.as_ref(), &mut self.cached_textures.borrow_mut()); let new_size = new_paintables .first() .map(|p| (f32::round(p.width) as u32, f32::round(p.height) as u32)) .unwrap(); let old_paintables = self.paintables.replace(new_paintables); let old_size = old_paintables .first() .map(|p| (f32::round(p.width) as u32, f32::round(p.height) as u32)); if Some(new_size) != old_size { gst::debug!( CAT, imp: self, "Size changed from {old_size:?} to {new_size:?}", ); self.obj().invalidate_size(); } self.obj().invalidate_contents(); } } pub(super) fn handle_flush_frames(&self) { gst::debug!(CAT, imp: self, "Flushing frames"); self.paintables.borrow_mut().clear(); self.cached_textures.borrow_mut().clear(); self.obj().invalidate_size(); self.obj().invalidate_contents(); } } gst-plugin-gtk4-0.12.3/src/sink/paintable/mod.rs000064400000000000000000000020651046102023000174710ustar 00000000000000// // Copyright (C) 2021 Bilal Elmoussaoui // Copyright (C) 2021 Jordan Petridis // Copyright (C) 2021 Sebastian Dröge // // This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at // . // // SPDX-License-Identifier: MPL-2.0 use crate::sink::frame::Frame; use gtk::subclass::prelude::*; use gtk::{gdk, glib}; mod imp; glib::wrapper! { pub struct Paintable(ObjectSubclass) @implements gdk::Paintable; } impl Paintable { pub fn new(context: Option) -> Self { glib::Object::builder() .property("gl-context", context) .build() } } impl Paintable { pub(crate) fn handle_frame_changed(&self, frame: Option) { self.imp().handle_frame_changed(frame); } pub(crate) fn handle_flush_frames(&self) { self.imp().handle_flush_frames(); } } gst-plugin-gtk4-0.12.3/src/sink/paintable/premult.glsl000064400000000000000000000003441046102023000207150ustar 00000000000000uniform sampler2D u_texture1; void mainImage( out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv ) { fragColor = GskTexture(u_texture1, uv); fragColor.rgb = fragColor.rgb * fragColor.a; } gst-plugin-gtk4-0.12.3/src/utils.rs000064400000000000000000000006401046102023000151440ustar 00000000000000use gtk::glib; use std::sync::mpsc; pub(crate) fn invoke_on_main_thread(func: F) -> T where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { let context = glib::MainContext::default(); let (send, recv) = mpsc::channel(); context.invoke(move || { send.send(func()).expect("Somehow we dropped the receiver"); }); recv.recv().expect("Somehow we dropped the sender") }