From eedce3608327106bb5cde9f822697446ef58a48c Mon Sep 17 00:00:00 2001 From: Luca Date: Sat, 4 Jan 2025 05:33:03 +0100 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 643 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 16 + src/lib.rs | 20 ++ src/pingxelflutsink/imp.rs | 266 +++++++++++++++ src/pingxelflutsink/mod.rs | 22 ++ 6 files changed, 968 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/pingxelflutsink/imp.rs create mode 100644 src/pingxelflutsink/mod.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5b00d47 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,643 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cfg-expr" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" +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 = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gio-sys" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8446d9b475730ebef81802c1738d972db42fde1c5a36a627ebc4d665fc87db04" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys", +] + +[[package]] +name = "glib" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f969edf089188d821a30cde713b6f9eb08b20c63fc2e584aba2892a7984a8cc0" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b360ff0f90d71de99095f79c526a5888c9c92fc9ee1b19da06c6f5e75f0c2a53" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a56235e971a63bfd75abb13ef70064e1346388723422a68580d8a6fbac6423" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gst-plugin-pingxelflut" +version = "0.1.0" +dependencies = [ + "gstreamer", + "gstreamer-base", + "gstreamer-video", +] + +[[package]] +name = "gstreamer" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "700cb1b2e86dda424f85eb728102a111602317e40b4dd71cf1c0dc04e0cc5d95" +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.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d152db7983f98d5950cf64e53805286548063475fb61a5e5450fba4cec05899b" +dependencies = [ + "atomic_refcell", + "cfg-if", + "glib", + "gstreamer", + "gstreamer-base-sys", + "libc", +] + +[[package]] +name = "gstreamer-base-sys" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d47cc2d15f2a3d5eb129e5dacbbeec9600432b706805c15dff57b6aa11b2791c" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-sys" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16cf1ae0a869aa7066ce3c685b76053b4b4f48f364a5b18c4b1f36ef57469719" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-video" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fa41e40319e923236e96f0b691711d1504746ab9c89607d77d22aa84777f33f" +dependencies = [ + "cfg-if", + "futures-channel", + "glib", + "gstreamer", + "gstreamer-base", + "gstreamer-video-sys", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gstreamer-video-sys" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31dc0f49c117f4867b0f98c712aa55ebf25580151d794be8f9179ec2d877fd14" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "option-operations" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0" +dependencies = [ + "paste", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6cf1e80 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "gst-plugin-pingxelflut" +version = "0.1.0" +edition = "2021" +description = "GStreamer pingxelflut sink element" +repository = "https://git.luj0ga.de/luca/gst-plugin-pingxelflut" +license = "MPL-2.0" + +[lib] +name = "gstpingxelflut" +crate-type = ["cdylib"] + +[dependencies] +gstreamer = "0.23.4" +gstreamer-base = "0.23.4" +gstreamer-video = "0.23.4" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..73674a9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,20 @@ +mod pingxelflutsink; + +use gstreamer::glib::BoolError; +use gstreamer::Plugin; + +fn plugin_init(plugin: &Plugin) -> Result<(), BoolError> { + pingxelflutsink::register(plugin)?; + Ok(()) +} + +gstreamer::plugin_define!( + pingxelflut, + env!("CARGO_PKG_DESCRIPTION"), + plugin_init, + env!("CARGO_PKG_VERSION"), + env!("CARGO_PKG_LICENSE"), + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_REPOSITORY") +); diff --git a/src/pingxelflutsink/imp.rs b/src/pingxelflutsink/imp.rs new file mode 100644 index 0000000..1701202 --- /dev/null +++ b/src/pingxelflutsink/imp.rs @@ -0,0 +1,266 @@ +use gstreamer::glib::{self, ParamSpec, ParamSpecString, Value}; +use gstreamer::prelude::*; +use gstreamer::subclass::prelude::*; +use gstreamer::subclass::ElementMetadata; +use gstreamer::{ + Buffer, Caps, CapsIntersectMode, DebugCategory, DebugColorFlags, ErrorMessage, FlowError, + FlowSuccess, LoggableError, PadDirection, PadPresence, PadTemplate, ResourceError, +}; + +use gstreamer_base::prelude::*; +use gstreamer_base::subclass::prelude::*; + +use gstreamer_video::prelude::*; +use gstreamer_video::subclass::prelude::*; +use gstreamer_video::{VideoCapsBuilder, VideoFormat, VideoFrame, VideoInfo, VideoSink}; + +use std::cmp; +use std::net::{Ipv6Addr, SocketAddr, UdpSocket}; +use std::sync::{LazyLock, Mutex}; + +const HEIGHT: u32 = 196; + +const WIDTH: u32 = 346; + +static CAT: LazyLock = LazyLock::new(|| { + DebugCategory::new( + "pingxelflutsink", + DebugColorFlags::empty(), + Some("Pingxelflut sink"), + ) +}); + +#[derive(Default)] +struct Settings { + bind_address: Option, + prefix: Option, +} + +#[derive(Default)] +enum State { + #[default] + Stopped, + Started { + socket: UdpSocket, + }, +} + +#[derive(Default)] +pub struct PingxelflutSink { + caps: Mutex>, + info: Mutex>, + settings: Mutex, + state: Mutex, +} + +#[glib::object_subclass] +impl ObjectSubclass for PingxelflutSink { + const NAME: &'static str = "GstPingxelflutSink"; + + type Type = super::PingxelflutSink; + type ParentType = VideoSink; +} + +impl ObjectImpl for PingxelflutSink { + fn properties() -> &'static [ParamSpec] { + static PROPERTIES: LazyLock> = LazyLock::new(|| { + vec![ + ParamSpecString::builder("bind-address") + .nick("Bind address") + .blurb("Local address to bind to") + .build(), + ParamSpecString::builder("prefix") + .nick("Destination prefix") + .blurb("Prefix of pingxelflut server") + .build(), + ] + }); + + PROPERTIES.as_ref() + } + + fn set_property(&self, _id: usize, value: &Value, pspec: &ParamSpec) { + let mut settings = self.settings.lock().unwrap(); + + match pspec.name() { + "bind-address" => { + settings.bind_address = value + .get::>() + .unwrap() + .map(|s| s.parse().ok()) + .unwrap_or(None); + } + "prefix" => { + settings.prefix = value + .get::>() + .unwrap() + .map(|s| s.parse().ok()) + .unwrap_or(None); + } + _ => unimplemented!(), + } + } + + fn property(&self, _id: usize, pspec: &ParamSpec) -> Value { + let settings = self.settings.lock().unwrap(); + + match pspec.name() { + "bind-address" => settings + .bind_address + .as_ref() + .map(|addr| addr.to_string()) + .to_value(), + "prefix" => settings + .prefix + .as_ref() + .map(|addr| addr.to_string()) + .to_value(), + _ => unimplemented!(), + } + } +} + +impl GstObjectImpl for PingxelflutSink {} + +impl ElementImpl for PingxelflutSink { + fn metadata() -> Option<&'static ElementMetadata> { + static ELEMENT_METADATA: LazyLock = LazyLock::new(|| { + ElementMetadata::new( + "Pingxelflut sink", + "Sink/Video", + "Send stream to a pingxelflut screen", + "Luca ", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [PadTemplate] { + static PAD_TEMPLATES: LazyLock> = LazyLock::new(|| { + vec![PadTemplate::new( + "sink", + PadDirection::Sink, + PadPresence::Always, + &VideoCapsBuilder::new().format(VideoFormat::Rgb).build(), + ) + .unwrap()] + }); + + PAD_TEMPLATES.as_ref() + } +} + +impl BaseSinkImpl for PingxelflutSink { + fn start(&self) -> Result<(), ErrorMessage> { + let mut state = self.state.lock().unwrap(); + if let State::Started { .. } = *state { + unreachable!("PingxelflutSink already started"); + } + + let settings = self.settings.lock().unwrap(); + let bind_address = settings.bind_address.as_ref().ok_or_else(|| { + gstreamer::error_msg!(ResourceError::Settings, ["Bind address is not defined"]) + })?; + + if let None = settings.prefix { + return Err(gstreamer::error_msg!( + ResourceError::Settings, + ["Prefix is not defined"] + )); + } + + let socket = UdpSocket::bind(bind_address).map_err(|_| { + gstreamer::error_msg!( + ResourceError::OpenWrite, + ["Could not bind UDP socket to address {}", bind_address] + ) + })?; + + *state = State::Started { socket }; + + Ok(()) + } + + fn stop(&self) -> Result<(), ErrorMessage> { + let mut state = self.state.lock().unwrap(); + if let State::Stopped = *state { + return Err(gstreamer::error_msg!( + ResourceError::Settings, + ["PingxelflutSink not started"] + )); + } + + *state = State::Stopped; + + Ok(()) + } + + fn caps(&self, filter: Option<&Caps>) -> Option { + let mut caps = self + .caps + .lock() + .unwrap() + .clone() + .unwrap_or_else(|| Self::pad_templates()[0].caps().clone()); + + if let Some(filter_caps) = filter { + caps = filter_caps.intersect_with_mode(&caps, CapsIntersectMode::First); + } + + Some(caps) + } + + fn set_caps(&self, caps: &Caps) -> Result<(), LoggableError> { + let video_info = VideoInfo::from_caps(caps) + .map_err(|_| gstreamer::loggable_error!(CAT, "Invalid caps"))?; + + *self.info.lock().unwrap() = Some(video_info); + + Ok(()) + } +} + +impl VideoSinkImpl for PingxelflutSink { + fn show_frame(&self, buffer: &Buffer) -> Result { + let info = self.info.lock().unwrap(); + let info = info.as_ref().ok_or_else(|| FlowError::NotNegotiated)?; + + let frame = + VideoFrame::from_buffer_readable(buffer.clone(), info).map_err(|_| FlowError::Error)?; + let data = frame.plane_data(0).unwrap(); + + let settings = self.settings.lock().unwrap(); + let prefix = settings.prefix.as_ref().unwrap(); + + if let State::Started { ref socket } = *self.state.lock().unwrap() { + for y in 0..cmp::min(frame.height(), HEIGHT) { + for x in 0..cmp::min(frame.width(), WIDTH) { + let i = + (y * frame.plane_stride()[0] as u32 + x * frame.n_components()) as usize; + let (r, g, b) = (data[i] as u16, data[i + 1] as u16, data[i + 2] as u16); + + let _ = socket.send_to( + &[], + ( + prefix + | Ipv6Addr::new( + 0, + 0, + 0, + 0, + x as u16, + y as u16, + (r << 8) | g, + b << 8, + ), + 1337, + ), + ); + } + } + } + + Ok(FlowSuccess::Ok) + } +} diff --git a/src/pingxelflutsink/mod.rs b/src/pingxelflutsink/mod.rs new file mode 100644 index 0000000..734f04c --- /dev/null +++ b/src/pingxelflutsink/mod.rs @@ -0,0 +1,22 @@ +mod imp; + +use gstreamer::glib::{self, BoolError}; +use gstreamer::prelude::*; +use gstreamer::{Element, Object, Plugin, Rank}; + +use gstreamer_base::BaseSink; + +use gstreamer_video::VideoSink; + +glib::wrapper! { + pub struct PingxelflutSink(ObjectSubclass) @extends VideoSink, BaseSink, Element, Object; +} + +pub fn register(plugin: &Plugin) -> Result<(), BoolError> { + Element::register( + Some(plugin), + "pingxelflutsink", + Rank::NONE, + PingxelflutSink::static_type(), + ) +}