From 680b08e6b9d64284b7992fb52a23e5f891291406 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Thu, 9 Mar 2023 22:48:33 +0100 Subject: rename + readme --- COPYING | 4 +- bv1/.gitignore | 2 + bv1/Cargo.lock | 552 +++++++++++++++++++++++++++++++++++++++++++ bv1/Cargo.toml | 3 + bv1/app/Cargo.toml | 9 + bv1/app/src/bin/bench.rs | 28 +++ bv1/app/src/bin/main.rs | 68 ++++++ bv1/app/src/bin/test.rs | 47 ++++ bv1/codec-web/Cargo.toml | 13 + bv1/codec-web/src/lib.rs | 60 +++++ bv1/codec-web/web/.gitignore | 2 + bv1/codec-web/web/index.html | 14 ++ bv1/codec-web/web/main.ts | 69 ++++++ bv1/codec/Cargo.toml | 12 + bv1/codec/src/debug.rs | 42 ++++ bv1/codec/src/decode.rs | 76 ++++++ bv1/codec/src/diff.rs | 131 ++++++++++ bv1/codec/src/encode.rs | 190 +++++++++++++++ bv1/codec/src/frameio.rs | 35 +++ bv1/codec/src/huff.rs | 190 +++++++++++++++ bv1/codec/src/impls.rs | 165 +++++++++++++ bv1/codec/src/lib.rs | 55 +++++ bv1/codec/src/serialize.rs | 116 +++++++++ bv1/codec/src/split.rs | 42 ++++ bv1/tools/d-disp | 2 + bv1/tools/d-dispd | 2 + bv1/tools/d-save | 2 + bv1/tools/d-saved | 2 + bv1/tools/e | 2 + lvc/.gitignore | 2 - lvc/Cargo.lock | 552 ------------------------------------------- lvc/Cargo.toml | 3 - lvc/app/Cargo.toml | 9 - lvc/app/src/bin/bench.rs | 28 --- lvc/app/src/bin/main.rs | 68 ------ lvc/app/src/bin/test.rs | 47 ---- lvc/codec-web/Cargo.toml | 13 - lvc/codec-web/src/lib.rs | 60 ----- lvc/codec-web/web/.gitignore | 2 - lvc/codec-web/web/index.html | 14 -- lvc/codec-web/web/main.ts | 67 ------ lvc/codec/Cargo.toml | 12 - lvc/codec/src/debug.rs | 42 ---- lvc/codec/src/decode.rs | 76 ------ lvc/codec/src/diff.rs | 131 ---------- lvc/codec/src/encode.rs | 190 --------------- lvc/codec/src/frameio.rs | 35 --- lvc/codec/src/huff.rs | 190 --------------- lvc/codec/src/impls.rs | 165 ------------- lvc/codec/src/lib.rs | 55 ----- lvc/codec/src/serialize.rs | 116 --------- lvc/codec/src/split.rs | 42 ---- lvc/tools/d-disp | 2 - lvc/tools/d-dispd | 2 - lvc/tools/d-save | 2 - lvc/tools/d-saved | 2 - lvc/tools/e | 2 - readme.md | 15 ++ 58 files changed, 1948 insertions(+), 1931 deletions(-) create mode 100644 bv1/.gitignore create mode 100644 bv1/Cargo.lock create mode 100644 bv1/Cargo.toml create mode 100644 bv1/app/Cargo.toml create mode 100644 bv1/app/src/bin/bench.rs create mode 100644 bv1/app/src/bin/main.rs create mode 100644 bv1/app/src/bin/test.rs create mode 100644 bv1/codec-web/Cargo.toml create mode 100644 bv1/codec-web/src/lib.rs create mode 100644 bv1/codec-web/web/.gitignore create mode 100644 bv1/codec-web/web/index.html create mode 100644 bv1/codec-web/web/main.ts create mode 100644 bv1/codec/Cargo.toml create mode 100644 bv1/codec/src/debug.rs create mode 100644 bv1/codec/src/decode.rs create mode 100644 bv1/codec/src/diff.rs create mode 100644 bv1/codec/src/encode.rs create mode 100644 bv1/codec/src/frameio.rs create mode 100644 bv1/codec/src/huff.rs create mode 100644 bv1/codec/src/impls.rs create mode 100644 bv1/codec/src/lib.rs create mode 100644 bv1/codec/src/serialize.rs create mode 100644 bv1/codec/src/split.rs create mode 100755 bv1/tools/d-disp create mode 100755 bv1/tools/d-dispd create mode 100755 bv1/tools/d-save create mode 100755 bv1/tools/d-saved create mode 100755 bv1/tools/e delete mode 100644 lvc/.gitignore delete mode 100644 lvc/Cargo.lock delete mode 100644 lvc/Cargo.toml delete mode 100644 lvc/app/Cargo.toml delete mode 100644 lvc/app/src/bin/bench.rs delete mode 100644 lvc/app/src/bin/main.rs delete mode 100644 lvc/app/src/bin/test.rs delete mode 100644 lvc/codec-web/Cargo.toml delete mode 100644 lvc/codec-web/src/lib.rs delete mode 100644 lvc/codec-web/web/.gitignore delete mode 100644 lvc/codec-web/web/index.html delete mode 100644 lvc/codec-web/web/main.ts delete mode 100644 lvc/codec/Cargo.toml delete mode 100644 lvc/codec/src/debug.rs delete mode 100644 lvc/codec/src/decode.rs delete mode 100644 lvc/codec/src/diff.rs delete mode 100644 lvc/codec/src/encode.rs delete mode 100644 lvc/codec/src/frameio.rs delete mode 100644 lvc/codec/src/huff.rs delete mode 100644 lvc/codec/src/impls.rs delete mode 100644 lvc/codec/src/lib.rs delete mode 100644 lvc/codec/src/serialize.rs delete mode 100644 lvc/codec/src/split.rs delete mode 100755 lvc/tools/d-disp delete mode 100755 lvc/tools/d-dispd delete mode 100755 lvc/tools/d-save delete mode 100755 lvc/tools/d-saved delete mode 100755 lvc/tools/e diff --git a/COPYING b/COPYING index bb3c8b6..4ad19f2 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,5 @@ -bad-avcodec (dhwt-codec, vgcodec) -Copyright (C) 2022 metamuffin +bv1 (and others) - video compression experiments +Copyright (C) 2023 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as diff --git a/bv1/.gitignore b/bv1/.gitignore new file mode 100644 index 0000000..2f22d72 --- /dev/null +++ b/bv1/.gitignore @@ -0,0 +1,2 @@ +/data +/target diff --git a/bv1/Cargo.lock b/bv1/Cargo.lock new file mode 100644 index 0000000..cd51668 --- /dev/null +++ b/bv1/Cargo.lock @@ -0,0 +1,552 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "app" +version = "0.1.0" +dependencies = [ + "bv1", + "clap", +] + +[[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 = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bv1" +version = "0.1.0" +dependencies = [ + "rayon", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[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.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codec-web" +version = "0.1.0" +dependencies = [ + "bv1", + "console_error_panic_hook", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[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.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[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.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[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.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +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.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/bv1/Cargo.toml b/bv1/Cargo.toml new file mode 100644 index 0000000..489eec7 --- /dev/null +++ b/bv1/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["codec", "app", "codec-web"] +default-members = ["app", "codec-web"] diff --git a/bv1/app/Cargo.toml b/bv1/app/Cargo.toml new file mode 100644 index 0000000..e433e4f --- /dev/null +++ b/bv1/app/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" +default-run = "main" + +[dependencies] +clap = { version = "4.1.8", features = ["derive"] } +bv1 = { path = "../codec" } diff --git a/bv1/app/src/bin/bench.rs b/bv1/app/src/bin/bench.rs new file mode 100644 index 0000000..b9c6464 --- /dev/null +++ b/bv1/app/src/bin/bench.rs @@ -0,0 +1,28 @@ +use bv1::{diff::*, Frame, Ref, View, P2}; +use std::time::Instant; + +fn measure(f: impl FnOnce()) { + let t1 = Instant::now(); + f(); + let t2 = Instant::now(); + eprintln!("took {:?}", (t2 - t1)); +} + +fn main() { + let size = P2 { x: 2000, y: 2000 }; + let f1 = Frame::new(size); + let f2 = Frame::new(size); + measure(|| { + diff([&f1, &f2], View::all(size), Ref::default()); + }); +} + +// #[test] +// fn bench_fast_diff() { +// let size = P2 { x: 2000, y: 2000 }; +// let f1 = Frame::new(size); +// let f2 = Frame::new(size); +// measure(|| { +// diff_fast([&f1, &f2], View::all(size), Ref::default()); +// }); +// } diff --git a/bv1/app/src/bin/main.rs b/bv1/app/src/bin/main.rs new file mode 100644 index 0000000..0b3596e --- /dev/null +++ b/bv1/app/src/bin/main.rs @@ -0,0 +1,68 @@ +use bv1::{ + encode::{encode, EncodeConfig}, + P2, decode, +}; +use clap::{Parser, Subcommand}; +use std::io::{stdin, stdout}; + +#[derive(Parser)] +#[clap(about, version)] +struct Args { + // Width of the video signal + #[arg(short = 'W', long)] + width: u16, + // Height of the video signal + #[arg(short = 'H', long)] + height: u16, + #[clap(subcommand)] + action: Action, +} + +#[derive(Clone, Subcommand)] +enum Action { + // Compress video + Encode { + #[arg(short, long, default_value_t = 800)] + max_block_size: usize, + #[arg(short, long, default_value_t = 10_000)] + attention_split: u32, + #[arg(short, long, default_value_t = 10.)] + threshold: f32, + #[arg(short, long, default_value_t = 10)] + keyframe_interval: usize, + }, + // Decompress video + Decode { + #[arg(short, long)] + debug: bool, + }, +} + +fn main() { + let args = Args::parse(); + + let size = P2 { + x: args.width as i32, + y: args.height as i32, + }; + match args.action { + Action::Encode { + max_block_size, + threshold, + attention_split, + keyframe_interval, + } => { + let config = EncodeConfig { + threshold, + max_block_size, + attention_split, + keyframe_interval, + }; + + encode(config, size, stdin(), stdout()).unwrap(); + } + Action::Decode { debug } => { + decode(size, debug, stdin(), stdout()).unwrap(); + } + } +} diff --git a/bv1/app/src/bin/test.rs b/bv1/app/src/bin/test.rs new file mode 100644 index 0000000..4071ff1 --- /dev/null +++ b/bv1/app/src/bin/test.rs @@ -0,0 +1,47 @@ +#[cfg(test)] +mod test { + + use bv1::huff::{read_huff, write_huff, BitIO}; + use std::io::Cursor; + + #[test] + fn test_bitio() { + let mut buf = Vec::::new(); + + { + let mut b = BitIO::new(Cursor::new(&mut buf)); + b.wbit(true).unwrap(); + b.wbit(true).unwrap(); + b.wbit(true).unwrap(); + b.wbit(true).unwrap(); + b.wbit(false).unwrap(); + b.wbit(true).unwrap(); + b.wbit(true).unwrap(); + b.wbit(true).unwrap(); + b.wbit(true).unwrap(); + b.wbyte(0xff).unwrap(); + b.flush().unwrap(); + } + { + let mut b = BitIO::new(Cursor::new(&mut buf)); + for _ in 0..17 { + let _v = b.rbit().unwrap(); + // eprintln!("{:?}", _v) + } + } + } + + #[test] + fn test_huff() { + let a = vec![1, 2, 3, 4, 5, 1, 3, 6, 3, 2, 4, 6, 7, 4, 3, 2, 1, 3, 4]; + let mut b = vec![]; + + let mut buf = Vec::::new(); + write_huff(&a, &mut Cursor::new(&mut buf)).unwrap(); + read_huff(&mut Cursor::new(&mut buf), &mut b).unwrap(); + + assert_eq!(a, b) + } +} + +fn main() {} diff --git a/bv1/codec-web/Cargo.toml b/bv1/codec-web/Cargo.toml new file mode 100644 index 0000000..f1a3f6a --- /dev/null +++ b/bv1/codec-web/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "codec-web" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +bv1 = { path = "../codec" } +wasm-bindgen = "0.2.84" +js-sys = "0.3.61" +console_error_panic_hook = "0.1.7" diff --git a/bv1/codec-web/src/lib.rs b/bv1/codec-web/src/lib.rs new file mode 100644 index 0000000..cb2ad3c --- /dev/null +++ b/bv1/codec-web/src/lib.rs @@ -0,0 +1,60 @@ +use bv1::{Decoder, Frame, P2}; +use std::{collections::VecDeque, sync::RwLock}; +use wasm_bindgen::prelude::*; + +static DECODER: RwLock> = RwLock::new(None); + +struct State { + buffer: VecDeque, + frame: Frame, + decoder: Decoder, +} + +// #[wasm_bindgen] +// extern "C" { +// #[wasm_bindgen(js_namespace = console)] +// fn log(s: &str); +// #[wasm_bindgen(js_namespace = console, js_name = "log")] +// fn logs(s: String); +// } + +#[wasm_bindgen(start)] +fn panic_init() { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); +} + +#[wasm_bindgen] +pub fn decode_init(width: i32, height: i32) { + let size = P2 { + x: width, + y: height, + }; + *DECODER.write().unwrap() = Some(State { + frame: Frame::new(size), + decoder: Decoder::new(size), + buffer: VecDeque::new(), + }); +} + +#[wasm_bindgen] +pub fn decode_frame(buf: &[u8], debug: bool) -> Vec { + let mut arr = Vec::new(); + let mut g = DECODER.write().unwrap(); + let state = g.as_mut().unwrap(); + state.buffer.extend(buf.iter()); + + state + .decoder + .decode_frame(&mut state.buffer, &mut state.frame, debug) + .unwrap(); + + for y in 0..state.frame.size.y { + for x in 0..state.frame.size.x { + arr.push(state.frame[P2 { x, y }].r.clamp(0, 255) as u8); + arr.push(state.frame[P2 { x, y }].g.clamp(0, 255) as u8); + arr.push(state.frame[P2 { x, y }].b.clamp(0, 255) as u8); + } + } + + arr +} diff --git a/bv1/codec-web/web/.gitignore b/bv1/codec-web/web/.gitignore new file mode 100644 index 0000000..93d7118 --- /dev/null +++ b/bv1/codec-web/web/.gitignore @@ -0,0 +1,2 @@ +/codec_web* +/bundle* diff --git a/bv1/codec-web/web/index.html b/bv1/codec-web/web/index.html new file mode 100644 index 0000000..b572671 --- /dev/null +++ b/bv1/codec-web/web/index.html @@ -0,0 +1,14 @@ + + + + + + + bv1 web player + + + + +

loading…

+ + diff --git a/bv1/codec-web/web/main.ts b/bv1/codec-web/web/main.ts new file mode 100644 index 0000000..b1b1df7 --- /dev/null +++ b/bv1/codec-web/web/main.ts @@ -0,0 +1,69 @@ +/// + +import init, { decode_frame, decode_init } from "./codec_web.js" +console.log("init wasm"); +await init() +console.log("done"); + +index("..") + +async function index(url: string) { + const res = await fetch(url) + if (!res.ok) throw new Error("not ok"); + document.body.innerHTML = await res.text(); + const h1 = document.createElement("h1") + h1.textContent = "bv1 web player" + document.body.prepend(h1) + document.body.querySelectorAll("a").forEach(e => { + const u = url + "/" + e.textContent + e.onclick = ev => { + ev.preventDefault() + if (!u.endsWith(".bv1")) return alert("thats not bv1!") + document.body.innerHTML = "" + play(u.toString()) + } + }) +} + +async function play(url: string) { + decode_init(1920, 1080) + document.body.innerText = "downloading…" + const res = await fetch(url) + if (!res.ok) throw new Error("not ok"); + let buf: Uint8Array | undefined = new Uint8Array(await res.arrayBuffer()) + document.body.innerText = "" + + const canvas = document.createElement("canvas") + canvas.width = 1920 + canvas.height = 1080 + document.body.append(canvas) + document.body.style.backgroundColor = "#111" + const ctx = canvas.getContext("2d")! + let debug = false; + + document.body.addEventListener("keydown", ev => { + if (ev.code == "KeyD") debug = !debug; + }) + + setInterval(() => { + const frame = decode_frame(buf ?? new Uint8Array(), debug); + buf = undefined + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const data = imageData.data; + + for (let y = 0; y < 1080; y++) { + for (let x = 0; x < 1920; x++) { + const ti = (x + y * 1920) * 4; + const si = (x + y * 1920) * 3; + data[ti + 0] = frame[si + 0] + data[ti + 1] = frame[si + 1] + data[ti + 2] = frame[si + 2] + data[ti + 3] = 255 + } + } + + ctx.putImageData(imageData, 0, 0); + + }, 1000 / 30) +} diff --git a/bv1/codec/Cargo.toml b/bv1/codec/Cargo.toml new file mode 100644 index 0000000..0c52ad2 --- /dev/null +++ b/bv1/codec/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bv1" +version = "0.1.0" +edition = "2021" + +[dependencies] +rayon = {version="1.7.0",optional = true} + +[features] +default = ["parallel"] +parallel = ["dep:rayon"] + diff --git a/bv1/codec/src/debug.rs b/bv1/codec/src/debug.rs new file mode 100644 index 0000000..d6c37c2 --- /dev/null +++ b/bv1/codec/src/debug.rs @@ -0,0 +1,42 @@ +use crate::{split::split, Block, Frame, Pixel, View, P2}; + +pub fn draw_debug(frame: &mut Frame, view: View, block: &Block) { + match block { + Block::Lit(_) => rect(frame, view, Pixel::GREEN), + Block::Split(a, b) => { + let [av, bv] = split(view); + draw_debug(frame, av, &a); + draw_debug(frame, bv, &b); + } + Block::Ref(r) => { + let v = View { + a: view.a, + b: view.a + P2 { x: 2, y: 2 }, + }; + if r.pos_off != P2::ZERO { + fill_rect(frame, v + P2 { x: 0, y: 0 }, Pixel::BLUE) + } + if r.color_off != Pixel::BLACK { + fill_rect(frame, v + P2 { x: 2, y: 0 }, Pixel::RED) + } + } + } +} + +fn rect(frame: &mut Frame, view: View, color: Pixel) { + for x in view.a.x..view.b.x { + frame[P2 { x, y: view.a.y }] = color; + frame[P2 { x, y: view.b.y - 1 }] = color; + } + for y in view.a.y..view.b.y { + frame[P2 { y, x: view.a.x }] = color; + frame[P2 { y, x: view.b.x - 1 }] = color; + } +} +fn fill_rect(frame: &mut Frame, view: View, color: Pixel) { + for y in view.a.y..view.b.y { + for x in view.a.x..view.b.x { + frame[P2 { x, y }] = color; + } + } +} diff --git a/bv1/codec/src/decode.rs b/bv1/codec/src/decode.rs new file mode 100644 index 0000000..616b7af --- /dev/null +++ b/bv1/codec/src/decode.rs @@ -0,0 +1,76 @@ +use crate::{ + debug::draw_debug, huff::read_huff, impls::join, split::split, Block, Frame, View, P2, +}; +use std::io::{BufReader, BufWriter, Read, Result, Write}; + +pub fn decode(size: P2, debug: bool, input: impl Read, output: impl Write) -> Result<()> { + let mut input = BufReader::new(input); + let mut output = BufWriter::new(output); + let mut d = Decoder::new(size); + let mut f = Frame::new(size); + loop { + d.decode_frame(&mut input, &mut f, debug)?; + Frame::write(&mut output, &f)?; + } +} + +pub struct Decoder { + last_frame: Frame, + size: P2, +} + +impl Decoder { + pub fn new(size: P2) -> Self { + Self { + size, + last_frame: Frame::new(size), + } + } + pub fn decode_frame( + &mut self, + mut input: impl Read, + output: &mut Frame, + debug: bool, + ) -> Result<()> { + let huff = true; + let b = if huff { + let mut buf = vec![]; + read_huff(&mut input, &mut buf)?; + eprintln!("{}", buf.len()); + let mut buf = std::io::Cursor::new(&mut buf); + Block::read(&mut buf, View::all(self.size))? + } else { + Block::read(&mut input, View::all(self.size))? + }; + + decode_block(&self.last_frame, output, View::all(self.size), &b); + self.last_frame.pixels.copy_from_slice(&output.pixels); // TODO use mem::swap + if debug { + draw_debug(output, View::all(self.size), &b); + } + Ok(()) + } +} + +pub fn decode_block(last_frame: &Frame, frame: &mut Frame, view: View, block: &Block) { + match block { + Block::Lit(pxs) => frame.import(view, &pxs), + Block::Split(a, b) => { + let [av, bv] = split(view); + let (frame1, frame2) = + unsafe { (&mut *(frame as *mut Frame), &mut *(frame as *mut Frame)) }; + join( + || decode_block(last_frame, frame1, av, &a), + || decode_block(last_frame, frame2, bv, &b), + ); + } + Block::Ref(r) => { + for y in view.a.y..view.b.y { + for x in view.a.x..view.b.x { + let p = P2 { x, y }; + frame[p] = last_frame[p + r.pos_off] + r.color_off + } + } + } + } +} diff --git a/bv1/codec/src/diff.rs b/bv1/codec/src/diff.rs new file mode 100644 index 0000000..4d1a805 --- /dev/null +++ b/bv1/codec/src/diff.rs @@ -0,0 +1,131 @@ +use crate::{Frame, Pixel, Ref, View, P2}; + +// 4ms +pub fn diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { + let mut k = 0; + for y in view.a.y..view.b.y { + for x in view.a.x..view.b.x { + let pos = P2 { x, y }; + let p1 = frame1[pos + rp.pos_off] + rp.color_off; + let p2 = frame2[pos]; + k += pixel_diff(p1, p2) + } + } + k +} + +#[inline(always)] +pub fn pixel_diff(p1: Pixel, p2: Pixel) -> u32 { + p1.r.abs_diff(p2.r) as u32 + p1.g.abs_diff(p2.g) as u32 + p1.b.abs_diff(p2.b) as u32 +} + +// pub fn diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { +// let mut k = 0; +// for y in view.a.y..view.b.y { +// let s1_ystart = (y * frame1.size.x) as usize; +// let s2_ystart = ((y + rp.pos_off.y) * frame1.size.x + rp.pos_off.x) as usize; +// let s1 = &frame1.pixels[s1_ystart + view.a.x as usize..s1_ystart + view.b.x as usize]; +// let s2 = &frame2.pixels[s2_ystart + view.a.x as usize..s2_ystart + view.b.x as usize]; +// let s1 = unsafe { std::mem::transmute::<_, &[i16]>(s1) }; +// let s2 = unsafe { std::mem::transmute::<_, &[i16]>(s2) }; + +// k += s1 +// .iter() +// .zip(s2.iter()) +// .map(|(a, b)| a.abs_diff(*b) as u32) +// .sum::() +// } +// k +// } + +// pub fn fast_diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { +// assert!(view.size().x % 5 == 0); + +// let mut diff_lanes = i32x16::from_array([0; 16]); +// let mut k1 = [0; 32]; +// let mut k2 = [0; 32]; + +// let next_line = frame1.size.x as usize - view.size().x as usize; +// let index_start = view.a.x as usize + view.a.y as usize * frame1.size.x as usize; +// let index_end = view.b.x as usize + (view.b.y as usize - 1) * frame1.size.x as usize; + +// let mut i = index_start; +// let mut x = view.a.x; + +// while i < index_end { + +// let f1: &[u8] = +// unsafe { std::slice::from_raw_parts(frame1.pixels[sl_start..].as_ptr() as *mut u8, sl_size) }; +// let f2: &[u8] = +// unsafe { std::slice::from_raw_parts(frame2.pixels[sl_start..].as_ptr() as *mut u8, sl_size) }; + +// for i in 0..15 { +// k1[i] = f1[i] as i32; +// k2[i] = f2[i] as i32; +// } + +// // for j in 0..5 { +// // let j3 = j * 3; +// // k1[j3] = frame1.pixels[i + j].r as i32; +// // k2[j3] = frame2.pixels[i + j].r as i32; +// // k1[j3 + 1] = frame1.pixels[i + j].g as i32; +// // k2[j3 + 1] = frame2.pixels[i + j].g as i32; +// // k1[j3 + 2] = frame1.pixels[i + j].b as i32; +// // k2[j3 + 2] = frame2.pixels[i + j].b as i32; +// // } +// let pl1 = i16x32::from_array(k1); +// let pl2 = i16x32::from_array(k2); +// diff_lanes += (pl1 - pl2).abs(); + +// i += 5; +// x += 5; +// if x > view.b.x { +// i += next_line; +// x = view.a.x +// } +// } + +// return diff_lanes.reduce_sum() as u32; +// } + +// pub fn fast_diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { +// let mut diff_lanes = i32x16::from_array([0; 16]); +// let mut k1 = [0; 16]; +// let mut k2 = [0; 16]; + +// let next_line = frame1.size.x as usize - view.size().x as usize; +// let index_start = view.a.x as usize + view.a.y as usize * frame1.size.x as usize; +// let index_end = view.b.x as usize + (view.b.y as usize - 1) * frame1.size.x as usize; + +// let mut i = index_start; +// let mut x = view.a.x; +// let mut kfill = 0; + +// while i < index_end { +// k1[kfill] = frame1.pixels[i].r as i32; +// k2[kfill] = frame2.pixels[i].r as i32; +// kfill += 1; +// k1[kfill] = frame1.pixels[i].g as i32; +// k2[kfill] = frame2.pixels[i].g as i32; +// kfill += 1; +// k1[kfill] = frame1.pixels[i].b as i32; +// k2[kfill] = frame2.pixels[i].b as i32; +// kfill += 1; + +// i += 1; +// x += 1; +// if x > view.b.x { +// i += next_line; +// x = view.a.x +// } + +// if kfill == 15 { +// let pl1 = i32x16::from_array(k1); +// let pl2 = i32x16::from_array(k2); +// diff_lanes += (pl1 - pl2).abs(); +// kfill = 0; +// } +// } + +// return diff_lanes.reduce_sum() as u32; +// } diff --git a/bv1/codec/src/encode.rs b/bv1/codec/src/encode.rs new file mode 100644 index 0000000..564b0b3 --- /dev/null +++ b/bv1/codec/src/encode.rs @@ -0,0 +1,190 @@ +use crate::diff::{diff, pixel_diff}; +use crate::huff::write_huff; +use crate::impls::join; +use crate::split::split; +use crate::{decode::decode_block, Block, Frame, Pixel, Ref, View, P2}; +use std::io::{BufReader, BufWriter, Read, Write}; +use std::time::Instant; + +#[derive(Debug, Clone)] +pub struct EncodeConfig { + pub threshold: f32, + pub max_block_size: usize, + pub attention_split: u32, + pub keyframe_interval: usize, +} + +pub fn encode( + config: EncodeConfig, + size: P2, + input: impl Read, + output: impl Write, +) -> std::io::Result<()> { + let mut input = BufReader::new(input); + let mut output = BufWriter::new(output); + + let mut last_frame = Frame::new(size); + for frame_number in 0.. { + let mut frame = Frame::read(&mut input, size)?; + + let mut config = config.clone(); + if frame_number % config.keyframe_interval != 0 { + config.threshold = std::f32::INFINITY; + } + + let t = Instant::now(); + let b: Block = encode_block(&last_frame, &frame, View::all(size), &config); + let time_encode = t.elapsed(); + + let t = Instant::now(); + decode_block(&last_frame, &mut frame, View::all(size), &b); + last_frame = frame; + let time_decode = t.elapsed(); + + if true { + let mut buf = vec![]; + let mut bufw = std::io::Cursor::new(&mut buf); + b.write(&mut bufw)?; + drop(bufw); + let t = Instant::now(); + let bits_raw = buf.len() * 8; + let bits_huff = write_huff(&buf, &mut output)?; + let time_huff = t.elapsed(); + drop(buf); + + eprintln!( + "frame {frame_number}: {:?}", + time_decode + time_huff + time_encode + ); + eprintln!( + "\tencode {time_encode:?} ({:.2}%)", + (bits_raw as f32 / (size.area() * 24) as f32) * 100.0 + ); + eprintln!( + "\thuff {time_huff:?} ({:.2}%)", + (bits_huff as f32 / bits_raw as f32) * 100.0 + ); + eprintln!("\tdecode {time_decode:?}"); + } else { + b.write(&mut output)?; + } + } + Ok(()) +} + +pub fn encode_block(last_frame: &Frame, frame: &Frame, view: View, config: &EncodeConfig) -> Block { + let view_area = view.size().area(); + if view_area > config.max_block_size + || (view_area > 64 && attention(frame, view) > config.attention_split) + { + let [av, bv] = split(view); + let (ab, bb) = join( + || Box::new(encode_block(last_frame, frame, av, config)), + || Box::new(encode_block(last_frame, frame, bv, config)), + ); + return Block::Split(ab, bb); + } + + let mut r = Ref::default(); + let mut d = diff([last_frame, frame], view, r); + + // let att = 1. - attention(frame, view) as f32 * 0.000001; + // let thres = (config.threshold as f32 * att.clamp(0.2, 1.0)) as u32; + let thres = (config.threshold * view_area as f32) as u32; + + let target_average = average_color(frame, view); + + for granularity in [2, 1, 2, 1, 2, 1, 2, 1] { + let (nd, nrp) = optimize_ref(last_frame, frame, view, r, granularity, target_average); + if nd < d { + r = nrp; + d = nd; + } else { + break; + } + } + + if d < thres { + return Block::Ref(r); + } else { + Block::Lit(frame.export(view)) + } +} + +pub fn optimize_ref( + last_frame: &Frame, + frame: &Frame, + view: View, + r: Ref, + g: i32, + target_average: Pixel, +) -> (u32, Ref) { + let g2 = g * 2; + [ + Some(r.apply(|r| r.pos_off += P2 { x: g, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: g, y: g })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g })), + Some(r.apply(|r| r.pos_off += P2 { x: -g, y: g })), + Some(r.apply(|r| r.pos_off += P2 { x: -g, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g, y: -g })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g })), + Some(r.apply(|r| r.pos_off += P2 { x: g, y: -g })), + Some(r.apply(|r| r.pos_off += P2 { x: g2, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: g2, y: g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: -g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: g2, y: -g2 })), + { + let mut r = r; + let last_avr = average_color(last_frame, view); + let diff = target_average - last_avr; + r.color_off = diff; + if diff != Pixel::BLACK { + Some(r) + } else { + None + } + }, + ] + .into_iter() + .flatten() + .map(|r| (diff([last_frame, frame], view, r), r)) + .min_by_key(|e| e.0) + .unwrap() +} + +pub fn attention(frame: &Frame, view: View) -> u32 { + let mut k = 0; + for y in view.a.y..view.b.y - 1 { + for x in view.a.x..view.b.x - 1 { + let p = P2 { x, y }; + k += pixel_diff(frame[p], frame[p + P2::X]).pow(2); + k += pixel_diff(frame[p], frame[p + P2::Y]).pow(2); + } + } + k +} + +pub fn average_color(frame: &Frame, view: View) -> Pixel { + let mut r = 0u32; + let mut g = 0u32; + let mut b = 0u32; + + for y in view.a.y..view.b.y { + for x in view.a.x..view.b.x { + let p = frame[P2 { x, y }]; + r += p.r as u32; + g += p.g as u32; + b += p.b as u32; + } + } + let area = view.size().area() as u32; + Pixel { + r: (r / area) as i16, + g: (g / area) as i16, + b: (b / area) as i16, + } +} diff --git a/bv1/codec/src/frameio.rs b/bv1/codec/src/frameio.rs new file mode 100644 index 0000000..f6cbcbf --- /dev/null +++ b/bv1/codec/src/frameio.rs @@ -0,0 +1,35 @@ +use crate::{Frame, Pixel, PixelValue, P2}; +use std::io::{Read, Result, Write}; + +impl Frame { + pub fn read(inp: &mut impl Read, size: P2) -> Result { + let mut f = Frame::new(size); + + for y in 0..size.y { + for x in 0..size.x { + let mut cc = [0u8; 3]; + inp.read_exact(&mut cc)?; + f[P2 { x, y }] = Pixel { + r: cc[0] as PixelValue, + g: cc[1] as PixelValue, + b: cc[2] as PixelValue, + }; + } + } + Ok(f) + } + + pub fn write(out: &mut impl Write, frame: &Frame) -> Result<()> { + for y in 0..frame.size.y { + for x in 0..frame.size.x { + let p = frame[P2 { x, y }]; + let mut cc = [0u8; 3]; + cc[0] = p.r.clamp(0, 255) as u8; + cc[1] = p.g.clamp(0, 255) as u8; + cc[2] = p.b.clamp(0, 255) as u8; + out.write_all(&mut cc)?; + } + } + Ok(()) + } +} diff --git a/bv1/codec/src/huff.rs b/bv1/codec/src/huff.rs new file mode 100644 index 0000000..6d74c42 --- /dev/null +++ b/bv1/codec/src/huff.rs @@ -0,0 +1,190 @@ +use std::io::{Read, Result, Write}; + +#[derive(Debug, Clone)] +enum HT { + Branch(Box, Box), + Terminal(u8), +} + +pub fn write_huff(buf: &[u8], w: &mut impl Write) -> Result { + let mut w = BitIO::new(w); + assert!(buf.len() <= 0xffffff, "huff frame too big"); + w.wbyte((buf.len() & 0xff) as u8)?; + w.wbyte(((buf.len() >> 8) & 0xff) as u8)?; + w.wbyte(((buf.len() >> 16) & 0xff) as u8)?; + + let mut probs = [0usize; 256]; + for b in buf { + probs[*b as usize] += 1; + } + let tree = HT::from_probabilities(probs); + let mut table = [0u32; 256]; + tree.create_lut(&mut table, 1); + tree.write(&mut w)?; + + + for b in buf { + let mut k = table[*b as usize]; + while k != 1 { + w.wbit((k & 1) == 1)?; + k >>= 1; + } + } + + w.flush()?; + Ok(w.position) +} + +pub fn read_huff(r: &mut impl Read, buf: &mut Vec) -> Result { + let mut r = BitIO::new(r); + + let mut len = 0usize; + len |= r.rbyte()? as usize; + len |= (r.rbyte()? as usize) << 8; + len |= (r.rbyte()? as usize) << 16; + + let root = HT::read(&mut r)?; + let root = match &root { + HT::Branch(a, b) => [a, b], + _ => panic!("no!"), + }; + + let mut cursor = root; + while buf.len() != len { + let v = r.rbit()?; + match cursor[v as usize].as_ref() { + HT::Branch(a, b) => { + cursor = [a, b]; + } + HT::Terminal(n) => { + buf.push(*n); + cursor = root; + } + } + } + Ok(r.position) +} + +impl HT { + pub fn from_probabilities(ps: [usize; 256]) -> Self { + let mut parts = ps + .into_iter() + .enumerate() + .map(|(n, p)| (p, HT::Terminal(n as u8))) + .collect::>(); + + while parts.len() != 1 { + parts.sort_by_key(|e| -(e.0 as isize)); + let ((ap, at), (bp, bt)) = (parts.pop().unwrap(), parts.pop().unwrap()); + parts.push((ap + bp + 1, HT::Branch(box at, box bt))) + } + parts[0].1.clone() + } + pub fn create_lut(&self, table: &mut [u32; 256], mut prefix: u32) { + assert!(self.depth() < 30, "too deep! doesnt fit {}", self.depth()); + match self { + HT::Branch(a, b) => { + let pz = 32 - prefix.leading_zeros(); + prefix ^= 1 << pz; + prefix ^= 1 << (pz - 1); + a.create_lut(table, prefix); + prefix ^= 1 << (pz - 1); + b.create_lut(table, prefix); + } + HT::Terminal(n) => { + assert_eq!(table[*n as usize], 0); + table[*n as usize] = prefix + } + } + } + pub fn depth(&self) -> usize { + match self { + HT::Branch(a, b) => a.depth().max(b.depth()) + 1, + HT::Terminal(_) => 0, + } + } + pub fn write(&self, w: &mut BitIO) -> Result<()> { + match self { + HT::Branch(a, b) => { + w.wbit(false)?; + a.write(w)?; + b.write(w)?; + } + HT::Terminal(n) => { + w.wbit(true)?; + w.wbyte(*n)?; + } + } + Ok(()) + } + pub fn read(r: &mut BitIO) -> Result { + match r.rbit()? { + false => Ok(Self::Branch(box Self::read(r)?, box Self::read(r)?)), + true => Ok(Self::Terminal(r.rbyte()?)), + } + } +} + +pub struct BitIO { + inner: T, + byte: u8, + position: usize, +} +impl BitIO { + pub fn new(inner: T) -> Self { + Self { + inner, + byte: 0, + position: 0, + } + } +} +impl BitIO { + #[inline] + pub fn wbit(&mut self, b: bool) -> Result<()> { + self.byte <<= 1; + self.byte |= b as u8; + self.position += 1; + if self.position & 0b111 == 0 { + self.inner.write_all(&[self.byte])?; + } + Ok(()) + } + #[inline] + pub fn wbyte(&mut self, v: u8) -> Result<()> { + for i in 0..8 { + self.wbit((v & (1 << i)) != 0)?; + } + Ok(()) + } + pub fn flush(&mut self) -> Result<()> { + while self.position & 0b111 != 0 { + self.wbit(false)?; + } + Ok(()) + } +} + +impl BitIO { + #[inline] + pub fn rbit(&mut self) -> Result { + if self.position & 0b111 == 0 { + let mut buf = [0]; + self.inner.read_exact(&mut buf)?; + self.byte = buf[0]; + } + + let v = (self.byte & 0b10000000) != 0; + self.byte <<= 1; + self.position += 1; + Ok(v) + } + #[inline] + pub fn rbyte(&mut self) -> Result { + let mut v = 0u8; + for i in 0..8 { + v |= (self.rbit()? as u8) << i; + } + Ok(v) + } +} diff --git a/bv1/codec/src/impls.rs b/bv1/codec/src/impls.rs new file mode 100644 index 0000000..b4cc119 --- /dev/null +++ b/bv1/codec/src/impls.rs @@ -0,0 +1,165 @@ +use crate::{Frame, Pixel, Ref, View, P2}; +use std::ops::{Add, AddAssign, Index, IndexMut, Sub}; + +#[cfg(feature = "parallel")] +pub use rayon::join; +#[cfg(not(feature = "parallel"))] +pub fn join(oper_a: A, oper_b: B) -> (RA, RB) +where + A: FnOnce() -> RA + Send, + B: FnOnce() -> RB + Send, + RA: Send, + RB: Send, +{ + (oper_a(), oper_b()) +} + +impl Frame { + pub fn export(&self, view: View) -> Vec { + let mut o = vec![]; + for y in view.a.y..view.b.y { + for x in view.a.x..view.b.x { + o.push(self[P2 { x, y }]) + } + } + o + } + pub fn import(&mut self, view: View, mut source: &[Pixel]) { + for y in view.a.y..view.b.y { + for x in view.a.x..view.b.x { + self[P2 { x, y }] = source[0]; + source = &source[1..]; + } + } + assert_eq!(source.len(), 0) + } + pub fn new(size: P2) -> Self { + Self { + pixels: vec![Pixel::default(); size.area()], + size, + } + } +} +impl Ref { + #[inline] + pub fn apply(mut self, f: F) -> Self { + f(&mut self); + self + } +} +impl Pixel { + pub const BLACK: Pixel = Pixel { r: 0, g: 0, b: 0 }; + 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 }; +} +impl AddAssign for P2 { + fn add_assign(&mut self, rhs: Self) { + self.x += rhs.x; + self.y += rhs.y; + } +} +impl Add for Pixel { + type Output = Pixel; + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Self { + r: self.r + rhs.r, + g: self.g + rhs.g, + b: self.b + rhs.b, + } + } +} +impl Sub for Pixel { + type Output = Pixel; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self { + r: self.r - rhs.r, + g: self.g - rhs.g, + b: self.b - rhs.b, + } + } +} +impl P2 { + pub const ZERO: P2 = P2 { x: 0, y: 0 }; + pub const X: P2 = P2 { x: 1, y: 0 }; + pub const Y: P2 = P2 { x: 0, y: 1 }; + + #[inline] + pub fn area(&self) -> usize { + (self.x * self.y) as usize + } +} +impl View { + #[inline] + pub fn all(b: P2) -> Self { + Self { + a: P2::default(), + b, + } + } + #[inline] + pub fn size(&self) -> P2 { + self.b - self.a + } +} +impl Add for View { + type Output = View; + #[inline] + fn add(self, rhs: P2) -> Self::Output { + View { + a: self.a + rhs, + b: self.b + rhs, + } + } +} +impl Add for P2 { + type Output = P2; + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Self { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} +impl Sub for P2 { + type Output = P2; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} + +impl Index for Frame { + type Output = Pixel; + #[inline] + fn index(&self, P2 { x, y }: P2) -> &Self::Output { + &self + .pixels + .get(x as usize + (y as usize * self.size.x as usize)) + .unwrap_or(&Pixel { r: 0, g: 0, b: 0 }) + } +} +impl IndexMut for Frame { + #[inline] + fn index_mut(&mut self, P2 { x, y }: P2) -> &mut Self::Output { + &mut self.pixels[x as usize + (y as usize * self.size.x as usize)] + } +} + +pub trait ToArray { + type Output; + fn to_array(self) -> [Self::Output; 2]; +} +impl ToArray for (A, A) { + type Output = A; + #[inline] + fn to_array(self) -> [A; 2] { + [self.0, self.1] + } +} diff --git a/bv1/codec/src/lib.rs b/bv1/codec/src/lib.rs new file mode 100644 index 0000000..c764211 --- /dev/null +++ b/bv1/codec/src/lib.rs @@ -0,0 +1,55 @@ +#![feature(portable_simd)] +#![feature(io_error_other)] +#![feature(box_syntax)] + +pub mod debug; +pub mod decode; +pub mod diff; +pub mod encode; +pub mod frameio; +pub mod huff; +pub mod impls; +pub mod serialize; +pub mod split; + +pub type PixelValue = i16; + +pub use decode::{decode, Decoder}; +pub use encode::encode; + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct Pixel { + pub r: PixelValue, + pub g: PixelValue, + pub b: PixelValue, +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct P2 { + pub x: i32, + pub y: i32, +} + +pub struct Frame { + pub size: P2, + pub pixels: Vec, +} + +#[derive(Debug, Clone, Copy)] +pub struct View { + pub a: P2, + pub b: P2, +} + +#[derive(Debug, Clone)] +pub enum Block { + Split(Box, Box), + Lit(Vec), + Ref(Ref), +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct Ref { + pub pos_off: P2, + pub color_off: Pixel, +} diff --git a/bv1/codec/src/serialize.rs b/bv1/codec/src/serialize.rs new file mode 100644 index 0000000..960aa1b --- /dev/null +++ b/bv1/codec/src/serialize.rs @@ -0,0 +1,116 @@ +use crate::{split::split, Block, Pixel, Ref, View, P2}; +use std::io::{Read, Result, Write}; + +impl Pixel { + #[inline] + pub fn write(&self, w: &mut impl Write) -> Result<()> { + w.write_all(&[ + self.r.clamp(0, 255) as u8, + self.g.clamp(0, 255) as u8, + self.b.clamp(0, 255) as u8, + ]) + } + #[inline] + pub fn read(r: &mut impl Read) -> Result { + Ok(Self { + r: read_byte(r)? as i16, + g: read_byte(r)? as i16, + b: read_byte(r)? as i16, + }) + } +} +impl Pixel { + #[inline] + pub fn write_full(&self, w: &mut impl Write) -> Result<()> { + write_word(w, self.r)?; + write_word(w, self.g)?; + write_word(w, self.b)?; + Ok(()) + } + #[inline] + pub fn read_full(r: &mut impl Read) -> Result { + Ok(Self { + r: read_word(r)?, + g: read_word(r)?, + b: read_word(r)?, + }) + } +} +impl P2 { + #[inline] + pub fn write(&self, w: &mut impl Write) -> Result<()> { + write_word(w, self.x as i16)?; + write_word(w, self.y as i16)?; + Ok(()) + } + + #[inline] + pub fn read(r: &mut impl Read) -> Result { + Ok(Self { + x: read_word(r)? as i32, + y: read_word(r)? as i32, + }) + } +} + +impl Block { + pub fn write(&self, w: &mut impl Write) -> Result<()> { + match self { + Block::Split(a, b) => { + w.write_all(&[0])?; + a.write(w)?; + b.write(w)?; + } + Block::Lit(pixels) => { + w.write_all(&[1])?; + for p in pixels { + p.write(w)?; + } + } + Block::Ref(k) => { + w.write_all(&[2])?; + k.pos_off.write(w)?; + k.color_off.write_full(w)?; + } + } + Ok(()) + } + pub fn read(r: &mut impl Read, view: View) -> Result { + match read_byte(r)? { + 0 => { + let [av, bv] = split(view); + Ok(Block::Split( + box Block::read(r, av)?, + box Block::read(r, bv)?, + )) + } + 1 => { + let mut px = vec![]; + for _ in 0..view.size().area() { + px.push(Pixel::read(r)?) + } + Ok(Block::Lit(px)) + } + 2 => Ok(Block::Ref(Ref { + pos_off: P2::read(r)?, + color_off: Pixel::read_full(r)?, + })), + _ => Err(std::io::Error::other("unknown block variant")), + } + } +} + +#[inline] +fn read_byte(r: &mut impl Read) -> Result { + let mut buf = [0u8]; + r.read_exact(&mut buf)?; + Ok(buf[0]) +} +#[inline] +fn write_word(w: &mut impl Write, v: i16) -> Result<()> { + w.write_all(&[(v & 0xff) as u8, (v >> 8) as u8]) +} +#[inline] +fn read_word(r: &mut impl Read) -> Result { + Ok((read_byte(r)? as u16 | ((read_byte(r)? as u16) << 8)) as i16) +} diff --git a/bv1/codec/src/split.rs b/bv1/codec/src/split.rs new file mode 100644 index 0000000..c17179e --- /dev/null +++ b/bv1/codec/src/split.rs @@ -0,0 +1,42 @@ +use crate::{View, P2}; + +pub fn split(view: View) -> [View; 2] { + let s = view.size(); + if s.x > s.y { + let mid_x = (view.a.x + view.b.x) / 2; + [ + View { + a: view.a, + b: P2 { + x: mid_x, + y: view.b.y, + }, + }, + View { + a: P2 { + x: mid_x, + y: view.a.y, + }, + b: view.b, + }, + ] + } else { + let mid_y = (view.a.y + view.b.y) / 2; + [ + View { + a: view.a, + b: P2 { + x: view.b.x, + y: mid_y, + }, + }, + View { + a: P2 { + x: view.a.x, + y: mid_y, + }, + b: view.b, + }, + ] + } +} diff --git a/bv1/tools/d-disp b/bv1/tools/d-disp new file mode 100755 index 0000000..d7b8feb --- /dev/null +++ b/bv1/tools/d-disp @@ -0,0 +1,2 @@ +#!/bin/fish +cargo run --release -- -W 1920 -H 1080 decode | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 \ No newline at end of file diff --git a/bv1/tools/d-dispd b/bv1/tools/d-dispd new file mode 100755 index 0000000..97a2b90 --- /dev/null +++ b/bv1/tools/d-dispd @@ -0,0 +1,2 @@ +#!/bin/fish +cargo run --release -- -W 1920 -H 1080 decode --debug | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 \ No newline at end of file diff --git a/bv1/tools/d-save b/bv1/tools/d-save new file mode 100755 index 0000000..1f19994 --- /dev/null +++ b/bv1/tools/d-save @@ -0,0 +1,2 @@ +#!/bin/fish +cargo run --release -- -W 1920 -H 1080 decode | ffmpeg -pixel_format rgb24 -video_size 1920x1080 -framerate 30 -f rawvideo -i pipe:0 -i $argv[1] -map '0:v' -map '1:a' -c:v libsvtav1 -y data/output.webm diff --git a/bv1/tools/d-saved b/bv1/tools/d-saved new file mode 100755 index 0000000..16de45a --- /dev/null +++ b/bv1/tools/d-saved @@ -0,0 +1,2 @@ +#!/bin/fish +cargo run --release -- -W 1920 -H 1080 decode --debug | ffmpeg -pixel_format rgb24 -video_size 1920x1080 -framerate 30 -f rawvideo -i pipe:0 -i $argv[1] -map '0:v' -map '1:a' -c:v libsvtav1 -y data/output-debug.webm diff --git a/bv1/tools/e b/bv1/tools/e new file mode 100755 index 0000000..a85a767 --- /dev/null +++ b/bv1/tools/e @@ -0,0 +1,2 @@ +#!/bin/fish +ffmpeg -loglevel quiet -i $argv[1] -vf format=rgb24 -f rawvideo pipe:1 | cargo run --release -- -W 1920 -H 1080 encode $argv[2..] diff --git a/lvc/.gitignore b/lvc/.gitignore deleted file mode 100644 index 2f22d72..0000000 --- a/lvc/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/data -/target diff --git a/lvc/Cargo.lock b/lvc/Cargo.lock deleted file mode 100644 index cd51668..0000000 --- a/lvc/Cargo.lock +++ /dev/null @@ -1,552 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "app" -version = "0.1.0" -dependencies = [ - "bv1", - "clap", -] - -[[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 = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "bv1" -version = "0.1.0" -dependencies = [ - "rayon", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[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.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" -dependencies = [ - "bitflags", - "clap_derive", - "clap_lex", - "is-terminal", - "once_cell", - "strsim", - "termcolor", -] - -[[package]] -name = "clap_derive" -version = "4.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "codec-web" -version = "0.1.0" -dependencies = [ - "bv1", - "console_error_panic_hook", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[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.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "io-lifetimes" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "is-terminal" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys", -] - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[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.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "rustix" -version = "0.36.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[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.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -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.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/lvc/Cargo.toml b/lvc/Cargo.toml deleted file mode 100644 index 489eec7..0000000 --- a/lvc/Cargo.toml +++ /dev/null @@ -1,3 +0,0 @@ -[workspace] -members = ["codec", "app", "codec-web"] -default-members = ["app", "codec-web"] diff --git a/lvc/app/Cargo.toml b/lvc/app/Cargo.toml deleted file mode 100644 index e433e4f..0000000 --- a/lvc/app/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "app" -version = "0.1.0" -edition = "2021" -default-run = "main" - -[dependencies] -clap = { version = "4.1.8", features = ["derive"] } -bv1 = { path = "../codec" } diff --git a/lvc/app/src/bin/bench.rs b/lvc/app/src/bin/bench.rs deleted file mode 100644 index b9c6464..0000000 --- a/lvc/app/src/bin/bench.rs +++ /dev/null @@ -1,28 +0,0 @@ -use bv1::{diff::*, Frame, Ref, View, P2}; -use std::time::Instant; - -fn measure(f: impl FnOnce()) { - let t1 = Instant::now(); - f(); - let t2 = Instant::now(); - eprintln!("took {:?}", (t2 - t1)); -} - -fn main() { - let size = P2 { x: 2000, y: 2000 }; - let f1 = Frame::new(size); - let f2 = Frame::new(size); - measure(|| { - diff([&f1, &f2], View::all(size), Ref::default()); - }); -} - -// #[test] -// fn bench_fast_diff() { -// let size = P2 { x: 2000, y: 2000 }; -// let f1 = Frame::new(size); -// let f2 = Frame::new(size); -// measure(|| { -// diff_fast([&f1, &f2], View::all(size), Ref::default()); -// }); -// } diff --git a/lvc/app/src/bin/main.rs b/lvc/app/src/bin/main.rs deleted file mode 100644 index 0b3596e..0000000 --- a/lvc/app/src/bin/main.rs +++ /dev/null @@ -1,68 +0,0 @@ -use bv1::{ - encode::{encode, EncodeConfig}, - P2, decode, -}; -use clap::{Parser, Subcommand}; -use std::io::{stdin, stdout}; - -#[derive(Parser)] -#[clap(about, version)] -struct Args { - // Width of the video signal - #[arg(short = 'W', long)] - width: u16, - // Height of the video signal - #[arg(short = 'H', long)] - height: u16, - #[clap(subcommand)] - action: Action, -} - -#[derive(Clone, Subcommand)] -enum Action { - // Compress video - Encode { - #[arg(short, long, default_value_t = 800)] - max_block_size: usize, - #[arg(short, long, default_value_t = 10_000)] - attention_split: u32, - #[arg(short, long, default_value_t = 10.)] - threshold: f32, - #[arg(short, long, default_value_t = 10)] - keyframe_interval: usize, - }, - // Decompress video - Decode { - #[arg(short, long)] - debug: bool, - }, -} - -fn main() { - let args = Args::parse(); - - let size = P2 { - x: args.width as i32, - y: args.height as i32, - }; - match args.action { - Action::Encode { - max_block_size, - threshold, - attention_split, - keyframe_interval, - } => { - let config = EncodeConfig { - threshold, - max_block_size, - attention_split, - keyframe_interval, - }; - - encode(config, size, stdin(), stdout()).unwrap(); - } - Action::Decode { debug } => { - decode(size, debug, stdin(), stdout()).unwrap(); - } - } -} diff --git a/lvc/app/src/bin/test.rs b/lvc/app/src/bin/test.rs deleted file mode 100644 index 4071ff1..0000000 --- a/lvc/app/src/bin/test.rs +++ /dev/null @@ -1,47 +0,0 @@ -#[cfg(test)] -mod test { - - use bv1::huff::{read_huff, write_huff, BitIO}; - use std::io::Cursor; - - #[test] - fn test_bitio() { - let mut buf = Vec::::new(); - - { - let mut b = BitIO::new(Cursor::new(&mut buf)); - b.wbit(true).unwrap(); - b.wbit(true).unwrap(); - b.wbit(true).unwrap(); - b.wbit(true).unwrap(); - b.wbit(false).unwrap(); - b.wbit(true).unwrap(); - b.wbit(true).unwrap(); - b.wbit(true).unwrap(); - b.wbit(true).unwrap(); - b.wbyte(0xff).unwrap(); - b.flush().unwrap(); - } - { - let mut b = BitIO::new(Cursor::new(&mut buf)); - for _ in 0..17 { - let _v = b.rbit().unwrap(); - // eprintln!("{:?}", _v) - } - } - } - - #[test] - fn test_huff() { - let a = vec![1, 2, 3, 4, 5, 1, 3, 6, 3, 2, 4, 6, 7, 4, 3, 2, 1, 3, 4]; - let mut b = vec![]; - - let mut buf = Vec::::new(); - write_huff(&a, &mut Cursor::new(&mut buf)).unwrap(); - read_huff(&mut Cursor::new(&mut buf), &mut b).unwrap(); - - assert_eq!(a, b) - } -} - -fn main() {} diff --git a/lvc/codec-web/Cargo.toml b/lvc/codec-web/Cargo.toml deleted file mode 100644 index f1a3f6a..0000000 --- a/lvc/codec-web/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "codec-web" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -bv1 = { path = "../codec" } -wasm-bindgen = "0.2.84" -js-sys = "0.3.61" -console_error_panic_hook = "0.1.7" diff --git a/lvc/codec-web/src/lib.rs b/lvc/codec-web/src/lib.rs deleted file mode 100644 index cb2ad3c..0000000 --- a/lvc/codec-web/src/lib.rs +++ /dev/null @@ -1,60 +0,0 @@ -use bv1::{Decoder, Frame, P2}; -use std::{collections::VecDeque, sync::RwLock}; -use wasm_bindgen::prelude::*; - -static DECODER: RwLock> = RwLock::new(None); - -struct State { - buffer: VecDeque, - frame: Frame, - decoder: Decoder, -} - -// #[wasm_bindgen] -// extern "C" { -// #[wasm_bindgen(js_namespace = console)] -// fn log(s: &str); -// #[wasm_bindgen(js_namespace = console, js_name = "log")] -// fn logs(s: String); -// } - -#[wasm_bindgen(start)] -fn panic_init() { - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); -} - -#[wasm_bindgen] -pub fn decode_init(width: i32, height: i32) { - let size = P2 { - x: width, - y: height, - }; - *DECODER.write().unwrap() = Some(State { - frame: Frame::new(size), - decoder: Decoder::new(size), - buffer: VecDeque::new(), - }); -} - -#[wasm_bindgen] -pub fn decode_frame(buf: &[u8], debug: bool) -> Vec { - let mut arr = Vec::new(); - let mut g = DECODER.write().unwrap(); - let state = g.as_mut().unwrap(); - state.buffer.extend(buf.iter()); - - state - .decoder - .decode_frame(&mut state.buffer, &mut state.frame, debug) - .unwrap(); - - for y in 0..state.frame.size.y { - for x in 0..state.frame.size.x { - arr.push(state.frame[P2 { x, y }].r.clamp(0, 255) as u8); - arr.push(state.frame[P2 { x, y }].g.clamp(0, 255) as u8); - arr.push(state.frame[P2 { x, y }].b.clamp(0, 255) as u8); - } - } - - arr -} diff --git a/lvc/codec-web/web/.gitignore b/lvc/codec-web/web/.gitignore deleted file mode 100644 index 93d7118..0000000 --- a/lvc/codec-web/web/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/codec_web* -/bundle* diff --git a/lvc/codec-web/web/index.html b/lvc/codec-web/web/index.html deleted file mode 100644 index b572671..0000000 --- a/lvc/codec-web/web/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - bv1 web player - - - - -

loading…

- - diff --git a/lvc/codec-web/web/main.ts b/lvc/codec-web/web/main.ts deleted file mode 100644 index bdaa1f6..0000000 --- a/lvc/codec-web/web/main.ts +++ /dev/null @@ -1,67 +0,0 @@ -/// - -import init, { decode_frame, decode_init } from "./codec_web.js" -console.log("init wasm"); -await init() -console.log("done"); - -index("..") - -async function index(url: string) { - const res = await fetch(url) - if (!res.ok) throw new Error("not ok"); - document.body.innerHTML = await res.text(); - const h1 = document.createElement("h1") - h1.textContent = "bv1 web player" - document.body.prepend(h1) - document.body.querySelectorAll("a").forEach(e => { - const u = url + "/" + e.textContent - e.onclick = ev => { - ev.preventDefault() - document.body.innerHTML = "" - play(u.toString()) - } - }) -} - -async function play(url: string) { - decode_init(1920, 1080) - - const res = await fetch(url) - if (!res.ok) throw new Error("not ok"); - let buf: Uint8Array | undefined = new Uint8Array(await res.arrayBuffer()) - - const canvas = document.createElement("canvas") - canvas.width = 1920 - canvas.height = 1080 - document.body.append(canvas) - document.body.style.backgroundColor = "#111" - const ctx = canvas.getContext("2d")! - let debug = false; - - document.body.addEventListener("keydown", ev => { - if (ev.code == "KeyD") debug = !debug; - }) - - setInterval(() => { - const frame = decode_frame(buf ?? new Uint8Array(), debug); - buf = undefined - - const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - const data = imageData.data; - - for (let y = 0; y < 1080; y++) { - for (let x = 0; x < 1920; x++) { - const ti = (x + y * 1920) * 4; - const si = (x + y * 1920) * 3; - data[ti + 0] = frame[si + 0] - data[ti + 1] = frame[si + 1] - data[ti + 2] = frame[si + 2] - data[ti + 3] = 255 - } - } - - ctx.putImageData(imageData, 0, 0); - - }, 1000 / 30) -} diff --git a/lvc/codec/Cargo.toml b/lvc/codec/Cargo.toml deleted file mode 100644 index 0c52ad2..0000000 --- a/lvc/codec/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "bv1" -version = "0.1.0" -edition = "2021" - -[dependencies] -rayon = {version="1.7.0",optional = true} - -[features] -default = ["parallel"] -parallel = ["dep:rayon"] - diff --git a/lvc/codec/src/debug.rs b/lvc/codec/src/debug.rs deleted file mode 100644 index d6c37c2..0000000 --- a/lvc/codec/src/debug.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{split::split, Block, Frame, Pixel, View, P2}; - -pub fn draw_debug(frame: &mut Frame, view: View, block: &Block) { - match block { - Block::Lit(_) => rect(frame, view, Pixel::GREEN), - Block::Split(a, b) => { - let [av, bv] = split(view); - draw_debug(frame, av, &a); - draw_debug(frame, bv, &b); - } - Block::Ref(r) => { - let v = View { - a: view.a, - b: view.a + P2 { x: 2, y: 2 }, - }; - if r.pos_off != P2::ZERO { - fill_rect(frame, v + P2 { x: 0, y: 0 }, Pixel::BLUE) - } - if r.color_off != Pixel::BLACK { - fill_rect(frame, v + P2 { x: 2, y: 0 }, Pixel::RED) - } - } - } -} - -fn rect(frame: &mut Frame, view: View, color: Pixel) { - for x in view.a.x..view.b.x { - frame[P2 { x, y: view.a.y }] = color; - frame[P2 { x, y: view.b.y - 1 }] = color; - } - for y in view.a.y..view.b.y { - frame[P2 { y, x: view.a.x }] = color; - frame[P2 { y, x: view.b.x - 1 }] = color; - } -} -fn fill_rect(frame: &mut Frame, view: View, color: Pixel) { - for y in view.a.y..view.b.y { - for x in view.a.x..view.b.x { - frame[P2 { x, y }] = color; - } - } -} diff --git a/lvc/codec/src/decode.rs b/lvc/codec/src/decode.rs deleted file mode 100644 index 616b7af..0000000 --- a/lvc/codec/src/decode.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::{ - debug::draw_debug, huff::read_huff, impls::join, split::split, Block, Frame, View, P2, -}; -use std::io::{BufReader, BufWriter, Read, Result, Write}; - -pub fn decode(size: P2, debug: bool, input: impl Read, output: impl Write) -> Result<()> { - let mut input = BufReader::new(input); - let mut output = BufWriter::new(output); - let mut d = Decoder::new(size); - let mut f = Frame::new(size); - loop { - d.decode_frame(&mut input, &mut f, debug)?; - Frame::write(&mut output, &f)?; - } -} - -pub struct Decoder { - last_frame: Frame, - size: P2, -} - -impl Decoder { - pub fn new(size: P2) -> Self { - Self { - size, - last_frame: Frame::new(size), - } - } - pub fn decode_frame( - &mut self, - mut input: impl Read, - output: &mut Frame, - debug: bool, - ) -> Result<()> { - let huff = true; - let b = if huff { - let mut buf = vec![]; - read_huff(&mut input, &mut buf)?; - eprintln!("{}", buf.len()); - let mut buf = std::io::Cursor::new(&mut buf); - Block::read(&mut buf, View::all(self.size))? - } else { - Block::read(&mut input, View::all(self.size))? - }; - - decode_block(&self.last_frame, output, View::all(self.size), &b); - self.last_frame.pixels.copy_from_slice(&output.pixels); // TODO use mem::swap - if debug { - draw_debug(output, View::all(self.size), &b); - } - Ok(()) - } -} - -pub fn decode_block(last_frame: &Frame, frame: &mut Frame, view: View, block: &Block) { - match block { - Block::Lit(pxs) => frame.import(view, &pxs), - Block::Split(a, b) => { - let [av, bv] = split(view); - let (frame1, frame2) = - unsafe { (&mut *(frame as *mut Frame), &mut *(frame as *mut Frame)) }; - join( - || decode_block(last_frame, frame1, av, &a), - || decode_block(last_frame, frame2, bv, &b), - ); - } - Block::Ref(r) => { - for y in view.a.y..view.b.y { - for x in view.a.x..view.b.x { - let p = P2 { x, y }; - frame[p] = last_frame[p + r.pos_off] + r.color_off - } - } - } - } -} diff --git a/lvc/codec/src/diff.rs b/lvc/codec/src/diff.rs deleted file mode 100644 index 4d1a805..0000000 --- a/lvc/codec/src/diff.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::{Frame, Pixel, Ref, View, P2}; - -// 4ms -pub fn diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { - let mut k = 0; - for y in view.a.y..view.b.y { - for x in view.a.x..view.b.x { - let pos = P2 { x, y }; - let p1 = frame1[pos + rp.pos_off] + rp.color_off; - let p2 = frame2[pos]; - k += pixel_diff(p1, p2) - } - } - k -} - -#[inline(always)] -pub fn pixel_diff(p1: Pixel, p2: Pixel) -> u32 { - p1.r.abs_diff(p2.r) as u32 + p1.g.abs_diff(p2.g) as u32 + p1.b.abs_diff(p2.b) as u32 -} - -// pub fn diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { -// let mut k = 0; -// for y in view.a.y..view.b.y { -// let s1_ystart = (y * frame1.size.x) as usize; -// let s2_ystart = ((y + rp.pos_off.y) * frame1.size.x + rp.pos_off.x) as usize; -// let s1 = &frame1.pixels[s1_ystart + view.a.x as usize..s1_ystart + view.b.x as usize]; -// let s2 = &frame2.pixels[s2_ystart + view.a.x as usize..s2_ystart + view.b.x as usize]; -// let s1 = unsafe { std::mem::transmute::<_, &[i16]>(s1) }; -// let s2 = unsafe { std::mem::transmute::<_, &[i16]>(s2) }; - -// k += s1 -// .iter() -// .zip(s2.iter()) -// .map(|(a, b)| a.abs_diff(*b) as u32) -// .sum::() -// } -// k -// } - -// pub fn fast_diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { -// assert!(view.size().x % 5 == 0); - -// let mut diff_lanes = i32x16::from_array([0; 16]); -// let mut k1 = [0; 32]; -// let mut k2 = [0; 32]; - -// let next_line = frame1.size.x as usize - view.size().x as usize; -// let index_start = view.a.x as usize + view.a.y as usize * frame1.size.x as usize; -// let index_end = view.b.x as usize + (view.b.y as usize - 1) * frame1.size.x as usize; - -// let mut i = index_start; -// let mut x = view.a.x; - -// while i < index_end { - -// let f1: &[u8] = -// unsafe { std::slice::from_raw_parts(frame1.pixels[sl_start..].as_ptr() as *mut u8, sl_size) }; -// let f2: &[u8] = -// unsafe { std::slice::from_raw_parts(frame2.pixels[sl_start..].as_ptr() as *mut u8, sl_size) }; - -// for i in 0..15 { -// k1[i] = f1[i] as i32; -// k2[i] = f2[i] as i32; -// } - -// // for j in 0..5 { -// // let j3 = j * 3; -// // k1[j3] = frame1.pixels[i + j].r as i32; -// // k2[j3] = frame2.pixels[i + j].r as i32; -// // k1[j3 + 1] = frame1.pixels[i + j].g as i32; -// // k2[j3 + 1] = frame2.pixels[i + j].g as i32; -// // k1[j3 + 2] = frame1.pixels[i + j].b as i32; -// // k2[j3 + 2] = frame2.pixels[i + j].b as i32; -// // } -// let pl1 = i16x32::from_array(k1); -// let pl2 = i16x32::from_array(k2); -// diff_lanes += (pl1 - pl2).abs(); - -// i += 5; -// x += 5; -// if x > view.b.x { -// i += next_line; -// x = view.a.x -// } -// } - -// return diff_lanes.reduce_sum() as u32; -// } - -// pub fn fast_diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 { -// let mut diff_lanes = i32x16::from_array([0; 16]); -// let mut k1 = [0; 16]; -// let mut k2 = [0; 16]; - -// let next_line = frame1.size.x as usize - view.size().x as usize; -// let index_start = view.a.x as usize + view.a.y as usize * frame1.size.x as usize; -// let index_end = view.b.x as usize + (view.b.y as usize - 1) * frame1.size.x as usize; - -// let mut i = index_start; -// let mut x = view.a.x; -// let mut kfill = 0; - -// while i < index_end { -// k1[kfill] = frame1.pixels[i].r as i32; -// k2[kfill] = frame2.pixels[i].r as i32; -// kfill += 1; -// k1[kfill] = frame1.pixels[i].g as i32; -// k2[kfill] = frame2.pixels[i].g as i32; -// kfill += 1; -// k1[kfill] = frame1.pixels[i].b as i32; -// k2[kfill] = frame2.pixels[i].b as i32; -// kfill += 1; - -// i += 1; -// x += 1; -// if x > view.b.x { -// i += next_line; -// x = view.a.x -// } - -// if kfill == 15 { -// let pl1 = i32x16::from_array(k1); -// let pl2 = i32x16::from_array(k2); -// diff_lanes += (pl1 - pl2).abs(); -// kfill = 0; -// } -// } - -// return diff_lanes.reduce_sum() as u32; -// } diff --git a/lvc/codec/src/encode.rs b/lvc/codec/src/encode.rs deleted file mode 100644 index 564b0b3..0000000 --- a/lvc/codec/src/encode.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::diff::{diff, pixel_diff}; -use crate::huff::write_huff; -use crate::impls::join; -use crate::split::split; -use crate::{decode::decode_block, Block, Frame, Pixel, Ref, View, P2}; -use std::io::{BufReader, BufWriter, Read, Write}; -use std::time::Instant; - -#[derive(Debug, Clone)] -pub struct EncodeConfig { - pub threshold: f32, - pub max_block_size: usize, - pub attention_split: u32, - pub keyframe_interval: usize, -} - -pub fn encode( - config: EncodeConfig, - size: P2, - input: impl Read, - output: impl Write, -) -> std::io::Result<()> { - let mut input = BufReader::new(input); - let mut output = BufWriter::new(output); - - let mut last_frame = Frame::new(size); - for frame_number in 0.. { - let mut frame = Frame::read(&mut input, size)?; - - let mut config = config.clone(); - if frame_number % config.keyframe_interval != 0 { - config.threshold = std::f32::INFINITY; - } - - let t = Instant::now(); - let b: Block = encode_block(&last_frame, &frame, View::all(size), &config); - let time_encode = t.elapsed(); - - let t = Instant::now(); - decode_block(&last_frame, &mut frame, View::all(size), &b); - last_frame = frame; - let time_decode = t.elapsed(); - - if true { - let mut buf = vec![]; - let mut bufw = std::io::Cursor::new(&mut buf); - b.write(&mut bufw)?; - drop(bufw); - let t = Instant::now(); - let bits_raw = buf.len() * 8; - let bits_huff = write_huff(&buf, &mut output)?; - let time_huff = t.elapsed(); - drop(buf); - - eprintln!( - "frame {frame_number}: {:?}", - time_decode + time_huff + time_encode - ); - eprintln!( - "\tencode {time_encode:?} ({:.2}%)", - (bits_raw as f32 / (size.area() * 24) as f32) * 100.0 - ); - eprintln!( - "\thuff {time_huff:?} ({:.2}%)", - (bits_huff as f32 / bits_raw as f32) * 100.0 - ); - eprintln!("\tdecode {time_decode:?}"); - } else { - b.write(&mut output)?; - } - } - Ok(()) -} - -pub fn encode_block(last_frame: &Frame, frame: &Frame, view: View, config: &EncodeConfig) -> Block { - let view_area = view.size().area(); - if view_area > config.max_block_size - || (view_area > 64 && attention(frame, view) > config.attention_split) - { - let [av, bv] = split(view); - let (ab, bb) = join( - || Box::new(encode_block(last_frame, frame, av, config)), - || Box::new(encode_block(last_frame, frame, bv, config)), - ); - return Block::Split(ab, bb); - } - - let mut r = Ref::default(); - let mut d = diff([last_frame, frame], view, r); - - // let att = 1. - attention(frame, view) as f32 * 0.000001; - // let thres = (config.threshold as f32 * att.clamp(0.2, 1.0)) as u32; - let thres = (config.threshold * view_area as f32) as u32; - - let target_average = average_color(frame, view); - - for granularity in [2, 1, 2, 1, 2, 1, 2, 1] { - let (nd, nrp) = optimize_ref(last_frame, frame, view, r, granularity, target_average); - if nd < d { - r = nrp; - d = nd; - } else { - break; - } - } - - if d < thres { - return Block::Ref(r); - } else { - Block::Lit(frame.export(view)) - } -} - -pub fn optimize_ref( - last_frame: &Frame, - frame: &Frame, - view: View, - r: Ref, - g: i32, - target_average: Pixel, -) -> (u32, Ref) { - let g2 = g * 2; - [ - Some(r.apply(|r| r.pos_off += P2 { x: g, y: 0 })), - Some(r.apply(|r| r.pos_off += P2 { x: g, y: g })), - Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g })), - Some(r.apply(|r| r.pos_off += P2 { x: -g, y: g })), - Some(r.apply(|r| r.pos_off += P2 { x: -g, y: 0 })), - Some(r.apply(|r| r.pos_off += P2 { x: -g, y: -g })), - Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g })), - Some(r.apply(|r| r.pos_off += P2 { x: g, y: -g })), - Some(r.apply(|r| r.pos_off += P2 { x: g2, y: 0 })), - Some(r.apply(|r| r.pos_off += P2 { x: g2, y: g2 })), - Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g2 })), - Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: g2 })), - Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: 0 })), - Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: -g2 })), - Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g2 })), - Some(r.apply(|r| r.pos_off += P2 { x: g2, y: -g2 })), - { - let mut r = r; - let last_avr = average_color(last_frame, view); - let diff = target_average - last_avr; - r.color_off = diff; - if diff != Pixel::BLACK { - Some(r) - } else { - None - } - }, - ] - .into_iter() - .flatten() - .map(|r| (diff([last_frame, frame], view, r), r)) - .min_by_key(|e| e.0) - .unwrap() -} - -pub fn attention(frame: &Frame, view: View) -> u32 { - let mut k = 0; - for y in view.a.y..view.b.y - 1 { - for x in view.a.x..view.b.x - 1 { - let p = P2 { x, y }; - k += pixel_diff(frame[p], frame[p + P2::X]).pow(2); - k += pixel_diff(frame[p], frame[p + P2::Y]).pow(2); - } - } - k -} - -pub fn average_color(frame: &Frame, view: View) -> Pixel { - let mut r = 0u32; - let mut g = 0u32; - let mut b = 0u32; - - for y in view.a.y..view.b.y { - for x in view.a.x..view.b.x { - let p = frame[P2 { x, y }]; - r += p.r as u32; - g += p.g as u32; - b += p.b as u32; - } - } - let area = view.size().area() as u32; - Pixel { - r: (r / area) as i16, - g: (g / area) as i16, - b: (b / area) as i16, - } -} diff --git a/lvc/codec/src/frameio.rs b/lvc/codec/src/frameio.rs deleted file mode 100644 index f6cbcbf..0000000 --- a/lvc/codec/src/frameio.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::{Frame, Pixel, PixelValue, P2}; -use std::io::{Read, Result, Write}; - -impl Frame { - pub fn read(inp: &mut impl Read, size: P2) -> Result { - let mut f = Frame::new(size); - - for y in 0..size.y { - for x in 0..size.x { - let mut cc = [0u8; 3]; - inp.read_exact(&mut cc)?; - f[P2 { x, y }] = Pixel { - r: cc[0] as PixelValue, - g: cc[1] as PixelValue, - b: cc[2] as PixelValue, - }; - } - } - Ok(f) - } - - pub fn write(out: &mut impl Write, frame: &Frame) -> Result<()> { - for y in 0..frame.size.y { - for x in 0..frame.size.x { - let p = frame[P2 { x, y }]; - let mut cc = [0u8; 3]; - cc[0] = p.r.clamp(0, 255) as u8; - cc[1] = p.g.clamp(0, 255) as u8; - cc[2] = p.b.clamp(0, 255) as u8; - out.write_all(&mut cc)?; - } - } - Ok(()) - } -} diff --git a/lvc/codec/src/huff.rs b/lvc/codec/src/huff.rs deleted file mode 100644 index 6d74c42..0000000 --- a/lvc/codec/src/huff.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::io::{Read, Result, Write}; - -#[derive(Debug, Clone)] -enum HT { - Branch(Box, Box), - Terminal(u8), -} - -pub fn write_huff(buf: &[u8], w: &mut impl Write) -> Result { - let mut w = BitIO::new(w); - assert!(buf.len() <= 0xffffff, "huff frame too big"); - w.wbyte((buf.len() & 0xff) as u8)?; - w.wbyte(((buf.len() >> 8) & 0xff) as u8)?; - w.wbyte(((buf.len() >> 16) & 0xff) as u8)?; - - let mut probs = [0usize; 256]; - for b in buf { - probs[*b as usize] += 1; - } - let tree = HT::from_probabilities(probs); - let mut table = [0u32; 256]; - tree.create_lut(&mut table, 1); - tree.write(&mut w)?; - - - for b in buf { - let mut k = table[*b as usize]; - while k != 1 { - w.wbit((k & 1) == 1)?; - k >>= 1; - } - } - - w.flush()?; - Ok(w.position) -} - -pub fn read_huff(r: &mut impl Read, buf: &mut Vec) -> Result { - let mut r = BitIO::new(r); - - let mut len = 0usize; - len |= r.rbyte()? as usize; - len |= (r.rbyte()? as usize) << 8; - len |= (r.rbyte()? as usize) << 16; - - let root = HT::read(&mut r)?; - let root = match &root { - HT::Branch(a, b) => [a, b], - _ => panic!("no!"), - }; - - let mut cursor = root; - while buf.len() != len { - let v = r.rbit()?; - match cursor[v as usize].as_ref() { - HT::Branch(a, b) => { - cursor = [a, b]; - } - HT::Terminal(n) => { - buf.push(*n); - cursor = root; - } - } - } - Ok(r.position) -} - -impl HT { - pub fn from_probabilities(ps: [usize; 256]) -> Self { - let mut parts = ps - .into_iter() - .enumerate() - .map(|(n, p)| (p, HT::Terminal(n as u8))) - .collect::>(); - - while parts.len() != 1 { - parts.sort_by_key(|e| -(e.0 as isize)); - let ((ap, at), (bp, bt)) = (parts.pop().unwrap(), parts.pop().unwrap()); - parts.push((ap + bp + 1, HT::Branch(box at, box bt))) - } - parts[0].1.clone() - } - pub fn create_lut(&self, table: &mut [u32; 256], mut prefix: u32) { - assert!(self.depth() < 30, "too deep! doesnt fit {}", self.depth()); - match self { - HT::Branch(a, b) => { - let pz = 32 - prefix.leading_zeros(); - prefix ^= 1 << pz; - prefix ^= 1 << (pz - 1); - a.create_lut(table, prefix); - prefix ^= 1 << (pz - 1); - b.create_lut(table, prefix); - } - HT::Terminal(n) => { - assert_eq!(table[*n as usize], 0); - table[*n as usize] = prefix - } - } - } - pub fn depth(&self) -> usize { - match self { - HT::Branch(a, b) => a.depth().max(b.depth()) + 1, - HT::Terminal(_) => 0, - } - } - pub fn write(&self, w: &mut BitIO) -> Result<()> { - match self { - HT::Branch(a, b) => { - w.wbit(false)?; - a.write(w)?; - b.write(w)?; - } - HT::Terminal(n) => { - w.wbit(true)?; - w.wbyte(*n)?; - } - } - Ok(()) - } - pub fn read(r: &mut BitIO) -> Result { - match r.rbit()? { - false => Ok(Self::Branch(box Self::read(r)?, box Self::read(r)?)), - true => Ok(Self::Terminal(r.rbyte()?)), - } - } -} - -pub struct BitIO { - inner: T, - byte: u8, - position: usize, -} -impl BitIO { - pub fn new(inner: T) -> Self { - Self { - inner, - byte: 0, - position: 0, - } - } -} -impl BitIO { - #[inline] - pub fn wbit(&mut self, b: bool) -> Result<()> { - self.byte <<= 1; - self.byte |= b as u8; - self.position += 1; - if self.position & 0b111 == 0 { - self.inner.write_all(&[self.byte])?; - } - Ok(()) - } - #[inline] - pub fn wbyte(&mut self, v: u8) -> Result<()> { - for i in 0..8 { - self.wbit((v & (1 << i)) != 0)?; - } - Ok(()) - } - pub fn flush(&mut self) -> Result<()> { - while self.position & 0b111 != 0 { - self.wbit(false)?; - } - Ok(()) - } -} - -impl BitIO { - #[inline] - pub fn rbit(&mut self) -> Result { - if self.position & 0b111 == 0 { - let mut buf = [0]; - self.inner.read_exact(&mut buf)?; - self.byte = buf[0]; - } - - let v = (self.byte & 0b10000000) != 0; - self.byte <<= 1; - self.position += 1; - Ok(v) - } - #[inline] - pub fn rbyte(&mut self) -> Result { - let mut v = 0u8; - for i in 0..8 { - v |= (self.rbit()? as u8) << i; - } - Ok(v) - } -} diff --git a/lvc/codec/src/impls.rs b/lvc/codec/src/impls.rs deleted file mode 100644 index b4cc119..0000000 --- a/lvc/codec/src/impls.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::{Frame, Pixel, Ref, View, P2}; -use std::ops::{Add, AddAssign, Index, IndexMut, Sub}; - -#[cfg(feature = "parallel")] -pub use rayon::join; -#[cfg(not(feature = "parallel"))] -pub fn join(oper_a: A, oper_b: B) -> (RA, RB) -where - A: FnOnce() -> RA + Send, - B: FnOnce() -> RB + Send, - RA: Send, - RB: Send, -{ - (oper_a(), oper_b()) -} - -impl Frame { - pub fn export(&self, view: View) -> Vec { - let mut o = vec![]; - for y in view.a.y..view.b.y { - for x in view.a.x..view.b.x { - o.push(self[P2 { x, y }]) - } - } - o - } - pub fn import(&mut self, view: View, mut source: &[Pixel]) { - for y in view.a.y..view.b.y { - for x in view.a.x..view.b.x { - self[P2 { x, y }] = source[0]; - source = &source[1..]; - } - } - assert_eq!(source.len(), 0) - } - pub fn new(size: P2) -> Self { - Self { - pixels: vec![Pixel::default(); size.area()], - size, - } - } -} -impl Ref { - #[inline] - pub fn apply(mut self, f: F) -> Self { - f(&mut self); - self - } -} -impl Pixel { - pub const BLACK: Pixel = Pixel { r: 0, g: 0, b: 0 }; - 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 }; -} -impl AddAssign for P2 { - fn add_assign(&mut self, rhs: Self) { - self.x += rhs.x; - self.y += rhs.y; - } -} -impl Add for Pixel { - type Output = Pixel; - #[inline] - fn add(self, rhs: Self) -> Self::Output { - Self { - r: self.r + rhs.r, - g: self.g + rhs.g, - b: self.b + rhs.b, - } - } -} -impl Sub for Pixel { - type Output = Pixel; - #[inline] - fn sub(self, rhs: Self) -> Self::Output { - Self { - r: self.r - rhs.r, - g: self.g - rhs.g, - b: self.b - rhs.b, - } - } -} -impl P2 { - pub const ZERO: P2 = P2 { x: 0, y: 0 }; - pub const X: P2 = P2 { x: 1, y: 0 }; - pub const Y: P2 = P2 { x: 0, y: 1 }; - - #[inline] - pub fn area(&self) -> usize { - (self.x * self.y) as usize - } -} -impl View { - #[inline] - pub fn all(b: P2) -> Self { - Self { - a: P2::default(), - b, - } - } - #[inline] - pub fn size(&self) -> P2 { - self.b - self.a - } -} -impl Add for View { - type Output = View; - #[inline] - fn add(self, rhs: P2) -> Self::Output { - View { - a: self.a + rhs, - b: self.b + rhs, - } - } -} -impl Add for P2 { - type Output = P2; - #[inline] - fn add(self, rhs: Self) -> Self::Output { - Self { - x: self.x + rhs.x, - y: self.y + rhs.y, - } - } -} -impl Sub for P2 { - type Output = P2; - #[inline] - fn sub(self, rhs: Self) -> Self::Output { - Self { - x: self.x - rhs.x, - y: self.y - rhs.y, - } - } -} - -impl Index for Frame { - type Output = Pixel; - #[inline] - fn index(&self, P2 { x, y }: P2) -> &Self::Output { - &self - .pixels - .get(x as usize + (y as usize * self.size.x as usize)) - .unwrap_or(&Pixel { r: 0, g: 0, b: 0 }) - } -} -impl IndexMut for Frame { - #[inline] - fn index_mut(&mut self, P2 { x, y }: P2) -> &mut Self::Output { - &mut self.pixels[x as usize + (y as usize * self.size.x as usize)] - } -} - -pub trait ToArray { - type Output; - fn to_array(self) -> [Self::Output; 2]; -} -impl
ToArray for (A, A) { - type Output = A; - #[inline] - fn to_array(self) -> [A; 2] { - [self.0, self.1] - } -} diff --git a/lvc/codec/src/lib.rs b/lvc/codec/src/lib.rs deleted file mode 100644 index c764211..0000000 --- a/lvc/codec/src/lib.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![feature(portable_simd)] -#![feature(io_error_other)] -#![feature(box_syntax)] - -pub mod debug; -pub mod decode; -pub mod diff; -pub mod encode; -pub mod frameio; -pub mod huff; -pub mod impls; -pub mod serialize; -pub mod split; - -pub type PixelValue = i16; - -pub use decode::{decode, Decoder}; -pub use encode::encode; - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] -pub struct Pixel { - pub r: PixelValue, - pub g: PixelValue, - pub b: PixelValue, -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] -pub struct P2 { - pub x: i32, - pub y: i32, -} - -pub struct Frame { - pub size: P2, - pub pixels: Vec, -} - -#[derive(Debug, Clone, Copy)] -pub struct View { - pub a: P2, - pub b: P2, -} - -#[derive(Debug, Clone)] -pub enum Block { - Split(Box, Box), - Lit(Vec), - Ref(Ref), -} - -#[derive(Debug, Clone, Copy, Default)] -pub struct Ref { - pub pos_off: P2, - pub color_off: Pixel, -} diff --git a/lvc/codec/src/serialize.rs b/lvc/codec/src/serialize.rs deleted file mode 100644 index 960aa1b..0000000 --- a/lvc/codec/src/serialize.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::{split::split, Block, Pixel, Ref, View, P2}; -use std::io::{Read, Result, Write}; - -impl Pixel { - #[inline] - pub fn write(&self, w: &mut impl Write) -> Result<()> { - w.write_all(&[ - self.r.clamp(0, 255) as u8, - self.g.clamp(0, 255) as u8, - self.b.clamp(0, 255) as u8, - ]) - } - #[inline] - pub fn read(r: &mut impl Read) -> Result { - Ok(Self { - r: read_byte(r)? as i16, - g: read_byte(r)? as i16, - b: read_byte(r)? as i16, - }) - } -} -impl Pixel { - #[inline] - pub fn write_full(&self, w: &mut impl Write) -> Result<()> { - write_word(w, self.r)?; - write_word(w, self.g)?; - write_word(w, self.b)?; - Ok(()) - } - #[inline] - pub fn read_full(r: &mut impl Read) -> Result { - Ok(Self { - r: read_word(r)?, - g: read_word(r)?, - b: read_word(r)?, - }) - } -} -impl P2 { - #[inline] - pub fn write(&self, w: &mut impl Write) -> Result<()> { - write_word(w, self.x as i16)?; - write_word(w, self.y as i16)?; - Ok(()) - } - - #[inline] - pub fn read(r: &mut impl Read) -> Result { - Ok(Self { - x: read_word(r)? as i32, - y: read_word(r)? as i32, - }) - } -} - -impl Block { - pub fn write(&self, w: &mut impl Write) -> Result<()> { - match self { - Block::Split(a, b) => { - w.write_all(&[0])?; - a.write(w)?; - b.write(w)?; - } - Block::Lit(pixels) => { - w.write_all(&[1])?; - for p in pixels { - p.write(w)?; - } - } - Block::Ref(k) => { - w.write_all(&[2])?; - k.pos_off.write(w)?; - k.color_off.write_full(w)?; - } - } - Ok(()) - } - pub fn read(r: &mut impl Read, view: View) -> Result { - match read_byte(r)? { - 0 => { - let [av, bv] = split(view); - Ok(Block::Split( - box Block::read(r, av)?, - box Block::read(r, bv)?, - )) - } - 1 => { - let mut px = vec![]; - for _ in 0..view.size().area() { - px.push(Pixel::read(r)?) - } - Ok(Block::Lit(px)) - } - 2 => Ok(Block::Ref(Ref { - pos_off: P2::read(r)?, - color_off: Pixel::read_full(r)?, - })), - _ => Err(std::io::Error::other("unknown block variant")), - } - } -} - -#[inline] -fn read_byte(r: &mut impl Read) -> Result { - let mut buf = [0u8]; - r.read_exact(&mut buf)?; - Ok(buf[0]) -} -#[inline] -fn write_word(w: &mut impl Write, v: i16) -> Result<()> { - w.write_all(&[(v & 0xff) as u8, (v >> 8) as u8]) -} -#[inline] -fn read_word(r: &mut impl Read) -> Result { - Ok((read_byte(r)? as u16 | ((read_byte(r)? as u16) << 8)) as i16) -} diff --git a/lvc/codec/src/split.rs b/lvc/codec/src/split.rs deleted file mode 100644 index c17179e..0000000 --- a/lvc/codec/src/split.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{View, P2}; - -pub fn split(view: View) -> [View; 2] { - let s = view.size(); - if s.x > s.y { - let mid_x = (view.a.x + view.b.x) / 2; - [ - View { - a: view.a, - b: P2 { - x: mid_x, - y: view.b.y, - }, - }, - View { - a: P2 { - x: mid_x, - y: view.a.y, - }, - b: view.b, - }, - ] - } else { - let mid_y = (view.a.y + view.b.y) / 2; - [ - View { - a: view.a, - b: P2 { - x: view.b.x, - y: mid_y, - }, - }, - View { - a: P2 { - x: view.a.x, - y: mid_y, - }, - b: view.b, - }, - ] - } -} diff --git a/lvc/tools/d-disp b/lvc/tools/d-disp deleted file mode 100755 index d7b8feb..0000000 --- a/lvc/tools/d-disp +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/fish -cargo run --release -- -W 1920 -H 1080 decode | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 \ No newline at end of file diff --git a/lvc/tools/d-dispd b/lvc/tools/d-dispd deleted file mode 100755 index 97a2b90..0000000 --- a/lvc/tools/d-dispd +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/fish -cargo run --release -- -W 1920 -H 1080 decode --debug | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 \ No newline at end of file diff --git a/lvc/tools/d-save b/lvc/tools/d-save deleted file mode 100755 index 1f19994..0000000 --- a/lvc/tools/d-save +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/fish -cargo run --release -- -W 1920 -H 1080 decode | ffmpeg -pixel_format rgb24 -video_size 1920x1080 -framerate 30 -f rawvideo -i pipe:0 -i $argv[1] -map '0:v' -map '1:a' -c:v libsvtav1 -y data/output.webm diff --git a/lvc/tools/d-saved b/lvc/tools/d-saved deleted file mode 100755 index 16de45a..0000000 --- a/lvc/tools/d-saved +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/fish -cargo run --release -- -W 1920 -H 1080 decode --debug | ffmpeg -pixel_format rgb24 -video_size 1920x1080 -framerate 30 -f rawvideo -i pipe:0 -i $argv[1] -map '0:v' -map '1:a' -c:v libsvtav1 -y data/output-debug.webm diff --git a/lvc/tools/e b/lvc/tools/e deleted file mode 100755 index a85a767..0000000 --- a/lvc/tools/e +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/fish -ffmpeg -loglevel quiet -i $argv[1] -vf format=rgb24 -f rawvideo pipe:1 | cargo run --release -- -W 1920 -H 1080 encode $argv[2..] diff --git a/readme.md b/readme.md index 9461af4..79ae13a 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,16 @@ # Experiments on video codecs + +My experiments on lossy video compression. + +## `bv1` + +A somewhat minimal video codec that is able to achieve quality comparable to +H.264. + +## Other codecs + +- `evc` is my first proper attempt at video compression. features motion + compensation and broken dct. +- `vgcodec` approximates images by drawing circles (on the GPU). +- (`dhwt-codec` tries to compress using a discrete haar wavelet across all three + dimensions. that doesnt work well) -- cgit v1.2.3-70-g09d2