diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index 2386f17dd..74e4efa99 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -593,7 +593,7 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; esac @@ -761,7 +761,7 @@ jobs: ln -s /usr/include /vcpkg/installed/arm64-linux/include export VCPKG_ROOT=/vcpkg # disable hwcodec for compilation - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; armv7) cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/ @@ -771,7 +771,7 @@ jobs: ln -s /usr/include /vcpkg/installed/arm-linux/include export VCPKG_ROOT=/vcpkg # disable hwcodec for compilation - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; esac diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index ffcadd18b..b08193971 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -732,7 +732,7 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; esac @@ -900,7 +900,7 @@ jobs: ln -s /usr/include /vcpkg/installed/arm64-linux/include export VCPKG_ROOT=/vcpkg # disable hwcodec for compilation - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; armv7) cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/ @@ -910,7 +910,7 @@ jobs: ln -s /usr/include /vcpkg/installed/arm-linux/include export VCPKG_ROOT=/vcpkg # disable hwcodec for compilation - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; esac diff --git a/Cargo.lock b/Cargo.lock index 115845b50..8483cbac1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,9 +254,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -271,9 +271,9 @@ version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -377,8 +377,8 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "regex", "rustc-hash", "shlex", @@ -397,12 +397,12 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "regex", "rustc-hash", "shlex", - "syn", + "syn 1.0.105", ] [[package]] @@ -588,11 +588,11 @@ dependencies = [ "heck 0.4.0", "indexmap", "log", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "serde 1.0.149", "serde_json 1.0.89", - "syn", + "syn 1.0.105", "tempfile", "toml", ] @@ -721,9 +721,9 @@ checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck 0.4.0", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1119,10 +1119,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "scratch", - "syn", + "syn 1.0.105", ] [[package]] @@ -1137,9 +1137,9 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1177,10 +1177,10 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "strsim 0.10.0", - "syn", + "syn 1.0.105", ] [[package]] @@ -1190,8 +1190,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", - "quote", - "syn", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1373,9 +1373,9 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1384,9 +1384,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1481,6 +1481,29 @@ dependencies = [ "libloading", ] +[[package]] +name = "dlopen" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" +dependencies = [ + "dlopen_derive", + "lazy_static", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "dlopen_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" +dependencies = [ + "libc", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "dlv-list" version = "0.3.0" @@ -1592,9 +1615,9 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9045e2676cd5af83c3b167d917b0a5c90a4d8e266e2683d6631b235c457fc27" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1604,9 +1627,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" dependencies = [ "once_cell", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1625,9 +1648,9 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1670,10 +1693,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e" dependencies = [ "proc-macro-error", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "rustversion", - "syn", + "syn 1.0.105", "synstructure", ] @@ -1747,9 +1770,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5216e387a76eebaaf11f6d871ec8a4aae0b25f05456ee21f228e024b1b3610" dependencies = [ "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -1878,11 +1901,11 @@ dependencies = [ "lazy_static", "log", "pathdiff", - "quote", + "quote 1.0.21", "regex", "serde 1.0.149", "serde_yaml", - "syn", + "syn 1.0.105", "tempfile", "thiserror", "toml", @@ -2035,9 +2058,9 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -2299,9 +2322,9 @@ dependencies = [ "itertools 0.9.0", "proc-macro-crate 0.1.5", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -2314,9 +2337,9 @@ dependencies = [ "heck 0.4.0", "proc-macro-crate 1.2.1", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -2550,9 +2573,9 @@ dependencies = [ "anyhow", "proc-macro-crate 1.2.1", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -2856,8 +2879,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", ] [[package]] @@ -3564,9 +3587,9 @@ checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ "darling", "proc-macro-crate 1.2.1", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -3713,9 +3736,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -3805,9 +3828,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -4121,9 +4144,9 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -4230,9 +4253,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", "version_check", ] @@ -4242,11 +4265,20 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "version_check", ] +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.47" @@ -4374,13 +4406,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.47", ] [[package]] @@ -4846,6 +4887,7 @@ dependencies = [ "dbus-crossroads", "default-net", "dispatch", + "dlopen", "enigo", "errno", "evdev", @@ -5157,9 +5199,9 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -5191,9 +5233,9 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -5452,9 +5494,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck 0.3.3", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -5464,10 +5506,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "rustversion", - "syn", + "syn 1.0.105", +] + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", ] [[package]] @@ -5476,8 +5529,8 @@ version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "unicode-ident", ] @@ -5487,10 +5540,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", + "unicode-xid 0.2.4", ] [[package]] @@ -5629,9 +5682,9 @@ name = "tao-macros" version = "0.1.0" source = "git+https://github.com/tauri-apps/tao?branch=muda#676bd90a80286b893d8850cc4e3813a0c4a27dcf" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -5724,9 +5777,9 @@ version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -5830,9 +5883,9 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -5919,9 +5972,9 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -6032,6 +6085,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -6176,9 +6235,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", "wasm-bindgen-shared", ] @@ -6200,7 +6259,7 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote", + "quote 1.0.21", "wasm-bindgen-macro-support", ] @@ -6210,9 +6269,9 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6280,8 +6339,8 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "xml-rs", ] @@ -6506,9 +6565,9 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -6517,9 +6576,9 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] [[package]] @@ -6961,10 +7020,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45066039ebf3330820e495e854f8b312abb68f0a39e97972d092bd72e8bb3e8e" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2", - "quote", + "proc-macro2 1.0.47", + "quote 1.0.21", "regex", - "syn", + "syn 1.0.105", ] [[package]] @@ -7037,7 +7096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155247a5d1ab55e335421c104ccd95d64f17cebbd02f50cdbc1c33385f9c4d81" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.105", ] diff --git a/Cargo.toml b/Cargo.toml index f685e3f2e..c20366983 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ inline = [] hbbs = [] cli = [] with_rc = ["simple_rc"] +flutter_texture_render = [] appimage = [] flatpak = [] use_samplerate = ["samplerate"] @@ -63,6 +64,7 @@ flutter_rust_bridge = { version = "1.61.1", optional = true } errno = "0.2.8" rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.1", features = ["serde"] } +dlopen = "0.1" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } chrono = "0.4.23" diff --git a/build.py b/build.py index 9e490166f..727b53fe0 100755 --- a/build.py +++ b/build.py @@ -239,6 +239,7 @@ def get_features(args): features.append('hwcodec') if args.flutter: features.append('flutter') + features.append('flutter_texture_render') if args.flatpak: features.append('flatpak') if args.appimage: diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index ff8dfbb09..6d3e4c3b7 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -19,6 +19,7 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:flutter_hbb/utils/platform_channel.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; +import 'package:texture_rgba_renderer/texture_rgba_renderer.dart'; import 'package:uni_links/uni_links.dart'; import 'package:uni_links_desktop/uni_links_desktop.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -44,6 +45,10 @@ var isWeb = false; var isWebDesktop = false; var version = ""; int androidVersion = 0; +/// Incriment count for textureId. +int _textureId = 0; +int get newTextureId => _textureId ++; +final textureRenderer = TextureRgbaRenderer(); /// only available for Windows target int windowsBuildNumber = 0; diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index f9db985d9..e52334512 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -63,6 +63,9 @@ class _RemotePageState extends State late RxBool _zoomCursor; late RxBool _remoteCursorMoved; late RxBool _keyboardEnabled; + late RxInt _textureId; + late int _textureKey; + final useTextureRender = bind.mainUseTextureRender(); final _blockableOverlayState = BlockableOverlayState(); @@ -85,6 +88,8 @@ class _RemotePageState extends State _showRemoteCursor = ShowRemoteCursorState.find(id); _keyboardEnabled = KeyboardEnabledState.find(id); _remoteCursorMoved = RemoteCursorMovedState.find(id); + _textureKey = newTextureId; + _textureId = RxInt(-1); } void _removeStates(String id) { @@ -119,6 +124,18 @@ class _RemotePageState extends State if (!Platform.isLinux) { Wakelock.enable(); } + // Register texture. + _textureId.value = -1; + if (useTextureRender) { + textureRenderer.createTexture(_textureKey).then((id) async { + debugPrint("id: $id, texture_key: $_textureKey"); + if (id != -1) { + final ptr = await textureRenderer.getTexturePtr(_textureKey); + platformFFI.registerTexture(widget.id, ptr); + _textureId.value = id; + } + }); + } _ffi.ffiModel.updateEventListener(widget.id); _ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); // Session option should be set after models.dart/FFI.start @@ -183,6 +200,10 @@ class _RemotePageState extends State @override void dispose() { debugPrint("REMOTE PAGE dispose ${widget.id}"); + if (useTextureRender) { + platformFFI.registerTexture(widget.id, 0); + textureRenderer.closeTexture(_textureKey); + } // ensure we leave this session, this is a double check bind.sessionEnterOrLeave(id: widget.id, enter: false); DesktopMultiWindow.removeListener(this); @@ -346,6 +367,8 @@ class _RemotePageState extends State cursorOverImage: _cursorOverImage, keyboardEnabled: _keyboardEnabled, remoteCursorMoved: _remoteCursorMoved, + textureId: _textureId, + useTextureRender: useTextureRender, listenerBuilder: (child) => _buildRawPointerMouseRegion(child, enterView, leaveView), ); @@ -383,6 +406,8 @@ class ImagePaint extends StatefulWidget { final RxBool cursorOverImage; final RxBool keyboardEnabled; final RxBool remoteCursorMoved; + final RxInt textureId; + final bool useTextureRender; final Widget Function(Widget)? listenerBuilder; ImagePaint( @@ -392,6 +417,8 @@ class ImagePaint extends StatefulWidget { required this.cursorOverImage, required this.keyboardEnabled, required this.remoteCursorMoved, + required this.textureId, + required this.useTextureRender, this.listenerBuilder}) : super(key: key); @@ -466,10 +493,19 @@ class _ImagePaintState extends State { final imageWidth = c.getDisplayWidth() * s; final imageHeight = c.getDisplayHeight() * s; final imageSize = Size(imageWidth, imageHeight); - final imageWidget = CustomPaint( - size: imageSize, - painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s), - ); + late final Widget imageWidget; + if (widget.useTextureRender) { + imageWidget = SizedBox( + width: imageWidth, + height: imageHeight, + child: Obx(() => Texture(textureId: widget.textureId.value)), + ); + } else { + imageWidget = CustomPaint( + size: imageSize, + painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s), + ); + } return NotificationListener( onNotification: (notification) { @@ -493,11 +529,31 @@ class _ImagePaintState extends State { context, _buildListener(imageWidget), c.size, imageSize)), )); } else { - final imageWidget = CustomPaint( - size: Size(c.size.width, c.size.height), - painter: ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), - ); - return mouseRegion(child: _buildListener(imageWidget)); + late final Widget imageWidget; + if (c.size.width > 0 && c.size.height > 0) { + if (widget.useTextureRender) { + imageWidget = Stack( + children: [ + Positioned( + left: c.x, + top: c.y, + width: c.getDisplayWidth() * s, + height: c.getDisplayHeight() * s, + child: Texture(textureId: widget.textureId.value), + ) + ], + ); + } else { + imageWidget = CustomPaint( + size: Size(c.size.width, c.size.height), + painter: + ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), + ); + } + return mouseRegion(child: _buildListener(imageWidget)); + } else { + return Container(); + } } } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 1afb5b147..5ef72a0af 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -252,6 +252,8 @@ class FfiModel with ChangeNotifier { parent.target?.cursorModel.updateDisplayOrigin(_display.x, _display.y); } + _updateSessionWidthHeight(peerId, display.width, display.height); + try { CurrentDisplayState.find(peerId).value = _pi.currentDisplay; } catch (e) { @@ -367,6 +369,10 @@ class FfiModel with ChangeNotifier { }); } + _updateSessionWidthHeight(String id, int width, int height) { + bind.sessionSetSize(id: id, width: display.width, height: display.height); + } + /// Handle the peer info event based on [evt]. handlePeerInfo(Map evt, String peerId) async { // recent peer updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs) @@ -420,6 +426,7 @@ class FfiModel with ChangeNotifier { stateGlobal.displaysCount.value = _pi.displays.length; if (_pi.currentDisplay < _pi.displays.length) { _display = _pi.displays[_pi.currentDisplay]; + _updateSessionWidthHeight(peerId, display.width, display.height); } if (displays.isNotEmpty) { parent.target?.dialogManager.showLoading( @@ -485,19 +492,18 @@ class ImageModel with ChangeNotifier { WeakReference parent; - final List _callbacksOnFirstImage = []; + final List callbacksOnFirstImage = []; ImageModel(this.parent); - addCallbackOnFirstImage(Function(String) cb) => - _callbacksOnFirstImage.add(cb); + addCallbackOnFirstImage(Function(String) cb) => callbacksOnFirstImage.add(cb); onRgba(Uint8List rgba) { if (_waitForImage[id]!) { _waitForImage[id] = false; parent.target?.dialogManager.dismissAll(); if (isDesktop) { - for (final cb in _callbacksOnFirstImage) { + for (final cb in callbacksOnFirstImage) { cb(id); } } @@ -1432,6 +1438,7 @@ class FFI { final stream = bind.sessionStart(id: id); final cb = ffiModel.startEventListener(id); () async { + final useTextureRender = bind.mainUseTextureRender(); // Preserved for the rgba data. await for (final message in stream) { if (message is EventToUI_Event) { @@ -1445,14 +1452,26 @@ class FFI { debugPrint('json.decode fail1(): $e, ${message.field0}'); } } else if (message is EventToUI_Rgba) { - // Fetch the image buffer from rust codes. - final sz = platformFFI.getRgbaSize(id); - if (sz == null || sz == 0) { - return; - } - final rgba = platformFFI.getRgba(id, sz); - if (rgba != null) { - imageModel.onRgba(rgba); + if (useTextureRender) { + if (_waitForImage[id]!) { + _waitForImage[id] = false; + dialogManager.dismissAll(); + for (final cb in imageModel.callbacksOnFirstImage) { + cb(id); + } + await canvasModel.updateViewStyle(); + await canvasModel.updateScrollStyle(); + } + } else { + // Fetch the image buffer from rust codes. + final sz = platformFFI.getRgbaSize(id); + if (sz == null || sz == 0) { + return; + } + final rgba = platformFFI.getRgba(id, sz); + if (rgba != null) { + imageModel.onRgba(rgba); + } } } } diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index ba62b775e..13f5b4587 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -30,6 +30,9 @@ typedef F4Dart = int Function(Pointer); typedef F5 = Void Function(Pointer); typedef F5Dart = void Function(Pointer); typedef HandleEvent = Future Function(Map evt); +// pub fn session_register_texture(id: *const char, ptr: usize) +typedef F6 = Void Function(Pointer, Uint64); +typedef F6Dart = void Function(Pointer, int); /// FFI wrapper around the native Rust core. /// Hides the platform differences. @@ -52,6 +55,8 @@ class PlatformFFI { F3? _session_get_rgba; F4Dart? _session_get_rgba_size; F5Dart? _session_next_rgba; + F6Dart? _session_register_texture; + static get localeName => Platform.localeName; @@ -130,6 +135,13 @@ class PlatformFFI { malloc.free(a); } + void registerTexture(String id, int ptr) { + if (_session_register_texture == null) return; + final a = id.toNativeUtf8(); + _session_register_texture!(a, ptr); + malloc.free(a); + } + /// Init the FFI class, loads the native Rust core library. Future init(String appType) async { _appType = appType; @@ -150,6 +162,7 @@ class PlatformFFI { dylib.lookupFunction("session_get_rgba_size"); _session_next_rgba = dylib.lookupFunction("session_next_rgba"); + _session_register_texture = dylib.lookupFunction("session_register_texture"); try { // SYSTEM user failed _dir = (await getApplicationDocumentsDirectory()).path; diff --git a/flutter/macos/Podfile.lock b/flutter/macos/Podfile.lock index 3187c6349..16dc0d352 100644 --- a/flutter/macos/Podfile.lock +++ b/flutter/macos/Podfile.lock @@ -21,6 +21,8 @@ PODS: - sqflite (0.0.2): - FlutterMacOS - FMDB (>= 2.7.5) + - texture_rgba_renderer (0.0.1): + - FlutterMacOS - uni_links_desktop (0.0.1): - FlutterMacOS - url_launcher_macos (0.0.1): @@ -42,6 +44,7 @@ DEPENDENCIES: - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) + - texture_rgba_renderer (from `Flutter/ephemeral/.symlinks/plugins/texture_rgba_renderer/macos`) - uni_links_desktop (from `Flutter/ephemeral/.symlinks/plugins/uni_links_desktop/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) @@ -71,6 +74,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos sqflite: :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos + texture_rgba_renderer: + :path: Flutter/ephemeral/.symlinks/plugins/texture_rgba_renderer/macos uni_links_desktop: :path: Flutter/ephemeral/.symlinks/plugins/uni_links_desktop/macos url_launcher_macos: @@ -93,6 +98,7 @@ SPEC CHECKSUMS: path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea + texture_rgba_renderer: cbed959a3c127122194a364e14b8577bd62dc8f2 uni_links_desktop: 45900fb319df48fcdea2df0756e9c2626696b026 url_launcher_macos: c04e4fa86382d4f94f6b38f14625708be3ae52e2 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 diff --git a/flutter/macos/Runner/MainFlutterWindow.swift b/flutter/macos/Runner/MainFlutterWindow.swift index 21e870320..e9043da71 100644 --- a/flutter/macos/Runner/MainFlutterWindow.swift +++ b/flutter/macos/Runner/MainFlutterWindow.swift @@ -17,6 +17,7 @@ import url_launcher_macos import wakelock_macos import window_manager import window_size +import texture_rgba_renderer class MainFlutterWindow: NSWindow { override func awakeFromNib() { @@ -49,6 +50,7 @@ class MainFlutterWindow: NSWindow { UrlLauncherPlugin.register(with: controller.registrar(forPlugin: "UrlLauncherPlugin")) WakelockMacosPlugin.register(with: controller.registrar(forPlugin: "WakelockMacosPlugin")) WindowSizePlugin.register(with: controller.registrar(forPlugin: "WindowSizePlugin")) + TextureRgbaRendererPlugin.register(with: controller.registrar(forPlugin: "TextureRgbaRendererPlugin")) } super.awakeFromNib() diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index a07df9c2e..5ffe805b8 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -1563,5 +1563,5 @@ packages: source: hosted version: "0.1.1" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=2.18.0 <4.0.0" flutter: ">=3.3.0" diff --git a/flutter/windows/runner/flutter_window.cpp b/flutter/windows/runner/flutter_window.cpp index b43b9095e..2f1f36f73 100644 --- a/flutter/windows/runner/flutter_window.cpp +++ b/flutter/windows/runner/flutter_window.cpp @@ -2,6 +2,9 @@ #include +#include +#include + #include "flutter/generated_plugin_registrant.h" FlutterWindow::FlutterWindow(const flutter::DartProject& project) @@ -25,6 +28,13 @@ bool FlutterWindow::OnCreate() { return false; } RegisterPlugins(flutter_controller_->engine()); + DesktopMultiWindowSetWindowCreatedCallback([](void *controller) { + auto *flutter_view_controller = + reinterpret_cast(controller); + auto *registry = flutter_view_controller->engine(); + TextureRgbaRendererPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("TextureRgbaRendererPlugin")); + }); SetChildContent(flutter_controller_->view()->GetNativeWindow()); return true; } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index acfd4c674..3adc24a14 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -11,7 +11,7 @@ use crate::hwcodec::*; use crate::mediacodec::{ MediaCodecDecoder, MediaCodecDecoders, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT, }; -use crate::vpxcodec::*; +use crate::{vpxcodec::*, ImageFormat}; use hbb_common::{ anyhow::anyhow, @@ -306,16 +306,17 @@ impl Decoder { pub fn handle_video_frame( &mut self, frame: &video_frame::Union, + fmt: ImageFormat, rgb: &mut Vec, ) -> ResultType { match frame { video_frame::Union::Vp9s(vp9s) => { - Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, rgb) + Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, fmt, rgb) } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { if let Some(decoder) = &mut self.hw.h264 { - Decoder::handle_hw_video_frame(decoder, h264s, rgb, &mut self.i420) + Decoder::handle_hw_video_frame(decoder, h264s, fmt, rgb, &mut self.i420) } else { Err(anyhow!("don't support h264!")) } @@ -323,7 +324,7 @@ impl Decoder { #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { if let Some(decoder) = &mut self.hw.h265 { - Decoder::handle_hw_video_frame(decoder, h265s, rgb, &mut self.i420) + Decoder::handle_hw_video_frame(decoder, h265s, fmt, rgb, &mut self.i420) } else { Err(anyhow!("don't support h265!")) } @@ -331,7 +332,7 @@ impl Decoder { #[cfg(feature = "mediacodec")] video_frame::Union::H264s(h264s) => { if let Some(decoder) = &mut self.media_codec.h264 { - Decoder::handle_mediacodec_video_frame(decoder, h264s, rgb) + Decoder::handle_mediacodec_video_frame(decoder, h264s, fmt, rgb) } else { Err(anyhow!("don't support h264!")) } @@ -339,7 +340,7 @@ impl Decoder { #[cfg(feature = "mediacodec")] video_frame::Union::H265s(h265s) => { if let Some(decoder) = &mut self.media_codec.h265 { - Decoder::handle_mediacodec_video_frame(decoder, h265s, rgb) + Decoder::handle_mediacodec_video_frame(decoder, h265s, fmt, rgb) } else { Err(anyhow!("don't support h265!")) } @@ -351,6 +352,7 @@ impl Decoder { fn handle_vp9s_video_frame( decoder: &mut VpxDecoder, vp9s: &EncodedVideoFrames, + fmt: ImageFormat, rgb: &mut Vec, ) -> ResultType { let mut last_frame = Image::new(); @@ -367,7 +369,7 @@ impl Decoder { if last_frame.is_null() { Ok(false) } else { - last_frame.rgb(1, true, rgb); + last_frame.to(fmt, 1, rgb); Ok(true) } } @@ -376,14 +378,15 @@ impl Decoder { fn handle_hw_video_frame( decoder: &mut HwDecoder, frames: &EncodedVideoFrames, - rgb: &mut Vec, + fmt: ImageFormat, + raw: &mut Vec, i420: &mut Vec, ) -> ResultType { let mut ret = false; for h264 in frames.frames.iter() { for image in decoder.decode(&h264.data)? { // TODO: just process the last frame - if image.bgra(rgb, i420).is_ok() { + if image.to_fmt(fmt, raw, i420).is_ok() { ret = true; } } @@ -395,11 +398,12 @@ impl Decoder { fn handle_mediacodec_video_frame( decoder: &mut MediaCodecDecoder, frames: &EncodedVideoFrames, - rgb: &mut Vec, + fmt: ImageFormat, + raw: &mut Vec, ) -> ResultType { let mut ret = false; for h264 in frames.frames.iter() { - return decoder.decode(&h264.data, rgb); + return decoder.decode(&h264.data, fmt, raw); } return Ok(false); } diff --git a/libs/scrap/src/common/convert.rs b/libs/scrap/src/common/convert.rs index 2b0223a0a..f3ad51a21 100644 --- a/libs/scrap/src/common/convert.rs +++ b/libs/scrap/src/common/convert.rs @@ -103,6 +103,19 @@ extern "C" { height: c_int, ) -> c_int; + pub fn I420ToABGR( + src_y: *const u8, + src_stride_y: c_int, + src_u: *const u8, + src_stride_u: c_int, + src_v: *const u8, + src_stride_v: c_int, + dst_rgba: *mut u8, + dst_stride_rgba: c_int, + width: c_int, + height: c_int, + ) -> c_int; + pub fn NV12ToARGB( src_y: *const u8, src_stride_y: c_int, @@ -113,6 +126,17 @@ extern "C" { width: c_int, height: c_int, ) -> c_int; + + pub fn NV12ToABGR( + src_y: *const u8, + src_stride_y: c_int, + src_uv: *const u8, + src_stride_uv: c_int, + dst_rgba: *mut u8, + dst_stride_rgba: c_int, + width: c_int, + height: c_int, + ) -> c_int; } // https://github.com/webmproject/libvpx/blob/master/vpx/src/vpx_image.c @@ -246,6 +270,7 @@ pub unsafe fn nv12_to_i420( #[cfg(feature = "hwcodec")] pub mod hw { use hbb_common::{anyhow::anyhow, ResultType}; + use crate::ImageFormat; #[cfg(target_os = "windows")] use hwcodec::{ffmpeg::ffmpeg_linesize_offset_length, AVPixelFormat}; @@ -315,7 +340,8 @@ pub mod hw { } #[cfg(target_os = "windows")] - pub fn hw_nv12_to_bgra( + pub fn hw_nv12_to( + fmt: ImageFormat, width: usize, height: usize, src_y: &[u8], @@ -355,18 +381,39 @@ pub mod hw { width as _, height as _, ); - super::I420ToARGB( - i420_offset_y, - i420_stride_y, - i420_offset_u, - i420_stride_u, - i420_offset_v, - i420_stride_v, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ); + match fmt { + ImageFormat::ARGB => { + super::I420ToARGB( + i420_offset_y, + i420_stride_y, + i420_offset_u, + i420_stride_u, + i420_offset_v, + i420_stride_v, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + ); + } + ImageFormat::ABGR => { + super::I420ToABGR( + i420_offset_y, + i420_stride_y, + i420_offset_u, + i420_stride_u, + i420_offset_v, + i420_stride_v, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + ); + } + _ => { + return Err(anyhow!("unsupported image format")); + } + } return Ok(()); }; } @@ -374,7 +421,8 @@ pub mod hw { } #[cfg(not(target_os = "windows"))] - pub fn hw_nv12_to_bgra( + pub fn hw_nv12_to( + fmt: ImageFormat, width: usize, height: usize, src_y: &[u8], @@ -387,23 +435,46 @@ pub mod hw { ) -> ResultType<()> { dst.resize(width * height * 4, 0); unsafe { - match super::NV12ToARGB( - src_y.as_ptr(), - src_stride_y as _, - src_uv.as_ptr(), - src_stride_uv as _, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ) { - 0 => Ok(()), - _ => Err(anyhow!("NV12ToARGB failed")), + match fmt { + ImageFormat::ARGB => { + match super::NV12ToARGB( + src_y.as_ptr(), + src_stride_y as _, + src_uv.as_ptr(), + src_stride_uv as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + ) { + 0 => Ok(()), + _ => Err(anyhow!("NV12ToARGB failed")), + } + } + ImageFormat::ABGR => { + match super::NV12ToABGR( + src_y.as_ptr(), + src_stride_y as _, + src_uv.as_ptr(), + src_stride_uv as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + ) { + 0 => Ok(()), + _ => Err(anyhow!("NV12ToABGR failed")), + } + } + _ => { + Err(anyhow!("unsupported image format")) + } } } } - pub fn hw_i420_to_bgra( + pub fn hw_i420_to( + fmt: ImageFormat, width: usize, height: usize, src_y: &[u8], @@ -419,18 +490,38 @@ pub mod hw { let src_v = src_v.as_ptr(); dst.resize(width * height * 4, 0); unsafe { - super::I420ToARGB( - src_y, - src_stride_y as _, - src_u, - src_stride_u as _, - src_v, - src_stride_v as _, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ); + match fmt { + ImageFormat::ARGB => { + super::I420ToARGB( + src_y, + src_stride_y as _, + src_u, + src_stride_u as _, + src_v, + src_stride_v as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + ); + } + ImageFormat::ABGR => { + super::I420ToABGR( + src_y, + src_stride_y as _, + src_u, + src_stride_u as _, + src_v, + src_stride_v as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + ); + } + _ => { + } + } }; } } diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 27b157b79..d2b9f414f 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -1,6 +1,6 @@ use crate::{ codec::{EncoderApi, EncoderCfg}, - hw, HW_STRIDE_ALIGN, + hw, ImageFormat, HW_STRIDE_ALIGN, }; use hbb_common::{ anyhow::{anyhow, Context}, @@ -236,22 +236,24 @@ pub struct HwDecoderImage<'a> { } impl HwDecoderImage<'_> { - pub fn bgra(&self, bgra: &mut Vec, i420: &mut Vec) -> ResultType<()> { + pub fn to_fmt(&self, fmt: ImageFormat, fmt_data: &mut Vec, i420: &mut Vec) -> ResultType<()> { let frame = self.frame; match frame.pixfmt { - AVPixelFormat::AV_PIX_FMT_NV12 => hw::hw_nv12_to_bgra( + AVPixelFormat::AV_PIX_FMT_NV12 => hw::hw_nv12_to( + fmt, frame.width as _, frame.height as _, &frame.data[0], &frame.data[1], frame.linesize[0] as _, frame.linesize[1] as _, - bgra, + fmt_data, i420, HW_STRIDE_ALIGN, ), AVPixelFormat::AV_PIX_FMT_YUV420P => { - hw::hw_i420_to_bgra( + hw::hw_i420_to( + fmt, frame.width as _, frame.height as _, &frame.data[0], @@ -260,12 +262,20 @@ impl HwDecoderImage<'_> { frame.linesize[0] as _, frame.linesize[1] as _, frame.linesize[2] as _, - bgra, + fmt_data, ); return Ok(()); } } } + + pub fn bgra(&self, bgra: &mut Vec, i420: &mut Vec) -> ResultType<()> { + self.to_fmt(ImageFormat::ARGB, bgra, i420) + } + + pub fn rgba(&self, rgba: &mut Vec, i420: &mut Vec) -> ResultType<()> { + self.to_fmt(ImageFormat::ABGR, rgba, i420) + } } fn get_config(k: &str) -> ResultType { diff --git a/libs/scrap/src/common/mediacodec.rs b/libs/scrap/src/common/mediacodec.rs index 406baecb5..7bda0b69d 100644 --- a/libs/scrap/src/common/mediacodec.rs +++ b/libs/scrap/src/common/mediacodec.rs @@ -1,5 +1,4 @@ -use hbb_common::anyhow::Error; -use hbb_common::{bail, ResultType}; +use hbb_common::{log, anyhow::Error, bail, ResultType}; use ndk::media::media_codec::{MediaCodec, MediaCodecDirection, MediaFormat}; use std::ops::Deref; use std::{ @@ -8,9 +7,10 @@ use std::{ time::Duration, }; +use crate::ImageFormat; use crate::{ codec::{EncoderApi, EncoderCfg}, - I420ToARGB, + I420ToABGR, I420ToARGB, }; /// MediaCodec mime type name @@ -50,7 +50,7 @@ impl MediaCodecDecoder { MediaCodecDecoders { h264, h265 } } - pub fn decode(&mut self, data: &[u8], rgb: &mut Vec) -> ResultType { + pub fn decode(&mut self, data: &[u8], fmt: ImageFormat, raw: &mut Vec) -> ResultType { match self.dequeue_input_buffer(Duration::from_millis(10))? { Some(mut input_buffer) => { let mut buf = input_buffer.buffer_mut(); @@ -83,23 +83,44 @@ impl MediaCodecDecoder { let bps = 4; let u = buf.len() * 2 / 3; let v = buf.len() * 5 / 6; - rgb.resize(h * w * bps, 0); + raw.resize(h * w * bps, 0); let y_ptr = buf.as_ptr(); let u_ptr = buf[u..].as_ptr(); let v_ptr = buf[v..].as_ptr(); unsafe { - I420ToARGB( - y_ptr, - stride, - u_ptr, - stride / 2, - v_ptr, - stride / 2, - rgb.as_mut_ptr(), - (w * bps) as _, - w as _, - h as _, - ); + match fmt { + ImageFormat::ARGB => { + I420ToARGB( + y_ptr, + stride, + u_ptr, + stride / 2, + v_ptr, + stride / 2, + raw.as_mut_ptr(), + (w * bps) as _, + w as _, + h as _, + ); + } + ImageFormat::ARGB => { + I420ToABGR( + y_ptr, + stride, + u_ptr, + stride / 2, + v_ptr, + stride / 2, + raw.as_mut_ptr(), + (w * bps) as _, + w as _, + h as _, + ); + } + _ => { + bail!("Unsupported image format"); + } + } } self.release_output_buffer(output_buffer, false)?; Ok(true) diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 45aafe7c5..c7da57734 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -43,6 +43,13 @@ pub const HW_STRIDE_ALIGN: usize = 0; // recommended by av_frame_get_buffer pub mod record; mod vpx; +#[derive(Copy, Clone)] +pub enum ImageFormat { + Raw, + ABGR, + ARGB, +} + #[inline] pub fn would_block_if_equal(old: &mut Vec, b: &[u8]) -> std::io::Result<()> { // does this really help? diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 5164886a1..7a65b193d 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -6,8 +6,8 @@ use hbb_common::anyhow::{anyhow, Context}; use hbb_common::message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}; use hbb_common::{get_time, ResultType}; -use crate::codec::EncoderApi; use crate::STRIDE_ALIGN; +use crate::{codec::EncoderApi, ImageFormat}; use super::vpx::{vp8e_enc_control_id::*, vpx_codec_err_t::*, *}; use hbb_common::bytes::Bytes; @@ -417,7 +417,7 @@ impl VpxDecoder { Ok(Self { ctx }) } - pub fn decode2rgb(&mut self, data: &[u8], rgba: bool) -> Result> { + pub fn decode2rgb(&mut self, data: &[u8], fmt: ImageFormat) -> Result> { let mut img = Image::new(); for frame in self.decode(data)? { drop(img); @@ -431,7 +431,7 @@ impl VpxDecoder { Ok(Vec::new()) } else { let mut out = Default::default(); - img.rgb(1, rgba, &mut out); + img.to(fmt, 1, &mut out); Ok(out) } } @@ -539,40 +539,60 @@ impl Image { self.inner().stride[iplane] } - pub fn rgb(&self, stride_align: usize, rgba: bool, dst: &mut Vec) { + pub fn to(&self, fmt: ImageFormat, stride_align: usize, dst: &mut Vec) { let h = self.height(); let mut w = self.width(); - let bps = if rgba { 4 } else { 3 }; + let bps = match fmt { + ImageFormat::Raw => 3, + ImageFormat::ARGB | ImageFormat::ABGR => 4, + }; w = (w + stride_align - 1) & !(stride_align - 1); dst.resize(h * w * bps, 0); let img = self.inner(); unsafe { - if rgba { - super::I420ToARGB( - img.planes[0], - img.stride[0], - img.planes[1], - img.stride[1], - img.planes[2], - img.stride[2], - dst.as_mut_ptr(), - (w * bps) as _, - self.width() as _, - self.height() as _, - ); - } else { - super::I420ToRAW( - img.planes[0], - img.stride[0], - img.planes[1], - img.stride[1], - img.planes[2], - img.stride[2], - dst.as_mut_ptr(), - (w * bps) as _, - self.width() as _, - self.height() as _, - ); + match fmt { + ImageFormat::Raw => { + super::I420ToRAW( + img.planes[0], + img.stride[0], + img.planes[1], + img.stride[1], + img.planes[2], + img.stride[2], + dst.as_mut_ptr(), + (w * bps) as _, + self.width() as _, + self.height() as _, + ); + } + ImageFormat::ARGB => { + super::I420ToARGB( + img.planes[0], + img.stride[0], + img.planes[1], + img.stride[1], + img.planes[2], + img.stride[2], + dst.as_mut_ptr(), + (w * bps) as _, + self.width() as _, + self.height() as _, + ); + } + ImageFormat::ABGR => { + super::I420ToABGR( + img.planes[0], + img.stride[0], + img.planes[1], + img.stride[1], + img.planes[2], + img.stride[2], + dst.as_mut_ptr(), + (w * bps) as _, + self.width() as _, + self.height() as _, + ); + } } } } diff --git a/src/client.rs b/src/client.rs index aa3523185..ebfda7283 100644 --- a/src/client.rs +++ b/src/client.rs @@ -45,6 +45,7 @@ use scrap::{ codec::{Decoder, DecoderCfg}, record::{Recorder, RecorderContext}, VpxDecoderConfig, VpxVideoCodecId, + ImageFormat, }; use crate::{ @@ -943,7 +944,12 @@ impl VideoHandler { } match &vf.union { Some(frame) => { - let res = self.decoder.handle_video_frame(frame, &mut self.rgb); + // windows && flutter_texture_render, fmt is ImageFormat::ABGR + #[cfg(all(target_os = "windows", feature = "flutter_texture_render"))] + let fmt = ImageFormat::ABGR; + #[cfg(not(all(target_os = "windows", feature = "flutter_texture_render")))] + let fmt = ImageFormat::ARGB; + let res = self.decoder.handle_video_frame(frame, fmt, &mut self.rgb); if self.record { self.recorder .lock() diff --git a/src/flutter.rs b/src/flutter.rs index bad6e0008..d366a0eda 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -3,12 +3,21 @@ use crate::{ flutter_ffi::EventToUI, ui_session_interface::{io_loop, InvokeUiSession, Session}, }; +#[cfg(feature = "flutter_texture_render")] +use dlopen::{ + symbor::{Library, Symbol}, + Error as LibError, +}; use flutter_rust_bridge::StreamSink; +#[cfg(feature = "flutter_texture_render")] +use hbb_common::libc::c_void; use hbb_common::{ - bail, config::LocalConfig, get_version_number, message_proto::*, rendezvous_proto::ConnType, - ResultType, + bail, config::LocalConfig, get_version_number, log, message_proto::*, + rendezvous_proto::ConnType, ResultType, }; use serde_json::json; + +#[cfg(not(feature = "flutter_texture_render"))] use std::sync::atomic::{AtomicBool, Ordering}; use std::{ collections::HashMap, @@ -29,6 +38,21 @@ lazy_static::lazy_static! { pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } +#[cfg(all(target_os = "windows", feature = "flutter_texture_render"))] +lazy_static::lazy_static! { + pub static ref TEXTURE_RGBA_RENDERER_PLUGIN: Result = Library::open("texture_rgba_renderer_plugin.dll"); +} + +#[cfg(all(target_os = "linux", feature = "flutter_texture_render"))] +lazy_static::lazy_static! { + pub static ref TEXTURE_RGBA_RENDERER_PLUGIN: Result = Library::open("libtexture_rgba_renderer_plugin.so"); +} + +#[cfg(all(target_os = "macos", feature = "flutter_texture_render"))] +lazy_static::lazy_static! { + pub static ref TEXTURE_RGBA_RENDERER_PLUGIN: Result = Library::open_self(); +} + /// FFI for rustdesk core's main entry. /// Return true if the app should continue running with UI(possibly Flutter), false if the app should exit. #[cfg(not(windows))] @@ -108,6 +132,16 @@ pub unsafe extern "C" fn free_c_args(ptr: *mut *mut c_char, len: c_int) { // Afterwards the vector will be dropped and thus freed. } +#[cfg(feature = "flutter_texture_render")] +#[derive(Default, Clone)] +pub struct FlutterHandler { + pub event_stream: Arc>>>, + notify_rendered: Arc>, + renderer: Arc>, + peer_info: Arc>, +} + +#[cfg(not(feature = "flutter_texture_render"))] #[derive(Default, Clone)] pub struct FlutterHandler { pub event_stream: Arc>>>, @@ -115,6 +149,84 @@ pub struct FlutterHandler { // We must check the `rgba_valid` before reading [rgba]. pub rgba: Arc>>, pub rgba_valid: Arc, + peer_info: Arc>, +} + +#[cfg(feature = "flutter_texture_render")] +pub type FlutterRgbaRendererPluginOnRgba = + unsafe extern "C" fn(texture_rgba: *mut c_void, buffer: *const u8, width: c_int, height: c_int); + +// Video Texture Renderer in Flutter +#[cfg(feature = "flutter_texture_render")] +#[derive(Clone)] +struct VideoRenderer { + // TextureRgba pointer in flutter native. + ptr: usize, + width: i32, + height: i32, + data_len: usize, + on_rgba_func: Option>, +} + +#[cfg(feature = "flutter_texture_render")] +impl Default for VideoRenderer { + fn default() -> Self { + let on_rgba_func = match &*TEXTURE_RGBA_RENDERER_PLUGIN { + Ok(lib) => { + let find_sym_res = unsafe { + lib.symbol::("FlutterRgbaRendererPluginOnRgba") + }; + match find_sym_res { + Ok(sym) => Some(sym), + Err(e) => { + log::error!("Failed to find symbol FlutterRgbaRendererPluginOnRgba, {e}"); + None + } + } + } + Err(e) => { + log::error!("Failed to load texture rgba renderer plugin, {e}"); + None + } + }; + Self { + ptr: 0, + width: 0, + height: 0, + data_len: 0, + on_rgba_func, + } + } +} + +#[cfg(feature = "flutter_texture_render")] +impl VideoRenderer { + #[inline] + pub fn set_size(&mut self, width: i32, height: i32) { + self.width = width; + self.height = height; + self.data_len = if width > 0 && height > 0 { + (width * height * 4) as usize + } else { + 0 + }; + } + + pub fn on_rgba(&self, rgba: &Vec) { + if self.ptr == usize::default() || rgba.len() != self.data_len { + return; + } + if let Some(func) = &self.on_rgba_func { + unsafe { + func( + self.ptr as _, + rgba.as_ptr() as _, + self.width as _, + self.height as _, + ) + }; + } + } } impl FlutterHandler { @@ -156,6 +268,19 @@ impl FlutterHandler { } serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned()) } + + #[inline] + #[cfg(feature = "flutter_texture_render")] + pub fn register_texture(&mut self, ptr: usize) { + self.renderer.write().unwrap().ptr = ptr; + } + + #[inline] + #[cfg(feature = "flutter_texture_render")] + pub fn set_size(&mut self, width: i32, height: i32) { + *self.notify_rendered.write().unwrap() = false; + self.renderer.write().unwrap().set_size(width, height); + } } impl InvokeUiSession for FlutterHandler { @@ -315,6 +440,8 @@ impl InvokeUiSession for FlutterHandler { // unused in flutter fn adapt_size(&self) {} + #[inline] + #[cfg(not(feature = "flutter_texture_render"))] fn on_rgba(&self, data: &mut Vec) { // If the current rgba is not fetched by flutter, i.e., is valid. // We give up sending a new event to flutter. @@ -329,6 +456,19 @@ impl InvokeUiSession for FlutterHandler { } } + #[inline] + #[cfg(feature = "flutter_texture_render")] + fn on_rgba(&self, data: &mut Vec) { + self.renderer.read().unwrap().on_rgba(data); + if *self.notify_rendered.read().unwrap() { + return; + } + if let Some(stream) = &*self.event_stream.read().unwrap() { + stream.add(EventToUI::Rgba); + *self.notify_rendered.write().unwrap() = true; + } + } + fn set_peer_info(&self, pi: &PeerInfo) { let displays = Self::make_displays_msg(&pi.displays); let mut features: HashMap<&str, i32> = Default::default(); @@ -340,6 +480,7 @@ impl InvokeUiSession for FlutterHandler { features.insert("privacy_mode", 0); } let features = serde_json::ser::to_string(&features).unwrap_or("".to_owned()); + *self.peer_info.write().unwrap() = pi.clone(); self.push_event( "peer_info", vec![ @@ -356,6 +497,7 @@ impl InvokeUiSession for FlutterHandler { } fn set_displays(&self, displays: &Vec) { + self.peer_info.write().unwrap().displays = displays.clone(); self.push_event( "sync_peer_info", vec![("displays", &Self::make_displays_msg(displays))], @@ -444,6 +586,7 @@ impl InvokeUiSession for FlutterHandler { #[inline] fn get_rgba(&self) -> *const u8 { + #[cfg(not(feature = "flutter_texture_render"))] if self.rgba_valid.load(Ordering::Relaxed) { return self.rgba.read().unwrap().as_ptr(); } @@ -451,7 +594,8 @@ impl InvokeUiSession for FlutterHandler { } #[inline] - fn next_rgba(&mut self) { + fn next_rgba(&self) { + #[cfg(not(feature = "flutter_texture_render"))] self.rgba_valid.store(false, Ordering::Relaxed); } } @@ -517,6 +661,13 @@ pub fn session_add( /// * `events2ui` - The events channel to ui. pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultType<()> { if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { + #[cfg(feature = "flutter_texture_render")] + log::info!( + "Session {} start, render by flutter texture rgba plugin", + id + ); + #[cfg(not(feature = "flutter_texture_render"))] + log::info!("Session {} start, render by flutter paint widget", id); *session.event_stream.write().unwrap() = Some(event_stream); let session = session.clone(); std::thread::spawn(move || { @@ -711,21 +862,28 @@ pub fn set_cur_session_id(id: String) { } #[no_mangle] +#[cfg(not(feature = "flutter_texture_render"))] pub fn session_get_rgba_size(id: *const char) -> usize { let id = unsafe { std::ffi::CStr::from_ptr(id as _) }; if let Ok(id) = id.to_str() { - if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { + if let Some(session) = SESSIONS.read().unwrap().get(id) { return session.rgba.read().unwrap().len(); } } 0 } +#[no_mangle] +#[cfg(feature = "flutter_texture_render")] +pub fn session_get_rgba_size(_id: *const char) -> usize { + 0 +} + #[no_mangle] pub fn session_get_rgba(id: *const char) -> *const u8 { let id = unsafe { std::ffi::CStr::from_ptr(id as _) }; if let Ok(id) = id.to_str() { - if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { + if let Some(session) = SESSIONS.read().unwrap().get(id) { return session.get_rgba(); } } @@ -736,8 +894,23 @@ pub fn session_get_rgba(id: *const char) -> *const u8 { pub fn session_next_rgba(id: *const char) { let id = unsafe { std::ffi::CStr::from_ptr(id as _) }; if let Ok(id) = id.to_str() { - if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { + if let Some(session) = SESSIONS.read().unwrap().get(id) { return session.next_rgba(); } } } + +#[no_mangle] +#[cfg(feature = "flutter_texture_render")] +pub fn session_register_texture(id: *const char, ptr: usize) { + let id = unsafe { std::ffi::CStr::from_ptr(id as _) }; + if let Ok(id) = id.to_str() { + if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { + return session.register_texture(ptr); + } + } +} + +#[no_mangle] +#[cfg(not(feature = "flutter_texture_render"))] +pub fn session_register_texture(_id: *const char, _ptr: usize) {} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 7eeb96b5c..14906d568 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -529,6 +529,13 @@ pub fn session_switch_sides(id: String) { } } +pub fn session_set_size(_id: String, _width: i32, _height: i32) { + #[cfg(feature = "flutter_texture_render")] + if let Some(session) = SESSIONS.write().unwrap().get_mut(&_id) { + session.set_size(_width, _height); + } +} + pub fn main_get_sound_inputs() -> Vec { #[cfg(not(any(target_os = "android", target_os = "ios")))] return get_sound_inputs(); @@ -1300,6 +1307,17 @@ pub fn main_hide_docker() -> SyncReturn { SyncReturn(true) } +pub fn main_use_texture_render() -> SyncReturn { + #[cfg(not(feature = "flutter_texture_render"))] + { + SyncReturn(false) + } + #[cfg(feature = "flutter_texture_render")] + { + SyncReturn(true) + } +} + pub fn cm_start_listen_ipc_thread() { #[cfg(not(any(target_os = "android", target_os = "ios")))] crate::flutter::connection_manager::start_listen_ipc_thread(); diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 4794efb65..7b31c84e9 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -298,7 +298,7 @@ impl InvokeUiSession for SciterHandler { std::ptr::null() } - fn next_rgba(&mut self) {} + fn next_rgba(&self) {} } pub struct SciterSession(Session); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 5a83ee572..5fbf2f4e7 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -798,7 +798,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn on_voice_call_waiting(&self); fn on_voice_call_incoming(&self); fn get_rgba(&self) -> *const u8; - fn next_rgba(&mut self); + fn next_rgba(&self); } impl Deref for Session {