diff options
Diffstat (limited to 'old/evc')
33 files changed, 2765 insertions, 0 deletions
diff --git a/old/evc/.gitignore b/old/evc/.gitignore new file mode 100644 index 0000000..b88f3af --- /dev/null +++ b/old/evc/.gitignore @@ -0,0 +1,6 @@ +/target +/samples +/reports +/flamegraph.svg +/perf.data* + diff --git a/old/evc/Cargo.lock b/old/evc/Cargo.lock new file mode 100644 index 0000000..727d55e --- /dev/null +++ b/old/evc/Cargo.lock @@ -0,0 +1,663 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" +dependencies = [ + "bitflags 1.3.2", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "console" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b6515d269224923b26b5febea2ed42b2d5f2ce37284a4dd670fedd6cb8347a" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.42.0", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indicatif" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "libreschmux" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "env_logger", + "indicatif", + "log", + "proc-macro2", + "rayon", + "rustdct", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "portable-atomic" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" + +[[package]] +name = "primal-check" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0" +dependencies = [ + "num-integer", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustdct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551" +dependencies = [ + "rustfft", +] + +[[package]] +name = "rustfft" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", + "version_check", +] + +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "transpose" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6522d49d03727ffb138ae4cbc1283d3774f0d10aa7f9bf52e6784c45daf9b23" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/old/evc/Cargo.toml b/old/evc/Cargo.toml new file mode 100644 index 0000000..84df2aa --- /dev/null +++ b/old/evc/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "libreschmux" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = { version = "*", features = ["derive"] } +anyhow = "1.0.82" +log = "0.4.21" +rayon = "1.10.0" +env_logger = "0.11.3" +rustdct = "0.7.1" +indicatif = "*" +proc-macro2 = "1.0.81" + +[[bin]] +path = "src/bin/encode.rs" +name = "reschmux-encode" + +[[bin]] +path = "src/bin/decode.rs" +name = "reschmux-decode" + +[[bin]] +path = "src/bin/info.rs" +name = "reschmux-info" diff --git a/old/evc/scripts/bench_modes b/old/evc/scripts/bench_modes new file mode 100755 index 0000000..2e66454 --- /dev/null +++ b/old/evc/scripts/bench_modes @@ -0,0 +1,22 @@ +#!/bin/fish +set w $argv[1] +set h $argv[2] +set t $argv[3] + +ffmpeg -hide_banner -i $argv[4] -to {$t} -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 >samples/raw +# ffmpeg -hide_banner -y -i $argv[4] -to {$t} -vf scale={$w}x{$h},fps=30,format=rgb24 -c:v vp9 samples/reference.webm + +echo +echo "file: "$argv[4] +echo "resolution: "{$w}x{$h} +echo "frames: "(math $t \* 30) +echo "reference (raw): "(du -h samples/raw | cut -f 1) +echo "reference (input): "(du -h $argv[4] | cut -f 1) +# echo "reference (vp8): "(du -h samples/reference.webm | cut -f 1) +# for mode in trivial simple-exhaustive simple-fast advanced advanced-partial +for mode in trivial simple-fast advanced + echo ----------- + echo "mode: $mode" + echo "time: $(command time -f %U ./target/release/reschmux-encode -W {$w} -H {$h} --mode $mode $argv[5..] <samples/raw >samples/encoded-$mode 2>| tail -n 1)s" + echo "size: $(du -h samples/encoded-$mode | cut -f 1)" +end diff --git a/old/evc/scripts/bench_out b/old/evc/scripts/bench_out new file mode 100755 index 0000000..d55cf08 --- /dev/null +++ b/old/evc/scripts/bench_out @@ -0,0 +1,11 @@ +#!/bin/fish +set w $argv[1] +set h $argv[2] + +# for mode in trivial simple-exhaustive simple-fast advanced advanced-partial +for mode in trivial simple-fast advanced + cargo run --release --bin reschmux-decode -- --debug < samples/encoded-$mode | + ffmpeg -y -hide_banner -framerate 25 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded-$mode-debug.mp4 + cargo run --release --bin reschmux-decode -- < samples/encoded-$mode | + ffmpeg -y -hide_banner -framerate 25 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded-$mode.mp4 +end diff --git a/old/evc/scripts/gen b/old/evc/scripts/gen new file mode 100755 index 0000000..6c8fa3c --- /dev/null +++ b/old/evc/scripts/gen @@ -0,0 +1,10 @@ +#!/bin/fish +set w $argv[1] +set h $argv[2] +ffmpeg -hide_banner -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 | + LOG=info cargo run --release --bin reschmux-encode -- -W {$w} -H {$h} $argv[4..] >samples/encoded +ffmpeg -hide_banner -y -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 samples/reference.webm +LOG=info cargo run --release --bin decode -- <samples/encoded | + ffmpeg -hide_banner -y -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded.webm +LOG=info cargo run --release --bin decode -- --debug <samples/encoded | + ffmpeg -hide_banner -y -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded-debug.webm diff --git a/old/evc/scripts/report b/old/evc/scripts/report new file mode 100755 index 0000000..06a5517 --- /dev/null +++ b/old/evc/scripts/report @@ -0,0 +1,9 @@ +#!/bin/fish +cargo build --release +begin + echo "version: $argv[1]" + echo "commit: $(cat ../.git/refs/heads/master | head -c 10)" + echo "encode: $(command time -f %U ./target/release/reschmux-encode -W (math 1920 / 2) -H (math 1080 / 2) <samples/raw >samples/encoded 2>| tail -n 1)s" + echo "decode: $(command time -f %U ./target/release/reschmux-decode <samples/encoded >samples/decoded 2>| tail -n 1)s" + echo "size: $(du -h samples/encoded | cut -f 1)" +end | tee -a reports/(date) diff --git a/old/evc/scripts/stream b/old/evc/scripts/stream new file mode 100755 index 0000000..7e9400d --- /dev/null +++ b/old/evc/scripts/stream @@ -0,0 +1,7 @@ +#!/bin/fish +set w $argv[1] +set h $argv[2] +ffmpeg -hide_banner -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 | + cargo run --release --bin reschmux-encode -- -W {$w} -H {$h} $argv[4..] | + cargo run --release --bin reschmux-decode -- --debug | + ffplay -hide_banner -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo pipe:0 diff --git a/old/evc/scripts/stream-nodebug b/old/evc/scripts/stream-nodebug new file mode 100755 index 0000000..380ec5f --- /dev/null +++ b/old/evc/scripts/stream-nodebug @@ -0,0 +1,7 @@ +#!/bin/fish +set w $argv[1] +set h $argv[2] +ffmpeg -hide_banner -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 | + cargo run --release --bin reschmux-encode -- -W {$w} -H {$h} $argv[4..] | + cargo run --release --bin reschmux-decode -- | + ffplay -hide_banner -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo pipe:0 diff --git a/old/evc/spec.md b/old/evc/spec.md new file mode 100644 index 0000000..42be71f --- /dev/null +++ b/old/evc/spec.md @@ -0,0 +1,60 @@ +# Low-Efficiency Video Codec + +The codec compresses image sequences by avoiding redundancies by reusing parts +of previous frames. A frame is one block that is then subdivided further as +needed. + +## Literal-Block + +Stores the pixels' RGB values as is. + +## Split-Block + +Delegates to two sub-blocks. The block is split orthorgonally on the longest +axis. If needed, the left/top block is rounded down and the right/bottom. + +## Reference-Block + +Indicates that parts of the last frame are reused. The data reused is at the +position of this block in the last frame with the translation added. + +## Advanced-Reference-Block + +Like Reference-Block but instead translates by non-integer amount of pixels, +applying a 2x2 matrix from the center of the block and multiplying the color +value of each component, interpolating if necessary. + +## Compressed-Literal-Block + +_**JPEG? just DCT? idk**_ + +## File format + +- magic bytes: `5e b1 c3 08` +- resolution: _`u16, u16`_ +- frame count: _`u64`_ +- frames (repeated [frame count]-times) + - block kind: _`u8` (see tags below)_ + - block: _one of the following_ + - 0 **Literal-Block** + - pixels: _`[[u8; 3]; (inferred)]`_ (stored line-by-line) + - 1 **Compressed-Literal-Block** + - length: _`u32`_ + - data: _`[[u8; length]`_ + - 2 **Split-Block** + - sub-blocks: _`[block; 2]` (see above)_ + - 3 **Reference-Block** + - translation: _`i8, i8`_ + - 4 **Advanced-Reference-Block** + - translation: _`s8, s8`_ (translation) + - transform: _`s8, s8, s8, s8`_ (2x2-matrix applied before sampling + relative to the center) + - value_scale: _`i8`_ (multiplication of each color component by $1.05^n$) + +### Data Types + +- _`u<n>`_: unsigned n-bit integer (little-endian) +- _`i<n>`_: signed n-bit integer using twos-complement (little-endian) +- _`s8`_: 8-bit scalar. When read as _`i8`_ represents a value of + $\frac{x}{|x|} * \sqrt{2}^{|{x}| - 4}$ for $x \neq 0$, otherwise 0 +- _`[<T>; <N>]`_: Array of T with length N diff --git a/old/evc/src/bin/decode.rs b/old/evc/src/bin/decode.rs new file mode 100644 index 0000000..fbf5624 --- /dev/null +++ b/old/evc/src/bin/decode.rs @@ -0,0 +1,63 @@ +#![feature(box_patterns)] +use anyhow::Context; +use clap::Parser; +use indicatif::ProgressBar; +use libreschmux::{ + block::Block, + codec::decode::{decode_block, DecodeConfig}, + debug::draw_debug, + format::{header::Header, ser::Source}, + frame::Frame, +}; +use log::{info, warn}; +use std::io::{BufReader, BufWriter}; + +#[derive(Parser)] +#[clap(about, version)] +pub struct DecodeArgs { + #[arg(long)] + debug: bool, +} + +fn main() -> anyhow::Result<()> { + env_logger::init_from_env("LOG"); + let args = DecodeArgs::parse(); + + let mut input = BufReader::new(std::io::stdin()); + let mut output = BufWriter::new(std::io::stdout()); + + let header = input.get::<Header>().context("reading header")?; + info!("{header:?}"); + if header.resolution.x * header.resolution.y > 100_000_000 { + warn!("resolution is quite big. video is likely corrupt."); + } + let size = header.resolution; + + let config = DecodeConfig {}; + + let progress_bar = ProgressBar::new(header.frame_count as u64); + + let mut prev = Frame::new(size); + for i in 0..header.frame_count { + info!("decode frame {i}"); + + let block = Block::read(&mut input, size).context("reading encoded frame")?; + let mut frame = Frame::new(size); + + decode_block(&block, frame.view_mut(), prev.view(), &config); + + progress_bar.inc(1); + + if args.debug { + let mut f2 = frame.clone(); + draw_debug(&block, f2.view_mut()); + f2.write(&mut output).context("writing raw frame")?; + } else { + frame.write(&mut output).context("writing raw frame")?; + } + + prev = frame; + } + drop(input); + Ok(()) +} diff --git a/old/evc/src/bin/encode.rs b/old/evc/src/bin/encode.rs new file mode 100644 index 0000000..43f2c57 --- /dev/null +++ b/old/evc/src/bin/encode.rs @@ -0,0 +1,111 @@ +use anyhow::Context; +use clap::Parser; +use indicatif::ProgressBar; +use libreschmux::{ + codec::{ + decode::{decode_block, DecodeConfig}, + encode::{encode_block, EncodeConfig, EncodeMode}, + }, + format::{header::Header, ser::Sink}, + frame::Frame, + helpers::vector::Vec2, +}; +use log::info; +use std::io::{BufReader, BufWriter}; + +#[derive(Parser)] +#[clap(about, version)] +pub struct EncodeArgs { + #[arg(short = 'W', long)] + width: usize, + #[arg(short = 'H', long)] + height: usize, + + #[arg(short, long)] + mode: EncodeMode, + + #[arg(long)] + no_linear_transform: bool, + #[arg(long)] + no_value_scale: bool, + #[arg(long)] + no_translate: bool, + + #[arg[short = 'L', long]] + length: Option<usize>, + + #[arg(short, long, default_value = "8")] + min_block_size: isize, + + #[arg(short = 't', long, default_value = "200")] + ref_thres: f64, +} + +fn main() -> anyhow::Result<()> { + env_logger::init_from_env("LOG"); + let args = EncodeArgs::parse(); + + let mut input = BufReader::new(std::io::stdin()); + let mut output = BufWriter::new(std::io::stdout()); + + let config = EncodeConfig { + mode: args.mode, + ref_thres: args.ref_thres, + weight_factor: 50.0, + max_diff_area: 10_000, + min_block_size: args.min_block_size, + do_translate: !args.no_translate, + do_linear_transform: !args.no_linear_transform, + do_value_scale: !args.no_value_scale, + }; + + let size = Vec2 { + x: args.width as isize, + y: args.height as isize, + }; + output + .put(Header { + resolution: size, + frame_count: args.length.unwrap_or(usize::MAX), + }) + .context("writing header")?; + + let progress_bar = args.length.map(|len| ProgressBar::new(len as u64)); + let mut prev_frame = Frame::new(size); + + for i in 0.. { + info!("encode frame {i}"); + let mut frame = Frame::read(&mut input, size).context("reading raw frame")?; + + let v1 = frame.view(); + let v2 = prev_frame.view(); + + let (error, root) = encode_block(v1, v2, &config); + + // compress_block( + // &mut root, + // Vec2 { + // x: size.x as usize, + // y: size.y as usize, + // }, + // ); + + root.write(&mut output, size) + .context("writing encoded frame")?; + + info!( + "cumulative error: {error} ({} per pixel)", + error / frame.view().area() as f64 + ); + + if let Some(progress_bar) = &progress_bar { + progress_bar.inc(1); + } + + decode_block(&root, frame.view_mut(), prev_frame.view(), &DecodeConfig {}); + + prev_frame = frame; + } + + Ok(()) +} diff --git a/old/evc/src/bin/info.rs b/old/evc/src/bin/info.rs new file mode 100644 index 0000000..ebd8442 --- /dev/null +++ b/old/evc/src/bin/info.rs @@ -0,0 +1,10 @@ +use anyhow::Context; +use libreschmux::format::{header::Header, ser::Source}; +use std::io::BufReader; + +fn main() { + env_logger::init_from_env("LOG"); + let mut input = BufReader::new(std::io::stdin()); + let header = input.get::<Header>().context("reading header").unwrap(); + eprintln!("{header:#?}") +} diff --git a/old/evc/src/block.rs b/old/evc/src/block.rs new file mode 100644 index 0000000..d0f940b --- /dev/null +++ b/old/evc/src/block.rs @@ -0,0 +1,123 @@ +use crate::{ + format::ser::{ConstSizeSerExt, Ser, Sink, Small, Source}, + helpers::vector::Vec2, + helpers::{matrix::Mat2, pixel::Pixel}, +}; +use anyhow::bail; + +#[derive(Clone, Debug, PartialEq)] +pub enum Block { + Literal(Vec<Pixel>), + CompressedLiteral(Vec<u8>), + Split(Box<[Block; 2]>), + Reference { translation: Vec2<isize> }, + AdvancedReference(AdvancedReference), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AdvancedReference { + pub translation: Vec2<i8>, + pub transform: Mat2<i8>, + pub value_scale: i8, +} + +impl Block { + pub const REFZERO: Block = Block::Reference { + translation: Vec2::<isize>::ZERO, + }; + + pub fn write(&self, sink: &mut impl std::io::Write, size: Vec2<isize>) -> anyhow::Result<()> { + match &self { + Block::Literal(pixels) => { + sink.put(0u8)?; + pixels.write_const_size(sink, size.area() as usize)?; + } + Block::CompressedLiteral(data) => { + sink.put(1u8)?; + data.write(sink)?; + } + Block::Split(box [a, b]) => { + sink.put(2u8)?; + let [asize, bsize] = split_size(size); + a.write(sink, asize)?; + b.write(sink, bsize)?; + } + Block::Reference { translation } => { + sink.put(3u8)?; + sink.put(Small(*translation))?; + } + Block::AdvancedReference(AdvancedReference { + translation, + transform, + value_scale, + }) => { + sink.put(4u8)?; + sink.put((*translation, *transform, *value_scale))?; + } + } + Ok(()) + } + + pub fn read(source: &mut impl std::io::Read, size: Vec2<isize>) -> anyhow::Result<Self> { + let variant = source.get::<u8>()?; + Ok(match variant { + 0 => Block::Literal(Vec::read_const_size(source, size.area() as usize)?), + 1 => Block::CompressedLiteral(Vec::read(source)?), + 2 => Block::Split(Box::new({ + let [asize, bsize] = split_size(size); + let a = Block::read(source, asize)?; + let b = Block::read(source, bsize)?; + [a, b] + })), + 3 => Block::Reference { + translation: source.get::<Small<Vec2<isize>>>()?.0, + }, + 4 => Block::AdvancedReference(AdvancedReference { + translation: source.get()?, + transform: source.get()?, + value_scale: source.get()?, + }), + x => bail!("corrupt block type ({})", x), + }) + } +} + +pub fn split_size(size: Vec2<isize>) -> [Vec2<isize>; 2] { + let vert = size.x > size.y; + [ + if vert { + (size.x / 2, size.y).into() + } else { + (size.x, size.y / 2).into() + }, + if vert { + (size.x - size.x / 2, size.y).into() + } else { + (size.x, size.y - size.y / 2).into() + }, + ] +} + +impl Block { + pub fn is_literal(&self) -> bool { + matches!(self, Block::Literal(..)) + } + pub fn identical_ref(a: &Self, b: &Self) -> bool { + matches!(a, Block::Reference { .. }) && a == b + } +} + +impl Default for AdvancedReference { + fn default() -> Self { + Self { + translation: Vec2 { x: 0, y: 0 }, + transform: Mat2 { + a: 4, + b: 0, + c: 0, + d: 4, + }, + value_scale: 0, + } + } +} diff --git a/old/evc/src/codec/compress.rs b/old/evc/src/codec/compress.rs new file mode 100644 index 0000000..09d1f29 --- /dev/null +++ b/old/evc/src/codec/compress.rs @@ -0,0 +1,155 @@ +use crate::{ + block::Block, + frame::Frame, + helpers::{pixel::Pixel, vector::Vec2}, + view::View, +}; +use rustdct::DctPlanner; + +pub fn compress_block(block: &mut Block, size: Vec2<usize>) { + match block { + Block::Literal(p) => { + let comp = lit_compress(size.x, size.y, &p); + *block = Block::CompressedLiteral(comp) + } + Block::Split(box [a, b]) => { + let vert = size.x > size.y; + compress_block( + a, + if vert { + Vec2 { + x: size.x / 2, + y: size.y, + } + } else { + Vec2 { + x: size.x, + y: size.y / 2, + } + }, + ); + compress_block( + b, + if vert { + Vec2 { + x: size.x - size.x / 2, + y: size.y, + } + } else { + Vec2 { + x: size.x, + y: size.y - size.y / 2, + } + }, + ); + } + _ => (), + } +} + +pub fn lit_compress(w: usize, h: usize, pixels: &[Pixel]) -> Vec<u8> { + let mut out = vec![]; + for ci in 0..3 { + let mut ch = vec![0.; w * h]; + for y in 0..h { + for x in 0..w { + let p = pixels[x + y * w]; + ch[x + y * w] = match ci { + 0 => p.r, + 1 => p.g, + 2 => p.b, + _ => unreachable!(), + } as f32 + } + } + + norm_dct_channel(w, h, &mut ch); + for i in 0..w * h { + out.extend(unsafe { std::mem::transmute::<_, [u8; 4]>(ch[i]) }); + } + } + out +} + +pub fn lit_decompress(compressed: &[u8], mut target: View<&mut Frame>) { + let (w, h) = (target.size.x as usize, target.size.y as usize); + for ci in 0..3 { + let mut ch = compressed[ci * w * h..(ci + 1) * w * h] + .chunks_exact(4) + .map(|v| unsafe { + std::mem::transmute::<_, f32>(TryInto::<[u8; 4]>::try_into(v).unwrap()) + }) + .collect::<Vec<_>>(); + norm_idct_channel(w, h, &mut ch); + for y in 0..h { + for x in 0..w { + let p = &mut target[Vec2 { + x: x as isize, + y: y as isize, + }]; + *(match ci { + 0 => &mut p.r, + 1 => &mut p.g, + 2 => &mut p.b, + _ => unreachable!(), + }) = ch[x + y * w] as u8 + } + } + } +} + +fn norm_dct_channel(w: usize, h: usize, values: &mut [f32]) { + let d = DctPlanner::new().plan_dct2(w); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values.chunks_exact_mut(w) { + d.process_dct2_with_scratch(c, &mut temp) + } + + let mut values_trans = transpose(w, h, values); + let d = DctPlanner::new().plan_dct2(h); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values_trans.chunks_exact_mut(h) { + d.process_dct2_with_scratch(c, &mut temp) + } + + let scale = 2. / (w as f32 * h as f32).sqrt(); + for (i, v) in values_trans.iter().enumerate() { + values[i] = *v * scale * 127.0 + } +} + +fn norm_idct_channel(h: usize, w: usize, values: &mut [f32]) { + let scale = 2. / (w as f32 * h as f32).sqrt(); + for v in values.iter_mut() { + *v /= scale * 127.0 + } + + let d = DctPlanner::new().plan_dct2(w); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values.chunks_exact_mut(w) { + d.process_dct3_with_scratch(c, &mut temp) + } + + let mut values_trans = transpose(w, h, values); + let d = DctPlanner::new().plan_dct2(h); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values_trans.chunks_exact_mut(h) { + d.process_dct3_with_scratch(c, &mut temp) + } + values.copy_from_slice(&values_trans) +} + +fn transpose(w: usize, h: usize, values: &[f32]) -> Vec<f32> { + let mut io = 0; + let mut it; + let mut transposed = vec![0.; values.len()]; + for row in 0..h { + it = row; + for _ in 0..w { + transposed[it] = values[io]; + io += 1; + it += h; + } + } + transposed +} diff --git a/old/evc/src/codec/decode.rs b/old/evc/src/codec/decode.rs new file mode 100644 index 0000000..c042278 --- /dev/null +++ b/old/evc/src/codec/decode.rs @@ -0,0 +1,34 @@ +use super::compress::lit_decompress; +use crate::{block::Block, frame::Frame, refsampler::Sampler, view::View}; + +pub struct DecodeConfig {} + +pub fn decode_block( + block: &Block, + mut target: View<&mut Frame>, + prev: View<&Frame>, + config: &DecodeConfig, +) { + match &block { + Block::Literal(pixels) => target.set_pixels(pixels), + Block::Split(box [a, b]) => { + let [a, b] = unsafe { std::mem::transmute::<_, [&'static Block; 2]>([a, b]) }; + let [at, bt] = unsafe { + std::mem::transmute::<_, [View<&'static mut Frame>; 2]>(target.split_mut_unsafe()) + }; + let [ap, bp] = + unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) }; + let config = unsafe { std::mem::transmute::<_, &'static DecodeConfig>(config) }; + + rayon::join( + move || decode_block(a, at, ap, config), + move || decode_block(b, bt, bp, config), + ); + } + Block::CompressedLiteral(data) => { + lit_decompress(&data, target); + } + Block::Reference { translation } => target.copy_from(&prev.offset(*translation)), + Block::AdvancedReference(r) => target.copy_from_sampler(&Sampler::from_refblock(prev, r)), + } +} diff --git a/old/evc/src/codec/encode/advanced.rs b/old/evc/src/codec/encode/advanced.rs new file mode 100644 index 0000000..0e45176 --- /dev/null +++ b/old/evc/src/codec/encode/advanced.rs @@ -0,0 +1,115 @@ +use super::EncodeConfig; +use crate::{ + block::{AdvancedReference, Block}, + frame::Frame, + refsampler::Sampler, + view::View, +}; + +pub fn default( + view: &View<&Frame>, + prev: &View<&Frame>, + config: &EncodeConfig, + max_diff: f64, +) -> (f64, Block) { + let mut pm = AdvancedReference::default(); + let sampler = Sampler::from_refblock(prev.clone(), &pm); + let mut diff = View::diff_sampler(&view, &sampler); + if diff < max_diff { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut p) = (diff, pm.clone()); + + if config.do_translate { + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); + } + if config.do_value_scale { + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1); + } + if config.do_linear_transform { + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d += 1); + } + + if d >= diff { + break (diff, Block::AdvancedReference(pm)); + } + + diff = d; + pm = p; + } + } +} + +pub fn partial( + view: &View<&Frame>, + prev: &View<&Frame>, + _config: &EncodeConfig, + max_diff: f64, +) -> (f64, Block) { + let mut pm = AdvancedReference::default(); + let sampler = Sampler::from_refblock(prev.clone(), &pm); + let mut diff = View::diff_sampler(&view, &sampler); + if diff < max_diff { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut p) = (diff, pm.clone()); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); + + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1); + + if d >= diff { + break (diff, Block::AdvancedReference(pm)); + } + diff = d; + pm = p; + } + } +} + +#[inline] +fn pk<F: FnMut(&mut AdvancedReference) -> ()>( + view: &View<&Frame>, + prev: &View<&Frame>, + diff: &mut f64, + params: &mut AdvancedReference, + initial_params: &AdvancedReference, + mut f: F, +) { + let mut p = initial_params.clone(); + f(&mut p); + let sampler = Sampler::from_refblock(prev.clone(), &p); + let d = View::diff_sampler(view, &sampler); + if d < *diff { + *diff = d; + *params = p; + } +} diff --git a/old/evc/src/codec/encode/mod.rs b/old/evc/src/codec/encode/mod.rs new file mode 100644 index 0000000..30342f0 --- /dev/null +++ b/old/evc/src/codec/encode/mod.rs @@ -0,0 +1,112 @@ +pub mod advanced; +pub mod simple; + +use crate::{ + block::Block, + frame::Frame, + helpers::{pixel::Pixel, vector::Vec2}, + view::View, +}; +use clap::ValueEnum; + +#[derive(Debug, Clone)] +pub struct EncodeConfig { + pub mode: EncodeMode, + pub ref_thres: f64, + pub max_diff_area: isize, + pub min_block_size: isize, + pub weight_factor: f64, + + pub do_translate: bool, + pub do_value_scale: bool, + pub do_linear_transform: bool, +} + +#[derive(Debug, Clone, ValueEnum)] +pub enum EncodeMode { + Trivial, + SimpleFast, + SimpleExhaustive, + Advanced, + AdvancedPartial, +} + +pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfig) -> (f64, Block) { + let (diff, refblock) = if view.area() > config.max_diff_area { + ( + f64::INFINITY, + Block::Reference { + translation: Vec2::<isize>::ZERO, + }, + ) + } else { + let weight = importance(&view).max(0.5); + let irrelevance = config.weight_factor / weight; + let max_diff = config.ref_thres - irrelevance; + let (diff, refblock) = match config.mode { + EncodeMode::Trivial => ( + View::diff(&view, &prev), + Block::Reference { + translation: Vec2::<isize>::ZERO, + }, + ), + EncodeMode::SimpleExhaustive => simple::exhaustive(&view, &prev, config, max_diff), + EncodeMode::SimpleFast => simple::fast(&view, &prev, config, max_diff), + EncodeMode::Advanced => advanced::default(&view, &prev, config, max_diff), + EncodeMode::AdvancedPartial => advanced::partial(&view, &prev, config, max_diff), + }; + (diff - irrelevance, refblock) + }; + if diff < config.ref_thres { + (diff, refblock) + } else { + if view.size.x < config.min_block_size || view.size.y < config.min_block_size { + (0.0, Block::Literal(view.pixels())) + } else { + let [av, bv] = + unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(view.split()) }; + let [ap, bp] = + unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) }; + let config = unsafe { std::mem::transmute::<_, &'static EncodeConfig>(config) }; + + // only bother to do multithreading, when the block is big. + let ((ad, a), (bd, b)) = if view.area() > 100 { + rayon::join( + || encode_block(av, ap, config), + || encode_block(bv, bp, config), + ) + } else { + (encode_block(av, ap, config), encode_block(bv, bp, config)) + }; + + ( + ad + bd, + if a.is_literal() && b.is_literal() { + Block::Literal(view.pixels()) + } else if Block::identical_ref(&a, &b) { + Block::Reference { + translation: Vec2::<isize>::ZERO, + } + } else { + Block::Split(Box::new([a, b])) + }, + ) + } + } +} + +pub fn importance(view: &View<&Frame>) -> f64 { + let mut acc = 0; + for x in 0..view.size.x { + for y in 0..view.size.y { + let p = Vec2 { x, y }; + if x > 0 { + acc += Pixel::distance(view[p], view[p + Vec2::<isize>::LEFT]); + } + if y > 0 { + acc += Pixel::distance(view[p], view[p + Vec2::<isize>::UP]); + } + } + } + (acc / view.area() as usize) as f64 +} diff --git a/old/evc/src/codec/encode/simple.rs b/old/evc/src/codec/encode/simple.rs new file mode 100644 index 0000000..2a971af --- /dev/null +++ b/old/evc/src/codec/encode/simple.rs @@ -0,0 +1,65 @@ +use crate::{block::Block, frame::Frame, helpers::vector::Vec2, view::View}; + +use super::EncodeConfig; + +pub fn exhaustive( + view: &View<&Frame>, + prev: &View<&Frame>, + _config: &EncodeConfig, + _max_diff: f64, +) -> (f64, Block) { + let mut diff = f64::INFINITY; + let mut translation = Vec2::<isize>::ZERO; + const OFFSETS: &[isize] = &[-64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64]; + for x in OFFSETS { + for y in OFFSETS { + let t = Vec2 { x: *x, y: *y }; + let d = View::diff(&view, &prev.offset(t)); + if d < diff { + translation = t; + diff = d; + } + } + } + (diff, Block::Reference { translation }) +} + +pub fn fast( + view: &View<&Frame>, + prev: &View<&Frame>, + _config: &EncodeConfig, + max_diff: f64, +) -> (f64, Block) { + let mut offset = Vec2::<isize>::ZERO; + let mut diff = View::diff(&view, &prev); + if diff < max_diff { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut o) = (diff, offset); + let mut probe = |test_o: Vec2<isize>| { + let test_d = View::diff(&prev.clone().offset(test_o), &view); + if test_d < d { + d = test_d; + o = test_o; + } + }; + + probe(offset + Vec2 { x: 8, y: 0 }); + probe(offset + Vec2 { x: -8, y: 0 }); + probe(offset + Vec2 { x: 0, y: 8 }); + probe(offset + Vec2 { x: 0, y: -8 }); + + probe(offset + Vec2 { x: 1, y: 0 }); + probe(offset + Vec2 { x: -1, y: 0 }); + probe(offset + Vec2 { x: 0, y: 1 }); + probe(offset + Vec2 { x: 0, y: -1 }); + + if d >= diff { + break (diff, Block::Reference { translation: o }); + } + diff = d; + offset = o; + } + } +} diff --git a/old/evc/src/codec/mod.rs b/old/evc/src/codec/mod.rs new file mode 100644 index 0000000..3203e6e --- /dev/null +++ b/old/evc/src/codec/mod.rs @@ -0,0 +1,3 @@ +pub mod decode; +pub mod encode; +pub mod compress; diff --git a/old/evc/src/debug.rs b/old/evc/src/debug.rs new file mode 100644 index 0000000..a7a9545 --- /dev/null +++ b/old/evc/src/debug.rs @@ -0,0 +1,103 @@ +use crate::{ + block::Block, + format::ser::map_scalar8, + frame::Frame, + helpers::vector::Vec2, + helpers::{matrix::Mat2, pixel::Pixel}, + view::View, +}; + +impl View<&mut Frame> { + pub fn draw_box(&mut self, color: Pixel) { + let w = self.size.x; + let h = self.size.y; + for x in 0..w { + self[(x, 0)] = color; + self[(x, h - 1)] = color; + } + for y in 0..h { + self[(0, y)] = color; + self[(w - 1, y)] = color; + } + } +} + +impl Frame { + pub fn draw_line(&mut self, start: Vec2<f32>, end: Vec2<f32>, color: Pixel) { + let diff = end - start; + let len = (diff.x * diff.x + diff.y * diff.y).sqrt(); + let normal = Vec2 { + x: diff.x / len, + y: diff.y / len, + }; + let mut cursor = start.clone(); + let mut lc = 0.0; + while lc < len { + self.set(cursor.into(), color); + lc += 0.5; + cursor = cursor + normal.scale(0.5); + } + } +} + +impl Pixel { + pub const RED: Pixel = Pixel { r: 255, g: 0, b: 0 }; + pub const GREEN: Pixel = Pixel { r: 0, g: 255, b: 0 }; + pub const BLUE: Pixel = Pixel { r: 0, g: 0, b: 255 }; + pub const MAGENTA: Pixel = Pixel { + r: 255, + g: 0, + b: 255, + }; + pub const CYAN: Pixel = Pixel { + r: 0, + g: 255, + b: 255, + }; +} + +pub fn draw_debug(block: &Block, mut target: View<&mut Frame>) { + match &block { + Block::Literal(_) | Block::CompressedLiteral(_) => { + target.draw_box(Pixel::GREEN); + } + Block::Split(box [a, b]) => { + let [at, bt] = target.split_mut_unsafe(); + draw_debug(a, at); + draw_debug(b, bt); + } + Block::Reference { translation } => { + target.draw_box(Pixel::BLUE); + target.frame.draw_line( + target.center().into(), + (target.center() + *translation).into(), + Pixel::RED, + ) + } + Block::AdvancedReference(r) => { + let mat = Mat2 { + a: map_scalar8(r.transform.a), + b: map_scalar8(r.transform.b), + c: map_scalar8(r.transform.c), + d: map_scalar8(r.transform.d), + }; + let translation = Vec2 { + x: map_scalar8(r.translation.x), + y: map_scalar8(r.translation.y), + }; + let halfsize = Into::<Vec2<f32>>::into(target.size).scale(0.5); + let transform = |p| translation + mat.transform(p - halfsize) + halfsize; + let (tl, tr, bl, br) = ( + transform(Vec2::<f32>::ZERO) + target.offset.into(), + transform(target.size.x_only().into()) + target.offset.into(), + transform(target.size.y_only().into()) + target.offset.into(), + transform(target.size.into()) + target.offset.into(), + ); + target.draw_box(Pixel::CYAN); + target.frame.draw_line(tl, tr, Pixel::MAGENTA); + target.frame.draw_line(tr, br, Pixel::MAGENTA); + target.frame.draw_line(bl, br, Pixel::MAGENTA); + target.frame.draw_line(tl, bl, Pixel::MAGENTA); + } + }; +} diff --git a/old/evc/src/format/header.rs b/old/evc/src/format/header.rs new file mode 100644 index 0000000..ecbae89 --- /dev/null +++ b/old/evc/src/format/header.rs @@ -0,0 +1,29 @@ +use crate::{ + format::ser::{Ser, Sink, Source}, + helpers::vector::Vec2, +}; + +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct Header { + pub resolution: Vec2<isize>, + pub frame_count: usize, +} + +pub const MAGIC: [u8; 4] = [0x5eu8, 0xb1u8, 0xc3u8, 0x08u8]; + +impl Ser for Header { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put(MAGIC)?; + sink.put((Into::<Vec2<u16>>::into(self.resolution), self.frame_count))?; + Ok(()) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> { + assert_eq!(source.get::<[u8; 4]>()?, MAGIC); + let (resolution, frame_count): (Vec2<u16>, usize) = source.get()?; + Ok(Self { + resolution: resolution.into(), + frame_count, + }) + } +} diff --git a/old/evc/src/format/mod.rs b/old/evc/src/format/mod.rs new file mode 100644 index 0000000..d4fb18c --- /dev/null +++ b/old/evc/src/format/mod.rs @@ -0,0 +1,2 @@ +pub mod header; +pub mod ser;
\ No newline at end of file diff --git a/old/evc/src/format/ser.rs b/old/evc/src/format/ser.rs new file mode 100644 index 0000000..f063377 --- /dev/null +++ b/old/evc/src/format/ser.rs @@ -0,0 +1,335 @@ +use anyhow::Context; +use std::io::{Read, Write}; + +use crate::helpers::{matrix::Mat2, vector::Vec2}; + +pub trait Sink { + fn put<V: Ser>(&mut self, value: V) -> anyhow::Result<()>; +} +pub trait Source { + fn get<V: Ser>(&mut self) -> anyhow::Result<V>; +} + +impl<T: Write> Sink for T { + fn put<V: Ser>(&mut self, value: V) -> anyhow::Result<()> { + value.write(self) + } +} +impl<T: Read> Source for T { + fn get<V: Ser>(&mut self) -> anyhow::Result<V> { + V::read(self) + } +} + +pub trait Ser: Sized { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()>; + fn read(source: &mut impl Read) -> anyhow::Result<Self>; +} + +impl<A: Ser, B: Ser> Ser for (A, B) { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.0.write(sink).context("first tuple field")?; + self.1.write(sink).context("second tuple field")?; + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + Ok((A::read(source)?, B::read(source)?)) + } +} +impl<A: Ser, B: Ser, C: Ser> Ser for (A, B, C) { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.0.write(sink).context("first tuple field")?; + self.1.write(sink).context("second tuple field")?; + self.2.write(sink).context("third tuple field")?; + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + Ok((A::read(source)?, B::read(source)?, C::read(source)?)) + } +} +impl<A: Ser, B: Ser, C: Ser, D: Ser> Ser for (A, B, C, D) { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.0.write(sink).context("first tuple field")?; + self.1.write(sink).context("second tuple field")?; + self.2.write(sink).context("third tuple field")?; + self.3.write(sink).context("fourth tuple field")?; + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + Ok(( + A::read(source)?, + B::read(source)?, + C::read(source)?, + D::read(source)?, + )) + } +} + +impl<A: Ser, const N: usize> Ser for [A; N] { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + for e in self { + e.write(sink).context("some array")?; + } + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut k: [A; N] = unsafe { std::mem::zeroed() }; + for i in 0..N { + k[i] = A::read(source)?; + } + Ok(k) + } +} + +impl<T: Ser> Ser for Vec<T> { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.len().write(sink)?; + for e in self { + e.write(sink).context("some vec")?; + } + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut v = vec![]; + for _ in 0..usize::read(source)? { + v.push(T::read(source)?) + } + Ok(v) + } +} + +pub trait ConstSizeSerExt: Sized { + fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()>; + fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result<Self>; +} +impl<T: Ser> ConstSizeSerExt for Vec<T> { + fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()> { + assert_eq!(self.len(), size); + for e in self { + e.write(sink).context("some const-size vec")?; + } + Ok(()) + } + + fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result<Self> { + let mut v = vec![]; + for _ in 0..size { + v.push(T::read(source)?) + } + Ok(v) + } +} + +impl Ser for u8 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink.write_all(&[*self]).context("write u8")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 1]; + source.read_exact(&mut buf)?; + Ok(buf[0]) + } +} +impl Ser for i8 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 1]>(self) }) + .context("write i8")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 1]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for u16 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 2]>(self) }) + .context("write u16")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 2]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for u32 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 4]>(self) }) + .context("write u32")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 4]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for u64 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write u64")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for usize { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write usize")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for isize { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write isize")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for f32 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 4]>(self) }) + .context("write f32")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 4]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for f64 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write f64")?) + } + fn read(source: &mut impl Read) -> anyhow::Result<Self> { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} + +impl<T: Ser + Copy> Ser for Vec2<T> { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.x, self.y)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> { + let (x, y) = source.get()?; + Ok(Vec2 { x, y }) + } +} +impl<T: Ser + Copy> Ser for Mat2<T> { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.a, self.b, self.c, self.d)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> { + let (a, b, c, d) = source.get()?; + Ok(Mat2 { a, b, c, d }) + } +} + +pub struct Small<T>(pub T); +impl Ser for Small<Vec2<isize>> { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.0.x as i8, self.0.y as i8)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> { + let (x, y): (i8, i8) = source.get()?; + Ok(Small(Vec2 { + x: x as isize, + y: y as isize, + })) + } +} + +pub fn map_scalar8(v: i8) -> f32 { + match v { + 0 => 0.0, + x if x > 0 => 2f32.powf((x as f32).abs() / 2.0 - 2.0), + x => -2f32.powf((-x as f32).abs() / 2.0 - 2.0), + } +} +// const SCALAR8: [f32; 256] = gen_scalar8_lookup(); +// const fn gen_scalar8_lookup() -> [f32; 256] { +// let mut a = [0.0; 256]; +// let mut i = 0usize; +// while i < 256 { +// a[i as usize] = if i == 0 { +// 0.0 +// } else { +// let x = i as i8 as f32; +// x.signum() * 2f32.powf(x.abs() / 2.0 - 2.0) +// }; +// i += 1; +// } +// a +// } + +#[cfg(test)] +mod test { + use super::{Ser, Sink}; + use crate::format::header::Header; + use crate::{format::ser::Source, helpers::vector::Vec2}; + use std::fmt::Debug; + use std::io::Cursor; + + fn test_ser<T: PartialEq + Ser + Debug + Clone>(value: T) { + let mut buf = vec![]; + Cursor::new(&mut buf).put(value.clone()).unwrap(); + assert_eq!(value, Cursor::new(&mut buf).get().unwrap()); + } + + #[test] + fn simple() { + let mut buf = vec![]; + Cursor::new(&mut buf).put(10usize).unwrap(); + assert_eq!(10usize, Cursor::new(&mut buf).get().unwrap()); + } + #[test] + fn tuple() { + let mut buf = vec![]; + Cursor::new(&mut buf).put((10usize, 5u8, 3u16)).unwrap(); + assert_eq!((10usize, 5u8, 3u16), Cursor::new(&mut buf).get().unwrap()); + } + #[test] + fn header() { + test_ser(Header { + frame_count: 123, + resolution: Vec2 { x: 13, y: 37 }, + }); + } + #[test] + fn vec() { + test_ser(vec![1u16, 2, 3, 4]); + } + #[test] + fn array() { + test_ser([1u16, 2, 3, 4]); + } +} diff --git a/old/evc/src/frame.rs b/old/evc/src/frame.rs new file mode 100644 index 0000000..78d0e73 --- /dev/null +++ b/old/evc/src/frame.rs @@ -0,0 +1,109 @@ +use crate::{ + format::ser::{Sink, Source}, + helpers::{pixel::Pixel, vector::Vec2}, + view::View, +}; +use std::ops::{Index, IndexMut}; + +#[derive(Debug, Clone)] +pub struct Frame { + pub size: Vec2<isize>, + buffer: Vec<Pixel>, +} + +impl Frame { + pub fn new(size: Vec2<isize>) -> Self { + Self { + size, + buffer: (0..size.x * size.y).map(|_| Pixel::default()).collect(), + } + } + pub fn read(source: &mut impl Source, size: Vec2<isize>) -> anyhow::Result<Self> { + let mut frame = Frame::new(size); + for y in 0..size.y { + for x in 0..size.x { + let pixel = source.get::<Pixel>()?; + frame[(x, y)] = pixel; + } + } + Ok(frame) + } + pub fn write(&self, sink: &mut impl Sink) -> anyhow::Result<()> { + for y in 0..self.size.y { + for x in 0..self.size.x { + sink.put(self[(x, y)])?; + } + } + Ok(()) + } + pub fn view<'a>(&'a self) -> View<&'a Frame> { + View::new(self, Vec2::<isize>::ZERO, self.size) + } + pub fn view_mut<'a>(&'a mut self) -> View<&'a mut Frame> { + View::new(self, Vec2::<isize>::ZERO, self.size) + } + pub fn view_area<'a>(&'a self, offset: Vec2<isize>, size: Vec2<isize>) -> View<&'a Frame> { + View::new(self, offset, size) + } + pub fn view_area_mut<'a>( + &'a mut self, + offset: Vec2<isize>, + size: Vec2<isize>, + ) -> View<&'a mut Frame> { + View::new(self, offset, size) + } + pub fn set(&mut self, pos: Vec2<isize>, color: Pixel) { + if pos.x >= 0 && pos.y >= 0 && pos.x < self.size.x && pos.y < self.size.y { + self[pos] = color + } + } +} + +impl Index<Vec2<isize>> for Frame { + type Output = Pixel; + #[inline] + fn index(&self, Vec2 { x, y }: Vec2<isize>) -> &Self::Output { + &self.buffer + [(x.clamp(0, self.size.x - 1) + y.clamp(0, self.size.y - 1) * self.size.x) as usize] + } +} +impl IndexMut<Vec2<isize>> for Frame { + #[inline] + fn index_mut(&mut self, Vec2 { x, y }: Vec2<isize>) -> &mut Self::Output { + &mut self.buffer[(x + y * self.size.x) as usize] + } +} + +impl Frame { + #[inline] + pub fn sample(&self, p: Vec2<f32>) -> Pixel { + self[Vec2 { + x: p.x as isize, + y: p.y as isize, + }] + // let fx = p.x.floor() as isize; + // let fy = p.y.floor() as isize; + // let cx = p.x.ceil() as isize; + // let cy = p.y.ceil() as isize; + + // // TODO dont loose accuracy here + // Pixel::average( + // Pixel::average(self[Vec2 { x: fx, y: fy }], self[Vec2 { x: fx, y: cy }]), + // Pixel::average(self[Vec2 { x: cx, y: fx }], self[Vec2 { x: cx, y: fy }]), + // ) + } +} + +impl Index<(isize, isize)> for Frame { + type Output = Pixel; + #[inline] + fn index(&self, (x, y): (isize, isize)) -> &Self::Output { + &self[Vec2 { x, y }] + } +} +impl IndexMut<(isize, isize)> for Frame { + #[inline] + fn index_mut(&mut self, (x, y): (isize, isize)) -> &mut Self::Output { + &mut self[Vec2 { x, y }] + } +} diff --git a/old/evc/src/helpers/matrix.rs b/old/evc/src/helpers/matrix.rs new file mode 100644 index 0000000..0007440 --- /dev/null +++ b/old/evc/src/helpers/matrix.rs @@ -0,0 +1,33 @@ +use crate::helpers::vector::Vec2; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Mat2<T> { + pub a: T, + pub b: T, + pub c: T, + pub d: T, +} + +impl<T: std::ops::Mul<Output = T> + std::ops::Add<Output = T> + Copy> Mat2<T> { + #[inline] + pub fn transform(&self, v: Vec2<T>) -> Vec2<T> { + Vec2 { + x: self.a * v.x + self.b * v.y, + y: self.c * v.x + self.d * v.y, + } + } +} + +impl<T: std::ops::Mul<Output = T> + std::ops::Add<Output = T> + Copy> std::ops::Mul for Mat2<T> { + type Output = Mat2<T>; + #[inline] + fn mul(self, rhs: Mat2<T>) -> Mat2<T> { + let (x, y) = (self, rhs); + Mat2 { + a: x.a * y.a + x.b * y.c, + b: x.a * y.b + x.b * y.d, + c: x.c * y.a + x.d * y.c, + d: x.c * y.b + x.d * y.d, + } + } +} diff --git a/old/evc/src/helpers/mod.rs b/old/evc/src/helpers/mod.rs new file mode 100644 index 0000000..d3aa3d2 --- /dev/null +++ b/old/evc/src/helpers/mod.rs @@ -0,0 +1,4 @@ +pub mod vector; +pub mod threading; +pub mod matrix; +pub mod pixel; diff --git a/old/evc/src/helpers/pixel.rs b/old/evc/src/helpers/pixel.rs new file mode 100644 index 0000000..39fe98c --- /dev/null +++ b/old/evc/src/helpers/pixel.rs @@ -0,0 +1,81 @@ +use crate::format::ser::{Ser, Sink, Source}; + +#[derive(Copy, Clone, Debug, Default, PartialEq)] +pub struct Pixel { + pub r: u8, + pub g: u8, + pub b: u8, +} + +impl Ser for Pixel { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.r, self.g, self.b)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> { + let (r, g, b) = source.get()?; + Ok(Self { r, g, b }) + } +} + +impl Pixel { + pub const BLACK: Pixel = Pixel { r: 0, g: 0, b: 0 }; + #[inline] + pub fn distance(a: Pixel, b: Pixel) -> usize { + let (rd, gd, bd) = ( + a.r.abs_diff(b.r) as usize, + a.g.abs_diff(b.g) as usize, + a.b.abs_diff(b.b) as usize, + ); + // fast_sqrt(rd * rd + gd * gd + bd * bd) + // SQRT[rd + gd + bd] + rd + gd + bd + } + + #[inline] + pub fn average(a: Pixel, b: Pixel) -> Pixel { + Pixel { + r: ((a.r as u16 + b.r as u16) >> 1) as u8, + g: ((a.g as u16 + b.g as u16) >> 1) as u8, + b: ((a.b as u16 + b.b as u16) >> 1) as u8, + } + } + + #[inline] + pub fn scale(&self, factor: f32) -> Pixel { + Pixel { + r: ((self.r as f32) * factor).clamp(0.0, 255.0) as u8, + g: ((self.g as f32) * factor).clamp(0.0, 255.0) as u8, + b: ((self.b as f32) * factor).clamp(0.0, 255.0) as u8, + } + } +} + +pub const SQRT: [usize; 256 * 3] = gen_sqrt_lookup(); + +const fn gen_sqrt_lookup<const N: usize>() -> [usize; N] { + let mut arr = [0; N]; + let mut i = 0; + while i < N { + arr[i] = sqrt(i as f32) as usize; + i += 1; + } + arr +} + +const fn sqrt(x: f32) -> f32 { + let a = 1.0; + let a = (a + x / a) * 0.5; + let a = (a + x / a) * 0.5; + let a = (a + x / a) * 0.5; + a +} + +pub fn fast_sqrt(x: usize) -> usize { + let a = 1; + let a = (a + x / (a + 1)) / 2; + let a = (a + x / (a + 1)) / 2; + // let a = (a + x / (a + 1)) / 2; + // let a = (a + x / (a + 1)) / 2; + a +} diff --git a/old/evc/src/helpers/threading.rs b/old/evc/src/helpers/threading.rs new file mode 100644 index 0000000..3291172 --- /dev/null +++ b/old/evc/src/helpers/threading.rs @@ -0,0 +1,26 @@ +use std::{ + sync::atomic::{AtomicUsize, Ordering}, + thread, +}; + +static THREADS_RUNNING: AtomicUsize = AtomicUsize::new(0); + +pub fn both_par<F1, F2, O1, O2>(f1: F1, f2: F2, max_threads: usize) -> (O1, O2) +where + F1: FnOnce() -> O1 + Send + 'static, + O1: Send + 'static, + F2: FnOnce() -> O2, +{ + if THREADS_RUNNING.load(Ordering::Relaxed) < max_threads { + THREADS_RUNNING.fetch_add(1, Ordering::Relaxed); + + let o1h = thread::spawn(move || f1()); + let o2 = f2(); + let o1 = o1h.join().unwrap(); + + THREADS_RUNNING.fetch_sub(1, Ordering::Relaxed); + (o1, o2) + } else { + (f1(), f2()) + } +} diff --git a/old/evc/src/helpers/vector.rs b/old/evc/src/helpers/vector.rs new file mode 100644 index 0000000..a4766d1 --- /dev/null +++ b/old/evc/src/helpers/vector.rs @@ -0,0 +1,126 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Vec2<T> { + pub x: T, + pub y: T, +} + +impl From<Vec2<isize>> for Vec2<u16> { + fn from(value: Vec2<isize>) -> Self { + Self { + x: value.x as u16, + y: value.y as u16, + } + } +} +impl From<Vec2<u16>> for Vec2<isize> { + fn from(value: Vec2<u16>) -> Self { + Self { + x: value.x as isize, + y: value.y as isize, + } + } +} +impl From<Vec2<f32>> for Vec2<isize> { + fn from(value: Vec2<f32>) -> Self { + Self { + x: value.x as isize, + y: value.y as isize, + } + } +} + +impl Vec2<isize> { + pub const ZERO: Vec2<isize> = Vec2 { x: 0, y: 0 }; + pub const UP: Vec2<isize> = Vec2 { x: 0, y: -1 }; + pub const LEFT: Vec2<isize> = Vec2 { x: -1, y: 0 }; +} +impl Vec2<f32> { + pub const ZERO: Vec2<f32> = Vec2 { x: 0.0, y: 0.0 }; + pub const UP: Vec2<f32> = Vec2 { x: 0.0, y: -1.0 }; + pub const LEFT: Vec2<f32> = Vec2 { x: -1.0, y: 0.0 }; +} + +impl<T: std::ops::Div<Output = T> + Copy> Vec2<T> { + pub fn downscale(&self, f: T) -> Self { + Self { + x: self.x / f, + y: self.y / f, + } + } +} + +impl<T: std::ops::Mul<Output = T> + Copy> Vec2<T> { + pub fn scale(&self, f: T) -> Self { + Self { + x: self.x * f, + y: self.y * f, + } + } + pub fn area(&self) -> T { + self.x * self.y + } +} + +impl Vec2<isize> { + pub fn x_only(&self) -> Self { + Self { x: self.x, y: 0 } + } + pub fn y_only(&self) -> Self { + Self { x: 0, y: self.y } + } +} + +impl Into<Vec2<f32>> for Vec2<isize> { + fn into(self) -> Vec2<f32> { + Vec2 { + x: self.x as f32, + y: self.y as f32, + } + } +} +impl From<(isize, isize)> for Vec2<f32> { + fn from((x, y): (isize, isize)) -> Self { + Vec2 { + x: x as f32, + y: y as f32, + } + } +} + +impl<T: std::ops::Add> std::ops::Add for Vec2<T> { + type Output = Vec2<T::Output>; + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} +impl<T: std::ops::Sub> std::ops::Sub for Vec2<T> { + type Output = Vec2<T::Output>; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} +impl<T: std::ops::Mul> std::ops::Mul for Vec2<T> { + type Output = Vec2<T::Output>; + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x * rhs.x, + y: self.y * rhs.y, + } + } +} + +impl<T> From<(T, T)> for Vec2<T> { + #[inline] + fn from((x, y): (T, T)) -> Self { + Vec2 { x, y } + } +} diff --git a/old/evc/src/lib.rs b/old/evc/src/lib.rs new file mode 100644 index 0000000..d269471 --- /dev/null +++ b/old/evc/src/lib.rs @@ -0,0 +1,13 @@ +#![feature(box_patterns)] +#![feature(const_fn_floating_point_arithmetic)] +// #![feature(const_for)] +// #![feature(const_mut_refs)] + +pub mod block; +pub mod codec; +pub mod debug; +pub mod frame; +pub mod view; +pub mod refsampler; +pub mod helpers; +pub mod format; diff --git a/old/evc/src/refsampler.rs b/old/evc/src/refsampler.rs new file mode 100644 index 0000000..8a8f44f --- /dev/null +++ b/old/evc/src/refsampler.rs @@ -0,0 +1,51 @@ +use crate::{ + block::AdvancedReference, + format::ser::map_scalar8, + frame::Frame, + helpers::{matrix::Mat2, pixel::Pixel, vector::Vec2}, + view::View, +}; + +#[derive(Debug, Clone)] +pub struct Sampler<'a> { + pub view: View<&'a Frame>, + pub halfsize: Vec2<f32>, + + pub translation: Vec2<f32>, + pub transform: Mat2<f32>, + + pub value_scale: f32, +} + +impl<'a> Sampler<'a> { + #[inline] + pub fn sample(&self, p: Vec2<f32>) -> Pixel { + self.view + .sample(self.translation + self.transform.transform(p - self.halfsize) + self.halfsize) + .scale(self.value_scale) + } + pub fn from_refblock( + view: View<&'a Frame>, + AdvancedReference { + translation, + transform, + value_scale, + }: &AdvancedReference, + ) -> Self { + Self { + transform: Mat2 { + a: map_scalar8(transform.a), + b: map_scalar8(transform.b), + c: map_scalar8(transform.c), + d: map_scalar8(transform.d), + }, + halfsize: Into::<Vec2<f32>>::into(view.size).scale(0.5), + translation: Vec2 { + x: map_scalar8(translation.x), + y: map_scalar8(translation.y), + }, + value_scale: 1.05f32.powf(*value_scale as f32), + view, + } + } +} diff --git a/old/evc/src/view.rs b/old/evc/src/view.rs new file mode 100644 index 0000000..6f34965 --- /dev/null +++ b/old/evc/src/view.rs @@ -0,0 +1,241 @@ +use crate::{ + frame::Frame, + helpers::{pixel::Pixel, vector::Vec2}, + refsampler::Sampler, +}; +use std::ops::{Index, IndexMut}; + +#[derive(Debug, Clone)] +pub struct View<T> { + pub frame: T, + pub offset: Vec2<isize>, + pub size: Vec2<isize>, +} + +impl<T> View<T> { + pub fn new(frame: T, offset: Vec2<isize>, size: Vec2<isize>) -> Self { + Self { + frame, + offset, + size, + } + } + pub fn area(&self) -> isize { + self.size.x * self.size.y + } + pub fn center(&self) -> Vec2<isize> { + self.offset + self.size.downscale(2) + } +} + +impl<T> View<&mut T> { + pub fn split_mut_unsafe(&mut self) -> [Self; 2] { + let vert = self.size.x > self.size.y; + [ + Self { + frame: unsafe { std::mem::transmute::<&mut T, &mut T>(&mut self.frame) }, + offset: self.offset, + size: if vert { + Vec2 { + x: self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y / 2, + } + }, + }, + Self { + frame: unsafe { std::mem::transmute::<&mut T, &mut T>(&mut self.frame) }, + offset: if vert { + Vec2 { + x: self.offset.x + self.size.x / 2, + y: self.offset.y, + } + } else { + Vec2 { + x: self.offset.x, + y: self.offset.y + self.size.y / 2, + } + }, + size: if vert { + Vec2 { + x: self.size.x - self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y - self.size.y / 2, + } + }, + }, + ] + } +} +impl<T: Copy> View<T> { + pub fn offset(&self, offset: Vec2<isize>) -> Self { + Self { + frame: self.frame, + offset: self.offset + offset, + size: self.size, + } + } + pub fn split(&self) -> [Self; 2] { + let vert = self.size.x > self.size.y; + [ + Self { + frame: self.frame, + offset: self.offset, + size: if vert { + Vec2 { + x: self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y / 2, + } + }, + }, + Self { + frame: self.frame, + offset: if vert { + Vec2 { + x: self.offset.x + self.size.x / 2, + y: self.offset.y, + } + } else { + Vec2 { + x: self.offset.x, + y: self.offset.y + self.size.y / 2, + } + }, + size: if vert { + Vec2 { + x: self.size.x - self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y - self.size.y / 2, + } + }, + }, + ] + } +} +impl<T: Index<Vec2<isize>, Output = Pixel>> View<&T> { + pub fn diff(va: &Self, vb: &Self) -> f64 { + assert_eq!(va.size, vb.size); + let mut acc = 0; + for x in 0..va.size.x { + for y in 0..va.size.y { + let a = va[(x, y)]; + let b = vb[(x, y)]; + acc += Pixel::distance(a, b); + } + } + acc as f64 + } + pub fn diff_sampler(va: &Self, vb: &Sampler<'_>) -> f64 { + assert_eq!(va.size, vb.view.size); + let mut acc = 0; + for x in 0..va.size.x { + for y in 0..va.size.y { + let a = va[(x, y)]; + let b = vb.sample(Vec2 { + x: x as f32, + y: y as f32, + }); + acc += Pixel::distance(a, b); + } + } + acc as f64 + } + + pub fn pixels(&self) -> Vec<Pixel> { + let mut v = vec![]; + for y in 0..self.size.y { + for x in 0..self.size.x { + v.push(self[(x, y)]); + } + } + v + } +} +impl View<&mut Frame> { + pub fn copy_from(&mut self, other: &View<&Frame>) { + for x in 0..self.size.x { + for y in 0..self.size.y { + self[(x, y)] = other[(x, y)]; + } + } + } + pub fn copy_from_sampler(&mut self, other: &Sampler) { + for x in 0..self.size.x { + for y in 0..self.size.y { + self[(x, y)] = other.sample((x, y).into()); + } + } + } + + pub fn set_pixels(&mut self, pixels: &Vec<Pixel>) { + for y in 0..self.size.y { + for x in 0..self.size.x { + self[(x, y)] = pixels[(y * self.size.x + x) as usize] + } + } + } +} +impl View<&Frame> { + pub fn sample(&self, p: Vec2<f32>) -> Pixel { + self.frame.sample(p + self.offset.into()) + } +} + +impl<T: Index<Vec2<isize>, Output = Pixel>> Index<Vec2<isize>> for View<&T> { + type Output = Pixel; + #[inline] + fn index(&self, p: Vec2<isize>) -> &Self::Output { + &self.frame[self.offset + p] + } +} +impl<T: Index<Vec2<isize>, Output = Pixel>> Index<Vec2<isize>> for View<&mut T> { + type Output = Pixel; + #[inline] + fn index(&self, p: Vec2<isize>) -> &Self::Output { + &self.frame[self.offset + p] + } +} +impl<T: IndexMut<Vec2<isize>, Output = Pixel>> IndexMut<Vec2<isize>> for View<&mut T> { + #[inline] + fn index_mut(&mut self, p: Vec2<isize>) -> &mut Self::Output { + &mut self.frame[self.offset + p] + } +} + +impl<T: Index<Vec2<isize>, Output = Pixel>> Index<(isize, isize)> for View<&T> { + type Output = Pixel; + #[inline] + fn index(&self, (x, y): (isize, isize)) -> &Self::Output { + &self[Vec2 { x, y }] + } +} +impl<T: Index<Vec2<isize>, Output = Pixel>> Index<(isize, isize)> for View<&mut T> { + type Output = Pixel; + #[inline] + fn index(&self, (x, y): (isize, isize)) -> &Self::Output { + &self[Vec2 { x, y }] + } +} +impl<T: IndexMut<Vec2<isize>, Output = Pixel>> IndexMut<(isize, isize)> for View<&mut T> { + #[inline] + fn index_mut(&mut self, (x, y): (isize, isize)) -> &mut Self::Output { + &mut self[Vec2 { x, y }] + } +} |