From 0eb2aaa8c12c441ab4a58be760638b5344c76d8c Mon Sep 17 00:00:00 2001 From: modeco80 Date: Thu, 17 Oct 2024 00:51:26 -0400 Subject: [PATCH] replace video with letsplay_av_ffmpeg yay. --- server/Cargo.lock | 297 +++++++----------- server/Cargo.toml | 4 +- server/src/main.rs | 21 +- server/src/retro_thread.rs | 14 +- server/src/video/cuda_gl/bindgen.sh | 27 -- server/src/video/cuda_gl/gl.h | 1 - server/src/video/cuda_gl/mod.rs | 15 - server/src/video/cuda_gl/safe.rs | 151 --------- server/src/video/cuda_gl/sys.rs | 73 ----- server/src/video/encoder_thread.rs | 471 ---------------------------- server/src/video/h264_encoder.rs | 354 --------------------- server/src/video/hwdevice.rs | 88 ------ server/src/video/hwframe.rs | 121 ------- server/src/video/mod.rs | 22 -- 14 files changed, 140 insertions(+), 1519 deletions(-) delete mode 100755 server/src/video/cuda_gl/bindgen.sh delete mode 100644 server/src/video/cuda_gl/gl.h delete mode 100644 server/src/video/cuda_gl/mod.rs delete mode 100644 server/src/video/cuda_gl/safe.rs delete mode 100644 server/src/video/cuda_gl/sys.rs delete mode 100644 server/src/video/encoder_thread.rs delete mode 100644 server/src/video/h264_encoder.rs delete mode 100644 server/src/video/hwdevice.rs delete mode 100644 server/src/video/hwframe.rs delete mode 100644 server/src/video/mod.rs diff --git a/server/Cargo.lock b/server/Cargo.lock index adb5d8e..2598662 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "async-trait" @@ -45,15 +45,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -102,7 +102,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -110,11 +110,10 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck", "proc-macro2", "quote", "syn", @@ -122,30 +121,30 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", @@ -184,15 +183,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.16" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "shlex", ] @@ -225,9 +224,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -319,9 +318,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -334,9 +333,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -344,15 +343,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -361,15 +360,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -378,21 +377,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -429,9 +428,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gl" @@ -465,12 +464,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "hermit-abi" version = "0.3.9" @@ -513,9 +506,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -525,9 +518,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -544,9 +537,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-util", @@ -555,16 +548,7 @@ dependencies = [ "hyper", "pin-project-lite", "tokio", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", + "tower-service", ] [[package]] @@ -610,6 +594,20 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "letsplay_av_ffmpeg" +version = "0.1.0" +dependencies = [ + "anyhow", + "cudarc", + "ffmpeg-next", + "gl", + "letsplay_gpu", + "libloading", + "tokio", + "tracing", +] + [[package]] name = "letsplay_gpu" version = "0.1.0" @@ -620,9 +618,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" @@ -685,11 +683,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -736,18 +734,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "overload" @@ -784,26 +782,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -818,9 +796,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "ppv-lite86" @@ -833,9 +811,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -881,18 +859,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -902,9 +880,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -913,9 +891,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "retro_frontend" @@ -925,19 +903,12 @@ dependencies = [ "libc", "libloading", "libretro-sys", - "rgb565", "serde", "thiserror", "toml", "tracing", ] -[[package]] -name = "rgb565" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43e85498d0bb728f77a88b4313eaf4ed21673f3f8a05c36e835cf6c9c0d066" - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -952,9 +923,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -970,18 +941,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1093,9 +1064,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1116,18 +1087,18 @@ checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -1144,21 +1115,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.40.0" @@ -1190,9 +1146,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", @@ -1236,14 +1192,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper 0.1.2", "tokio", "tower-layer", "tower-service", @@ -1322,9 +1278,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" dependencies = [ "byteorder", "bytes", @@ -1335,7 +1291,6 @@ dependencies = [ "rand", "sha1", "thiserror", - "url", "utf-8", ] @@ -1345,37 +1300,11 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "url" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utf-8" @@ -1409,10 +1338,10 @@ dependencies = [ "async-trait", "axum", "cudarc", - "ffmpeg-next", "futures", "futures-util", "gl", + "letsplay_av_ffmpeg", "letsplay_gpu", "libloading", "rand", diff --git a/server/Cargo.toml b/server/Cargo.toml index 875e15a..3198be6 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,6 +10,7 @@ anyhow = "1.0.86" # Libretro Sex letsplay_gpu.path = "/home/lily/source/lets-play/crates/letsplay_gpu" retro_frontend.path = "/home/lily/source/lets-play/crates/retro_frontend" +letsplay_av_ffmpeg.path = "/home/lily/source/lets-play/crates/letsplay_av_ffmpeg" gl = "0.14.0" # async @@ -20,8 +21,7 @@ axum = { version = "0.7.5", features = ["ws", "macros"] } futures = "0.3" futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } -# ffmpeg -ffmpeg = { version = "7.0.0", package = "ffmpeg-next" } + # misc stuff rand = "0.8.5" diff --git a/server/src/main.rs b/server/src/main.rs index 498607d..d506f1a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,7 +1,9 @@ mod retro_thread; mod surface; mod types; -mod video; +//mod video; + +use letsplay_av_ffmpeg as video; mod transport; @@ -177,7 +179,7 @@ async fn main() -> anyhow::Result<()> { let resource = Arc::new(Mutex::new(GraphicsResource::new(&device))); - let (mut encoder_rx, encoder_tx) = encoder_thread::encoder_thread_spawn_hwframe( + let (mut encoder_rx, encoder_tx) = video::encoder_thread::hardware_frame::spawn( &device.clone(), &resource.clone(), &egl_ctx.clone(), @@ -205,13 +207,24 @@ async fn main() -> anyhow::Result<()> { )); */ + /* let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadCore( - "cores/pcsx2_libretro.so".into(), + "cores/pcsx2_new_libretro.so".into(), )); let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadGame( - "/data/sda/lily/ISOs/Sony PlayStation 2/ztx-hl.bin".into(), + "/data/sda/lily/ISOs/Sony PlayStation 2/Jonny Moseley Mad Trix (USA).bin".into(), )); + */ + let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadCore( + "cores/swanstation_libretro.so".into(), + )); + let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadGame( + // "/data/sda/lily/ISOs/Nintendo GameCube/Dave Mirra Freestyle BMX 2 (USA).ciso".into(), +// "roms/smb1.nes".into(), + "roms/merged/nmv2/jagb/nmv2jagb.cue".into(), + )); + // start the libretro thread looping now that we're alive let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::Start); diff --git a/server/src/retro_thread.rs b/server/src/retro_thread.rs index 9be7211..a9cbb7d 100644 --- a/server/src/retro_thread.rs +++ b/server/src/retro_thread.rs @@ -17,7 +17,9 @@ use retro_frontend::{ use gpu::egl_helpers::DeviceContext; use letsplay_gpu as gpu; -use crate::{surface::Surface, types::Size, video::cuda_gl::safe::GraphicsResource}; +use letsplay_av_ffmpeg::types::*; + +use crate::{surface::Surface, video::cuda_gl::safe::GraphicsResource}; /// Called by OpenGL. We use this to dump errors. extern "system" fn opengl_message_callback( @@ -183,19 +185,19 @@ impl RetroState { let size = framebuffer.size.clone(); let buffer = framebuffer.get_buffer(); - let has_disconnected_pitch = pitch != size.width as u32; + //let has_disconnected_pitch = pitch != size.width as u32; for _y in 0..size.height { // Trick to flip the buffer the way OpenGL likes. let y = (size.height - 1) - _y; let src_line_off = (y as u32 * pitch) as usize; - let mut dest_line_off = (_y as u32 * size.width) as usize; + let dest_line_off = (_y as u32 * size.width) as usize; // copy only - if has_disconnected_pitch { + //if has_disconnected_pitch { //dest_line_off = (y * pitch) as usize; - } + //} // Create slices repressenting each part let src_slice = &slice[src_line_off..src_line_off + size.width as usize]; @@ -217,7 +219,7 @@ impl FrontendInterface for RetroState { self.get_frontend().set_gl_fbo(raw); if !self.gl_rendering { - self.software_framebuffer.resize(Size { width, height }); + self.software_framebuffer.resize(crate::types::Size { width, height }); } // register the FBO's texture to our cuda interop resource diff --git a/server/src/video/cuda_gl/bindgen.sh b/server/src/video/cuda_gl/bindgen.sh deleted file mode 100755 index 1f9dade..0000000 --- a/server/src/video/cuda_gl/bindgen.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Does bindgen for CUDA cudaGL. (needs postprocessing) -set -exu - -# --allowlist-type="^CU.*" \ -# --allowlist-type="^cuuint(32|64)_t" \ -# --allowlist-type="^cudaError_enum" \ -# --allowlist-type="^cu.*Complex$" \ -# --allowlist-type="^cuda.*" \ -# --allowlist-type="^libraryPropertyType.*" \ -# --allowlist-var="^CU.*" \ - -echo "use cudarc::sys::*; /* Hack :3 */" > ./sys.rs - -bindgen \ - --allowlist-type="" \ - --allowlist-function="^cuGraphicsGL.*" \ - --default-enum-style=rust \ - --no-doc-comments \ - --with-derive-default \ - --with-derive-eq \ - --with-derive-hash \ - --with-derive-ord \ - --use-core \ - --dynamic-loading Lib \ - gl.h -- -I/opt/cuda/include \ - >> ./sys.rs \ No newline at end of file diff --git a/server/src/video/cuda_gl/gl.h b/server/src/video/cuda_gl/gl.h deleted file mode 100644 index 31e1454..0000000 --- a/server/src/video/cuda_gl/gl.h +++ /dev/null @@ -1 +0,0 @@ -#include "cudaGL.h" \ No newline at end of file diff --git a/server/src/video/cuda_gl/mod.rs b/server/src/video/cuda_gl/mod.rs deleted file mode 100644 index c8781b4..0000000 --- a/server/src/video/cuda_gl/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -#[allow(non_snake_case)] -pub mod sys; -use sys::*; - -pub mod safe; - -pub unsafe fn lib() -> &'static Lib { - static LIB: std::sync::OnceLock = std::sync::OnceLock::new(); - LIB.get_or_init(|| { - if let Ok(lib) = Lib::new(libloading::library_filename("cuda")) { - return lib; - } - panic!("cuda library doesn't exist."); - }) -} \ No newline at end of file diff --git a/server/src/video/cuda_gl/safe.rs b/server/src/video/cuda_gl/safe.rs deleted file mode 100644 index 6064104..0000000 --- a/server/src/video/cuda_gl/safe.rs +++ /dev/null @@ -1,151 +0,0 @@ -use cudarc::driver::{result as cuda_result, sys as cuda_sys, CudaDevice}; - -use std::sync::Arc; - -pub struct MappedGraphicsResource { - resource: cuda_sys::CUgraphicsResource, -} - -impl MappedGraphicsResource { - fn new(resource: cuda_sys::CUgraphicsResource) -> Self { - Self { resource } - } - - pub fn map(&mut self) -> Result<(), cuda_result::DriverError> { - unsafe { - cuda_sys::lib() - .cuGraphicsMapResources(1, &mut self.resource, std::ptr::null_mut()) - .result()?; - } - Ok(()) - } - - pub fn unmap(&mut self) -> Result<(), cuda_result::DriverError> { - unsafe { - cuda_sys::lib() - .cuGraphicsUnmapResources(1, &mut self.resource, std::ptr::null_mut()) - .result()?; - } - - Ok(()) - } - - pub fn get_mapped_array(&mut self) -> Result { - assert!( - !self.resource.is_null(), - "do not call GraphicsResource::get_mapped_array if no resource is actually registered" - ); - - let mut array: cuda_sys::CUarray = std::ptr::null_mut(); - - unsafe { - cuda_sys::lib() - .cuGraphicsSubResourceGetMappedArray(&mut array, self.resource, 0, 0) - .result()?; - } - - Ok(array) - } - - pub fn get_device_pointer( - &mut self, - ) -> Result { - assert!( - !self.resource.is_null(), - "do not call GraphicsResource::get_mapped_array if no resource is actually registered" - ); - - let mut array: cuda_sys::CUdeviceptr = 0; - let mut size: usize = 0; - - unsafe { - cuda_sys::lib() - .cuGraphicsResourceGetMappedPointer_v2(&mut array, &mut size, self.resource) - .result()?; - } - - Ok(array) - } -} - -impl Drop for MappedGraphicsResource { - fn drop(&mut self) { - let _ = self.unmap(); - } -} - -/// Wrapper over cuGraphicsGL* apis -pub struct GraphicsResource { - context: Arc, - resource: cuda_sys::CUgraphicsResource, -} - -impl GraphicsResource { - pub fn new(device: &Arc) -> Self { - Self { - context: device.clone(), - resource: std::ptr::null_mut(), - } - } - - pub fn device(&self) -> Arc { - self.context.clone() - } - - /// Maps this resource. - pub fn map(&mut self) -> Result { - let mut res = MappedGraphicsResource::new(self.resource); - res.map()?; - - Ok(res) - } - - pub fn register( - &mut self, - texture_id: gl::types::GLuint, - texture_kind: gl::types::GLuint, - ) -> Result<(), cuda_result::DriverError> { - // better to be safe than leak memory? idk. - if !self.resource.is_null() { - self.unregister()?; - } - - unsafe { - super::lib() - .cuGraphicsGLRegisterImage(&mut self.resource, texture_id, texture_kind, 1) - .result()?; - } - - Ok(()) - } - - pub fn is_registered(&self) -> bool { - !self.resource.is_null() - } - - pub fn unregister(&mut self) -> Result<(), cuda_result::DriverError> { - assert!( - !self.resource.is_null(), - "do not call if no resource is actually registered" - ); - - unsafe { - cuda_sys::lib() - .cuGraphicsUnregisterResource(self.resource) - .result()?; - } - - self.resource = std::ptr::null_mut(); - Ok(()) - } -} - -impl Drop for GraphicsResource { - fn drop(&mut self) { - if self.is_registered() { - let _ = self.unregister(); - } - } -} - -unsafe impl Send for GraphicsResource {} diff --git a/server/src/video/cuda_gl/sys.rs b/server/src/video/cuda_gl/sys.rs deleted file mode 100644 index f0c3313..0000000 --- a/server/src/video/cuda_gl/sys.rs +++ /dev/null @@ -1,73 +0,0 @@ -use cudarc::driver::sys::*; /* Hack :3 */ -use gl::types::{GLenum, GLuint}; -/* automatically generated by rust-bindgen 0.69.4 */ - -pub struct Lib { - __library: ::libloading::Library, - pub cuGraphicsGLRegisterBuffer: Result< - unsafe extern "C" fn( - pCudaResource: *mut CUgraphicsResource, - buffer: GLuint, - Flags: ::core::ffi::c_uint, - ) -> CUresult, - ::libloading::Error, - >, - pub cuGraphicsGLRegisterImage: Result< - unsafe extern "C" fn( - pCudaResource: *mut CUgraphicsResource, - image: GLuint, - target: GLenum, - Flags: ::core::ffi::c_uint, - ) -> CUresult, - ::libloading::Error, - >, -} -impl Lib { - pub unsafe fn new

(path: P) -> Result - where - P: AsRef<::std::ffi::OsStr>, - { - let library = ::libloading::Library::new(path)?; - Self::from_library(library) - } - pub unsafe fn from_library(library: L) -> Result - where - L: Into<::libloading::Library>, - { - let __library = library.into(); - let cuGraphicsGLRegisterBuffer = __library - .get(b"cuGraphicsGLRegisterBuffer\0") - .map(|sym| *sym); - let cuGraphicsGLRegisterImage = __library - .get(b"cuGraphicsGLRegisterImage\0") - .map(|sym| *sym); - Ok(Lib { - __library, - cuGraphicsGLRegisterBuffer, - cuGraphicsGLRegisterImage, - }) - } - pub unsafe fn cuGraphicsGLRegisterBuffer( - &self, - pCudaResource: *mut CUgraphicsResource, - buffer: GLuint, - Flags: ::core::ffi::c_uint, - ) -> CUresult { - (self - .cuGraphicsGLRegisterBuffer - .as_ref() - .expect("Expected function, got error."))(pCudaResource, buffer, Flags) - } - pub unsafe fn cuGraphicsGLRegisterImage( - &self, - pCudaResource: *mut CUgraphicsResource, - image: GLuint, - target: GLenum, - Flags: ::core::ffi::c_uint, - ) -> CUresult { - (self - .cuGraphicsGLRegisterImage - .as_ref() - .expect("Expected function, got error."))(pCudaResource, image, target, Flags) - } -} diff --git a/server/src/video/encoder_thread.rs b/server/src/video/encoder_thread.rs deleted file mode 100644 index c3f5fcc..0000000 --- a/server/src/video/encoder_thread.rs +++ /dev/null @@ -1,471 +0,0 @@ -use anyhow::Context; -use cudarc::{ - driver::{ - sys::{CUdeviceptr, CUmemorytype}, - CudaDevice, CudaSlice, DevicePtr, LaunchAsync, - }, - nvrtc::CompileOptions, -}; -use letsplay_gpu::egl_helpers::DeviceContext; -use std::{ - sync::{Arc, Mutex}, - time::Duration, -}; -use tokio::sync::mpsc::{self, error::TryRecvError}; - -use super::h264_encoder::H264Encoder; -use super::{cuda_gl::safe::GraphicsResource, ffmpeg}; - -pub enum EncodeThreadInput { - Init { size: crate::types::Size }, - ForceKeyframe, - SendFrame, -} - -#[derive(Clone)] -pub enum EncodeThreadOutput { - Frame { packet: ffmpeg::Packet }, -} - -struct EncoderState { - encoder: Option, - frame: Arc>>, - packet: ffmpeg::Packet, -} - -impl EncoderState { - fn new(frame: Arc>>) -> Self { - Self { - encoder: None, - frame: frame, - packet: ffmpeg::Packet::empty(), - } - } - - fn init(&mut self, size: crate::types::Size) -> anyhow::Result<()> { - self.encoder = Some(H264Encoder::new_nvenc_swframe( - size.clone(), - 60, - 2 * (1024 * 1024), - )?); - - // replace packet - self.packet = ffmpeg::Packet::empty(); - - Ok(()) - } - - //fn frame(&mut self) -> Arc>> { - // self.frame.clone() - //} - - fn send_frame(&mut self, pts: u64, force_keyframe: bool) -> Option { - let mut lk = self.frame.lock().expect("fuck"); - let frame = lk.as_mut().unwrap(); - let encoder = self.encoder.as_mut().unwrap(); - - // set frame metadata - unsafe { - if force_keyframe { - (*frame.as_mut_ptr()).pict_type = ffmpeg::sys::AVPictureType::AV_PICTURE_TYPE_I; - (*frame.as_mut_ptr()).flags = ffmpeg::sys::AV_FRAME_FLAG_KEY; - (*frame.as_mut_ptr()).key_frame = 1; - } else { - (*frame.as_mut_ptr()).pict_type = ffmpeg::sys::AVPictureType::AV_PICTURE_TYPE_NONE; - (*frame.as_mut_ptr()).flags = 0i32; - (*frame.as_mut_ptr()).key_frame = 0; - } - - (*frame.as_mut_ptr()).pts = pts as i64; - } - - encoder.send_frame(&*frame); - encoder - .receive_packet(&mut self.packet) - .expect("Failed to recieve packet"); - - unsafe { - if !self.packet.is_empty() { - return Some(self.packet.clone()); - } - } - - return None; - } -} - -struct EncoderStateHW { - encoder: Option, - frame: ffmpeg::frame::Video, - packet: ffmpeg::Packet, -} - -impl EncoderStateHW { - fn new() -> Self { - Self { - encoder: None, - frame: ffmpeg::frame::Video::empty(), - packet: ffmpeg::Packet::empty(), - } - } - - fn init(&mut self, device: &Arc, size: crate::types::Size) -> anyhow::Result<()> { - self.encoder = Some(H264Encoder::new_nvenc_hwframe( - &device, - size.clone(), - 60, - 2 * (1024 * 1024), - )?); - - // replace packet - self.packet = ffmpeg::Packet::empty(); - self.frame = self.encoder.as_mut().unwrap().create_frame()?; - - Ok(()) - } - - #[inline] - fn frame(&mut self) -> &mut ffmpeg::frame::Video { - &mut self.frame - } - - fn send_frame(&mut self, pts: u64, force_keyframe: bool) -> Option { - let frame = &mut self.frame; - let encoder = self.encoder.as_mut().unwrap(); - - // set frame metadata - unsafe { - if force_keyframe { - (*frame.as_mut_ptr()).pict_type = ffmpeg::sys::AVPictureType::AV_PICTURE_TYPE_I; - (*frame.as_mut_ptr()).flags = ffmpeg::sys::AV_FRAME_FLAG_KEY; - (*frame.as_mut_ptr()).key_frame = 1; - } else { - (*frame.as_mut_ptr()).pict_type = ffmpeg::sys::AVPictureType::AV_PICTURE_TYPE_NONE; - (*frame.as_mut_ptr()).flags = 0i32; - (*frame.as_mut_ptr()).key_frame = 0; - } - - (*frame.as_mut_ptr()).pts = pts as i64; - } - - encoder.send_frame(&*frame); - encoder - .receive_packet(&mut self.packet) - .expect("Failed to recieve packet"); - - unsafe { - if !self.packet.is_empty() { - return Some(self.packet.clone()); - } - } - - return None; - } -} - -fn encoder_thread_swframe_main( - mut rx: mpsc::Receiver, - tx: mpsc::Sender, - frame: &Arc>>, -) -> anyhow::Result<()> { - // FIXME: for HW frame support - //let dev = cudarc::driver::CudaDevice::new(0)?; - - let mut frame_number = 0u64; - let mut force_keyframe = false; - - let mut encoder = EncoderState::new(frame.clone()); - - loop { - match rx.try_recv() { - Ok(msg) => match msg { - EncodeThreadInput::Init { size } => { - frame_number = 0; - - if force_keyframe { - force_keyframe = false; - } - - encoder.init(size).expect("encoder init failed"); - } - - EncodeThreadInput::ForceKeyframe => { - force_keyframe = true; - } - - EncodeThreadInput::SendFrame => { - if let Some(pkt) = encoder.send_frame(frame_number as u64, force_keyframe) { - // A bit less clear than ::empty(), but it's "Safe" - if let Some(_) = pkt.data() { - let _ = tx.blocking_send(EncodeThreadOutput::Frame { - packet: pkt.clone(), - }); - } - - frame_number += 1; - } - - if force_keyframe { - force_keyframe = false; - } - } - }, - - Err(TryRecvError::Disconnected) => break, - Err(TryRecvError::Empty) => { - std::thread::sleep(Duration::from_millis(1)); - } - } - } - - Ok(()) -} - -pub fn encoder_thread_spawn_swframe( - frame: &Arc>>, -) -> ( - mpsc::Receiver, - mpsc::Sender, -) { - let (in_tx, in_rx) = mpsc::channel(1); - let (out_tx, out_rx) = mpsc::channel(1); - - let clone = Arc::clone(frame); - - std::thread::spawn(move || encoder_thread_swframe_main(in_rx, out_tx, &clone)); - - (out_rx, in_tx) -} - -/// Source for the kernel used to flip OpenGL framebuffers right-side up. -const OPENGL_FLIP_KERNEL_SRC: &str = " -extern \"C\" __global__ void flip_opengl( - const unsigned* pSrc, - unsigned* pDest, - int width, - int height -) { - const unsigned x = blockIdx.x * blockDim.x + threadIdx.x; - const unsigned y = blockIdx.y * blockDim.y + threadIdx.y; - - if (x < width && y < height) { - unsigned reversed_y = (height - 1) - y; - ((unsigned*)pDest)[y * width + x] = ((unsigned*)pSrc)[reversed_y * width + x]; - } -}"; - -fn encoder_thread_hwframe_main( - mut rx: mpsc::Receiver, - tx: mpsc::Sender, - - cuda_device: &Arc, - cuda_resource: &Arc>, - gl_context: &Arc>, -) -> anyhow::Result<()> { - let mut frame_number = 0u64; - let mut force_keyframe = false; - - let mut encoder = EncoderStateHW::new(); - - // :) - cuda_device.bind_to_thread()?; - - // Compile the support kernel - let ptx = cudarc::nvrtc::compile_ptx_with_opts( - &OPENGL_FLIP_KERNEL_SRC, - CompileOptions { - //options: vec!["--gpu-architecture=compute_50".into()], - ..Default::default() - }, - ) - .with_context(|| "compiling support kernel")?; - - // pop it in - cuda_device.load_ptx(ptx, "module", &["flip_opengl"])?; - - let mut memcpy = cudarc::driver::sys::CUDA_MEMCPY2D_st::default(); - - // setup the things that won't change about the cuda memcpy - - // src - memcpy.srcXInBytes = 0; - memcpy.srcY = 0; - memcpy.srcMemoryType = CUmemorytype::CU_MEMORYTYPE_ARRAY; - - // dest - memcpy.dstXInBytes = 0; - memcpy.dstY = 0; - memcpy.dstMemoryType = CUmemorytype::CU_MEMORYTYPE_DEVICE; - - // Temporary buffer used for opengl flip on the GPU. We copy to this buffer, - // then copy the flipped version (using the launched support kernel) to the CUDA device memory ffmpeg - // allocated. - let mut temp_buffer: CudaSlice = cuda_device.alloc_zeros::(48).expect("over"); - - loop { - match rx.blocking_recv() { - Some(msg) => match msg { - EncodeThreadInput::Init { size } => { - frame_number = 0; - - if force_keyframe { - force_keyframe = false; - } - - temp_buffer = cuda_device - .alloc_zeros::((size.width * size.height) as usize) - .expect("oh youre fucked anyways"); - - encoder - .init(cuda_device, size) - .expect("encoder init failed"); - } - - EncodeThreadInput::ForceKeyframe => { - force_keyframe = true; - } - - EncodeThreadInput::SendFrame => { - // benchmarking - //use std::time::Instant; - //let start = Instant::now(); - - // copy gl frame *ON THE GPU* to ffmpeg frame - { - let gl_ctx = gl_context.lock().expect("you dumb fuck"); - let mut gl_resource = - cuda_resource.lock().expect("couldnt lock GL resource!"); - - gl_ctx.make_current(); - - let mut mapped = gl_resource - .map() - .expect("couldnt map graphics resource. Its joever"); - - let array = mapped - .get_mapped_array() - .expect("well its all over anyways"); - - let frame = encoder.frame(); - - // setup the cuMemcpy2D operation to copy to the temporary buffer - // (we should probably abstract source and provide a way to elide this, - // and instead feed ffmpeg directly. for now it's *just* used with gl so /shrug) - { - memcpy.srcArray = array; - - unsafe { - let frame_ptr = frame.as_mut_ptr(); - memcpy.dstDevice = temp_buffer.device_ptr().clone(); - memcpy.dstPitch = (*frame_ptr).linesize[0] as usize; - memcpy.WidthInBytes = ((*frame_ptr).width * 4) as usize; - memcpy.Height = (*frame_ptr).height as usize; - } - } - - // copy to the temporary buffer and synchronize - unsafe { - cudarc::driver::sys::lib() - .cuMemcpy2DAsync_v2(&memcpy, std::ptr::null_mut()) - .result() - .expect("cuMemcpy2D fail epic"); - - cudarc::driver::sys::lib() - .cuStreamSynchronize(std::ptr::null_mut()) - .result()?; - } - - // launch kernel to flip the opengl framebuffer right-side up - { - let width = frame.width(); - let height = frame.height(); - - let launch_config = cudarc::driver::LaunchConfig { - grid_dim: (width / 16 + 1, height / 2 + 1, 1), - block_dim: (16, 2, 1), - shared_mem_bytes: 0, - }; - - let flip_opengl = cuda_device.get_func("module", "flip_opengl").expect( - "for some reason we couldn't get the support kenrel function", - ); - - unsafe { - let frame_ptr = frame.as_mut_ptr(); - - let mut slice = cuda_device.upgrade_device_ptr::( - (*frame_ptr).data[0] as CUdeviceptr, - (width * height) as usize * 4usize, - ); - - flip_opengl.launch( - launch_config, - (&mut temp_buffer, &mut slice, width, height), - )?; - - // leak so it doesn't free the memory - // (the device pointer we convert into a slice is owned by ffmpeg, so we shouldn't be the ones - // trying to free it!) - let _ = slice.leak(); - - // Synchronize for the final time - cudarc::driver::sys::lib() - .cuStreamSynchronize(std::ptr::null_mut()) - .result()?; - } - } - - // FIXME: ideally this would work on-drop but it doesn't. - mapped.unmap().expect("fuck you asshole"); - gl_ctx.release(); - } - - if let Some(pkt) = encoder.send_frame(frame_number as u64, force_keyframe) { - // A bit less clear than ::empty(), but it's "Safe" - if let Some(_) = pkt.data() { - let _ = tx.blocking_send(EncodeThreadOutput::Frame { - packet: pkt.clone(), - }); - } - - frame_number += 1; - } - - if force_keyframe { - force_keyframe = false; - } - - //tracing::info!("encoding frame {frame_number} took {:2?}", start.elapsed()); - } - }, - - None => break, - } - } - - //std::thread::sleep(Duration::from_millis(1)); - - Ok(()) -} - -pub fn encoder_thread_spawn_hwframe( - cuda_device: &Arc, - cuda_resource: &Arc>, - gl_context: &Arc>, -) -> ( - mpsc::Receiver, - mpsc::Sender, -) { - let (in_tx, in_rx) = mpsc::channel(1); - let (out_tx, out_rx) = mpsc::channel(1); - - let dev_clone = Arc::clone(cuda_device); - let rsrc_clone = Arc::clone(cuda_resource); - let gl_clone = Arc::clone(gl_context); - - std::thread::spawn(move || { - encoder_thread_hwframe_main(in_rx, out_tx, &dev_clone, &rsrc_clone, &gl_clone) - }); - - (out_rx, in_tx) -} diff --git a/server/src/video/h264_encoder.rs b/server/src/video/h264_encoder.rs deleted file mode 100644 index aa9875a..0000000 --- a/server/src/video/h264_encoder.rs +++ /dev/null @@ -1,354 +0,0 @@ -use super::ffmpeg; -use super::hwframe::HwFrameContext; -use anyhow::Context; -use cudarc::driver::CudaDevice; -use ffmpeg::error::EAGAIN; - -use ffmpeg::codec as lavc; // lavc - -use crate::types::Size; - -/// this is required for libx264 to like. Work -pub fn create_context_from_codec(codec: ffmpeg::Codec) -> Result { - unsafe { - let context = ffmpeg::sys::avcodec_alloc_context3(codec.as_ptr()); - if context.is_null() { - return Err(ffmpeg::Error::Unknown); - } - - let context = lavc::Context::wrap(context, None); - Ok(context) - } -} - -fn create_context_and_set_common_parameters( - codec: &str, - size: &Size, - max_framerate: u32, - bitrate: usize, -) -> anyhow::Result<(ffmpeg::Codec, ffmpeg::encoder::video::Video)> { - let encoder = ffmpeg::encoder::find_by_name(codec) - .expect(&format!("could not find the codec \"{codec}\"")); - - let mut video_encoder_context = create_context_from_codec(encoder)?.encoder().video()?; - - video_encoder_context.set_width(size.width); - video_encoder_context.set_height(size.height); - video_encoder_context.set_frame_rate(Some(ffmpeg::Rational(1, max_framerate as i32))); - - video_encoder_context.set_bit_rate(bitrate / 4); - video_encoder_context.set_max_bit_rate(bitrate); - - // qp TODO: - //video_encoder_context.set_qmax(30); - //video_encoder_context.set_qmin(35); - - video_encoder_context.set_time_base(ffmpeg::Rational(1, max_framerate as i32).invert()); - video_encoder_context.set_format(ffmpeg::format::Pixel::YUV420P); - - // The GOP here is setup to balance keyframe retransmission with bandwidth. - //video_encoder_context.set_gop((max_framerate * 4) as u32); - video_encoder_context.set_gop(i32::MAX as u32); - video_encoder_context.set_max_b_frames(0); - - unsafe { - (*video_encoder_context.as_mut_ptr()).delay = 0; - (*video_encoder_context.as_mut_ptr()).refs = 0; - } - - Ok((encoder, video_encoder_context)) -} - -/// A simple H.264 encoder. Currently software only, however -/// pieces are being put in place to eventually allow HW encoding. -pub enum H264Encoder { - Software { - encoder: ffmpeg::encoder::video::Encoder, - }, - - /// Hardware encoding, with frames uploaded to the GPU by ffmpeg. - NvencSWFrame { - encoder: ffmpeg::encoder::video::Encoder, - }, - - /// Hardware encoding, with frames already on the GPU. - NvencHWFrame { - encoder: ffmpeg::encoder::video::Encoder, - hw_context: HwFrameContext, - }, -} - -impl H264Encoder { - /// Creates a new software encoder. - pub fn new_software(size: Size, max_framerate: u32, bitrate: usize) -> anyhow::Result { - // Create the libx264 context - let (encoder, mut video_encoder_context) = - create_context_and_set_common_parameters("libx264", &size, max_framerate, bitrate)?; - - video_encoder_context.set_format(ffmpeg::format::Pixel::YUV420P); - - let threads = std::thread::available_parallelism().expect("ggg").get() / 8; - - // FIXME: tracing please. - println!("H264Encoder::new_software(): Using {threads} threads to encode"); - - // Frame-level threading causes [N] frames of latency - // so we use slice-level threading to reduce the latency - // as much as possible while still allowing threading - video_encoder_context.set_threading(ffmpeg::threading::Config { - kind: ffmpeg::threading::Type::Slice, - count: threads, - }); - - // Set libx264 applicable dictionary options - let mut dict = ffmpeg::Dictionary::new(); - dict.set("tune", "zerolatency"); - dict.set("preset", "veryfast"); - - // This could probably be moved but then it would mean returning the dictionary too - // which is fine I guess it just seems a bit rickity - dict.set("profile", "main"); - - // TODO: - dict.set("crf", "43"); - dict.set("crf_max", "48"); - - dict.set("forced-idr", "1"); - - let encoder = video_encoder_context - .open_as_with(encoder, dict) - .with_context(|| "While opening x264 video codec")?; - - Ok(Self::Software { encoder: encoder }) - } - - /// Creates a new hardware (NVIDIA NVENC) encoder, which encodes - /// frames from software input. FFmpeg handles uploading frames to the GPU. - pub fn new_nvenc_swframe( - size: Size, - max_framerate: u32, - bitrate: usize, - ) -> anyhow::Result { - let (encoder, mut video_encoder_context) = - create_context_and_set_common_parameters("h264_nvenc", &size, max_framerate, bitrate) - .with_context(|| "while trying to create encoder")?; - - video_encoder_context.set_format(ffmpeg::format::Pixel::ZRGB32); - - video_encoder_context.set_qmin(37); - video_encoder_context.set_qmax(33); - - // set h264_nvenc options - let mut dict = ffmpeg::Dictionary::new(); - - dict.set("tune", "ull"); - dict.set("preset", "p1"); - - dict.set("profile", "main"); - - // TODO: - dict.set("rc", "vbr"); - dict.set("qp", "35"); - - dict.set("forced-idr", "1"); - - // damn you - dict.set("delay", "0"); - dict.set("zerolatency", "1"); - - let encoder = video_encoder_context - .open_as_with(encoder, dict) - .with_context(|| "While opening h264_nvenc video codec")?; - - Ok(Self::NvencSWFrame { encoder: encoder }) - } - - /// Creates a new hardware (NVIDIA NVENC) encoder, which encodes - /// frames from GPU memory, via CUDA. - /// FFmpeg handles uploading frames to the GPU. - /// You are expected to handle uploading or otherwise working with a frame on the GPU. - pub fn new_nvenc_hwframe( - cuda_device: &CudaDevice, - size: Size, - max_framerate: u32, - bitrate: usize, - ) -> anyhow::Result { - let cuda_device_context = super::hwdevice::CudaDeviceContextBuilder::new()? - .set_cuda_context((*cuda_device.cu_primary_ctx()) as *mut _) - .build() - .with_context(|| "while trying to create CUDA device context")?; - - let mut hw_frame_context = super::hwframe::HwFrameContextBuilder::new(cuda_device_context)? - .set_width(size.width) - .set_height(size.height) - .set_sw_format(ffmpeg::format::Pixel::ZBGR32) - .set_format(ffmpeg::format::Pixel::CUDA) - .build() - .with_context(|| "while trying to create CUDA frame context")?; - - let (encoder, mut video_encoder_context) = - create_context_and_set_common_parameters("h264_nvenc", &size, max_framerate, bitrate) - .with_context(|| "while trying to create encoder")?; - - video_encoder_context.set_format(ffmpeg::format::Pixel::CUDA); - - video_encoder_context.set_qmin(35); - video_encoder_context.set_qmax(38); - - unsafe { - // FIXME: this currently breaks the avbufferref system a bit - (*video_encoder_context.as_mut_ptr()).hw_frames_ctx = - ffmpeg::sys::av_buffer_ref(hw_frame_context.as_raw_mut()); - (*video_encoder_context.as_mut_ptr()).hw_device_ctx = - ffmpeg::sys::av_buffer_ref(hw_frame_context.as_device_context_mut()); - } - - // set h264_nvenc options - let mut dict = ffmpeg::Dictionary::new(); - - dict.set("tune", "ull"); - dict.set("preset", "p1"); - - dict.set("profile", "main"); - - // TODO: - dict.set("rc", "vbr"); - dict.set("qp", "35"); - - dict.set("forced-idr", "1"); - - // damn you - dict.set("delay", "0"); - dict.set("zerolatency", "1"); - - let encoder = video_encoder_context - .open_as_with(encoder, dict) - .with_context(|| "While opening h264_nvenc video codec")?; - - Ok(Self::NvencHWFrame { - encoder: encoder, - hw_context: hw_frame_context, - }) - } - - // NOTE: It's a bit pointless to have this have a mut borrow, - // but you'll probably have a mutable borrow on this already.. - pub fn is_hardware(&mut self) -> bool { - match self { - Self::Software { .. } => false, - Self::NvencSWFrame { .. } => true, - Self::NvencHWFrame { .. } => true, - } - } - - //pub fn get_hw_context(&mut self) -> &mut HwFrameContext { - // match self { - // Self::Nvenc { encoder: _, hw_context } => hw_context, - // _ => panic!("should not use H264Encoder::get_hw_context() on a Software encoder") - // } - //} - - pub fn create_frame(&mut self) -> anyhow::Result { - match self { - Self::Software { encoder } | Self::NvencSWFrame { encoder } => { - return Ok(ffmpeg::frame::Video::new( - encoder.format(), - encoder.width(), - encoder.height(), - )); - } - - Self::NvencHWFrame { - encoder, - hw_context, - } => { - let mut frame = ffmpeg::frame::Video::empty(); - - unsafe { - (*frame.as_mut_ptr()).format = ffmpeg::format::Pixel::CUDA as i32; - (*frame.as_mut_ptr()).width = encoder.width() as i32; - (*frame.as_mut_ptr()).height = encoder.height() as i32; - (*frame.as_mut_ptr()).hw_frames_ctx = hw_context.as_raw_mut(); - - hw_context.get_buffer(&mut frame)?; - - (*frame.as_mut_ptr()).linesize[0] = (*frame.as_ptr()).width * 4; - - return Ok(frame); - } - } - } - } - - pub fn send_frame(&mut self, frame: &ffmpeg::Frame) { - match self { - Self::Software { encoder } => { - encoder.send_frame(frame).unwrap(); - } - - Self::NvencSWFrame { encoder } => { - encoder.send_frame(frame).unwrap(); - } - - Self::NvencHWFrame { - encoder, - hw_context: _, - } => { - encoder.send_frame(frame).unwrap(); - } - } - } - - pub fn send_eof(&mut self) { - match self { - Self::Software { encoder } => { - encoder.send_eof().unwrap(); - } - - Self::NvencSWFrame { encoder } => { - // Realistically this should be the same right? - encoder.send_eof().unwrap(); - // todo!("Requires support."); - } - - Self::NvencHWFrame { - encoder, - hw_context: _, - } => { - encoder.send_eof().unwrap(); - } - } - } - - fn receive_packet_impl(&mut self, packet: &mut ffmpeg::Packet) -> Result<(), ffmpeg::Error> { - return match self { - Self::Software { encoder } => encoder.receive_packet(packet), - Self::NvencSWFrame { encoder } => encoder.receive_packet(packet), - Self::NvencHWFrame { - encoder, - hw_context: _, - } => encoder.receive_packet(packet), - }; - } - - // Shuold this return a Result so we can make it easier to know when to continue? - pub fn receive_packet(&mut self, packet: &mut ffmpeg::Packet) -> anyhow::Result<()> { - loop { - match self.receive_packet_impl(packet) { - Ok(_) => break, - Err(ffmpeg::Error::Other { errno }) => { - if errno != EAGAIN { - return Err(ffmpeg::Error::Other { errno: errno }.into()); - } else { - // EAGAIN is not fatal, and simply means - // we should just try again - break; - } - } - Err(e) => return Err(e.into()), - } - } - - Ok(()) - } -} diff --git a/server/src/video/hwdevice.rs b/server/src/video/hwdevice.rs deleted file mode 100644 index a358dbe..0000000 --- a/server/src/video/hwdevice.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::ptr::null_mut; - -use super::check_ret; - -use super::ffmpeg; - -pub struct CudaDeviceContext { - buffer: *mut ffmpeg::sys::AVBufferRef, -} - -impl CudaDeviceContext { - fn new(buffer: *mut ffmpeg::sys::AVBufferRef) -> Self { - Self { buffer } - } - - // pub fn as_device_mut(&mut self) -> &mut ffmpeg::sys::AVHWDeviceContext { - // unsafe { &mut *((*self.buffer).data as *mut ffmpeg::sys::AVHWDeviceContext) } - // } - - // pub fn as_device(&self) -> &ffmpeg::sys::AVHWDeviceContext { - // unsafe { &*((*self.buffer).data as *const ffmpeg::sys::AVHWDeviceContext) } - // } - - pub fn as_raw_mut(&mut self) -> &mut ffmpeg::sys::AVBufferRef { - unsafe { &mut *self.buffer } - } - - // pub fn as_raw(&self) -> &ffmpeg::sys::AVBufferRef { - // unsafe { &*self.buffer } - // } -} - -impl Drop for CudaDeviceContext { - fn drop(&mut self) { - unsafe { - if !self.buffer.is_null() { - ffmpeg::sys::av_buffer_unref(&mut self.buffer); - } - } - } -} - -pub struct CudaDeviceContextBuilder { - buffer: *mut ffmpeg::sys::AVBufferRef, -} - -impl CudaDeviceContextBuilder { - pub fn new() -> anyhow::Result { - let buffer = unsafe { ffmpeg::sys::av_hwdevice_ctx_alloc(ffmpeg::sys::AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA) }; - if buffer.is_null() { - return Err(anyhow::anyhow!("could not allocate a hwdevice".to_string())); - } - - Ok(Self { buffer }) - } - - pub fn build(mut self) -> Result { - check_ret(unsafe { ffmpeg::sys::av_hwdevice_ctx_init(self.buffer) })?; - let result = Ok(CudaDeviceContext::new(self.buffer)); - self.buffer = null_mut(); - - result - } - - pub fn set_cuda_context(mut self, context: ffmpeg::sys::CUcontext) -> Self { - unsafe { - (*(self.as_device_mut().hwctx as *mut ffmpeg::sys::AVCUDADeviceContext)).cuda_ctx = context; - } - - self - } - - pub fn as_device_mut(&mut self) -> &mut ffmpeg::sys::AVHWDeviceContext { - unsafe { &mut *((*self.buffer).data as *mut ffmpeg::sys::AVHWDeviceContext) } - } - - // pub fn as_device(&self) -> &ffmpeg::sys::AVHWDeviceContext { - // unsafe { &*((*self.buffer).data as *const ffmpeg::sys::AVHWDeviceContext) } - // } - - // pub fn as_raw_mut(&mut self) -> &mut ffmpeg::sys::AVBufferRef { - // unsafe { &mut *self.buffer } - // } - - // pub fn as_raw(&self) -> &ffmpeg::sys::AVBufferRef { - // unsafe { &*self.buffer } - // } -} \ No newline at end of file diff --git a/server/src/video/hwframe.rs b/server/src/video/hwframe.rs deleted file mode 100644 index d08932d..0000000 --- a/server/src/video/hwframe.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::ptr::null_mut; - -use super::ffmpeg; - -use ffmpeg::format::Pixel; - -use super::{check_ret, hwdevice::CudaDeviceContext}; - -pub struct HwFrameContext { - _cuda_device_context: CudaDeviceContext, - buffer: *mut ffmpeg::sys::AVBufferRef, -} - -impl HwFrameContext { - fn new(cuda_device_context: CudaDeviceContext, buffer: *mut ffmpeg::sys::AVBufferRef) -> Self { - Self { - _cuda_device_context: cuda_device_context, - buffer, - } - } - - // pub fn as_context_mut(&mut self) -> &mut ffmpeg::sys::AVHWFramesContext { - // unsafe { &mut *((*self.buffer).data as *mut ffmpeg::sys::AVHWFramesContext) } - // } - - // pub fn as_context(&self) -> &ffmpeg::sys::AVHWFramesContext { - // unsafe { &*((*self.buffer).data as *const ffmpeg::sys::AVHWFramesContext) } - // } - - pub fn as_raw_mut(&mut self) -> &mut ffmpeg::sys::AVBufferRef { - unsafe { &mut *self.buffer } - } - - pub fn as_device_context_mut(&mut self) -> &mut ffmpeg::sys::AVBufferRef { - self._cuda_device_context.as_raw_mut() - } - - /// call once to allocate frame - pub fn get_buffer(&mut self, frame: &mut ffmpeg::frame::Video) -> Result<(), ffmpeg::Error> { - unsafe { - super::check_ret(ffmpeg::sys::av_hwframe_get_buffer( - self.buffer, - frame.as_mut_ptr(), - 0, - ))?; - } - - Ok(()) - } - - // pub fn as_raw(&self) -> &ffmpeg::sys::AVBufferRef { - // unsafe { &*self.buffer } - // } -} - -unsafe impl Send for HwFrameContext {} - -impl Drop for HwFrameContext { - fn drop(&mut self) { - unsafe { - if !self.buffer.is_null() { - ffmpeg::sys::av_buffer_unref(&mut self.buffer); - } - } - } -} - -pub struct HwFrameContextBuilder { - cuda_device_context: CudaDeviceContext, - buffer: *mut ffmpeg::sys::AVBufferRef, -} - -impl HwFrameContextBuilder { - pub fn new(mut cuda_device_context: CudaDeviceContext) -> anyhow::Result { - let buffer = unsafe { ffmpeg::sys::av_hwframe_ctx_alloc(cuda_device_context.as_raw_mut()) }; - if buffer.is_null() { - return Err(anyhow::anyhow!("could not allocate a hwframe context")); - } - - Ok(Self { - cuda_device_context, - buffer, - }) - } - - pub fn build(mut self) -> Result { - check_ret(unsafe { ffmpeg::sys::av_hwframe_ctx_init(self.buffer) })?; - let result = Ok(HwFrameContext::new(self.cuda_device_context, self.buffer)); - self.buffer = null_mut(); - - result - } - - pub fn set_width(mut self, width: u32) -> Self { - self.as_frame_mut().width = width as i32; - self - } - - pub fn set_height(mut self, height: u32) -> Self { - self.as_frame_mut().height = height as i32; - self - } - - pub fn set_sw_format(mut self, sw_format: Pixel) -> Self { - self.as_frame_mut().sw_format = sw_format.into(); - self - } - - pub fn set_format(mut self, format: Pixel) -> Self { - self.as_frame_mut().format = format.into(); - self - } - - pub fn as_frame_mut(&mut self) -> &mut ffmpeg::sys::AVHWFramesContext { - unsafe { &mut *((*self.buffer).data as *mut ffmpeg::sys::AVHWFramesContext) } - } - - // pub fn as_frame(&self) -> &ffmpeg::sys::AVHWFramesContext { - // unsafe { &*((*self.buffer).data as *const ffmpeg::sys::AVHWFramesContext) } - // } -} diff --git a/server/src/video/mod.rs b/server/src/video/mod.rs deleted file mode 100644 index 382e9f2..0000000 --- a/server/src/video/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub mod h264_encoder; -//pub mod lc_muxer; - -/// Re-export of `ffmpeg` crate. -pub use ffmpeg as ffmpeg; - -pub mod hwdevice; -pub mod hwframe; - -#[allow(unused)] // FIXME -pub mod encoder_thread; - -pub mod cuda_gl; - -// from hgaiser/moonshine -pub fn check_ret(error_code: i32) -> Result<(), ffmpeg::Error> { - if error_code != 0 { - return Err(ffmpeg::Error::from(error_code)); - } - - Ok(()) -} \ No newline at end of file