diff --git a/Makefile.in b/Makefile.in index a2426a7e..b7211a3b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -595,27 +595,6 @@ $(OBJ)/.vkd3d-proton-post-build64: -## -## mediaconv -## - -MEDIACONV_DEPENDS = gst_orc gstreamer gst_base - -$(eval $(call rules-source,mediaconv,$(SRCDIR)/media-converter)) -$(eval $(call rules-cargo,mediaconv,32)) -$(eval $(call rules-cargo,mediaconv,64)) - -$(OBJ)/.mediaconv-post-build64: - mkdir -p $(MEDIACONV_DST64)/lib64/gstreamer-1.0/ - cp -a $(MEDIACONV_OBJ64)/x86_64-unknown-linux-gnu/release/libprotonmediaconverter.so $(MEDIACONV_DST64)/lib64/gstreamer-1.0/ - touch $@ - -$(OBJ)/.mediaconv-post-build32: - mkdir -p $(MEDIACONV_DST32)/lib/gstreamer-1.0/ - cp -a $(MEDIACONV_OBJ32)/i686-unknown-linux-gnu/release/libprotonmediaconverter.so $(MEDIACONV_DST32)/lib/gstreamer-1.0/ - touch $@ - - ## ## BattlEye Bridge ## diff --git a/media-converter/Cargo.lock b/media-converter/Cargo.lock deleted file mode 100644 index 06a6353a..00000000 --- a/media-converter/Cargo.lock +++ /dev/null @@ -1,791 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" - -[[package]] -name = "array-init" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb6d71005dc22a708c7496eee5c8dc0300ee47355de6256c3b35b12b5fef596" - -[[package]] -name = "atomic_refcell" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d" - -[[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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" - -[[package]] -name = "cfg-expr" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" -dependencies = [ - "smallvec", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" -dependencies = [ - "iana-time-zone", - "num-integer", - "num-traits", - "winapi", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "filetime" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "windows-sys", -] - -[[package]] -name = "futures-channel" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" - -[[package]] -name = "futures-executor" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-macro" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-task" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" - -[[package]] -name = "futures-util" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" -dependencies = [ - "futures-core", - "futures-macro", - "futures-task", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "gio-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da1bba9d3f2ab13a6e9932c40f240dc99ebc9f0bdc35cfb130d1a3df36f374c" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5abffa711471e015eb93d65d6ea20e7e9f6f7951fc0a1042280439319b2de06" -dependencies = [ - "bitflags", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e195c1311fa6b04d7b896ea39385f6bd60ef5d25bf74a7c11c8c3f94f6c1a572" -dependencies = [ - "anyhow", - "heck", - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "glib-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33357bb421a77bd849f6a0bfcaf3b4b256a2577802971bb5dd522d530f27021" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "gobject-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ca11a57400f3d4fda594e002844be47900c9fb8b29e2155c6e37a1f24e51b3" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gst-plugin-version-helper" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747ffe0e4067acfb98d6f7cbbe0a1901794587a93ab2b36c4652bc75c28d865d" -dependencies = [ - "chrono", -] - -[[package]] -name = "gstreamer" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e428081934c617115320750b7827f8f13131d9c3ae90b647c14a5d6019f47b4" -dependencies = [ - "bitflags", - "cfg-if", - "futures-channel", - "futures-core", - "futures-util", - "glib", - "gstreamer-sys", - "libc", - "muldiv", - "num-integer", - "num-rational", - "once_cell", - "option-operations", - "paste", - "pretty-hex", - "thiserror", -] - -[[package]] -name = "gstreamer-audio" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affbf8dd22eb301f21a3ae659358a6e069850b35cab6522d40738c9500f85b17" -dependencies = [ - "bitflags", - "cfg-if", - "glib", - "gstreamer", - "gstreamer-audio-sys", - "gstreamer-base", - "libc", - "once_cell", -] - -[[package]] -name = "gstreamer-audio-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d6a3ad336150faf2125e29ac025b1fa152dca08b4cb2496f1e7d9c83b51e8b" -dependencies = [ - "glib-sys", - "gobject-sys", - "gstreamer-base-sys", - "gstreamer-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gstreamer-base" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "326674197c010e91a98d0f55a032abe22b1fd932456dbcdc3415450b4b653817" -dependencies = [ - "atomic_refcell", - "bitflags", - "cfg-if", - "glib", - "gstreamer", - "gstreamer-base-sys", - "libc", -] - -[[package]] -name = "gstreamer-base-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd55d3858fa65a99286c1cbe8db001f4ce5cff6a038f1c1253f5d99f840970de" -dependencies = [ - "glib-sys", - "gobject-sys", - "gstreamer-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gstreamer-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaafc66df32b334d4aa28025fd5d83cadc971e1910205e140ea070f4ac4834f" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gstreamer-video" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9b96daff8a3d853588e61207afac81a4879f3972430f6609721601ab757d7fd" -dependencies = [ - "bitflags", - "cfg-if", - "futures-channel", - "glib", - "gstreamer", - "gstreamer-base", - "gstreamer-video-sys", - "libc", - "once_cell", -] - -[[package]] -name = "gstreamer-video-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066ee44cd8d84f19a18c646128c1890878c034d3fb9f34d8d5f07311bbd9f41f" -dependencies = [ - "glib-sys", - "gobject-sys", - "gstreamer-base-sys", - "gstreamer-sys", - "libc", - "system-deps", -] - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "iana-time-zone" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.134" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "muldiv" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5136edda114182728ccdedb9f5eda882781f35fa6e80cc360af12a8932507f3" - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" - -[[package]] -name = "option-operations" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0" -dependencies = [ - "paste", -] - -[[package]] -name = "paste" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - -[[package]] -name = "pretty-hex" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" - -[[package]] -name = "proc-macro-crate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" -dependencies = [ - "once_cell", - "thiserror", - "toml", -] - -[[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.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proton-media-converter" -version = "7.0.0" -dependencies = [ - "array-init", - "crc32fast", - "filetime", - "glib", - "gst-plugin-version-helper", - "gstreamer", - "gstreamer-audio", - "gstreamer-base", - "gstreamer-video", - "once_cell", -] - -[[package]] -name = "quote" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "serde" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" - -[[package]] -name = "syn" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system-deps" -version = "6.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "thiserror" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - -[[package]] -name = "unicode-ident" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" - -[[package]] -name = "version-compare" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" - -[[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.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[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-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/media-converter/Cargo.toml b/media-converter/Cargo.toml deleted file mode 100644 index 43c36d95..00000000 --- a/media-converter/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "proton-media-converter" -version = "7.0.0" -authors = ["Andrew Eikum "] -repository = "https://github.com/ValveSoftware/Proton/" -license = "MIT/Apache-2.0" -edition = "2021" -description = "Proton media converter" - -[dependencies] -glib = "0.16" -gstreamer = "0.19.1" - -# the versions are not in sync, the submodules below haven't seen any changes -# since 0.19.0 release so .1 release haven't happened for them -gstreamer-base = "0.19.1" -gstreamer-video = "0.19.0" -gstreamer-audio = "0.19.0" - -once_cell = "1.9" -crc32fast = "1.3" -array-init = "2.0" -filetime = "0.2" - -[lib] -name = "protonmediaconverter" -crate-type = ["cdylib"] -path = "src/lib.rs" - -[build-dependencies] -gst-plugin-version-helper = "0.7.4" - -[profile.release] -lto = true -opt-level = 3 -debug = false -panic = 'unwind' - -[profile.dev] -opt-level = 1 - -#if you need local modifications to gstreamer-rs you can point to it here -#[patch.'https://github.com/sdroege/gstreamer-rs'] -#gstreamer = { path = "../gstreamer-rs/gstreamer" } -#gstreamer-base = { path = "../gstreamer-rs/gstreamer-base" } -#gstreamer-video = { path = "../gstreamer-rs/gstreamer-video" } -#gstreamer-audio = { path = "../gstreamer-rs/gstreamer-audio" } diff --git a/media-converter/Makefile b/media-converter/Makefile deleted file mode 100644 index 4136e3fe..00000000 --- a/media-converter/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -SONAME := libprotonmediaconverter.so - -TRIPLE64 := x86_64-unknown-linux-gnu -TRIPLE32 := i686-unknown-linux-gnu - -DESTDIR ?= dist/ -DIST_GST_DIR64 := $(DESTDIR)/lib64/gstreamer-1.0/ -DIST_GST_DIR32 := $(DESTDIR)/lib/gstreamer-1.0/ - -ifeq ($(DEBUG),) - CARGO_RELEASE_ARG := --release - TARGET_BUILD_TYPE := release -else - CARGO_RELEASE_ARG := - TARGET_BUILD_TYPE := debug -endif - -all: install - -build: blank.mkv blank.ptna - cargo build --target $(TRIPLE64) $(CARGO_RELEASE_ARG) - PKG_CONFIG_ALLOW_CROSS=1 cargo build --target $(TRIPLE32) $(CARGO_RELEASE_ARG) - -install: build - install -d "$(DIST_GST_DIR64)" - install -m 755 target/$(TRIPLE64)/$(TARGET_BUILD_TYPE)/$(SONAME) "$(DIST_GST_DIR64)" - install -d "$(DIST_GST_DIR32)" - install -m 755 target/$(TRIPLE32)/$(TARGET_BUILD_TYPE)/$(SONAME) "$(DIST_GST_DIR32)" - -blank.mkv: - #120 frames @ 30 FPS == 4 seconds - gst-launch-1.0 videotestsrc num-buffers=120 pattern=smpte ! 'video/x-raw,format=(string)I420,width=320,height=240,framerate=(fraction)30/1' ! av1enc ! queue ! mux. audiotestsrc num-buffers=400 freq=0 samplesperbuffer=441 ! 'audio/x-raw,rate=48000,channels=2' ! opusenc ! queue ! mux. matroskamux name=mux ! filesink location=blank.mkv - -make_blank_ptna: make_blank_ptna.c - gcc -Wall -O2 $(shell pkg-config --cflags opus) -o $@ $< -lm $(shell pkg-config --libs opus) - -blank.ptna: make_blank_ptna - ./make_blank_ptna $@ diff --git a/media-converter/README b/media-converter/README deleted file mode 100644 index b87a25fb..00000000 --- a/media-converter/README +++ /dev/null @@ -1,12 +0,0 @@ -This module is a gstreamer plugin which provides the ability to replace media -data encoded in certain formats with media encoded in another format. There -are two main components, `videoconv` for converting video data provided to -Quartz and Media Foundation, and `audioconv` for converting audio data -provided to XAudio2. - -The broad idea is to hash the incoming data and replace it with data looked up -from a cache. If there is a cache miss, then the data is recorded to disk and -instead replaced by "blank" media. The conversion should be transparent to the -application (Wine, FAudio) so no changes are required to the application. - -See the accompanying source files for more information. diff --git a/media-converter/blank.mkv b/media-converter/blank.mkv deleted file mode 100644 index 7a05753b..00000000 Binary files a/media-converter/blank.mkv and /dev/null differ diff --git a/media-converter/blank.ptna b/media-converter/blank.ptna deleted file mode 100644 index 44af1343..00000000 Binary files a/media-converter/blank.ptna and /dev/null differ diff --git a/media-converter/build.rs b/media-converter/build.rs deleted file mode 100644 index 72a8a02c..00000000 --- a/media-converter/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -extern crate gst_plugin_version_helper; - -fn main() { - gst_plugin_version_helper::info() -} diff --git a/media-converter/make_blank_ptna.c b/media-converter/make_blank_ptna.c deleted file mode 100644 index ace3de7f..00000000 --- a/media-converter/make_blank_ptna.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2020, Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Dumps a "blank" Proton Audio sample. For documentation of the Proton Audio - * format, see audioconv.rs. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - -/* indexes into frame_sizes[] */ -#define FRAME_2p5MS 0 -#define FRAME_SMALLEST FRAME_2p5MS -#define FRAME_5MS 1 -#define FRAME_10MS 2 -#define FRAME_20MS 3 -#define FRAME_40MS 4 -#define FRAME_60MS 5 -#define FRAME_LARGEST FRAME_60MS - -/* sample properties */ -static const int SAMPLERATE = 48000; -static const int CHANNELS = 1; -static const int SAMPLE_LENGTH = FRAME_10MS; /* must match value in audioconv.rs */ -static const float AUDIBLE_HZ = 400.f; /* freq for the --audible switch */ - -static const int frame_sizes[] = { - SAMPLERATE * 2.5 / 1000, - SAMPLERATE * 5 / 1000, - SAMPLERATE * 10 / 1000, - SAMPLERATE * 20 / 1000, - SAMPLERATE * 40 / 1000, - SAMPLERATE * 60 / 1000, -}; - -static float theta = 0.f; - -static const uint32_t FLAG_HEADER = 0x10000000; -static const uint32_t AUDIOCONV_PADDING_LENGTH_SHIFT = 12; - -static int complete_write(const int file, const void *buf, const size_t len) -{ - size_t written = 0; - ssize_t ret; - while(written < len){ - ret = write(file, ((const char *)buf) + written, len - written); - if(ret < 0){ - if(errno != EINTR && errno != EAGAIN) - return 0; - }else - written += ret; - } - return written == len; -} - -static int dump_hz(float hz, int samples, OpusEncoder *encoder, int outfile) -{ - int i = 0, j, c, frame_size, tot = 0, padding; - opus_int32 written; - uint32_t pkt_header; - float val, *vals; - unsigned char packet[4000 /* suggested by opus_encoder(3) */ ]; - - fprintf(stdout, "dumping %u samples at %f hz\n", samples, hz); - - vals = malloc(sizeof(float) * CHANNELS * frame_sizes[FRAME_LARGEST]); - - while(i < samples){ - - /* find the largest packet we can write with the samples remaining */ - frame_size = -1; - for(j = ARRAY_SIZE(frame_sizes) - 1; j >= 0; --j){ - if(samples - i >= frame_sizes[j]){ - frame_size = frame_sizes[j]; - break; - } - } - - if(frame_size < 0){ - /* couldn't fill a whole packet, so write a partial */ - frame_size = frame_sizes[FRAME_SMALLEST]; - padding = frame_size - (samples - i); - }else - padding = 0; - - for(j = 0; j < frame_size - padding; ++j){ - val = sinf(theta); - for(c = 0; c < CHANNELS; ++c) - vals[j * CHANNELS + c] = val; - theta += hz * 2.f * M_PI / (float)SAMPLERATE; - while(theta >= 2.f * M_PI) - theta -= 2.f * M_PI; - } - - if(padding) - memset(vals + (frame_size - padding) * CHANNELS, 0, sizeof(float) * padding * CHANNELS); - - written = opus_encode_float(encoder, vals, frame_size, packet, sizeof(packet)); - if(written < 0){ - fprintf(stderr, "fatal: opus_encode failed!!!\n"); - free(vals); - return 0; - } - - pkt_header = written | (padding << AUDIOCONV_PADDING_LENGTH_SHIFT); - - fprintf(stdout, "encoded %u samples (%u are padding) to %u bytes\n", frame_size, padding, written); - if(!complete_write(outfile, &pkt_header, sizeof(pkt_header))){ - fprintf(stderr, "fatal: error writing packet header!!!\n"); - free(vals); - return 0; - } - if(!complete_write(outfile, packet, written)){ - fprintf(stderr, "fatal: error writing packet contents!!!\n"); - free(vals); - return 0; - } - tot += written + sizeof(written); - - i += frame_size; - } - - fprintf(stdout, "done dumping, %u bytes\n", tot); - - free(vals); - - return 1; -} - -/* OGG's opus header (from RFC 7845 Section 5.1) */ -struct __attribute__((__packed__)) opus_header { - uint64_t magic; - uint8_t version; - uint8_t channels; - uint16_t preskip; - uint32_t input_samplerate; - uint16_t output_gain; - uint8_t mapping_family; -}; - -static int dump_header(int outfile) -{ - struct opus_header header; - uint32_t sz = sizeof(header) | FLAG_HEADER; - - static const char magic[] = "OpusHead"; - - memcpy(&header.magic, magic, sizeof(magic)); - header.version = 1; - header.channels = CHANNELS; - header.preskip = 0; - header.input_samplerate = SAMPLERATE; - header.output_gain = 0; - header.mapping_family = 0; /* FIXME: we need to support mc */ - - if(!complete_write(outfile, &sz, sizeof(sz))){ - fprintf(stderr, "fatal: error writing opus header header!!!\n"); - return 0; - } - - if(!complete_write(outfile, &header, sizeof(header))){ - fprintf(stderr, "fatal: error writing opus header!!!\n"); - return 0; - } - - return 1; -} - -void usage(const char *name) -{ - fprintf(stderr, "usage:\n"); - fprintf(stderr, "\t%s [--help|-h] [--audible] \n", name); - fprintf(stderr, "\n"); - fprintf(stderr, "\t--audible\tGenerate an audible sound clip instead of silence.\n"); - fprintf(stderr, "\t--help -h\tPrint this help message.\n\n"); -} - -int main(int argc, char **argv) -{ - int err, outfile, i, audible = 0; - OpusEncoder *encoder; - const char *outfile_name = NULL; - - for(i = 1; i < argc; ++i){ - if(!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")){ - usage(argv[0]); - return 0; - }else if(!strcmp(argv[i], "--audible")){ - audible = 1; - }else if(outfile_name){ - fprintf(stderr, "error: too many arguments.\n\n"); - usage(argv[0]); - return 1; - }else{ - outfile_name = argv[i]; - } - } - - if(!outfile_name){ - fprintf(stderr, "error: missing filename.\n\n"); - usage(argv[0]); - return 1; - } - - encoder = opus_encoder_create(SAMPLERATE, CHANNELS, OPUS_APPLICATION_AUDIO, &err); - if(!encoder){ - fprintf(stderr, "couldn't create opus encoder, err: 0x%x\n", err); - return 1; - } - - outfile = open(outfile_name, O_CREAT | O_WRONLY | O_TRUNC, 0644); - if(outfile < 0){ - fprintf(stderr, "couldn't open file \"%s\": %s\n", outfile_name, strerror(errno)); - opus_encoder_destroy(encoder); - return 1; - } - - if(!dump_header(outfile)){ - close(outfile); - unlink(outfile_name); - opus_encoder_destroy(encoder); - return 1; - } - - if(!dump_hz(audible ? AUDIBLE_HZ : 0.f, frame_sizes[SAMPLE_LENGTH], encoder, outfile)){ - close(outfile); - unlink(outfile_name); - opus_encoder_destroy(encoder); - return 1; - } - - close(outfile); - opus_encoder_destroy(encoder); - - return 0; -} diff --git a/media-converter/src/audioconv/imp.rs b/media-converter/src/audioconv/imp.rs deleted file mode 100644 index b7289a32..00000000 --- a/media-converter/src/audioconv/imp.rs +++ /dev/null @@ -1,970 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -use crate::format_hash; -use crate::HASH_SEED; -use crate::discarding_disabled; -use crate::steam_compat_shader_path; -use crate::touch_file; - -use gst::glib; -use gst::prelude::*; -use gst::subclass::prelude::*; -use gst::EventView; -use gst::QueryViewMut; - -use std::sync::Mutex; -use std::io; -use std::io::Read; -use std::fs; -use std::fs::OpenOptions; -use std::collections::HashSet; - -#[cfg(target_arch = "x86")] -use crate::murmur3_x86_128::murmur3_x86_128_full as murmur3_128_full; -#[cfg(target_arch = "x86")] -use crate::murmur3_x86_128::murmur3_x86_128_state as murmur3_128_state; - -#[cfg(target_arch = "x86_64")] -use crate::murmur3_x64_128::murmur3_x64_128_full as murmur3_128_full; -#[cfg(target_arch = "x86_64")] -use crate::murmur3_x64_128::murmur3_x64_128_state as murmur3_128_state; - -use crate::fossilize; -use crate::copy_into_array; -use crate::BufferedReader; - -use once_cell::sync::Lazy; - -/* Algorithm - * --------- - * - * The application feeds encoded audio into XAudio2 in chunks. Since we don't have access to all - * chunks in a stream on initialization (as we do with the video converter), we continuously hash - * the stream as it is sent to us. Each "chunk" is identified as the hash of the entire stream up - * to that chunk. - * - * Since chunks are small (~2 kilobytes), this leads to a significant possibility of two different - * streams having identical intro chunks (imagine two streams that start with several seconds of - * silence). This means we need a tree of chunks. Suppose two incoming streams with chunks that - * hash as shown (i.e. identical intro chunks that diverge later): - * - * Stream 1: [AA BB CC DD] - * - * Stream 2: [AA BB YY ZZ] - * - * We record a tree and the transcoder will walk it depth-first in order to reconstruct each unique - * stream: - * - * AA => aa.ptna - * AA+BB => bb.ptna - * AA+BB+CC => cc.ptna - * AA+BB+CC+DD => dd.ptna - * AA+BB+YY => yy.ptna - * AA+BB+YY+ZZ => zz.ptna - * - * Upon playback, we chain each transcoded stream chunk together as the packets come in: - * - * AA -> start stream with aa.ptna - * BB -> play bb.ptna - * CC -> play cc.ptna - * DD -> play dd.ptna - * - * or: - * - * AA -> start stream with aa.ptna - * BB -> play bb.ptna - * YY -> play yy.ptna - * ZZ -> play zz.ptna - * - * or: - * - * AA -> start stream with aa.ptna - * NN -> not recognized, instead play blank.ptna and mark this stream as needs-transcoding - * OO -> play blank.ptna - * PP -> play blank.ptna - * When the Stream is destroyed, we'll record AA+NN+OO+PP into the needs-transcode database - * for the transcoder to convert later. - * - * - * Physical Format - * --------------- - * - * All stored values are little-endian. - * - * Transcoded audio is stored in the "transcoded" Fossilize database under the - * AUDIOCONV_FOZ_TAG_PTNADATA tag. Each chunk is stored in one entry with as many of the following - * "Proton Audio" (ptna) packets as are required to store the entire transcoded chunk: - * - * uint32_t packet_header: Information about the upcoming packet, see bitmask: - * MSB [FFFF PPPP PPPP PPPP PPPP LLLL LLLL LLLL] LSB - * L: Number of _bytes_ in this packet following this header. - * P: Number of _samples_ at the end of this packet which are padding and should be skipped. - * F: Flag bits: - * 0x1: This packet is an Opus header - * 0x2, 0x4, 0x8: Reserved for future use. - * - * If the Opus header flag is set: - * Following packet is an Opus identification header, as defined in RFC 7845 "Ogg - * Encapsulation for the Opus Audio Codec" Section 5.1. - * - * - * If the header flag is not set: - * Following packet is raw Opus data to be sent to an Opus decoder. - * - * - * If we encounter a stream which needs transcoding, we record the buffers and metadata in - * a Fossilize database. The database has three tag types: - * - * AUDIOCONV_FOZ_TAG_STREAM: This identifies each unique stream of buffers. For example: - * [hash(AA+BB+CC+DD)] -> [AA, BB, CC, DD] - * [hash(AA+BB+XX+YY)] -> [AA, BB, XX, YY] - * - * AUDIOCONV_FOZ_TAG_AUDIODATA: This contans the actual encoded audio data. For example: - * [AA] -> [AA's buffer data] - * [BB] -> [BB's buffer data] - * - * AUDIOCONV_FOZ_TAG_CODECINFO: This contans the codec data required to decode the buffer. Only - * the "head" of each stream is recorded. For example: - * [AA] -> [ - * uint32_t wmaversion (from WAVEFORMATEX.wFormatTag) - * uint32_t bitrate (from WAVEFORMATEX.nAvgBytesPerSec) - * uint32_t channels (WAVEFORMATEX.nChannels) - * uint32_t rate (WAVEFORMATEX.nSamplesPerSec) - * uint32_t block_align (WAVEFORMATEX.nBlockAlign) - * uint32_t depth (WAVEFORMATEX.wBitsPerSample) - * char[remainder of entry] codec_data (codec data which follows WAVEFORMATEX) - * ] - * - */ - -const AUDIOCONV_ENCODED_LENGTH_MASK: u32 = 0x00000fff; /* 4kB fits in here */ -const AUDIOCONV_PADDING_LENGTH_MASK: u32 = 0x0ffff000; /* 120ms of samples at 48kHz fits in here */ -const AUDIOCONV_PADDING_LENGTH_SHIFT: u32 = 12; -const AUDIOCONV_FLAG_MASK: u32 = 0xf0000000; -const AUDIOCONV_FLAG_HEADER: u32 = 0x10000000; /* this chunk is the Opus header */ -const _AUDIOCONV_FLAG_RESERVED1: u32 = 0x20000000; /* not yet used */ -const _AUDIOCONV_FLAG_RESERVED2: u32 = 0x40000000; /* not yet used */ -const _AUDIOCONV_FLAG_V2: u32 = 0x80000000; /* indicates a "version 2" header, process somehow differently (TBD) */ - -/* properties of the "blank" audio file */ -const _BLANK_AUDIO_FILE_LENGTH_MS: f32 = 10.0; -const _BLANK_AUDIO_FILE_RATE: f32 = 48000.0; - -static CAT: Lazy = Lazy::new(|| { - gst::DebugCategory::new( - "protonaudioconverter", - gst::DebugColorFlags::empty(), - Some("Proton audio converter")) -}); - -struct AudioConverterDumpFozdb { - fozdb: Option, - already_cleaned: bool, -} - -impl AudioConverterDumpFozdb { - fn new() -> Self { - Self { - fozdb: None, - already_cleaned: false, - } - } - - fn open(&mut self, create: bool) -> &mut Self { - if self.fozdb.is_none() { - let dump_file_path = match std::env::var("MEDIACONV_AUDIO_DUMP_FILE") { - Err(_) => { return self; }, - Ok(c) => c, - }; - - let dump_file_path = std::path::Path::new(&dump_file_path); - - if fs::create_dir_all(&dump_file_path.parent().unwrap()).is_err() { - return self; - } - - match fossilize::StreamArchive::new(&dump_file_path, OpenOptions::new().write(true).read(true).create(create), false /* read-only? */, AUDIOCONV_FOZ_NUM_TAGS) { - Ok(newdb) => { - self.fozdb = Some(newdb); - }, - Err(_) => { - return self; - }, - } - } - self - } - - fn close(&mut self) { - self.fozdb = None - } - - fn discard_transcoded(&mut self) { - if self.already_cleaned { - return; - } - if discarding_disabled() { - self.already_cleaned = true; - return; - } - if let Some(fozdb) = &mut self.open(false).fozdb { - if let Ok(read_fozdb_path) = std::env::var("MEDIACONV_AUDIO_TRANSCODED_FILE") { - if let Ok(read_fozdb) = fossilize::StreamArchive::new(&read_fozdb_path, OpenOptions::new().read(true), true /* read-only? */, AUDIOCONV_FOZ_NUM_TAGS) { - let mut chunks_to_discard = HashSet::<(u32, u128)>::new(); - let mut chunks_to_keep = HashSet::<(u32, u128)>::new(); - - for stream_id in fozdb.iter_tag(AUDIOCONV_FOZ_TAG_STREAM).cloned().collect::>() { - if let Ok(chunks_size) = fozdb.entry_size(AUDIOCONV_FOZ_TAG_STREAM, stream_id) { - let mut buf = vec![0u8; chunks_size].into_boxed_slice(); - if fozdb.read_entry(AUDIOCONV_FOZ_TAG_STREAM, stream_id, 0, &mut buf, fossilize::CRCCheck::WithCRC).is_ok() { - - let mut has_all = true; - let mut stream_chunks = Vec::<(u32, u128)>::new(); - - for i in 0..(chunks_size / 16) { - let offs = i * 16; - let chunk_id = u128::from_le_bytes(copy_into_array(&buf[offs..offs + 16])); - - if !read_fozdb.has_entry(AUDIOCONV_FOZ_TAG_PTNADATA, chunk_id) { - has_all = false; - break; - } - - stream_chunks.push((AUDIOCONV_FOZ_TAG_AUDIODATA, chunk_id)); - } - - for x in stream_chunks { - if has_all { - chunks_to_discard.insert(x); - chunks_to_discard.insert((AUDIOCONV_FOZ_TAG_CODECINFO, x.1)); - } else { - chunks_to_keep.insert(x); - chunks_to_keep.insert((AUDIOCONV_FOZ_TAG_CODECINFO, x.1)); - } - } - - if has_all { - chunks_to_discard.insert((AUDIOCONV_FOZ_TAG_STREAM, stream_id)); - } - } - } - } - - let mut chunks = Vec::<(u32, u128)>::new(); - for x in chunks_to_discard.difference(&chunks_to_keep) { - chunks.push(*x); - } - - if fozdb.discard_entries(&chunks).is_err() { - self.close(); - } - } - } - } - self.already_cleaned = true; - } -} - -static DUMP_FOZDB: Lazy> = Lazy::new(|| { - Mutex::new(AudioConverterDumpFozdb::new()) -}); - -static DUMPING_DISABLED: Lazy = Lazy::new(|| { - let v = match std::env::var("MEDIACONV_AUDIO_DONT_DUMP") { - Err(_) => { return false; }, - Ok(c) => c, - }; - v != "0" -}); - -#[derive(Clone)] -struct NeedTranscodeHead { - wmaversion: i32, - bitrate: i32, - channels: i32, - rate: i32, - block_align: i32, - depth: i32, - codec_data: Vec -} - -impl NeedTranscodeHead { - fn new_from_caps(caps: &gst::CapsRef) -> Result { - let s = caps.structure(0).ok_or_else(|| loggable_error!(CAT, "Caps have no WMA data!"))?; - - let wmaversion = s.get::("wmaversion").map_err(|_| loggable_error!(CAT, "Caps have no wmaversion field"))?; - let bitrate = s.get::("bitrate").map_err(|_| loggable_error!(CAT, "Caps have no bitrate field"))?; - let channels = s.get::("channels").map_err(|_| loggable_error!(CAT, "Caps have no channels field"))?; - let rate = s.get::("rate").map_err(|_| loggable_error!(CAT, "Caps have no rate field"))?; - let block_align = s.get::("block_align").map_err(|_| loggable_error!(CAT, "Caps have no block_align field"))?; - let depth = s.get::("depth").map_err(|_| loggable_error!(CAT, "Caps have no depth field"))?; - let codec_data_buf = s.get::("codec_data") - .map_err(|_| loggable_error!(CAT, "Caps have no codec_data field"))?; - - let mapped = codec_data_buf.into_mapped_buffer_readable().unwrap(); - let mut codec_data = Vec::new(); - codec_data.extend_from_slice(mapped.as_slice()); - - Ok(NeedTranscodeHead { - wmaversion, - bitrate, - channels, - rate, - block_align, - depth, - codec_data, - }) - } - - fn serialize(&self) -> Vec { - let mut ret = Vec::new(); - ret.extend_from_slice(&self.wmaversion.to_le_bytes()); - ret.extend_from_slice(&self.bitrate.to_le_bytes()); - ret.extend_from_slice(&self.channels.to_le_bytes()); - ret.extend_from_slice(&self.rate.to_le_bytes()); - ret.extend_from_slice(&self.block_align.to_le_bytes()); - ret.extend_from_slice(&self.depth.to_le_bytes()); - ret.extend(self.codec_data.iter()); - ret - } -} - -const AUDIOCONV_FOZ_TAG_STREAM: u32 = 0; -const AUDIOCONV_FOZ_TAG_CODECINFO: u32 = 1; -const AUDIOCONV_FOZ_TAG_AUDIODATA: u32 = 2; -const AUDIOCONV_FOZ_TAG_PTNADATA: u32 = 3; -const AUDIOCONV_FOZ_NUM_TAGS: usize = 4; - -/* represents a Stream, a sequence of buffers */ -struct StreamState { - hash_state: murmur3_128_state, - cur_hash: u128, - buffers: Vec<(u128, gst::MappedBuffer)>, - loop_buffers: Vec<(u128, gst::MappedBuffer)>, - codec_info: Option, - needs_dump: bool, -} - -enum LoopState { - NoLoop, - Looping, - LoopEnded, -} - -impl StreamState { - fn new() -> Self { - Self { - hash_state: murmur3_128_state::new(HASH_SEED), - buffers: Vec::<(u128, gst::MappedBuffer)>::new(), - loop_buffers: Vec::<(u128, gst::MappedBuffer)>::new(), - cur_hash: 0, - codec_info: None, - needs_dump: false, - } - } - - fn record_buffer(&mut self, buf_hash: u128, loop_hash: u128, buffer: gst::MappedBuffer, codec_info: Option<&NeedTranscodeHead>) -> io::Result { - if self.codec_info.is_none() { - if let Some(codec_info) = codec_info { - self.codec_info = Some(codec_info.clone()); - } - } - - if self.loop_buffers.len() < self.buffers.len() && - self.buffers[self.loop_buffers.len()].0 == loop_hash { - - self.loop_buffers.push((buf_hash /* not loop_hash! */, buffer)); - - if self.loop_buffers.len() == self.buffers.len() { - /* full loop, just drop them */ - self.loop_buffers.clear(); - return Ok(LoopState::LoopEnded); - } - - Ok(LoopState::Looping) - }else{ - if !self.loop_buffers.is_empty() { - /* partial loop, track them and then continue */ - self.buffers.append(&mut self.loop_buffers); - } - - self.buffers.push((buf_hash, buffer)); - - self.cur_hash = murmur3_128_full(&mut (&buf_hash.to_le_bytes() as &[u8]), &mut self.hash_state)?; - Ok(LoopState::NoLoop) - } - } - - fn write_to_foz(&self) -> Result<(), gst::LoggableError> { - if self.needs_dump && !self.buffers.is_empty() { - let db = &mut (*DUMP_FOZDB).lock().unwrap(); - let mut db = &mut db.open(true).fozdb; - let db = match &mut db { - Some(d) => d, - None => { return Err(loggable_error!(CAT, "Failed to open fossilize db!")) }, - }; - - let mut found = db.has_entry(AUDIOCONV_FOZ_TAG_STREAM, self.cur_hash); - - if !found { - /* are there any recorded streams of which this stream is a subset? */ - let stream_ids = db.iter_tag(AUDIOCONV_FOZ_TAG_STREAM).cloned().collect::>(); - - found = stream_ids.iter().any(|stream_id| { - let mut offs = 0; - - for cur_buf_id in self.buffers.iter() { - let mut buf = [0u8; 16]; - - let res = db.read_entry(AUDIOCONV_FOZ_TAG_STREAM, *stream_id, offs, &mut buf, fossilize::CRCCheck::WithCRC); - - let buffer_id = match res { - Err(_) => { return false; } - Ok(s) => { - if s != std::mem::size_of::() { - return false; - } - u128::from_le_bytes(buf) - } - }; - - if buffer_id != (*cur_buf_id).0 { - return false; - } - - offs += 16; - } - - gst::trace!(CAT, "stream id {} is a subset of {}, so not recording stream", self.cur_hash, *stream_id); - true - }); - } - - if !found { - if *DUMPING_DISABLED { - gst::trace!(CAT, "dumping disabled, so not recording stream id {}", self.cur_hash); - } else { - gst::trace!(CAT, "recording stream id {}", self.cur_hash); - db.write_entry(AUDIOCONV_FOZ_TAG_CODECINFO, - self.buffers[0].0, - &mut self.codec_info.as_ref().unwrap().serialize().as_slice(), - fossilize::CRCCheck::WithCRC) - .map_err(|e| loggable_error!(CAT, "Unable to write stream header: {:?}", e))?; - - db.write_entry(AUDIOCONV_FOZ_TAG_STREAM, - self.cur_hash, - &mut StreamStateSerializer::new(self), - fossilize::CRCCheck::WithCRC) - .map_err(|e| loggable_error!(CAT, "Unable to write stream: {:?}", e))?; - - for buffer in self.buffers.iter() { - db.write_entry(AUDIOCONV_FOZ_TAG_AUDIODATA, - buffer.0, - &mut buffer.1.as_slice(), - fossilize::CRCCheck::WithCRC) - .map_err(|e| loggable_error!(CAT, "Unable to write audio data: {:?}", e))?; - } - } - } - } - Ok(()) - } - - fn reset(&mut self) { - self.hash_state.reset(); - self.buffers.clear(); - self.loop_buffers.clear(); - self.cur_hash = 0; - self.codec_info = None; - self.needs_dump = false; - } -} - -struct StreamStateSerializer<'a> { - stream_state: &'a StreamState, - cur_idx: usize, -} - -impl<'a> StreamStateSerializer<'a> { - fn new(stream_state: &'a StreamState) -> Self { - StreamStateSerializer { - stream_state, - cur_idx: 0 - } - } -} - -impl<'a> Read for StreamStateSerializer<'a> { - fn read(&mut self, out: &mut [u8]) -> io::Result { - if self.cur_idx >= self.stream_state.buffers.len() { - return Ok(0); - } - - out[0..std::mem::size_of::()].copy_from_slice(&self.stream_state.buffers[self.cur_idx].0.to_le_bytes()); - self.cur_idx += 1; - - Ok(std::mem::size_of::()) - } -} - -fn hash_data(dat: &[u8], len: usize, hash_state: &mut murmur3_128_state) -> io::Result { - murmur3_128_full(&mut BufferedReader::new(dat, len), hash_state) -} - -struct AudioConvState { - sent_header: bool, - - codec_data: Option, - - hash_state: murmur3_128_state, - loop_hash_state: murmur3_128_state, - - stream_state: StreamState, - - read_fozdb: Option, -} - -impl AudioConvState { - fn new() -> Result { - - let read_fozdb_path = std::env::var("MEDIACONV_AUDIO_TRANSCODED_FILE").map_err(|_| { - loggable_error!(CAT, "MEDIACONV_AUDIO_TRANSCODED_FILE is not set!") - })?; - - let read_fozdb = match fossilize::StreamArchive::new(&read_fozdb_path, OpenOptions::new().read(true), true /* read-only? */, AUDIOCONV_FOZ_NUM_TAGS) { - Ok(s) => Some(s), - Err(_) => None, - }; - - Ok(AudioConvState { - sent_header: false, - - codec_data: None, - - hash_state: murmur3_128_state::new(HASH_SEED), - loop_hash_state: murmur3_128_state::new(HASH_SEED), - - stream_state: StreamState::new(), - - read_fozdb, - }) - } - - fn reset(&mut self) { - if let Err(e) = self.stream_state.write_to_foz() { - e.log(); - } - - self.stream_state.reset(); - - self.hash_state.reset(); - self.loop_hash_state.reset(); - } - - fn open_transcode_file(&mut self, buffer: gst::Buffer) -> io::Result> { - let mapped = buffer.into_mapped_buffer_readable().unwrap(); - let buf_len = mapped.size(); - - let hash = hash_data(mapped.as_slice(), buf_len, &mut self.hash_state) - .map_err(|e|{ gst::warning!(CAT, "Hashing buffer failed! {}", e); io::ErrorKind::Other })?; - let loop_hash = hash_data(mapped.as_slice(), buf_len, &mut self.loop_hash_state) - .map_err(|e|{ gst::warning!(CAT, "Hashing buffer failed! {}", e); io::ErrorKind::Other })?; - - let try_loop = match self.stream_state.record_buffer(hash, loop_hash, mapped, Some(self.codec_data.as_ref().unwrap()))? { - LoopState::NoLoop => { self.loop_hash_state.reset(); false }, - LoopState::Looping => { true }, - LoopState::LoopEnded => { self.loop_hash_state.reset(); true }, - }; - - if try_loop { - gst::log!(CAT, "Buffer hash: {} (loop: {})", format_hash(hash), format_hash(loop_hash)); - }else{ - gst::log!(CAT, "Buffer hash: {}", format_hash(hash)); - } - - /* try to read transcoded data */ - if let Some(read_fozdb) = &mut self.read_fozdb { - if let Ok(transcoded_size) = read_fozdb.entry_size(AUDIOCONV_FOZ_TAG_PTNADATA, hash) { - /* success */ - let mut buf = vec![0u8; transcoded_size].into_boxed_slice(); - if read_fozdb.read_entry(AUDIOCONV_FOZ_TAG_PTNADATA, hash, 0, &mut buf, fossilize::CRCCheck::WithoutCRC).is_ok() { - return Ok(buf); - } - } - if try_loop { - if let Ok(transcoded_size) = read_fozdb.entry_size(AUDIOCONV_FOZ_TAG_PTNADATA, loop_hash) { - /* success */ - let mut buf = vec![0u8; transcoded_size].into_boxed_slice(); - if read_fozdb.read_entry(AUDIOCONV_FOZ_TAG_PTNADATA, loop_hash, 0, &mut buf, fossilize::CRCCheck::WithoutCRC).is_ok() { - return Ok(buf); - } - } - } - } - - /* if we can't, return the blank file */ - self.stream_state.needs_dump = true; - - let buf = Box::new(*include_bytes!("../../blank.ptna")); - - match steam_compat_shader_path() { - None => gst::log!(CAT, "env STEAM_COMPAT_SHADER_PATH not set"), - Some(mut path) => { - path.push("placeholder-audio-used"); - if let Err(e) = touch_file(path) { gst::log!(CAT, "Failed to touch placeholder-audio-used file: {:?}", e) } - }, - }; - - Ok(buf) - } -} - -pub struct AudioConv { - state: Mutex>, - sinkpad: gst::Pad, - srcpad: gst::Pad, -} - -#[glib::object_subclass] -impl ObjectSubclass for AudioConv { - const NAME: &'static str = "ProtonAudioConverter"; - type Type = super::AudioConv; - type ParentType = gst::Element; - - fn with_class(klass: &Self::Class) -> Self { - let templ = klass.pad_template("sink").unwrap(); - let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink")) - .chain_function(|pad, parent, buffer| { - AudioConv::catch_panic_pad_function( - parent, - || Err(gst::FlowError::Error), - |audioconv| audioconv.chain(pad, buffer) - ) - }) - .event_function(|pad, parent, event| { - AudioConv::catch_panic_pad_function( - parent, - || false, - |audioconv| audioconv.sink_event(pad, event) - ) - }).build(); - - let templ = klass.pad_template("src").unwrap(); - let srcpad = gst::Pad::builder_with_template(&templ, Some("src")) - .query_function(|pad, parent, query| { - AudioConv::catch_panic_pad_function( - parent, - || false, - |audioconv| audioconv.src_query(pad, query) - ) - }) - .activatemode_function(|pad, parent, mode, active| { - AudioConv::catch_panic_pad_function( - parent, - || Err(loggable_error!(CAT, "Panic activating srcpad with mode")), - |audioconv| audioconv.src_activatemode(pad, mode, active) - ) - }).build(); - - AudioConv { - state: Mutex::new(None), - sinkpad, - srcpad, - } - } -} - -impl ObjectImpl for AudioConv { - fn constructed(&self) { - self.parent_constructed(); - - let obj = self.obj(); - - obj.add_pad(&self.sinkpad).unwrap(); - obj.add_pad(&self.srcpad).unwrap(); - } -} - -impl GstObjectImpl for AudioConv { } - -impl ElementImpl for AudioConv { - fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { - static ELEMENT_METADATA: Lazy = Lazy::new(|| { - gst::subclass::ElementMetadata::new( - "Proton audio converter", - "Codec/Decoder/Audio", - "Converts audio for Proton", - "Andrew Eikum ") - }); - - Some(&*ELEMENT_METADATA) - } - - fn pad_templates() -> &'static [gst::PadTemplate] { - static PAD_TEMPLATES: Lazy> = Lazy::new(|| { - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - caps.append(gst::Caps::builder("audio/x-wma").build()); - } - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps).unwrap(); - - let caps = gst::Caps::builder("audio/x-opus").build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps).unwrap(); - - vec![src_pad_template, sink_pad_template] - }); - - PAD_TEMPLATES.as_ref() - } - - fn change_state( - &self, - transition: gst::StateChange - ) -> Result { - - gst::log!(CAT, imp: self, "State transition: {:?}", transition); - - match transition { - gst::StateChange::NullToReady => { - /* do runtime setup */ - - { - /* open fozdb here; this is the right place to fail and opening may be - * expensive */ - (*DUMP_FOZDB).lock().unwrap().discard_transcoded(); - - let db = &mut (*DUMP_FOZDB).lock().unwrap(); - let db = &mut db.open(true).fozdb; - if db.is_none() { - gst::error!(CAT, "Failed to open fossilize db!"); - return Err(gst::StateChangeError); - } - } - - let new_state = AudioConvState::new().map_err(|err| { - err.log(); - gst::StateChangeError - })?; - - let mut state = self.state.lock().unwrap(); - assert!((*state).is_none()); - *state = Some(new_state); - }, - - gst::StateChange::ReadyToNull => { - /* do runtime teardown */ - - let old_state = self.state.lock().unwrap().take(); // dispose of state - if let Some(old_state) = old_state { - if old_state.stream_state.write_to_foz().is_err() { - gst::warning!(CAT, "Error writing out stream data!"); - } - } - }, - - _ => (), - }; - - self.parent_change_state(transition) - - /* XXX on ReadyToNull, sodium drops state _again_ here... why? */ - } -} - -impl AudioConv { - - fn chain( - &self, - _pad: &gst::Pad, - buffer: gst::Buffer - ) -> Result { - gst::log!(CAT, "Handling buffer {:?}", buffer); - - let mut state = self.state.lock().unwrap(); - let mut state = match &mut *state { - Some(s) => s, - None => { return Err(gst::FlowError::Error); }, - }; - - let ptnadata = state.open_transcode_file(buffer).map_err(|_| { - gst::error!(CAT, "ERROR! Failed to read transcoded audio! Things will go badly..."); gst::FlowError::Error - })?; - - let mut offs: usize = 0; - loop { - - if offs >= ptnadata.len() { - break; - } - - if offs + 4 >= ptnadata.len() { - gst::warning!(CAT, "Short read on ptna header?"); - break; - } - - let packet_hdr = u32::from_le_bytes(copy_into_array(&ptnadata[offs..offs + 4])); - offs += 4; - - let (flags, padding_len, encoded_len) = - ((packet_hdr & AUDIOCONV_FLAG_MASK), - (packet_hdr & AUDIOCONV_PADDING_LENGTH_MASK) >> AUDIOCONV_PADDING_LENGTH_SHIFT, - (packet_hdr & AUDIOCONV_ENCODED_LENGTH_MASK) as usize); - - if offs + encoded_len > ptnadata.len() { - gst::warning!(CAT, "Short read on ptna data?"); - break; - } - - let pkt_is_header = (flags & AUDIOCONV_FLAG_HEADER) != 0; - - if pkt_is_header && state.sent_header { - /* only send one header */ - offs += encoded_len; - continue; - } - - /* TODO: can we use a gstbuffer cache here? */ - let mut buffer = gst::Buffer::with_size(encoded_len as usize).unwrap(); - - if !pkt_is_header && padding_len > 0 { - gst_audio::AudioClippingMeta::add(buffer.get_mut().unwrap(), - gst::format::Default::ZERO, - gst::format::Default::from_u64(padding_len as u64)); - } - - let mut writable = buffer.into_mapped_buffer_writable().unwrap(); - - writable.as_mut_slice().copy_from_slice(&ptnadata[offs..offs + encoded_len]); - - gst::log!(CAT, "pushing one packet of len {}", encoded_len); - self.srcpad.push(writable.into_buffer())?; - - if pkt_is_header { - state.sent_header = true; - } - - offs += encoded_len; - } - - Ok(gst::FlowSuccess::Ok) - } - - fn sink_event( - &self, - pad: &gst::Pad, - event: gst::Event - ) -> bool { - gst::log!(CAT, obj:pad, "Got an event {:?}", event); - match event.view() { - EventView::Caps(event_caps) => { - - let mut state = self.state.lock().unwrap(); - if let Some(state) = &mut *state { - let head = match NeedTranscodeHead::new_from_caps(event_caps.caps()){ - Ok(h) => h, - Err(e) => { - gst::error!(CAT, "Invalid WMA caps!"); - e.log(); - return false; - }, - }; - state.codec_data = Some(head); - }; - drop(state); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - let s = gst::Structure::builder("audio/x-opus") - .field("channel-mapping-family", &0i32) - .build(); - caps.append_structure(s); - } - - self.srcpad.push_event(gst::event::Caps::new(&caps)) - }, - EventView::FlushStop(_) => { - let mut state = self.state.lock().unwrap(); - if let Some(state) = &mut *state { - state.reset(); - }; - drop(state); - - gst::Pad::event_default(pad, Some(&*self.obj()), event) - }, - _ => gst::Pad::event_default(pad, Some(&*self.obj()), event) - } - } - - fn src_query( - &self, - pad: &gst::Pad, - query: &mut gst::QueryRef) -> bool - { - gst::log!(CAT, obj: pad, "got query: {:?}", query); - match query.view_mut() { - QueryViewMut::Scheduling(q) => { - let mut peer_query = gst::query::Scheduling::new(); - let res = self.sinkpad.peer_query(&mut peer_query); - if ! res { - return res; - } - - let (flags, min, max, align) = peer_query.result(); - - q.set(flags, min, max, align); - true - }, - _ => gst::Pad::query_default(pad, Some(&*self.obj()), query) - } - } - - fn src_activatemode( - &self, - _pad: &gst::Pad, - mode: gst::PadMode, - active: bool - ) -> Result<(), gst::LoggableError> { - self.sinkpad - .activate_mode(mode, active)?; - - Ok(()) - } -} diff --git a/media-converter/src/audioconv/mod.rs b/media-converter/src/audioconv/mod.rs deleted file mode 100644 index cdd391a5..00000000 --- a/media-converter/src/audioconv/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -use gst::glib; -use gst::prelude::*; - -mod imp; - -glib::wrapper! { - pub struct AudioConv(ObjectSubclass) @extends gst::Element, gst::Object; -} - -pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { - gst::Element::register( - Some(plugin), - "protonaudioconverter", - gst::Rank::Marginal, - AudioConv::static_type()) -} diff --git a/media-converter/src/audioconvbin/imp.rs b/media-converter/src/audioconvbin/imp.rs deleted file mode 100644 index dc36d56a..00000000 --- a/media-converter/src/audioconvbin/imp.rs +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -use gst::glib; -use gst::prelude::*; -use gst::subclass::prelude::*; -use gst::EventView; - -use once_cell::sync::Lazy; - -/* Opus is a great fit for our usecase except for one problem: it only supports a few samplerates. - * Notably it doesn't support 44100 Hz, which is a very frequently used samplerate. This bin - * provides a capssetter element which will override the rate we get from Opus with the rate the - * application requested. Similarly, on the transcoder side, we just encode the audio as if it were - * at 48 kHz, even if it is actually at 44.1 kHz. - * - * The downside to this is a small decrease in audio quality. If Opus is most responsive between 20 - * Hz and 20 kHz, then when 44.1 audio is converted to 48, we'll gain noise between 18-20 Hz - * (although WMA probably already filtered that out) and start to lose audio above 18,375 kHz. This - * is at the very edge of human hearing, so we're unlikely to lose any noticeable quality. - * - * Resampling is an option, but has some problems. It's significantly more complicated, and more - * CPU-intensive. Also, XAudio2 buffers can be started and ended at arbitrary points, so if we - * start moving audio data from one buffer into another due to resampling, it may result in audible - * artifacts. I think just encoding at the wrong rate is the best compromise. If the application - * actually cared about audio quality, they probably would not have used WMA in the first place. - */ - -static CAT: Lazy = Lazy::new(|| { - gst::DebugCategory::new( - "protonaudioconverterbin", - gst::DebugColorFlags::empty(), - Some("Proton audio converter bin")) -}); - -pub struct AudioConvBin { - audioconv: gst::Element, - opusdec: gst::Element, - capssetter: gst::Element, - srcpad: gst::GhostPad, - sinkpad: gst::GhostPad, -} - -#[glib::object_subclass] -impl ObjectSubclass for AudioConvBin { - const NAME: &'static str = "ProtonAudioConverterBin"; - type Type = super::AudioConvBin; - type ParentType = gst::Bin; - - fn with_class(klass: &Self::Class) -> Self { - - let templ = klass.pad_template("src").unwrap(); - let srcpad = gst::GhostPad::builder_with_template(&templ, Some("src")).build(); - - let templ = klass.pad_template("sink").unwrap(); - let sinkpad = gst::GhostPad::builder_with_template(&templ, Some("sink")) - .event_function(|pad, parent, event| { - AudioConvBin::catch_panic_pad_function( - parent, - || false, - |audioconvbin| audioconvbin.sink_event(pad, event) - ) - }).build(); - - let audioconv = gst::ElementFactory::make("protonaudioconverter").build().unwrap(); - let opusdec = gst::ElementFactory::make("opusdec").build().unwrap(); - let capssetter = gst::ElementFactory::make("capssetter").build().unwrap(); - - AudioConvBin { - audioconv, - opusdec, - capssetter, - srcpad, - sinkpad, - } - } -} - -impl ObjectImpl for AudioConvBin { - fn constructed(&self) { - self.parent_constructed(); - - let obj = self.obj(); - - obj.add(&self.audioconv).unwrap(); - obj.add(&self.opusdec).unwrap(); - obj.add(&self.capssetter).unwrap(); - - self.audioconv.link(&self.opusdec).unwrap(); - self.opusdec.link(&self.capssetter).unwrap(); - - self.sinkpad - .set_target(Some(&self.audioconv.static_pad("sink").unwrap())) - .unwrap(); - self.srcpad - .set_target(Some(&self.capssetter.static_pad("src").unwrap())) - .unwrap(); - - obj.add_pad(&self.sinkpad).unwrap(); - obj.add_pad(&self.srcpad).unwrap(); - } -} - -impl GstObjectImpl for AudioConvBin { } - -impl BinImpl for AudioConvBin { } - -impl ElementImpl for AudioConvBin { - fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { - static ELEMENT_METADATA: Lazy = Lazy::new(|| { - gst::subclass::ElementMetadata::new( - "Proton audio converter with rate fixup", - "Codec/Decoder/Audio", - "Converts audio for Proton, fixing up samplerates", - "Andrew Eikum ") - }); - - Some(&*ELEMENT_METADATA) - } - - fn pad_templates() -> &'static [gst::PadTemplate] { - static PAD_TEMPLATES: Lazy> = Lazy::new(|| { - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - caps.append(gst::Caps::builder("audio/x-wma").build()); - } - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps).unwrap(); - - let caps = gst::Caps::builder("audio/x-raw") - .field("format", "S16LE") /* opusdec always output S16LE */ - .build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps).unwrap(); - - vec![src_pad_template, sink_pad_template] - }); - - PAD_TEMPLATES.as_ref() - } -} - -impl AudioConvBin { - fn sink_event( - &self, - pad: &gst::GhostPad, - event: gst::Event - ) -> bool { - match event.view() { - EventView::Caps(event_caps) => { - /* set up capssetter with this rate */ - if let Some(s) = event_caps.caps().structure(0) { - - if let Ok(override_rate) = s.get::("rate") { - - let mut rate_caps = gst::Caps::new_empty(); - { - let rate_caps = rate_caps.get_mut().unwrap(); - let s = gst::Structure::builder("audio/x-raw") - .field("rate", &override_rate) - .build(); - rate_caps.append_structure(s); - } - self.capssetter.set_property("caps", &rate_caps); - }else{ - gst::warning!(CAT, "event has no rate"); - } - } else { - gst::warning!(CAT, "event has no structure"); - } - - /* forward on to the real pad */ - self.audioconv.static_pad("sink").unwrap() - .send_event(event) - }, - _ => gst::Pad::event_default(pad, Some(&*self.obj()), event) - } - } -} diff --git a/media-converter/src/audioconvbin/mod.rs b/media-converter/src/audioconvbin/mod.rs deleted file mode 100644 index ce2111b9..00000000 --- a/media-converter/src/audioconvbin/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -use gst::glib; -use gst::prelude::*; - -mod imp; - -glib::wrapper! { - pub struct AudioConvBin(ObjectSubclass) @extends gst::Bin, gst::Element, gst::Object; -} - -pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { - gst::Element::register( - Some(plugin), - "protonaudioconverterbin", - gst::Rank::Marginal + 1, - AudioConvBin::static_type() - ) -} diff --git a/media-converter/src/fossilize.rs b/media-converter/src/fossilize.rs deleted file mode 100644 index 8e9ab966..00000000 --- a/media-converter/src/fossilize.rs +++ /dev/null @@ -1,554 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Based on "Fossilize," which is - * Copyright (c) 2018-2019 Hans-Kristian Arntzen - * https://github.com/ValveSoftware/Fossilize/ - */ - -/* This is a read/write implementation of the Fossilize database format. - * - * https://github.com/ValveSoftware/Fossilize/ - * - * That C++ implementation is specific to Vulkan, while this one tries to be generic to store any - * type of data. - * - * FIXME: It should probably live in that repo or in a separate project. - */ - -use std::fs; -use std::io; -use std::io::Read; -use std::io::Write; -use std::io::Seek; -use std::fs::OpenOptions; -use std::convert::From; -use std::collections::HashMap; - -use crate::*; - -/* Fossilize StreamArchive database format version 6: - * - * The file consists of a header, followed by an unlimited series of "entries". - * - * All multi-byte entities are little-endian. - * - * The file header is as follows: - * - * Field Type Description - * ----- ---- ----------- - * magic_number uint8_t[12] Constant value: "\x81""FOSSILIZEDB" - * version uint32_t StreamArchive version: 6 - * - * - * Each entry follows this format: - * - * Field Type Description - * ----- ---- ----------- - * name unsigned char[40] Application-defined entry identifier, stored in hexadecimal big-endian - * ASCII. Usually N-char tag followed by (40 - N)-char hash. - * stored_size uint32_t Size of the payload as stored in this file. - * flags uint32_t Flags for this entry (e.g. compression). See below. - * crc32 uint32_t CRC32 of the payload as stored in this file. - * payload_size uint32_t Size of this payload after decompression. - * payload uint8_t[stored_size] Entry data. - * - * The flags field may contain: - * 0x1: No compression. - * 0x2: Deflate compression. - */ - -const FOSSILIZE_MAGIC: [u8; 12] = [0x81, 0x46, 0x4f, 0x53, 0x53, 0x49, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x42]; -const FOSSILIZE_MIN_COMPAT_VERSION: u8 = 5; -const FOSSILIZE_VERSION: u8 = 6; -const MAGIC_LEN_BYTES: usize = 12 + 4; - -const FOSSILIZE_COMPRESSION_NONE: u32 = 1; -const _FOSSILIZE_COMPRESSION_DEFLATE: u32 = 2; - -#[derive(Debug)] -pub enum Error { - NotImplemented, - IO(io::Error), - CorruptDatabase, - DataTooLarge, - InvalidTag, - EntryNotFound, - FailedChecksum, -} - -impl From for Error { - fn from(e: io::Error) -> Error { - Error::IO(e) - } -} - -type FossilizeHash = u128; -const _FOSSILIZEHASH_ASCII_LEN: usize = (128 / 8) * 2; - -trait ToAscii { - fn to_ascii_bytes(&self) -> Vec; - - fn from_ascii_bytes(b: &[u8]) -> Result - where Self: std::marker::Sized; -} - -impl ToAscii for FossilizeHash { - fn to_ascii_bytes(&self) -> Vec { - format_hash(*self).into_bytes() - } - - fn from_ascii_bytes(b: &[u8]) -> Result { - let s = String::from_utf8(b.to_vec()) - .map_err(|_| Error::CorruptDatabase)?; - Self::from_str_radix(&s, 16) - .map_err(|_| Error::CorruptDatabase) - } -} - -type FossilizeTag = u32; -const FOSSILIZETAG_ASCII_LEN: usize = (32 / 8) * 2; - -impl ToAscii for FossilizeTag { - fn to_ascii_bytes(&self) -> Vec { - format!("{:08x}", *self).into_bytes() - } - - fn from_ascii_bytes(b: &[u8]) -> Result { - let s = String::from_utf8(b.to_vec()) - .map_err(|_| Error::CorruptDatabase)?; - Self::from_str_radix(&s, 16) - .map_err(|_| Error::CorruptDatabase) - } -} - -const PAYLOAD_NAME_LEN_BYTES: usize = 40; - -struct PayloadInfo { - size: u32, - compression: u32, - crc: u32, - full_size: u32, -} - -const PAYLOAD_HEADER_LEN_BYTES: usize = 4 * 4; - -impl PayloadInfo { - - fn new_from_slice(dat: &[u8]) -> Self { - Self { - size: u32::from_le_bytes(copy_into_array(&dat[0..4])), - compression: u32::from_le_bytes(copy_into_array(&dat[4..8])), - crc: u32::from_le_bytes(copy_into_array(&dat[8..12])), - full_size: u32::from_le_bytes(copy_into_array(&dat[12..16])), - } - } - - fn to_slice(&self) -> [u8; PAYLOAD_HEADER_LEN_BYTES] { - let mut ret = [0u8; PAYLOAD_HEADER_LEN_BYTES]; - ret[0..4].copy_from_slice(&self.size.to_le_bytes()); - ret[4..8].copy_from_slice(&self.compression.to_le_bytes()); - ret[8..12].copy_from_slice(&self.crc.to_le_bytes()); - ret[12..16].copy_from_slice(&self.full_size.to_le_bytes()); - ret - } -} - -pub struct PayloadEntry { - offset: u64, - payload_info: PayloadInfo, -} - -impl PayloadEntry { - fn new_from_slice(offset: u64, dat: &[u8]) -> Self { - Self { - offset, - payload_info: PayloadInfo::new_from_slice(dat), - } - } -} - -pub struct StreamArchive { - file: fs::File, - read_only: bool, - - seen_blobs: Vec>, - - write_pos: u64, -} - -pub enum CRCCheck { - WithoutCRC, - WithCRC, -} - -impl StreamArchive { - - pub fn new>(filename: P, fileopts: &OpenOptions, read_only: bool, num_tags: usize) -> Result { - - let file = fileopts.open(filename)?; - - let mut seen_blobs = Vec::new(); - for _ in 0..num_tags { - seen_blobs.push(HashMap::new()); - } - - let mut ret = Self { - file, - read_only, - seen_blobs, - write_pos: 0, - }; - - ret.prepare()?; - - Ok(ret) - } - - pub fn prepare(&mut self) -> Result<(), Error> { - self.write_pos = self.file.seek(io::SeekFrom::Start(0))?; - - if self.file.metadata().unwrap().len() > 0 { - let mut magic_and_version = [0_u8; MAGIC_LEN_BYTES]; - self.file.read_exact(&mut magic_and_version)?; - - let version = magic_and_version[15]; - - if magic_and_version[0..12] != FOSSILIZE_MAGIC || !(FOSSILIZE_MIN_COMPAT_VERSION..=FOSSILIZE_VERSION).contains(&version) { - return Err(Error::CorruptDatabase); - } - - self.write_pos = MAGIC_LEN_BYTES as u64; - - loop { - let mut name_and_header = [0u8; PAYLOAD_NAME_LEN_BYTES + PAYLOAD_HEADER_LEN_BYTES]; - let res = self.file.read_exact(&mut name_and_header); - - if let Err(fail) = res { - if fail.kind() == io::ErrorKind::UnexpectedEof { - break; - } - return Err(Error::IO(fail)); - } - - let name = &name_and_header[0..PAYLOAD_NAME_LEN_BYTES]; - - let tag: usize = FossilizeTag::from_ascii_bytes(&name[0..FOSSILIZETAG_ASCII_LEN])? as usize; - let hash = FossilizeHash::from_ascii_bytes(&name[FOSSILIZETAG_ASCII_LEN..])?; - - let payload_entry = PayloadEntry::new_from_slice( - self.file.seek(io::SeekFrom::Current(0))?, - &name_and_header[PAYLOAD_NAME_LEN_BYTES..] - ); - - let res = self.file.seek(io::SeekFrom::Current(payload_entry.payload_info.size as i64)); - match res { - Ok(p) => { - self.write_pos = p; - if tag >= self.seen_blobs.len() && self.read_only { - /* ignore unknown tags for read-only DBs, otherwise panic */ - continue; - } - self.seen_blobs[tag].insert(hash, payload_entry); - }, - - Err(e) => { - /* truncated chunk is not fatal */ - if e.kind() != io::ErrorKind::UnexpectedEof { - return Err(Error::IO(e)); - } - }, - } - } - } else { - /* new file, write foz header */ - self.file.write_all(&FOSSILIZE_MAGIC)?; - self.file.write_all(&[0u8, 0u8, 0u8, FOSSILIZE_VERSION])?; - - self.write_pos = MAGIC_LEN_BYTES as u64; - } - - Ok(()) - } - - pub fn has_entry(&self, tag: FossilizeTag, hash: FossilizeHash) -> bool { - self.seen_blobs[tag as usize].contains_key(&hash) - } - - pub fn iter_tag(&self, tag: FossilizeTag) -> std::collections::hash_map::Keys { - self.seen_blobs[tag as usize].keys() - } - - pub fn entry_size(&self, tag: FossilizeTag, hash: FossilizeHash) -> Result { - match self.seen_blobs[tag as usize].get(&hash) { - None => Err(Error::EntryNotFound), - Some(e) => Ok(e.payload_info.full_size as usize), - } - } - - pub fn read_entry(&mut self, tag: FossilizeTag, hash: FossilizeHash, offset: u64, buf: &mut [u8], crc_opt: CRCCheck) -> Result { - if tag as usize >= self.seen_blobs.len() { - return Err(Error::InvalidTag); - } - - let entry = &self.seen_blobs[tag as usize].get(&hash); - - let entry = match entry { - None => { return Err(Error::EntryNotFound); } - Some(e) => e, - }; - - if entry.payload_info.compression != FOSSILIZE_COMPRESSION_NONE { - return Err(Error::NotImplemented); - } - - if offset >= entry.payload_info.full_size as u64 { - return Ok(0); - } - - self.file.seek(io::SeekFrom::Start(entry.offset + offset))?; - - let to_copy = std::cmp::min(entry.payload_info.full_size as usize - offset as usize, buf.len()); - - self.file.read_exact(&mut buf[0..to_copy]) - .map_err(Error::IO)?; - - if entry.payload_info.crc != 0 { - if let CRCCheck::WithCRC = crc_opt { - let mut crc_hasher = crc32fast::Hasher::new(); - crc_hasher.update(&buf[0..to_copy]); - let got_crc = crc_hasher.finalize(); - if got_crc != entry.payload_info.crc { - return Err(Error::FailedChecksum); - } - } - } - - Ok(to_copy) - } - - pub fn write_entry(&mut self, tag: FossilizeTag, hash: FossilizeHash, data: &mut dyn Read, crc_opt: CRCCheck) -> Result<(), Error> { - if self.has_entry(tag, hash) { - return Ok(()); - } - - self.file.seek(io::SeekFrom::Start(self.write_pos))?; - - /* write entry name */ - let mut name = [0u8; PAYLOAD_NAME_LEN_BYTES]; - - name[0..FOSSILIZETAG_ASCII_LEN].copy_from_slice(&tag.to_ascii_bytes()); - - name[FOSSILIZETAG_ASCII_LEN..].copy_from_slice(&hash.to_ascii_bytes()); - - self.file.write_all(&name)?; - - /* allocate payload info space */ - let payload_info_pos = self.file.seek(io::SeekFrom::Current(0))?; - - let payload_info = PayloadInfo { - size: u32::max_value(), /* will be filled later */ - compression: FOSSILIZE_COMPRESSION_NONE, - crc: 0, /* will be filled later */ - full_size: u32::max_value(), /* will be filled later */ - }; - - self.file.write_all(&payload_info.to_slice())?; - - /* write data */ - let mut payload_entry = PayloadEntry { - offset: self.file.seek(io::SeekFrom::Current(0))?, - payload_info, - }; - - const BUFFER_COPY_BYTES: usize = 8 * 1024 * 1024; /* tuneable */ - let mut buf = box_array![0u8; BUFFER_COPY_BYTES]; - let mut size: usize = 0; - let mut crc_hasher = crc32fast::Hasher::new(); - loop { - let readed = data.read(&mut *buf)?; - if readed == 0 { - break; - } - - if size + readed > u32::max_value() as usize { - /* Fossilize format only supports 4 GiB entries */ - return Err(Error::DataTooLarge); - } - - size += readed; - - self.file.write_all(&buf[0..readed])?; - - if let CRCCheck::WithCRC = crc_opt { - crc_hasher.update(&buf[0..readed]); - } - } - - self.write_pos = self.file.seek(io::SeekFrom::Current(0))?; - - /* seek back and fill in the size */ - self.file.seek(io::SeekFrom::Start(payload_info_pos))?; - - payload_entry.payload_info.size = size as u32; - payload_entry.payload_info.full_size = size as u32; - - if let CRCCheck::WithCRC = crc_opt { - payload_entry.payload_info.crc = crc_hasher.finalize(); - } - - self.file.write_all(&payload_entry.payload_info.to_slice())?; - - /* success. record entry and exit */ - self.seen_blobs[tag as usize].insert(hash, payload_entry); - - Ok(()) - } - - /* rewrites the database, discarding entries listed in 'to_discard' */ - pub fn discard_entries(&mut self, to_discard: &[(FossilizeTag, FossilizeHash)]) -> Result<(), Error> { - self.write_pos = self.file.seek(io::SeekFrom::Start(0))?; - for v in self.seen_blobs.iter_mut() { - v.clear(); - } - - let mut magic_and_version = [0_u8; MAGIC_LEN_BYTES]; - self.file.read_exact(&mut magic_and_version)?; - - let version = magic_and_version[15]; - - if magic_and_version[0..12] != FOSSILIZE_MAGIC || !(FOSSILIZE_MIN_COMPAT_VERSION..=FOSSILIZE_VERSION).contains(&version) { - return Err(Error::CorruptDatabase); - } - - self.write_pos = MAGIC_LEN_BYTES as u64; - - loop { - let mut name_and_header = [0u8; PAYLOAD_NAME_LEN_BYTES + PAYLOAD_HEADER_LEN_BYTES]; - let res = self.file.read_exact(&mut name_and_header); - - if let Err(fail) = res { - if fail.kind() == io::ErrorKind::UnexpectedEof { - break; - } - return Err(Error::IO(fail)); - } - - let name = &name_and_header[0..PAYLOAD_NAME_LEN_BYTES]; - - let tag = FossilizeTag::from_ascii_bytes(&name[0..FOSSILIZETAG_ASCII_LEN])?; - let hash = FossilizeHash::from_ascii_bytes(&name[FOSSILIZETAG_ASCII_LEN..])?; - - let payload_entry = PayloadEntry::new_from_slice( - self.file.seek(io::SeekFrom::Current(0))?, - &name_and_header[PAYLOAD_NAME_LEN_BYTES..] - ); - - if to_discard.contains(&(tag, hash)) { - /* skip over this entry */ - let res = self.file.seek(io::SeekFrom::Current(payload_entry.payload_info.size as i64)); - match res { - Ok(_) => { - }, - - Err(e) => { - /* truncated chunk is not fatal */ - if e.kind() != io::ErrorKind::UnexpectedEof { - return Err(Error::IO(e)); - } - }, - } - } else { - let mut read_pos = self.file.seek(io::SeekFrom::Current(0))?; - if self.write_pos == read_pos - name_and_header.len() as u64 { - /* if we haven't dropped any chunks, we can just skip it rather than rewrite it */ - let res = self.file.seek(io::SeekFrom::Current(payload_entry.payload_info.size as i64)); - match res { - Ok(p) => { - self.write_pos = p; - }, - - Err(e) => { - /* truncated chunk is not fatal */ - if e.kind() != io::ErrorKind::UnexpectedEof { - return Err(Error::IO(e)); - } - }, - } - } else { - /* we're offset, so we have to rewrite */ - self.file.seek(io::SeekFrom::Start(self.write_pos))?; - - { - /* write header */ - let mut name = [0u8; PAYLOAD_NAME_LEN_BYTES]; - name[0..FOSSILIZETAG_ASCII_LEN].copy_from_slice(&tag.to_ascii_bytes()); - name[FOSSILIZETAG_ASCII_LEN..].copy_from_slice(&hash.to_ascii_bytes()); - self.file.write_all(&name)?; - self.write_pos += name.len() as u64; - - let buf = payload_entry.payload_info.to_slice(); - self.file.write_all(&buf)?; - self.write_pos += buf.len() as u64; - } - - /* copy contents */ - const BUFFER_COPY_BYTES: usize = 8 * 1024 * 1024; /* tuneable */ - let mut buf = box_array![0u8; BUFFER_COPY_BYTES]; - let end_read = read_pos + payload_entry.payload_info.size as u64; - loop { - let to_read = std::cmp::min((end_read - read_pos) as usize, BUFFER_COPY_BYTES); - if to_read == 0 { - break; - } - - self.file.seek(io::SeekFrom::Start(read_pos))?; - - let readed = self.file.read(&mut (*buf)[0..to_read])?; - if readed == 0 { - break; - } - - read_pos += readed as u64; - - self.file.seek(io::SeekFrom::Start(self.write_pos))?; - self.file.write_all(&buf[0..readed])?; - self.write_pos += readed as u64; - } - - self.file.seek(io::SeekFrom::Start(read_pos))?; - } - } - } - - self.file.set_len(self.write_pos)?; - - self.prepare() - } -} diff --git a/media-converter/src/lib.rs b/media-converter/src/lib.rs deleted file mode 100644 index ce36d414..00000000 --- a/media-converter/src/lib.rs +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#[macro_use] -extern crate gstreamer as gst; -extern crate gstreamer_base as gst_base; -extern crate gstreamer_video as gst_video; -extern crate gstreamer_audio as gst_audio; -extern crate once_cell; - -use std::fs::File; -use std::io; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; - -use filetime::FileTime; -use filetime::set_file_handle_times; - -#[cfg(target_arch = "x86")] -mod murmur3_x86_128; -#[cfg(target_arch = "x86_64")] -mod murmur3_x64_128; - -mod videoconv; -mod audioconv; -mod audioconvbin; -mod fossilize; - -// copy_into_array: -// -// Copyright (c) 2020 Stu Small -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software -// and associated documentation files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING -// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -fn copy_into_array(slice: &[T]) -> A -where - A: Default + AsMut<[T]>, - T: Copy, -{ - let mut a = A::default(); - >::as_mut(&mut a).copy_from_slice(slice); - a -} - -fn touch_file

(p: P) -> io::Result<()> -where - P: AsRef + std::fmt::Debug -{ - let f = File::create(p)?; - let now = FileTime::now(); - set_file_handle_times(&f, Some(now), Some(now))?; - Ok(()) -} - -fn steam_compat_shader_path() -> Option -{ - match std::env::var("STEAM_COMPAT_SHADER_PATH") { - Err(_) => None, - Ok(c) => Some(Path::new(&c).to_path_buf()), - } -} - -/* rust has a hard time with large heap allocations. below macro works around that. - * - * by @simias from https://github.com/rust-lang/rust/issues/53827 */ -#[macro_export] -macro_rules! box_array { - ($val:expr ; $len:expr) => {{ - // Use a generic function so that the pointer cast remains type-safe - fn vec_to_boxed_array(vec: Vec) -> Box<[T; $len]> { - let boxed_slice = vec.into_boxed_slice(); - - let ptr = ::std::boxed::Box::into_raw(boxed_slice) as *mut [T; $len]; - - unsafe { Box::from_raw(ptr) } - } - - vec_to_boxed_array(vec![$val; $len]) - }}; -} - -/* you MUST use this to consistently format the hash bytes into a string */ -fn format_hash(hash: u128) -> String { - format!("{:032x}", hash) -} - -/* changing this will invalidate the cache. you MUST clear it. */ -const HASH_SEED: u32 = 0x4AA61F63; - -pub struct BufferedReader<'a> { - dat: &'a [u8], - len: usize, - ofs: usize, -} - -impl<'a> BufferedReader<'a> { - fn new(dat: &'a [u8], len: usize) -> Self { - BufferedReader { - dat, - len, - ofs: 0, - } - } -} - -impl<'a> Read for BufferedReader<'a> { - fn read(&mut self, out: &mut [u8]) -> io::Result { - let to_copy = std::cmp::min(self.len - self.ofs, out.len()); - - if to_copy == 0 { - return Ok(0); - } - - if to_copy == out.len() { - out.copy_from_slice(&self.dat[self.ofs..(self.ofs + to_copy)]); - }else{ - out[0..to_copy].copy_from_slice(&self.dat[self.ofs..(self.ofs + to_copy)]); - } - - self.ofs += to_copy; - - Ok(to_copy) - } -} - -fn discarding_disabled() -> bool { - let v = match std::env::var("MEDIACONV_DONT_DISCARD") { - Err(_) => { return false; }, - Ok(c) => c, - }; - v != "0" -} - -fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { - videoconv::register(plugin)?; - audioconvbin::register(plugin)?; - audioconv::register(plugin)?; - Ok(()) -} - -plugin_define!( - protonmediaconverter, - env!("CARGO_PKG_DESCRIPTION"), - plugin_init, - concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")), - "MIT/X11", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_REPOSITORY"), - env!("BUILD_REL_DATE") -); diff --git a/media-converter/src/murmur3_x64_128.rs b/media-converter/src/murmur3_x64_128.rs deleted file mode 100644 index c1846385..00000000 --- a/media-converter/src/murmur3_x64_128.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) 2020 Stu Small -// -// Modified to return its internal state for continuous hashing: -// Copyright (c) 2020 Andrew Eikum for CodeWeavers -// -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , at your -// option. All files in the project carrying such notice may not be copied, -// modified, or distributed except according to those terms. - -use std::io::{Read, Result}; -use std::ops::Shl; - -use crate::copy_into_array; - -#[allow(non_camel_case_types)] -pub struct murmur3_x64_128_state { - seed: u32, - h1: u64, - h2: u64, - processed: usize, -} - -impl murmur3_x64_128_state { - pub fn new(seed: u32) -> Self { - murmur3_x64_128_state { - seed, - h1: seed as u64, - h2: seed as u64, - processed: 0, - } - } - - #[allow(dead_code)] - pub fn reset(&mut self) { - self.h1 = self.seed as u64; - self.h2 = self.seed as u64; - self.processed = 0; - } -} - -/// Use the x64 variant of the 128 bit murmur3 to hash some [Read] implementation. -/// -/// # Example -/// ``` -/// use std::io::Cursor; -/// use murmur3::murmur3_x64_128; -/// let hash_result = murmur3_x64_128(&mut Cursor::new("hello world"), 0); -/// ``` -pub fn murmur3_x64_128(source: &mut T, seed: u32) -> Result { - let mut state = murmur3_x64_128_state::new(seed); - murmur3_x64_128_full(source, &mut state) -} - -pub fn murmur3_x64_128_full(source: &mut T, state: &mut murmur3_x64_128_state) -> Result { - const C1: u64 = 0x87c3_7b91_1142_53d5; - const C2: u64 = 0x4cf5_ad43_2745_937f; - const C3: u64 = 0x52dc_e729; - const C4: u64 = 0x3849_5ab5; - const R1: u32 = 27; - const R2: u32 = 31; - const R3: u32 = 33; - const M: u64 = 5; - let mut h1: u64 = state.h1; - let mut h2: u64 = state.h2; - let mut buf = [0; 16]; - let mut processed: usize = state.processed; - loop { - let read = source.read(&mut buf[..])?; - processed += read; - if read == 16 { - let k1 = u64::from_le_bytes(copy_into_array(&buf[0..8])); - let k2 = u64::from_le_bytes(copy_into_array(&buf[8..])); - h1 ^= k1.wrapping_mul(C1).rotate_left(R2).wrapping_mul(C2); - h1 = h1 - .rotate_left(R1) - .wrapping_add(h2) - .wrapping_mul(M) - .wrapping_add(C3); - h2 ^= k2.wrapping_mul(C2).rotate_left(R3).wrapping_mul(C1); - h2 = h2 - .rotate_left(R2) - .wrapping_add(h1) - .wrapping_mul(M) - .wrapping_add(C4); - } else if read == 0 { - state.h1 = h1; - state.h2 = h2; - state.processed = processed; - h1 ^= processed as u64; - h2 ^= processed as u64; - h1 = h1.wrapping_add(h2); - h2 = h2.wrapping_add(h1); - h1 = fmix64(h1); - h2 = fmix64(h2); - h1 = h1.wrapping_add(h2); - h2 = h2.wrapping_add(h1); - let x = ((h2 as u128) << 64) | (h1 as u128); - return Ok(x); - } else { - let mut k1 = 0; - let mut k2 = 0; - if read >= 15 { - k2 ^= (buf[14] as u64).shl(48); - } - if read >= 14 { - k2 ^= (buf[13] as u64).shl(40); - } - if read >= 13 { - k2 ^= (buf[12] as u64).shl(32); - } - if read >= 12 { - k2 ^= (buf[11] as u64).shl(24); - } - if read >= 11 { - k2 ^= (buf[10] as u64).shl(16); - } - if read >= 10 { - k2 ^= (buf[9] as u64).shl(8); - } - if read >= 9 { - k2 ^= buf[8] as u64; - k2 = k2.wrapping_mul(C2).rotate_left(33).wrapping_mul(C1); - h2 ^= k2; - } - if read >= 8 { - k1 ^= (buf[7] as u64).shl(56); - } - if read >= 7 { - k1 ^= (buf[6] as u64).shl(48); - } - if read >= 6 { - k1 ^= (buf[5] as u64).shl(40); - } - if read >= 5 { - k1 ^= (buf[4] as u64).shl(32); - } - if read >= 4 { - k1 ^= (buf[3] as u64).shl(24); - } - if read >= 3 { - k1 ^= (buf[2] as u64).shl(16); - } - if read >= 2 { - k1 ^= (buf[1] as u64).shl(8); - } - if read >= 1 { - k1 ^= buf[0] as u64; - } - k1 = k1.wrapping_mul(C1); - k1 = k1.rotate_left(31); - k1 = k1.wrapping_mul(C2); - h1 ^= k1; - } - } -} - -fn fmix64(k: u64) -> u64 { - const C1: u64 = 0xff51_afd7_ed55_8ccd; - const C2: u64 = 0xc4ce_b9fe_1a85_ec53; - const R: u32 = 33; - let mut tmp = k; - tmp ^= tmp >> R; - tmp = tmp.wrapping_mul(C1); - tmp ^= tmp >> R; - tmp = tmp.wrapping_mul(C2); - tmp ^= tmp >> R; - tmp -} - -#[cfg(test)] -mod tests { - use super::*; - use std::cmp::min; - use std::io; - use std::io::Read; - - const TEST_SEED: u32 = 0x00000000; - - const CONST_DATA: [u8; 64] = - [ 0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, - 10u8, 11u8, 12u8, 13u8, 14u8, 15u8, 16u8, 17u8, - 20u8, 21u8, 22u8, 23u8, 24u8, 25u8, 26u8, 27u8, - 30u8, 31u8, 32u8, 33u8, 34u8, 35u8, 36u8, 37u8, - 40u8, 41u8, 42u8, 43u8, 44u8, 45u8, 46u8, 47u8, - 50u8, 51u8, 52u8, 53u8, 54u8, 55u8, 56u8, 57u8, - 60u8, 61u8, 62u8, 63u8, 64u8, 65u8, 66u8, 67u8, - 70u8, 71u8, 72u8, 73u8, 74u8, 75u8, 76u8, 77u8 ]; - - struct TestReader<'a> { - data: &'a [u8], - ofs: usize, - } - - impl<'a> TestReader<'a> { - fn new(data: &'a [u8]) -> Self { - TestReader { - data, - ofs: 0, - } - } - } - - impl<'a> Read for TestReader<'a> { - fn read(&mut self, out: &mut [u8]) -> io::Result { - let to_copy = min(out.len(), self.data.len() - self.ofs); - - if to_copy > 0 { - out[0..to_copy].copy_from_slice(&self.data[self.ofs..(self.ofs + to_copy)]); - self.ofs += to_copy; - } - - Ok(to_copy) - } - } - - #[test] - fn test_full_hash() { - /* test with the full buffer */ - let full_hash = murmur3_x64_128(&mut TestReader::new(&CONST_DATA), TEST_SEED).unwrap(); - assert_eq!(full_hash, 0xeb91a9599de8337d969b1e101c4ee3bc); - - /* accumulate hash across 16-byte chunks (short reads change hash due to 0-padding) */ - let mut hash_state = murmur3_x64_128_state::new(TEST_SEED); - murmur3_x64_128_full(&mut TestReader::new(&CONST_DATA[0..16]), &mut hash_state).unwrap(); - murmur3_x64_128_full(&mut TestReader::new(&CONST_DATA[16..32]), &mut hash_state).unwrap(); - murmur3_x64_128_full(&mut TestReader::new(&CONST_DATA[32..48]), &mut hash_state).unwrap(); - let hash = murmur3_x64_128_full(&mut TestReader::new(&CONST_DATA[48..64]), &mut hash_state).unwrap(); - assert_eq!(hash, full_hash); - } -} diff --git a/media-converter/src/murmur3_x86_128.rs b/media-converter/src/murmur3_x86_128.rs deleted file mode 100644 index d20ae158..00000000 --- a/media-converter/src/murmur3_x86_128.rs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) 2020 Stu Small -// -// Modified to return its internal state for continuous hashing: -// Copyright (c) 2020 Andrew Eikum for CodeWeavers -// -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , at your -// option. All files in the project carrying such notice may not be copied, -// modified, or distributed except according to those terms. - -use std::io::{Read, Result}; -use std::ops::Shl; - -use crate::copy_into_array; - -#[allow(non_camel_case_types)] -pub struct murmur3_x86_128_state { - seed: u32, - h1: u32, - h2: u32, - h3: u32, - h4: u32, - processed: usize, -} - -impl murmur3_x86_128_state { - pub fn new(seed: u32) -> Self { - murmur3_x86_128_state { - seed, - h1: seed, - h2: seed, - h3: seed, - h4: seed, - processed: 0, - } - } - - #[allow(dead_code)] - pub fn reset(&mut self) { - self.h1 = self.seed; - self.h2 = self.seed; - self.h3 = self.seed; - self.h4 = self.seed; - self.processed = 0; - } -} - -/// Use the x86 variant of the 128 bit murmur3 to hash some [Read] implementation. -/// -/// # Example -/// ``` -/// use std::io::Cursor; -/// use murmur3::murmur3_x86_128; -/// let hash_result = murmur3_x86_128(&mut Cursor::new("hello world"), 0); -/// ``` -pub fn murmur3_x86_128(source: &mut T, seed: u32) -> Result { - let mut state = murmur3_x86_128_state::new(seed); - murmur3_x86_128_full(source, &mut state) -} - -pub fn murmur3_x86_128_full(source: &mut T, state: &mut murmur3_x86_128_state) -> Result { - const C1: u32 = 0x239b_961b; - const C2: u32 = 0xab0e_9789; - const C3: u32 = 0x38b3_4ae5; - const C4: u32 = 0xa1e3_8b93; - const C5: u32 = 0x561c_cd1b; - const C6: u32 = 0x0bca_a747; - const C7: u32 = 0x96cd_1c35; - const C8: u32 = 0x32ac_3b17; - const M: u32 = 5; - - let mut h1: u32 = state.h1; - let mut h2: u32 = state.h2; - let mut h3: u32 = state.h3; - let mut h4: u32 = state.h4; - - let mut buf = [0; 16]; - let mut processed: usize = state.processed; - loop { - let read = source.read(&mut buf[..])?; - processed += read; - if read == 16 { - let k1 = u32::from_le_bytes(copy_into_array(&buf[0..4])); - let k2 = u32::from_le_bytes(copy_into_array(&buf[4..8])); - let k3 = u32::from_le_bytes(copy_into_array(&buf[8..12])); - let k4 = u32::from_le_bytes(copy_into_array(&buf[12..16])); - h1 ^= k1.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2); - h1 = h1 - .rotate_left(19) - .wrapping_add(h2) - .wrapping_mul(M) - .wrapping_add(C5); - h2 ^= k2.wrapping_mul(C2).rotate_left(16).wrapping_mul(C3); - h2 = h2 - .rotate_left(17) - .wrapping_add(h3) - .wrapping_mul(M) - .wrapping_add(C6); - h3 ^= k3.wrapping_mul(C3).rotate_left(17).wrapping_mul(C4); - h3 = h3 - .rotate_left(15) - .wrapping_add(h4) - .wrapping_mul(M) - .wrapping_add(C7); - h4 ^= k4.wrapping_mul(C4).rotate_left(18).wrapping_mul(C1); - h4 = h4 - .rotate_left(13) - .wrapping_add(h1) - .wrapping_mul(M) - .wrapping_add(C8); - } else if read == 0 { - state.h1 = h1; - state.h2 = h2; - state.h3 = h3; - state.h4 = h4; - state.processed = processed; - h1 ^= processed as u32; - h2 ^= processed as u32; - h3 ^= processed as u32; - h4 ^= processed as u32; - h1 = h1.wrapping_add(h2); - h1 = h1.wrapping_add(h3); - h1 = h1.wrapping_add(h4); - h2 = h2.wrapping_add(h1); - h3 = h3.wrapping_add(h1); - h4 = h4.wrapping_add(h1); - h1 = fmix32(h1); - h2 = fmix32(h2); - h3 = fmix32(h3); - h4 = fmix32(h4); - h1 = h1.wrapping_add(h2); - h1 = h1.wrapping_add(h3); - h1 = h1.wrapping_add(h4); - h2 = h2.wrapping_add(h1); - h3 = h3.wrapping_add(h1); - h4 = h4.wrapping_add(h1); - let x = ((h4 as u128) << 96) | ((h3 as u128) << 64) | ((h2 as u128) << 32) | h1 as u128; - return Ok(x); - } else { - let mut k1 = 0; - let mut k2 = 0; - let mut k3 = 0; - let mut k4 = 0; - if read >= 15 { - k4 ^= (buf[14] as u32).shl(16); - } - if read >= 14 { - k4 ^= (buf[13] as u32).shl(8); - } - if read >= 13 { - k4 ^= buf[12] as u32; - k4 = k4.wrapping_mul(C4).rotate_left(18).wrapping_mul(C1); - h4 ^= k4; - } - if read >= 12 { - k3 ^= (buf[11] as u32).shl(24); - } - if read >= 11 { - k3 ^= (buf[10] as u32).shl(16); - } - if read >= 10 { - k3 ^= (buf[9] as u32).shl(8); - } - if read >= 9 { - k3 ^= buf[8] as u32; - k3 = k3.wrapping_mul(C3).rotate_left(17).wrapping_mul(C4); - h3 ^= k3; - } - if read >= 8 { - k2 ^= (buf[7] as u32).shl(24); - } - if read >= 7 { - k2 ^= (buf[6] as u32).shl(16); - } - if read >= 6 { - k2 ^= (buf[5] as u32).shl(8); - } - if read >= 5 { - k2 ^= buf[4] as u32; - k2 = k2.wrapping_mul(C2).rotate_left(16).wrapping_mul(C3); - h2 ^= k2; - } - if read >= 4 { - k1 ^= (buf[3] as u32).shl(24); - } - if read >= 3 { - k1 ^= (buf[2] as u32).shl(16); - } - if read >= 2 { - k1 ^= (buf[1] as u32).shl(8); - } - if read >= 1 { - k1 ^= buf[0] as u32; - } - k1 = k1.wrapping_mul(C1); - k1 = k1.rotate_left(15); - k1 = k1.wrapping_mul(C2); - h1 ^= k1; - } - } -} - -fn fmix32(k: u32) -> u32 { - const C1: u32 = 0x85eb_ca6b; - const C2: u32 = 0xc2b2_ae35; - const R1: u32 = 16; - const R2: u32 = 13; - let mut tmp = k; - tmp ^= tmp >> R1; - tmp = tmp.wrapping_mul(C1); - tmp ^= tmp >> R2; - tmp = tmp.wrapping_mul(C2); - tmp ^= tmp >> R1; - tmp -} diff --git a/media-converter/src/videoconv/imp.rs b/media-converter/src/videoconv/imp.rs deleted file mode 100644 index 2dc0237e..00000000 --- a/media-converter/src/videoconv/imp.rs +++ /dev/null @@ -1,847 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -use crate::format_hash; -use crate::HASH_SEED; -use crate::box_array; -use crate::copy_into_array; -use crate::BufferedReader; -use crate::discarding_disabled; -use crate::steam_compat_shader_path; -use crate::touch_file; - -use gst::glib; -use gst::prelude::*; -use gst::subclass::prelude::*; -use gst::EventView; -use gst::QueryViewMut; - -use std::sync::Mutex; -use std::fs; -use std::io; -use std::io::Read; -use std::fs::OpenOptions; - -#[cfg(target_arch = "x86")] -use crate::murmur3_x86_128::murmur3_x86_128 as murmur3_128; -#[cfg(target_arch = "x86_64")] -use crate::murmur3_x64_128::murmur3_x64_128 as murmur3_128; - -use crate::fossilize; - -use once_cell::sync::Lazy; - -/* Algorithm - * --------- - * - * Nicely, both Quartz and Media Foundation allow us random access to the entire data stream. So we - * can easily hash the entire incoming stream and substitute it with our Ogg Theora video. If there - * is a cache miss, then we dump the entire incoming stream. In case of a cache hit, we dump - * nothing. - * - * Incoming video data is stored in the video.foz Fossilize database. - * - * Transcoded video data is stored in the transcoded_video.foz Fossilize database. - * - * - * Hashing algorithm - * ----------------- - * - * We use murmur3 hash with the seed given below. We use the x32 variant for 32-bit programs, and - * the x64 variant for 64-bit programs. - * - * For speed when hashing, we specify a stride which will skip over chunks of the input. However, - * we will always hash the first "stride" number of bytes, to try to avoid collisions on smaller - * files with size between chunk and stride. - * - * For example, the 'H's below are hashed, the 'x's are skipped: - * - * let chunk = 4; - * let stride = chunk * 3; - * H = hashed, x = skipped - * [HHHH HHHH HHHH HHHH xxxx xxxx HHHH xxxx xxxx HHHH xxxx] < data stream - * ^^^^ ^^^^ ^^^^ stride prefix, hashed - * ^^^^ chunk - * ^^^^ ^^^^ ^^^^ stride - * ^^^^ chunk - * ^^^^ ^^^^ ^^^^ stride - * ^^^^ chunk - * ^^^^ ^^^^ stride - */ - -/* changing any of these will invalidate the cache. you MUST clear it. */ -const HASH_CHUNK_SIZE: usize = 8 * 1024 /* to kiB */ * 1024 /* to MiB */; -const HASH_STRIDE: usize = 6 * HASH_CHUNK_SIZE; - -static CAT: Lazy = Lazy::new(|| { - gst::DebugCategory::new( - "protonvideoconverter", - gst::DebugColorFlags::empty(), - Some("Proton video converter")) -}); - -const VIDEOCONV_FOZ_TAG_VIDEODATA: u32 = 0; -const VIDEOCONV_FOZ_TAG_OGVDATA: u32 = 1; -const VIDEOCONV_FOZ_TAG_STREAM: u32 = 2; -const VIDEOCONV_FOZ_TAG_MKVDATA: u32 = 3; -const VIDEOCONV_FOZ_NUM_TAGS: usize = 4; - -struct VideoConverterDumpFozdb { - fozdb: Option, - already_cleaned: bool, -} - -impl VideoConverterDumpFozdb { - fn new() -> Self { - Self { - fozdb: None, - already_cleaned: false, - } - } - - fn open(&mut self, create: bool) -> &mut Self { - if self.fozdb.is_none() { - let dump_file_path = match std::env::var("MEDIACONV_VIDEO_DUMP_FILE") { - Err(_) => { return self; }, - Ok(c) => c, - }; - - let dump_file_path = std::path::Path::new(&dump_file_path); - - if fs::create_dir_all(&dump_file_path.parent().unwrap()).is_err() { - return self; - } - - match fossilize::StreamArchive::new(&dump_file_path, OpenOptions::new().write(true).read(true).create(create), false /* read-only? */, VIDEOCONV_FOZ_NUM_TAGS) { - Ok(newdb) => { - self.fozdb = Some(newdb); - }, - Err(_) => { - return self; - }, - } - } - self - } - - fn close(&mut self) { - self.fozdb = None - } - - fn discard_transcoded(&mut self) { - if self.already_cleaned { - return; - } - if discarding_disabled() { - self.already_cleaned = true; - return; - } - if let Some(fozdb) = &mut self.open(false).fozdb { - if let Ok(read_fozdb_path) = std::env::var("MEDIACONV_VIDEO_TRANSCODED_FILE") { - if let Ok(read_fozdb) = fossilize::StreamArchive::new(&read_fozdb_path, OpenOptions::new().read(true), true /* read-only? */, VIDEOCONV_FOZ_NUM_TAGS) { - let mut chunks = Vec::<(u32, u128)>::new(); - - for stream_id in fozdb.iter_tag(VIDEOCONV_FOZ_TAG_STREAM).cloned().collect::>() { - if read_fozdb.has_entry(VIDEOCONV_FOZ_TAG_MKVDATA, stream_id) { - if let Ok(chunks_size) = fozdb.entry_size(VIDEOCONV_FOZ_TAG_STREAM, stream_id) { - let mut buf = vec![0u8; chunks_size].into_boxed_slice(); - if fozdb.read_entry(VIDEOCONV_FOZ_TAG_STREAM, stream_id, 0, &mut buf, fossilize::CRCCheck::WithCRC).is_ok() { - for i in 0..(chunks_size / 16) { - let offs = i * 16; - let chunk_id = u128::from_le_bytes(copy_into_array(&buf[offs..offs + 16])); - chunks.push((VIDEOCONV_FOZ_TAG_VIDEODATA, chunk_id)); - } - } - } - chunks.push((VIDEOCONV_FOZ_TAG_STREAM, stream_id)); - } - } - - if fozdb.discard_entries(&chunks).is_err() { - self.close(); - } - } - } - } - self.already_cleaned = true; - } -} - -static DUMP_FOZDB: Lazy> = Lazy::new(|| { - Mutex::new(VideoConverterDumpFozdb::new()) -}); - -struct PadReader<'a> { - pad: &'a gst::Pad, - offs: usize, - chunk: Box<[u8; HASH_CHUNK_SIZE]>, - chunk_offs: usize, - chunk_end: usize, - stride: usize, /* set to usize::max_value() to skip no bytes */ -} - -impl<'a> PadReader<'a> { - - fn new_with_stride(pad: &'a gst::Pad, stride: usize) -> Self { - PadReader { - pad, - offs: 0, - chunk: box_array![0u8; HASH_CHUNK_SIZE], - chunk_offs: 0, - chunk_end: 0, - stride - } - } - - fn new(pad: &'a gst::Pad) -> Self { - Self::new_with_stride(pad, usize::max_value()) - } -} - -impl<'a> Read for PadReader<'a> { - fn read(&mut self, out: &mut [u8]) -> io::Result { - if self.chunk_offs >= self.chunk_end { - self.chunk_offs = 0; - self.chunk_end = 0; - - let buf = self.pad.pull_range(self.offs as u64, HASH_CHUNK_SIZE as u32); - - match buf { - Err(err) => { - /* on Eos, keep going; we'll return later */ - if err != gst::FlowError::Eos { - return Err(io::Error::new(io::ErrorKind::Other, "upstream pull_range failed" /* TODO can we print our gst err here? */)); - } - }, - Ok(buf) => { - let to_copy; - - if self.offs + buf.size() < self.stride { - to_copy = buf.size(); - self.offs += to_copy; - }else if self.offs < self.stride { - to_copy = self.stride - self.offs; - self.offs = self.stride; - }else{ - to_copy = buf.size(); - self.offs += self.stride; - }; - - if out.len() >= to_copy { - /* copy directly into out buffer and return */ - return Ok( - match buf.copy_to_slice(0, &mut out[0..to_copy]) { - Err(c) => c, - Ok(_) => to_copy, - }); - } else { - self.chunk_end = match buf.copy_to_slice(0, &mut (*self.chunk)[0..to_copy]) { - Err(c) => c, - Ok(_) => to_copy, - }; - } - } - } - } - - if self.chunk_offs >= self.chunk_end { - return Ok(0); - } - - let to_copy = std::cmp::min(self.chunk_end - self.chunk_offs, out.len()); - - if to_copy == 0 { - return Ok(0); - } - - out[..to_copy].copy_from_slice(&self.chunk[self.chunk_offs..(self.chunk_offs + to_copy)]); - - self.chunk_offs += to_copy; - - Ok(to_copy) - } -} - -struct VideoConvState { - transcode_hash: Option, - - read_fozdb: Option, - - upstream_duration: Option, - our_duration: Option, - - transcoded_tag: u32, - - need_stream_start: bool, -} - -impl VideoConvState { - fn new() -> VideoConvState { - - let read_fozdb_path = std::env::var("MEDIACONV_VIDEO_TRANSCODED_FILE"); - - if read_fozdb_path.is_err() { - gst::error!(CAT, "MEDIACONV_VIDEO_TRANSCODED_FILE is not set!") - } - - let read_fozdb = match read_fozdb_path { - Ok(path) => match fossilize::StreamArchive::new(&path, - OpenOptions::new().read(true), - true /* read-only? */, - VIDEOCONV_FOZ_NUM_TAGS) - { - Ok(s) => Some(s), - Err(_) => None - }, - Err(_) => None - }; - - - VideoConvState { - transcode_hash: None, - - read_fozdb, - - upstream_duration: None, - our_duration: None, - - transcoded_tag: VIDEOCONV_FOZ_TAG_MKVDATA, - - need_stream_start: true, - } - } - - /* true if the file is transcoded; false if not */ - fn begin_transcode(&mut self, hash: u128) -> bool { - if let Some(read_fozdb) = &mut self.read_fozdb { - if let Ok(transcoded_size) = read_fozdb.entry_size(VIDEOCONV_FOZ_TAG_MKVDATA, hash) { - gst::log!(CAT, "Found an MKV video for hash {}", format_hash(hash)); - self.transcode_hash = Some(hash); - self.our_duration = Some(transcoded_size as u64); - self.transcoded_tag = VIDEOCONV_FOZ_TAG_MKVDATA; - return true; - } - if let Ok(transcoded_size) = read_fozdb.entry_size(VIDEOCONV_FOZ_TAG_OGVDATA, hash) { - gst::log!(CAT, "Found an OGV video for hash {}", format_hash(hash)); - self.transcode_hash = Some(hash); - self.our_duration = Some(transcoded_size as u64); - self.transcoded_tag = VIDEOCONV_FOZ_TAG_OGVDATA; - return true; - } - } - - gst::log!(CAT, "No transcoded video for {}. Substituting a blank video.", format_hash(hash)); - - self.transcode_hash = None; - self.our_duration = Some(include_bytes!("../../blank.mkv").len() as u64); - - match steam_compat_shader_path() { - None => gst::log!(CAT, "env STEAM_COMPAT_SHADER_PATH not set"), - Some(mut path) => { - path.push("placeholder-video-used"); - if let Err(e) = touch_file(path) { gst::log!(CAT, "Failed to touch placeholder-video-used file: {:?}", e) } - }, - }; - - false - } - - fn fill_buffer(&mut self, offs: usize, out: &mut [u8]) -> Result { - match self.transcode_hash { - Some(hash) => { - let read_fozdb = self.read_fozdb.as_mut().unwrap(); - read_fozdb.read_entry(self.transcoded_tag, hash, offs as u64, out, fossilize::CRCCheck::WithoutCRC) - .map_err(|e| loggable_error!(CAT, "Error reading ogvdata: {:?}", e)) - }, - - None => { - let blank = include_bytes!("../../blank.mkv"); - - let to_copy = std::cmp::min(blank.len() - offs, out.len()); - - out[..to_copy].copy_from_slice(&blank[offs..(offs + to_copy)]); - - Ok(to_copy) - }, - } - } -} - -pub struct VideoConv { - state: Mutex>, - sinkpad: gst::Pad, - srcpad: gst::Pad, -} - -#[glib::object_subclass] -impl ObjectSubclass for VideoConv { - const NAME: &'static str = "ProtonVideoConverter"; - type Type = super::VideoConv; - type ParentType = gst::Element; - - fn with_class(klass: &Self::Class) -> Self { - let templ = klass.pad_template("sink").unwrap(); - let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink")) - .event_function(|pad, parent, event| { - VideoConv::catch_panic_pad_function( - parent, - || false, - |videoconv| videoconv.sink_event(pad, event) - ) - }).build(); - - let templ = klass.pad_template("src").unwrap(); - let srcpad = gst::Pad::builder_with_template(&templ, Some("src")) - .getrange_function(|pad, parent, offset, in_buf, size| { - VideoConv::catch_panic_pad_function( - parent, - || Err(gst::FlowError::Error), - |videoconv| videoconv.range(pad, offset, in_buf, size) - ) - }) - .query_function(|pad, parent, query| { - VideoConv::catch_panic_pad_function( - parent, - || false, - |videoconv| videoconv.src_query(pad, query) - ) - }) - .activatemode_function(|pad, parent, mode, active| { - VideoConv::catch_panic_pad_function( - parent, - || Err(loggable_error!(CAT, "Panic activating srcpad with mode")), - |videoconv| videoconv.src_activatemode(pad, mode, active) - ) - }).build(); - - VideoConv { - state: Mutex::new(None), - sinkpad, - srcpad, - } - } -} - -impl ObjectImpl for VideoConv { - fn constructed(&self) { - self.parent_constructed(); - - let obj = self.obj(); - - obj.add_pad(&self.sinkpad).unwrap(); - obj.add_pad(&self.srcpad).unwrap(); - } -} - -impl GstObjectImpl for VideoConv { } - -impl ElementImpl for VideoConv { - fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { - static ELEMENT_METADATA: Lazy = Lazy::new(|| { - gst::subclass::ElementMetadata::new( - "Proton video converter", - "Codec/Demuxer", - "Converts video for Proton", - "Andrew Eikum " - ) - }); - - Some(&*ELEMENT_METADATA) - } - - fn pad_templates() -> &'static [gst::PadTemplate] { - static PAD_TEMPLATES: Lazy> = Lazy::new(|| { - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - caps.append(gst::Caps::builder("video/x-ms-asf").build()); - caps.append(gst::Caps::builder("video/x-msvideo").build()); - caps.append(gst::Caps::builder("video/mpeg").build()); - caps.append(gst::Caps::builder("video/quicktime").build()); - } - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps).unwrap(); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - caps.append(gst::Caps::builder("video/x-matroska").build()); - caps.append(gst::Caps::builder("application/ogg").build()); - } - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps).unwrap(); - - vec![src_pad_template, sink_pad_template] - }); - - PAD_TEMPLATES.as_ref() - } - - fn change_state( - &self, - transition: gst::StateChange - ) -> Result { - - gst::log!(CAT, imp: self, "State transition: {:?}", transition); - - match transition { - gst::StateChange::NullToReady => { - /* do runtime setup */ - - let new_state = VideoConvState::new(); - - let mut state = self.state.lock().unwrap(); - assert!((*state).is_none()); - *state = Some(new_state); - }, - - gst::StateChange::ReadyToNull => { - /* do runtime teardown */ - - let _ = self.state.lock().unwrap().take(); // dispose of state - }, - - _ => (), - }; - - self.parent_change_state(transition) - - /* XXX on ReadyToNull, sodium drops state _again_ here... why? */ - } -} - -struct StreamSerializer<'a> { - stream: &'a [u128], - cur_idx: usize, -} - -impl<'a> StreamSerializer<'a> { - fn new(stream: &'a [u128]) -> Self { - StreamSerializer { - stream, - cur_idx: 0, - } - } -} - -impl <'a> Read for StreamSerializer<'a> { - fn read(&mut self, out: &mut [u8]) -> io::Result { - if self.cur_idx >= self.stream.len() { - return Ok(0) - } - - out[0..std::mem::size_of::()].copy_from_slice(&self.stream[self.cur_idx].to_le_bytes()); - self.cur_idx += 1; - - Ok(std::mem::size_of::()) - } -} - -impl VideoConv { - fn get_upstream_range(&self, offset: u64, requested_size: u32) -> Result<(u64, u32), gst::FlowError> { - let mut state = self.state.lock().unwrap(); - let state = match &mut *state { - Some(s) => s, - None => { return Err(gst::FlowError::Error); } - }; - - if state.upstream_duration.is_none() { - self.query_upstream_duration(state); - } - - let ups_offset = self.duration_ours_to_upstream(state, offset).unwrap(); - let ups_requested_size = self.duration_ours_to_upstream(state, requested_size as u64).unwrap() as u32; - - Ok((ups_offset, ups_requested_size)) - } - - fn range( - &self, - _pad: &gst::Pad, - offset: u64, - in_buf: Option<&mut gst::BufferRef>, - requested_size: u32, - ) -> Result { - let (ups_offset, ups_requested_size) = self.get_upstream_range(offset, requested_size)?; - - let mut state = self.state.lock().unwrap(); - let state = match &mut *state { - Some(s) => s, - None => { return Err(gst::FlowError::Error); } - }; - - /* read and ignore upstream bytes */ - self.sinkpad.pull_range(ups_offset, ups_requested_size)?; - - match in_buf { - Some(buf) => { - let readed; - - { - let mut map = buf.map_writable().unwrap(); - readed = state.fill_buffer(offset as usize, map.as_mut()) - .map_err(|e| { e.log(); gst::FlowError::Error })?; - } - - if readed > 0 || buf.size() == 0 { - buf.set_size(readed); - return Ok(gst::PadGetRangeSuccess::FilledBuffer); - } - - Err(gst::FlowError::Eos) - }, - - None => { - /* XXX: can we use a buffer cache here? */ - let b = gst::Buffer::with_size(requested_size as usize) - .map_err(|_| gst::FlowError::Error)?; - - let mut map = b.into_mapped_buffer_writable().unwrap(); - - let readed = state.fill_buffer(offset as usize, map.as_mut()) - .map_err(|e| { e.log(); gst::FlowError::Error })?; - - let mut b = map.into_buffer(); - - if readed > 0 || b.size() == 0 { - b.get_mut().unwrap().set_size(readed); - return Ok(gst::PadGetRangeSuccess::NewBuffer(b)); - } - - Err(gst::FlowError::Eos) - } - } - } - - fn sink_event( - &self, - pad: &gst::Pad, - event: gst::Event - ) -> bool { - gst::log!(CAT, obj:pad, "Got an event {:?}", event); - match event.view() { - EventView::Caps(_) => { - - /* push_event, below, can also grab state and cause a deadlock, so make sure it's - * released before calling */ - let caps = { - let mut state = self.state.lock().unwrap(); - - let state = match &mut *state { - Some(s) => s, - None => { gst::error!(CAT, "VideoConv not yet in READY state?"); return false; }, - }; - - if self.sinkpad.activate_mode(gst::PadMode::Pull, true).is_err() { - gst::error!(CAT, "Failed to activate sinkpad in pull mode"); - return false; - } - - self.init_transcode(state); - - match state.transcoded_tag { - VIDEOCONV_FOZ_TAG_MKVDATA => gst::Caps::builder("video/x-matroska").build(), - VIDEOCONV_FOZ_TAG_OGVDATA => gst::Caps::builder("application/ogg").build(), - _ => { return false; }, - } - }; - - self.srcpad.push_event(gst::event::Caps::new(&caps)) - } - _ => gst::Pad::event_default(pad, Some(&*self.obj()), event) - } - } - - fn query_upstream_duration(&self, state: &mut VideoConvState) { - let mut query = gst::query::Duration::new(gst::Format::Bytes); - - if self.sinkpad.peer_query(&mut query) { - state.upstream_duration = match query.result() { - gst::GenericFormattedValue::Bytes(Some(size)) => Some(*size), - _ => None, - } - }else{ - gst::warning!(CAT, "upstream duration query failure"); - } - } - - fn duration_ours_to_upstream(&self, state: &VideoConvState, pos: u64) -> Option { - if let Some(our) = state.our_duration { - if let Some(upstream) = state.upstream_duration { - return Some(pos * upstream / our); - } - } - None - } - - fn src_query( - &self, - pad: &gst::Pad, - query: &mut gst::QueryRef) -> bool - { - gst::log!(CAT, obj: pad, "got query: {:?}", query); - match query.view_mut() { - QueryViewMut::Scheduling(q) => { - let mut peer_query = gst::query::Scheduling::new(); - let res = self.sinkpad.peer_query(&mut peer_query); - if ! res { - return res; - } - - let (flags, min, max, align) = peer_query.result(); - - q.set(flags, min, max, align); - q.add_scheduling_modes(&[gst::PadMode::Pull]); - true - }, - QueryViewMut::Duration(ref mut q) => { - if q.format() != gst::Format::Bytes { return false }; - - let mut state = self.state.lock().unwrap(); - - let state = match &mut *state { - Some(s) => s, - None => { return false; } - }; - - if state.upstream_duration.is_none() { - self.query_upstream_duration(state); - } - - if let Some(sz) = state.our_duration { - q.set(gst::format::Bytes::from_u64(sz)); - return true - } - - false - } - _ => gst::Pad::query_default(pad, Some(&*self.obj()), query) - } - } - - fn hash_upstream_data(&self) -> io::Result { - murmur3_128(&mut PadReader::new_with_stride(&self.sinkpad, HASH_STRIDE), HASH_SEED) - } - - fn dump_upstream_data(&self, hash: u128) -> io::Result<()> { - - let db = &mut (*DUMP_FOZDB).lock().unwrap(); - let mut db = &mut db.open(true).fozdb; - let db = match &mut db { - Some(d) => d, - None => { gst::error!(CAT, "Unable to open fozdb!"); return Err(io::Error::new(io::ErrorKind::Other, "unable to open fozdb")); }, - }; - - let mut chunks = Vec::::new(); - - let mut reader = PadReader::new(&self.sinkpad); - - let mut buf = box_array![0u8; HASH_CHUNK_SIZE]; - loop { - let readed = reader.read(&mut *buf)?; - if readed == 0 { - break; - } - - let chunk_hash = murmur3_128(&mut BufferedReader::new(&*buf, readed), HASH_SEED)?; - chunks.push(chunk_hash); - - db.write_entry(VIDEOCONV_FOZ_TAG_VIDEODATA, chunk_hash, &mut BufferedReader::new(&*buf, readed), fossilize::CRCCheck::WithCRC) - .map_err(|e| { gst::warning!(CAT, "Error writing video data to fozdb: {:?}", e); io::Error::new(io::ErrorKind::Other, "error writing video data to fozdb") } )?; - } - - db.write_entry(VIDEOCONV_FOZ_TAG_STREAM, hash, &mut StreamSerializer::new(&chunks), fossilize::CRCCheck::WithCRC) - .map_err(|e| { gst::warning!(CAT, "Error writing stream data to fozdb: {:?}", e); io::Error::new(io::ErrorKind::Other, "error writing stream data to fozdb") } )?; - - Ok(()) - } - - fn init_transcode(&self, state: &mut VideoConvState) { - - if state.transcode_hash.is_none() { - (*DUMP_FOZDB).lock().unwrap().discard_transcoded(); - - let hash = self.hash_upstream_data(); - - if let Ok(hash) = hash { - if !state.begin_transcode(hash) { - match self.dump_upstream_data(hash) { - Ok(_) => { }, - Err(e) => { gst::error!(CAT, "{}", e.to_string())} - } - } - } - } - } - - fn src_activatemode( - &self, - _pad: &gst::Pad, - mode: gst::PadMode, - active: bool - ) -> Result<(), gst::LoggableError> { - self.sinkpad - .activate_mode(mode, active)?; - - if mode == gst::PadMode::Pull { - let need_stream_start; - let hash; - - /* push_event, below, can also grab state and cause a deadlock, so make sure it's - * released before calling */ - match &mut *self.state.lock().unwrap() { - Some(state) => { - self.init_transcode(state); - need_stream_start = state.need_stream_start; - hash = state.transcode_hash; - }, - None => { return Err(loggable_error!(CAT, "VideoConv not yet in READY state?")); } - }; - - if need_stream_start && active && hash.is_some() { - let stream_id = format!("{:032x}", hash.unwrap()); - self.srcpad.push_event(gst::event::StreamStart::new(&stream_id)); - - match &mut *self.state.lock().unwrap() { - Some(state) => { state.need_stream_start = false }, - None => { return Err(loggable_error!(CAT, "VideoConv not yet in READY state?")); } - }; - } - } - - Ok(()) - } -} diff --git a/media-converter/src/videoconv/mod.rs b/media-converter/src/videoconv/mod.rs deleted file mode 100644 index aaa6089a..00000000 --- a/media-converter/src/videoconv/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 2022 Valve Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -use gst::glib; -use gst::prelude::*; - -mod imp; - -glib::wrapper! { - pub struct VideoConv(ObjectSubclass) @extends gst::Element, gst::Object; -} - -pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { - gst::Element::register( - Some(plugin), - "protonvideoconverter", - gst::Rank::Marginal, - VideoConv::static_type()) -}