replace video with letsplay_av_ffmpeg

yay.
This commit is contained in:
Lily Tsuru 2024-10-17 00:51:26 -04:00
parent 4f585df501
commit 0eb2aaa8c1
14 changed files with 140 additions and 1519 deletions

297
server/Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -1 +0,0 @@
#include "cudaGL.h"

View file

@ -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<Lib> = 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.");
})
}

View file

@ -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<cuda_sys::CUarray, cuda_result::DriverError> {
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<cuda_sys::CUdeviceptr, cuda_result::DriverError> {
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<CudaDevice>,
resource: cuda_sys::CUgraphicsResource,
}
impl GraphicsResource {
pub fn new(device: &Arc<CudaDevice>) -> Self {
Self {
context: device.clone(),
resource: std::ptr::null_mut(),
}
}
pub fn device(&self) -> Arc<CudaDevice> {
self.context.clone()
}
/// Maps this resource.
pub fn map(&mut self) -> Result<MappedGraphicsResource, cuda_result::DriverError> {
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 {}

View file

@ -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<P>(path: P) -> Result<Self, ::libloading::Error>
where
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
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)
}
}

View file

@ -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<H264Encoder>,
frame: Arc<Mutex<Option<ffmpeg::frame::Video>>>,
packet: ffmpeg::Packet,
}
impl EncoderState {
fn new(frame: Arc<Mutex<Option<ffmpeg::frame::Video>>>) -> 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<Mutex<Option<ffmpeg::frame::Video>>> {
// self.frame.clone()
//}
fn send_frame(&mut self, pts: u64, force_keyframe: bool) -> Option<ffmpeg::Packet> {
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<H264Encoder>,
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<CudaDevice>, 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<ffmpeg::Packet> {
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<EncodeThreadInput>,
tx: mpsc::Sender<EncodeThreadOutput>,
frame: &Arc<Mutex<Option<ffmpeg::frame::Video>>>,
) -> 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<Mutex<Option<ffmpeg::frame::Video>>>,
) -> (
mpsc::Receiver<EncodeThreadOutput>,
mpsc::Sender<EncodeThreadInput>,
) {
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<EncodeThreadInput>,
tx: mpsc::Sender<EncodeThreadOutput>,
cuda_device: &Arc<CudaDevice>,
cuda_resource: &Arc<Mutex<GraphicsResource>>,
gl_context: &Arc<Mutex<DeviceContext>>,
) -> 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<u32> = cuda_device.alloc_zeros::<u32>(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::<u32>((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::<u32>(
(*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<CudaDevice>,
cuda_resource: &Arc<Mutex<GraphicsResource>>,
gl_context: &Arc<Mutex<DeviceContext>>,
) -> (
mpsc::Receiver<EncodeThreadOutput>,
mpsc::Sender<EncodeThreadInput>,
) {
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)
}

View file

@ -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<lavc::Context, ffmpeg::Error> {
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<Self> {
// 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<Self> {
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<Self> {
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<ffmpeg::frame::Video> {
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<ControlFlow> 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(())
}
}

View file

@ -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<Self> {
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<CudaDeviceContext, ffmpeg::Error> {
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 }
// }
}

View file

@ -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<Self> {
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<HwFrameContext, ffmpeg::Error> {
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) }
// }
}

View file

@ -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(())
}