diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e944336..b84ada5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,10 +26,10 @@ jobs: - name: Install rust toolchain run: sudo apt-get install -qy cargo-1.80 rust-1.80-clippy - name: Install system dependencies - run: sudo apt-get install -qy liblzma-dev libpipewire-0.3-dev libclang-dev libdbus-1-dev + run: sudo apt-get install -qy liblzma-dev libpipewire-0.3-dev libclang-dev libdbus-1-dev ffmpeg libavutil-dev libavformat-dev libavfilter-dev libavdevice-dev - name: Run Clippy run: cargo clippy --all-targets --all-features - name: Build - run: cargo build --release --verbose + run: cargo build --release diff --git a/Cargo.lock b/Cargo.lock index b9ad7ea..0f6ccc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arbitrary" @@ -148,7 +148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "annotate-snippets", - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools", @@ -162,6 +162,24 @@ dependencies = [ "syn", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -176,9 +194,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitstream-io" @@ -227,15 +245,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder-lite" @@ -245,15 +257,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.16" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", @@ -304,9 +316,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -314,9 +326,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -326,9 +338,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -437,7 +449,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation", "core-graphics-types", "foreign-types", @@ -516,9 +528,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.5" +version = "3.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" dependencies = [ "nix 0.29.0", "windows-sys", @@ -526,9 +538,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "dbus" @@ -568,9 +580,9 @@ dependencies = [ [[package]] name = "either" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_filter" @@ -584,14 +596,14 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] @@ -618,9 +630,9 @@ dependencies = [ [[package]] name = "fast_image_resize" -version = "5.1.2" +version = "5.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55264ccc579fc127eebf6c6c1841d0c160d79a44c8f6f97047b7bc4a9c0d1a5" +checksum = "e146c782f75f50995dae9ecf9ec189fc9d0d2906318cc6826ea9451717fe52ec" dependencies = [ "bytemuck", "cfg-if", @@ -640,10 +652,35 @@ dependencies = [ ] [[package]] -name = "flate2" -version = "1.1.0" +name = "ffmpeg-next" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "da02698288e0275e442a47fc12ca26d50daf0d48b15398ba5906f20ac2e2a9f9" +dependencies = [ + "bitflags 2.9.0", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc3234d0a4b2f7d083699d0860c6c9dd83713908771b60f94a96f8704adfe45" +dependencies = [ + "bindgen 0.70.1", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -789,9 +826,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -800,14 +837,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -828,9 +865,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -838,9 +875,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "heck" @@ -849,10 +886,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "http" -version = "1.2.0" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -861,21 +904,15 @@ dependencies = [ [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "image" -version = "0.25.5" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", @@ -912,9 +949,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -948,16 +985,41 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -987,9 +1049,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.170" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libdbus-sys" @@ -1026,7 +1088,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f3a4b81b2a2d8c7f300643676202debd1b7c929dbf5c9bb89402ea11d19810" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cc", "convert_case", "cookie-factory", @@ -1043,7 +1105,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf0d9716420364790e85cbb9d3ac2c950bde16a7dd36f3209b7dfdfc4a24d01f" dependencies = [ - "bindgen", + "bindgen 0.69.5", "cc", "system-deps", ] @@ -1066,9 +1128,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "loop9" @@ -1112,9 +1174,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -1132,7 +1194,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "libc", ] @@ -1143,7 +1205,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "cfg_aliases", "libc", @@ -1224,6 +1286,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -1265,9 +1337,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "parking_lot" @@ -1317,7 +1389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08e645ba5c45109106d56610b3ee60eb13a6f2beb8b74f8dc8186cf261788dda" dependencies = [ "anyhow", - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", "libspa", "libspa-sys", @@ -1333,16 +1405,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "849e188f90b1dda88fe2bfe1ad31fe5f158af2c98f80fb5d13726c44f3f01112" dependencies = [ - "bindgen", + "bindgen 0.69.5", "libspa-sys", "system-deps", ] [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" @@ -1358,19 +1430,34 @@ dependencies = [ ] [[package]] -name = "ppv-lite86" -version = "0.2.20" +name = "portable-atomic" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ - "zerocopy 0.7.35", + "portable-atomic", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -1411,13 +1498,19 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -1437,13 +1530,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.2", - "zerocopy 0.8.21", + "rand_core 0.9.3", ] [[package]] @@ -1463,7 +1555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.2", + "rand_core 0.9.3", ] [[package]] @@ -1472,17 +1564,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] name = "rand_core" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", - "zerocopy 0.8.21", + "getrandom 0.3.2", ] [[package]] @@ -1522,9 +1613,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" dependencies = [ "avif-serialize", "imgref", @@ -1557,11 +1648,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.9" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -1617,9 +1708,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "scap" @@ -1672,18 +1763,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -1701,29 +1792,30 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.13.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33abd53582a995aaf5d387be4a1f7eb294a084185f88f8cf61652b6272041660" +checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" dependencies = [ "bitvec", "log", "once_cell", "rust-lzma", - "thiserror 2.0.11", - "tungstenite", + "thiserror 2.0.12", ] [[package]] name = "servicepoint-cli" -version = "0.3.0" +version = "0.4.0" dependencies = [ "clap", "env_logger", "fast_image_resize", + "ffmpeg-next", "image", "log", "scap", "servicepoint", + "tungstenite", ] [[package]] @@ -1769,9 +1861,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "strsim" @@ -1781,9 +1873,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.98" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -1853,11 +1945,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -1873,9 +1965,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -1895,9 +1987,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -1907,18 +1999,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", @@ -1938,9 +2030,9 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.0", + "rand 0.9.1", "sha1", - "thiserror 2.0.11", + "thiserror 2.0.12", "utf-8", ] @@ -1952,9 +2044,9 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" @@ -2017,9 +2109,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -2131,17 +2223,40 @@ dependencies = [ ] [[package]] -name = "windows-capture" -version = "1.4.2" +name = "windows" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6001b777f61cafce437201de46a019ed7f4afed3b669f02e5ce4e0759164cb3e" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core 0.61.0", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-capture" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d10b4be8b907c7055bc7270dd68d2b920978ffacc1599dcb563a79f0e68d16" dependencies = [ "clap", "ctrlc", "parking_lot", "rayon", - "thiserror 1.0.69", - "windows 0.58.0", + "thiserror 2.0.12", + "windows 0.61.1", + "windows-future", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.0", ] [[package]] @@ -2159,13 +2274,36 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets", ] +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -2177,6 +2315,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -2188,6 +2337,33 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -2197,16 +2373,34 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -2282,20 +2476,20 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -2318,39 +2512,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" -dependencies = [ - "zerocopy-derive 0.8.21", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index a8cd60f..ca24e69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "servicepoint-cli" description = "A command line interface for the ServicePoint display." -version = "0.3.0" +version = "0.4.0" edition = "2021" rust-version = "1.80.0" publish = true @@ -13,10 +13,20 @@ homepage = "https://crates.io/crates/servicepoint-cli" keywords = ["cccb", "cccb-servicepoint", "cli"] [dependencies] -servicepoint = { version = "0.13.2", features = ["protocol_websocket"] } clap = { version = "4.5", features = ["derive"] } env_logger = "0.11" log = "0.4" scap = "0.0.8" image = "0.25.5" -fast_image_resize = { version = "5.1.2", features = ["image"] } +fast_image_resize = { version = "5.1", features = ["image"] } +tungstenite = "0.26" +ffmpeg-next = "7.1.0" + +[dependencies.servicepoint] +package = "servicepoint" +version = "0.14.1" + +[profile.release] +lto = true # Enable link-time optimization +codegen-units = 1 # Reduce number of codegen units to increase optimizations +strip = true # Strip symbols from binary diff --git a/README.md b/README.md index be1dab7..3876566 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # servicepoint-cli +[![Release](https://git.berlin.ccc.de/servicepoint/servicepoint-cli/badges/release.svg)](https://git.berlin.ccc.de/servicepoint/servicepoint-cli/releases) +[![crates.io](https://img.shields.io/crates/v/servicepoint-cli.svg)](https://crates.io/crates/servicepoint-cli) +[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint-cli)](https://crates.io/crates/servicepoint-cli) +![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint-cli) +[![CI](https://git.berlin.ccc.de/servicepoint/servicepoint-cli/badges/workflows/rust.yml/badge.svg)](https://git.berlin.ccc.de/servicepoint/servicepoint-cli) + This repository contains a command line interface for the ServicePoint display. To send commands, this uses the [servicepoint crate](https://crates.io/crates/servicepoint). @@ -33,15 +39,15 @@ cargo run -- ## Usage -``` +```text Usage: servicepoint-cli [OPTIONS] Commands: - reset-everything Reset both pixels and brightness [aliases: r] - pixels Commands for manipulating pixels [aliases: p] - brightness Commands for manipulating the brightness [aliases: b] - text Commands for sending text to the screen [aliases: t] - help Print this message or the help of the given subcommand(s) + reset Reset both pixels and brightness [aliases: r] + pixels Commands for manipulating pixels [aliases: p] + brightness Commands for manipulating the brightness [aliases: b] + text Commands for sending text to the screen [aliases: t] + help Print this message or the help of the given subcommand(s) Options: -d, --destination ip:port of the servicepoint display [default: 127.0.0.1:2342] @@ -53,7 +59,7 @@ Options: ### Pixels -``` +```text Commands for manipulating pixels Usage: servicepoint-cli pixels @@ -63,12 +69,13 @@ Commands: flip Invert the state of all pixels [aliases: f] on Set all pixels to the on state image Send an image file (e.g. jpeg or png) to the display. [aliases: i] + video Stream a video file (e.g. mp4) to the display. [aliases: v] screen Stream the default screen capture source to the display. On Linux Wayland, this pops up a screen or window chooser, but it also may directly start streaming your main screen. [aliases: s] ``` #### Image -``` +```text Send an image file (e.g. jpeg or png) to the display. Usage: servicepoint-cli pixels image [OPTIONS] @@ -85,9 +92,28 @@ Options: --no-aspect Do not keep aspect ratio when resizing. ``` +#### Video file + +```text +Stream a video file (e.g. mp4) to the display. + +Usage: servicepoint-cli pixels video [OPTIONS] + +Arguments: + + +Options: + --no-hist Disable histogram correction + --no-blur Disable blur + --no-sharp Disable sharpening + --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. + --no-spacers Do not remove the spacers from the image. + --no-aspect Do not keep aspect ratio when resizing. +``` + #### Screen -``` +```text Stream the default screen capture source to the display. On Linux Wayland, this pops up a screen or window chooser, but it also may directly start streaming your main screen. Usage: servicepoint-cli pixels screen [OPTIONS] @@ -104,7 +130,7 @@ Options: ### Brightness -``` +```text Commands for manipulating the brightness Usage: servicepoint-cli brightness @@ -117,18 +143,18 @@ Commands: ### Text -``` +```text Commands for sending text to the screen Usage: servicepoint-cli text Commands: - stdin Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` + stdin Pipe text to the display, example: `journalctl | servicepoint-cli text stdin` ``` #### Stdin -``` +```text Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` Usage: servicepoint-cli stream stdin [OPTIONS] @@ -137,6 +163,16 @@ Options: -s, --slow Wait for a short amount of time before sending the next line ``` +### Reset + +```text +Reset both pixels and brightness + +Usage: servicepoint-cli reset [OPTIONS] + +Options: +-f, --force hard reset screen +``` ## Contributing diff --git a/flake.lock b/flake.lock index e99b4ca..578e72d 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1739824009, - "narHash": "sha256-fcNrCMUWVLMG3gKC5M9CBqVOAnJtyRvGPxptQFl5mVg=", + "lastModified": 1745925850, + "narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=", "owner": "nix-community", "repo": "naersk", - "rev": "e5130d37369bfa600144c2424270c96f0ef0e11d", + "rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f", "type": "github" }, "original": { @@ -37,11 +37,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1740603184, - "narHash": "sha256-t+VaahjQAWyA+Ctn2idyo1yxRIYpaDxMgHkgCNiMJa4=", + "lastModified": 1746183838, + "narHash": "sha256-kwaaguGkAqTZ1oK0yXeQ3ayYjs8u/W7eEfrFpFfIDFA=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f44bd8ca21e026135061a0a57dcf3d0775b67a49", + "rev": "bf3287dac860542719fe7554e21e686108716879", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 405734a..e8230c9 100644 --- a/flake.nix +++ b/flake.nix @@ -90,8 +90,7 @@ { default = pkgs.mkShell rec { inputsFrom = [ self.packages.${system}.default ]; - packages = [ - pkgs.gdb + packages = with pkgs; [ (pkgs.symlinkJoin { name = "rust-toolchain"; paths = with pkgs; [ @@ -103,7 +102,11 @@ cargo-expand ]; }) - pkgs.cargo-flamegraph + + cargo-flamegraph + gdb + + ffmpeg-headless ]; LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}"; RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; diff --git a/src/brightness.rs b/src/brightness.rs index adead44..49ad898 100644 --- a/src/brightness.rs +++ b/src/brightness.rs @@ -1,8 +1,8 @@ -use crate::cli::BrightnessCommand; +use crate::{cli::BrightnessCommand, transport::Transport}; use log::info; -use servicepoint::{Brightness, Command, Connection}; +use servicepoint::{Brightness, GlobalBrightnessCommand}; -pub(crate) fn brightness(connection: &Connection, brightness_command: BrightnessCommand) { +pub(crate) fn brightness(connection: &Transport, brightness_command: BrightnessCommand) { match brightness_command { BrightnessCommand::Max => brightness_set(connection, Brightness::MAX), BrightnessCommand::Min => brightness_set(connection, Brightness::MIN), @@ -12,9 +12,9 @@ pub(crate) fn brightness(connection: &Connection, brightness_command: Brightness } } -pub(crate) fn brightness_set(connection: &Connection, brightness: Brightness) { +pub(crate) fn brightness_set(connection: &Transport, brightness: Brightness) { connection - .send(Command::Brightness(brightness)) + .send_command(GlobalBrightnessCommand::from(brightness)) .expect("Failed to set brightness"); info!("set brightness to {brightness:?}"); } diff --git a/src/cli.rs b/src/cli.rs index 365ff5a..c7a4a45 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -19,7 +19,7 @@ pub struct Cli { value_enum, default_value = "udp" )] - pub transport: Protocol, + pub transport: TransportType, #[clap(subcommand)] pub command: Mode, #[clap(short, long, help = "verbose logging")] @@ -29,7 +29,10 @@ pub struct Cli { #[derive(clap::Parser, std::fmt::Debug)] pub enum Mode { #[command(visible_alias = "r", about = "Reset both pixels and brightness")] - ResetEverything, + Reset { + #[arg(short, long, help = "hard reset screen")] + force: bool, + }, #[command(visible_alias = "p")] Pixels { #[clap(subcommand)] @@ -71,6 +74,16 @@ pub enum PixelCommand { #[command(flatten)] image_processing_options: ImageProcessingOptions, }, + #[command( + visible_alias = "v", + about = "Stream a video file (e.g. mp4) to the display." + )] + Video { + #[command(flatten)] + send_image_options: SendImageOptions, + #[command(flatten)] + image_processing_options: ImageProcessingOptions, + }, #[command( visible_alias = "s", about = "Stream the default screen capture source to the display. \ @@ -104,7 +117,7 @@ pub enum BrightnessCommand { } #[derive(clap::ValueEnum, Clone, Debug)] -pub enum Protocol { +pub enum TransportType { Udp, WebSocket, Fake, @@ -114,7 +127,7 @@ pub enum Protocol { #[clap(about = "Commands for sending text to the screen")] pub enum TextCommand { #[command( - about = "Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`" + about = "Pipe text to the display, example: `journalctl | servicepoint-cli text stdin`" )] Stdin { #[arg( diff --git a/src/image_processing.rs b/src/image_processing.rs index 4e6baa8..6e27cde 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -35,6 +35,7 @@ impl ImageProcessingPipeline { } } + #[must_use] pub fn process(&mut self, frame: DynamicImage) -> Bitmap { let start_time = Instant::now(); @@ -100,7 +101,7 @@ impl ImageProcessingPipeline { let result = if self.options.no_dither { let cutoff = median_brightness(&orig); let bits = orig.iter().map(move |x| x > &cutoff).collect(); - Bitmap::from_bitvec(orig.width() as usize, bits) + Bitmap::from_bitvec(orig.width() as usize, bits).unwrap() } else { ostromoukhov_dither(orig, u8::MAX / 2) }; @@ -113,7 +114,7 @@ impl ImageProcessingPipeline { let width = source.width(); let result_height = Self::calc_height_without_spacers(source.height()); - let mut result = Bitmap::new(width, result_height); + let mut result = Bitmap::new(width, result_height).unwrap(); let mut source_y = 0; for result_y in 0..result_height { diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index d0e4b43..c1caed4 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,7 +1,8 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand use image::GrayImage; -use servicepoint::{BitVec, Bitmap, PIXEL_HEIGHT}; +use log::debug; +use servicepoint::{Bitmap, DisplayBitVec, PIXEL_HEIGHT}; type GrayHistogram = [usize; 256]; @@ -169,10 +170,11 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { assert_eq!(width % 8, 0); let mut source = source.into_raw(); - let mut destination = BitVec::repeat(false, source.len()); + let mut destination = DisplayBitVec::repeat(false, source.len()); for y in 0..height as usize { let start = y * width as usize; + let last_row = y == (height - 1) as usize; if y % 2 == 0 { for x in start..start + width as usize { ostromoukhov_dither_pixel( @@ -180,7 +182,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { &mut destination, x, width as usize, - y == (height - 1) as usize, + last_row, 1, bias, ); @@ -192,7 +194,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { &mut destination, x, width as usize, - y == (height - 1) as usize, + last_row, -1, bias, ); @@ -200,13 +202,13 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { } } - Bitmap::from_bitvec(width as usize, destination) + Bitmap::from_bitvec(width as usize, destination).unwrap() } #[inline] fn ostromoukhov_dither_pixel( source: &mut [u8], - destination: &mut BitVec, + destination: &mut DisplayBitVec, position: usize, width: usize, last_row: bool, @@ -217,8 +219,16 @@ fn ostromoukhov_dither_pixel( destination.set(position, destination_value); let mut diffuse = |to: usize, mat: i16| { - let diffuse_value = source[to] as i16 + mat; - source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8; + match source.get(to) { + None => { + // last row has a out of bounds error on the last pixel + // TODO fix the iter bounds instead of ignoring here + } + Some(val) => { + let diffuse_value = *val as i16 + mat; + source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8; + } + }; }; let lookup = if destination_value { @@ -229,11 +239,14 @@ fn ostromoukhov_dither_pixel( diffuse((position as isize + direction) as usize, lookup[0]); if !last_row { + debug!("begin"); diffuse( ((position + width) as isize - direction) as usize, lookup[1], ); + debug!("mit"); diffuse(((position + width) as isize) as usize, lookup[2]); + debug!("end"); } } diff --git a/src/main.rs b/src/main.rs index 4ca1df4..cdae13c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ use crate::{ brightness::{brightness, brightness_set}, - cli::{Cli, Mode, Protocol}, + cli::{Cli, Mode}, pixels::{pixels, pixels_off}, - text::text + text::text, + transport::Transport, }; use clap::Parser; use log::debug; -use servicepoint::{Brightness, Connection}; +use servicepoint::{Brightness, HardResetCommand}; mod brightness; mod cli; @@ -16,40 +17,32 @@ mod pixels; mod stream_stdin; mod stream_window; mod text; +mod transport; fn main() { let cli = Cli::parse(); init_logging(cli.verbose); debug!("running with arguments: {:?}", cli); - let connection = make_connection(cli.destination, cli.transport); - debug!("connection established: {:#?}", connection); + let transport = Transport::connect(cli.transport, &cli.destination); + debug!("connection established: {:#?}", transport); - execute_mode(cli.command, connection); + execute_mode(cli.command, transport); } -pub fn execute_mode(mode: Mode, connection: Connection) { +pub fn execute_mode(mode: Mode, connection: Transport) { match mode { - Mode::ResetEverything => { - brightness_set(&connection, Brightness::MAX); - pixels_off(&connection); + Mode::Reset { force } => { + if force { + connection.send_command(HardResetCommand).unwrap() + } else { + brightness_set(&connection, Brightness::MAX); + pixels_off(&connection); + } } Mode::Pixels { pixel_command } => pixels(&connection, pixel_command), Mode::Brightness { brightness_command } => brightness(&connection, brightness_command), - Mode::Text { text_command} => text(&connection, text_command), - } -} - -fn make_connection(destination: String, transport: Protocol) -> Connection { - match transport { - Protocol::Udp => Connection::open(destination).expect("Failed to open UDP connection"), - Protocol::WebSocket => { - let url = destination.parse().expect( - "provided destination is not a valid url - make sure it starts with 'ws://'", - ); - Connection::open_websocket(url).expect("Failed to open WebSocket connection") - } - Protocol::Fake => Connection::Fake, + Mode::Text { text_command } => text(&connection, text_command), } } diff --git a/src/pixels.rs b/src/pixels.rs index da1aa6d..3f9665d 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,12 +1,18 @@ use crate::{ - image_processing::ImageProcessingPipeline, cli::{ImageProcessingOptions, PixelCommand, SendImageOptions}, - stream_window::stream_window + image_processing::ImageProcessingPipeline, + stream_window::stream_window, + transport::Transport, }; +use ffmpeg_next as ffmpeg; +use image::{DynamicImage, RgbImage}; use log::info; -use servicepoint::{BitVec, Command, CompressionCode, Connection, Origin, PIXEL_COUNT}; +use servicepoint::{ + BinaryOperation, BitVecCommand, BitmapCommand, ClearCommand, CompressionCode, DisplayBitVec, + Origin, PIXEL_COUNT, +}; -pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { +pub(crate) fn pixels(connection: &Transport, pixel_command: PixelCommand) { match pixel_command { PixelCommand::Off => pixels_off(connection), PixelCommand::Flip => pixels_invert(connection), @@ -19,34 +25,50 @@ pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { stream_options, image_processing, } => stream_window(connection, stream_options, image_processing), + PixelCommand::Video { + image_processing_options: processing_options, + send_image_options: image_options, + } => pixels_video(connection, image_options, processing_options), } } -fn pixels_on(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); +fn pixels_on(connection: &Transport) { + let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); + let command = BitVecCommand { + offset: 0, + bitvec: mask, + compression: CompressionCode::Lzma, + operation: BinaryOperation::Overwrite, + }; connection - .send(Command::BitmapLinear(0, mask, CompressionCode::Lzma)) + .send_command(command) .expect("could not send command"); info!("turned on all pixels") } -fn pixels_invert(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); +fn pixels_invert(connection: &Transport) { + let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); + let command = BitVecCommand { + offset: 0, + bitvec: mask, + compression: CompressionCode::Lzma, + operation: BinaryOperation::Xor, + }; connection - .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) + .send_command(command) .expect("could not send command"); info!("inverted all pixels"); } -pub(crate) fn pixels_off(connection: &Connection) { +pub(crate) fn pixels_off(connection: &Transport) { connection - .send(Command::Clear) + .send_command(ClearCommand) .expect("failed to clear pixels"); info!("reset pixels"); } fn pixels_image( - connection: &Connection, + connection: &Transport, options: SendImageOptions, processing_options: ImageProcessingOptions, ) { @@ -54,11 +76,82 @@ fn pixels_image( let mut pipeline = ImageProcessingPipeline::new(processing_options); let bitmap = pipeline.process(image); connection - .send(Command::BitmapLinearWin( - Origin::ZERO, + .send_command(BitmapCommand { + origin: Origin::ZERO, bitmap, - CompressionCode::default(), - )) + compression: CompressionCode::default(), + }) .expect("failed to send image command"); info!("sent image to display"); } + +fn pixels_video( + connection: &Transport, + options: SendImageOptions, + processing_options: ImageProcessingOptions, +) { + ffmpeg::init().unwrap(); + + let mut ictx = ffmpeg::format::input(&options.file_name).expect("failed to open video input file"); + + let input = ictx + .streams() + .best(ffmpeg::media::Type::Video) + .ok_or(ffmpeg::Error::StreamNotFound) + .expect("could not get video stream from input file"); + let video_stream_index = input.index(); + + let context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters()) + .expect("could not extract video context from parameters"); + let mut decoder = context_decoder.decoder().video() + .expect("failed to create decoder for video stream"); + + let src_width = decoder.width(); + let src_height = decoder.height(); + + let mut scaler = ffmpeg::software::scaling::Context::get( + decoder.format(), + src_width, + src_height, + ffmpeg::format::Pixel::RGB24, + src_width, + src_height, + ffmpeg::software::scaling::Flags::BILINEAR, + ).expect("failed to create scaling context"); + + let mut frame_index = 0; + + let mut processing_pipeline = ImageProcessingPipeline::new(processing_options); + + let mut receive_and_process_decoded_frames = + |decoder: &mut ffmpeg::decoder::Video| -> Result<(), ffmpeg::Error> { + let mut decoded = ffmpeg::util::frame::video::Video::empty(); + let mut rgb_frame = ffmpeg::util::frame::video::Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + scaler.run(&decoded, &mut rgb_frame) + .expect("failed to scale frame"); + + let image = RgbImage::from_raw(src_width, src_height, rgb_frame.data(0).to_owned()) + .expect("could not read rgb data to image"); + let image = DynamicImage::from(image); + let bitmap= processing_pipeline.process(image); + connection.send_command(BitmapCommand::from(bitmap)) + .expect("failed to send image command"); + + frame_index += 1; + } + Ok(()) + }; + + for (stream, packet) in ictx.packets() { + if stream.index() == video_stream_index { + decoder.send_packet(&packet) + .expect("failed to send video packet"); + receive_and_process_decoded_frames(&mut decoder) + .expect("failed to process video packet"); + } + } + decoder.send_eof().expect("failed to send eof"); + receive_and_process_decoded_frames(&mut decoder) + .expect("failed to eof packet"); +} diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index b8b6cfb..872461c 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -1,8 +1,9 @@ +use crate::transport::Transport; use log::warn; use servicepoint::*; use std::thread::sleep; -pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { +pub(crate) fn stream_stdin(connection: &Transport, slow: bool) { warn!("This mode will break when using multi-byte characters and does not support ANSI escape sequences yet."); let mut app = App { connection, @@ -14,7 +15,7 @@ pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { } struct App<'t> { - connection: &'t Connection, + connection: &'t Transport, mirror: CharGrid, y: usize, slow: bool, @@ -23,7 +24,7 @@ struct App<'t> { impl App<'_> { fn run(&mut self) { self.connection - .send(Command::Clear) + .send_command(ClearCommand) .expect("couldn't clear screen"); let last_y = self.mirror.height() - 1; for line in std::io::stdin().lines() { @@ -63,10 +64,10 @@ impl App<'_> { fn send_mirror(&self) { self.connection - .send(Command::Utf8Data( - Origin::ZERO, - self.mirror.clone(), - )) + .send_command(CharGridCommand { + origin: Origin::ZERO, + grid: self.mirror.clone(), + }) .expect("couldn't send screen to display"); } @@ -76,7 +77,10 @@ impl App<'_> { Self::line_onto_grid(&mut line_grid, 0, line); Self::line_onto_grid(&mut self.mirror, self.y, line); self.connection - .send(Command::Utf8Data(Origin::new(0, self.y), line_grid)) + .send_command(CharGridCommand { + origin: Origin::new(0, self.y), + grid: line_grid, + }) .expect("couldn't send single line to screen"); } } diff --git a/src/stream_window.rs b/src/stream_window.rs index f8e41ec..ffbc717 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,6 +1,7 @@ use crate::{ cli::{ImageProcessingOptions, StreamScreenOptions}, image_processing::ImageProcessingPipeline, + transport::Transport, }; use image::{DynamicImage, ImageBuffer, Rgb, Rgba}; use log::{debug, error, info, trace, warn}; @@ -9,11 +10,11 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{Command, CompressionCode, Connection, Origin, FRAME_PACING}; +use servicepoint::{BitmapCommand, CompressionCode, Origin, FRAME_PACING}; use std::time::{Duration, Instant}; pub fn stream_window( - connection: &Connection, + connection: &Transport, options: StreamScreenOptions, processing_options: ImageProcessingOptions, ) { @@ -36,11 +37,11 @@ pub fn stream_window( trace!("bitmap ready to send in: {:?}", start.elapsed()); connection - .send(Command::BitmapLinearWin( - Origin::ZERO, - bitmap.clone(), - CompressionCode::default(), - )) + .send_command(BitmapCommand { + origin: Origin::ZERO, + bitmap: bitmap.clone(), + compression: CompressionCode::default(), + }) .expect("failed to send frame to display"); debug!("frame time: {:?}", start.elapsed()); diff --git a/src/text.rs b/src/text.rs index 247b9ad..b5fbd4c 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,7 +1,7 @@ -use servicepoint::Connection; -use crate::cli::TextCommand; -use crate::stream_stdin::stream_stdin; +use crate::{cli::TextCommand, stream_stdin::stream_stdin, transport::Transport}; -pub fn text(connection: &Connection, command: TextCommand) { - match command { TextCommand::Stdin { slow } => stream_stdin(connection, slow), } +pub fn text(connection: &Transport, command: TextCommand) { + match command { + TextCommand::Stdin { slow } => stream_stdin(connection, slow), + } } diff --git a/src/transport.rs b/src/transport.rs new file mode 100644 index 0000000..03c651a --- /dev/null +++ b/src/transport.rs @@ -0,0 +1,51 @@ +use crate::cli::TransportType; +use servicepoint::{FakeConnection, Packet, UdpSocketExt}; +use std::fmt::Debug; +use std::net::{TcpStream, UdpSocket}; +use std::sync::Mutex; +use tungstenite::client::IntoClientRequest; +use tungstenite::stream::MaybeTlsStream; +use tungstenite::{ClientRequestBuilder, WebSocket}; + +#[derive(Debug)] +pub enum Transport { + Fake, + Udp(UdpSocket), + WebSocket(Mutex>>), +} + +impl Transport { + pub fn connect(kind: TransportType, destination: &str) -> Transport { + match kind { + TransportType::Udp => { + Self::Udp(UdpSocket::bind_connect(destination).expect("failed to bind socket")) + } + TransportType::WebSocket => { + let request = ClientRequestBuilder::new( + destination.parse().expect("Invalid destination url"), + ) + .into_client_request() + .unwrap(); + let (sock, _) = + tungstenite::connect(request).expect("failed to connect to websocket"); + Self::WebSocket(Mutex::new(sock)) + } + TransportType::Fake => Self::Fake, + } + } + + pub(crate) fn send_command>(&self, command: T) -> Option<()> + where + >::Error: Debug, + { + match self { + Self::Udp(socket) => socket.send_command(command), + Self::WebSocket(socket) => { + let bytes: Vec = command.try_into().unwrap().into(); + let mut socket = socket.lock().unwrap(); + socket.send(tungstenite::Message::Binary(bytes.into())).ok() + } + Self::Fake => FakeConnection.send_command(command), + } + } +}