Compare commits
5 commits
3c2d4cee6f
...
0eb2aaa8c1
Author | SHA1 | Date | |
---|---|---|---|
0eb2aaa8c1 | |||
4f585df501 | |||
889d7e42ce | |||
efc2800358 | |||
2e4e810e5f |
16 changed files with 253 additions and 1530 deletions
297
server/Cargo.lock
generated
297
server/Cargo.lock
generated
|
@ -4,18 +4,18 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.22.0"
|
version = "0.24.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler2"
|
||||||
version = "1.0.2"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
|
@ -28,9 +28,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.86"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
|
@ -45,15 +45,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.5"
|
version = "0.7.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
@ -89,9 +89,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.4.3"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
|
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -102,7 +102,7 @@ dependencies = [
|
||||||
"mime",
|
"mime",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"sync_wrapper 0.1.2",
|
"sync_wrapper 1.0.1",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -110,11 +110,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-macros"
|
name = "axum-macros"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa"
|
checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -122,30 +121,30 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.73"
|
version = "0.3.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.7"
|
version = "0.22.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.69.4"
|
version = "0.69.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
|
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
|
@ -184,15 +183,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.1"
|
version = "1.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.16"
|
version = "1.1.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b"
|
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
@ -225,9 +224,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.13"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
|
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -319,9 +318,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -334,9 +333,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
|
@ -344,15 +343,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-executor"
|
name = "futures-executor"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
|
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
@ -361,15 +360,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -378,21 +377,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -429,9 +428,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.29.0"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gl"
|
name = "gl"
|
||||||
|
@ -465,12 +464,6 @@ version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -513,9 +506,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.9.4"
|
version = "1.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpdate"
|
name = "httpdate"
|
||||||
|
@ -525,9 +518,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.4.1"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
@ -544,9 +537,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.7"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
|
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
@ -555,16 +548,7 @@ dependencies = [
|
||||||
"hyper",
|
"hyper",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
"tower-service",
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "idna"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-bidi",
|
|
||||||
"unicode-normalization",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -610,6 +594,20 @@ version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "letsplay_av_ffmpeg"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"cudarc",
|
||||||
|
"ffmpeg-next",
|
||||||
|
"gl",
|
||||||
|
"letsplay_gpu",
|
||||||
|
"libloading",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "letsplay_gpu"
|
name = "letsplay_gpu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -620,9 +618,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.158"
|
version = "0.2.159"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -685,11 +683,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.4"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -736,18 +734,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.4"
|
version = "0.36.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a"
|
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
|
@ -784,26 +782,6 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
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]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -818,9 +796,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
|
@ -833,9 +811,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.88"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -881,18 +859,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.3"
|
version = "0.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.6"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -902,9 +880,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -913,9 +891,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "retro_frontend"
|
name = "retro_frontend"
|
||||||
|
@ -925,19 +903,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"libloading",
|
"libloading",
|
||||||
"libretro-sys",
|
"libretro-sys",
|
||||||
"rgb565",
|
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rgb565"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d43e85498d0bb728f77a88b4313eaf4ed21673f3f8a05c36e835cf6c9c0d066"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
|
@ -952,9 +923,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.17"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
|
@ -970,18 +941,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.209"
|
version = "1.0.210"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
|
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.209"
|
version = "1.0.210"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
|
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1093,9 +1064,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.77"
|
version = "2.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1116,18 +1087,18 @@ checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1144,21 +1115,6 @@ dependencies = [
|
||||||
"once_cell",
|
"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]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.40.0"
|
version = "1.40.0"
|
||||||
|
@ -1190,9 +1146,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.21.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
|
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1236,14 +1192,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.4.13"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"pin-project",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"sync_wrapper 0.1.2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
@ -1322,9 +1278,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.21.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
|
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -1335,7 +1291,6 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1345,37 +1300,11 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-bidi"
|
|
||||||
version = "0.3.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
[[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",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf-8"
|
name = "utf-8"
|
||||||
|
@ -1409,10 +1338,10 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"cudarc",
|
"cudarc",
|
||||||
"ffmpeg-next",
|
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gl",
|
"gl",
|
||||||
|
"letsplay_av_ffmpeg",
|
||||||
"letsplay_gpu",
|
"letsplay_gpu",
|
||||||
"libloading",
|
"libloading",
|
||||||
"rand",
|
"rand",
|
||||||
|
|
|
@ -10,6 +10,7 @@ anyhow = "1.0.86"
|
||||||
# Libretro Sex
|
# Libretro Sex
|
||||||
letsplay_gpu.path = "/home/lily/source/lets-play/crates/letsplay_gpu"
|
letsplay_gpu.path = "/home/lily/source/lets-play/crates/letsplay_gpu"
|
||||||
retro_frontend.path = "/home/lily/source/lets-play/crates/retro_frontend"
|
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"
|
gl = "0.14.0"
|
||||||
|
|
||||||
# async
|
# async
|
||||||
|
@ -20,8 +21,7 @@ axum = { version = "0.7.5", features = ["ws", "macros"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
|
||||||
|
|
||||||
# ffmpeg
|
|
||||||
ffmpeg = { version = "7.0.0", package = "ffmpeg-next" }
|
|
||||||
|
|
||||||
# misc stuff
|
# misc stuff
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
mod retro_thread;
|
mod retro_thread;
|
||||||
mod surface;
|
mod surface;
|
||||||
mod types;
|
mod types;
|
||||||
mod video;
|
//mod video;
|
||||||
|
|
||||||
|
use letsplay_av_ffmpeg as video;
|
||||||
|
|
||||||
mod transport;
|
mod transport;
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use cudarc::driver::CudaDevice;
|
use cudarc::driver::CudaDevice;
|
||||||
|
@ -14,15 +15,14 @@ use retro_thread::{spawn_retro_thread, RetroEvent};
|
||||||
use transport::websocket::WebsocketTransport;
|
use transport::websocket::WebsocketTransport;
|
||||||
use transport::{Transport, TransportReciever};
|
use transport::{Transport, TransportReciever};
|
||||||
use video::cuda_gl::safe::GraphicsResource;
|
use video::cuda_gl::safe::GraphicsResource;
|
||||||
|
use video::encoder_thread;
|
||||||
use video::encoder_thread::EncodeThreadInput;
|
use video::encoder_thread::EncodeThreadInput;
|
||||||
use video::{encoder_thread, ffmpeg};
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
mpsc::{self, error::TryRecvError},
|
mpsc::{self, error::TryRecvError},
|
||||||
Mutex as TokioMutex,
|
Mutex as TokioMutex,
|
||||||
|
@ -179,7 +179,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let resource = Arc::new(Mutex::new(GraphicsResource::new(&device)));
|
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(),
|
&device.clone(),
|
||||||
&resource.clone(),
|
&resource.clone(),
|
||||||
&egl_ctx.clone(),
|
&egl_ctx.clone(),
|
||||||
|
@ -198,13 +198,33 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.name("retro_event_rx".into())
|
.name("retro_event_rx".into())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
// load game
|
// load game
|
||||||
|
/*
|
||||||
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadCore(
|
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadCore(
|
||||||
"cores/swanstation_libretro.so".into(),
|
"cores/swanstation_libretro.so".into(),
|
||||||
));
|
));
|
||||||
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadGame(
|
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadGame(
|
||||||
"roms/merged/nmv2/jagb/nmv2jagb.cue".into(),
|
"roms/merged/nmv2/jagb/nmv2jagb.cue".into(),
|
||||||
));
|
));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadCore(
|
||||||
|
"cores/pcsx2_new_libretro.so".into(),
|
||||||
|
));
|
||||||
|
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::LoadGame(
|
||||||
|
"/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
|
// start the libretro thread looping now that we're alive
|
||||||
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::Start);
|
let _ = retro_input_event_tx.blocking_send(retro_thread::RetroInEvent::Start);
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,16 @@ use anyhow::Result;
|
||||||
|
|
||||||
use retro_frontend::{
|
use retro_frontend::{
|
||||||
frontend::{Frontend, FrontendInterface, HwGlInitData},
|
frontend::{Frontend, FrontendInterface, HwGlInitData},
|
||||||
input_devices::{InputDevice, RetroPad},
|
input_devices::{AnalogRetroPad, InputDevice, RetroPad},
|
||||||
libretro_sys_new,
|
libretro_sys_new,
|
||||||
};
|
};
|
||||||
|
|
||||||
use gpu::egl_helpers::DeviceContext;
|
use gpu::egl_helpers::DeviceContext;
|
||||||
use letsplay_gpu as gpu;
|
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.
|
/// Called by OpenGL. We use this to dump errors.
|
||||||
extern "system" fn opengl_message_callback(
|
extern "system" fn opengl_message_callback(
|
||||||
|
@ -183,16 +185,19 @@ impl RetroState {
|
||||||
let size = framebuffer.size.clone();
|
let size = framebuffer.size.clone();
|
||||||
let buffer = framebuffer.get_buffer();
|
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;
|
||||||
|
|
||||||
for y in 0..size.height {
|
|
||||||
let src_line_off = (y as u32 * pitch) as usize;
|
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
|
// copy only
|
||||||
if has_disconnected_pitch {
|
//if has_disconnected_pitch {
|
||||||
dest_line_off = (y * pitch) as usize;
|
//dest_line_off = (y * pitch) as usize;
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Create slices repressenting each part
|
// Create slices repressenting each part
|
||||||
let src_slice = &slice[src_line_off..src_line_off + size.width as usize];
|
let src_slice = &slice[src_line_off..src_line_off + size.width as usize];
|
||||||
|
@ -214,24 +219,24 @@ impl FrontendInterface for RetroState {
|
||||||
self.get_frontend().set_gl_fbo(raw);
|
self.get_frontend().set_gl_fbo(raw);
|
||||||
|
|
||||||
if !self.gl_rendering {
|
if !self.gl_rendering {
|
||||||
self.software_framebuffer.resize(Size { width, height });
|
self.software_framebuffer.resize(crate::types::Size { width, height });
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
// register the FBO's texture to our cuda interop resource
|
||||||
}
|
|
||||||
|
|
||||||
// map to cuda
|
|
||||||
{
|
{
|
||||||
let mut locked = self
|
let mut locked = self
|
||||||
.cuda_resource
|
.cuda_resource
|
||||||
.lock()
|
.lock()
|
||||||
.expect("YOU MOTHERFUCKER PISS GO COUNT YOUR DICK");
|
.expect("Failed to lock CUDA resource");
|
||||||
|
|
||||||
locked.device().bind_to_thread().expect("fuck");
|
locked
|
||||||
|
.device()
|
||||||
|
.bind_to_thread()
|
||||||
|
.expect("Failed to bind CUDA device to thread");
|
||||||
|
|
||||||
locked
|
locked
|
||||||
.register(self.gl_framebuffer.texture_id(), gl::TEXTURE_2D)
|
.register(self.gl_framebuffer.texture_id(), gl::TEXTURE_2D)
|
||||||
.expect("you fucking asswater");
|
.expect("Failed to register OpenGL texture with CUDA Graphics resource");
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.event_tx.blocking_send(RetroEvent::Resize {
|
let _ = self.event_tx.blocking_send(RetroEvent::Resize {
|
||||||
|
@ -244,19 +249,24 @@ impl FrontendInterface for RetroState {
|
||||||
|
|
||||||
let size = self.software_framebuffer.size.clone();
|
let size = self.software_framebuffer.size.clone();
|
||||||
|
|
||||||
// upload texture to GPU
|
// Upload the software framebuffer to the GPU
|
||||||
|
// (we already flip it to make opengl happy)
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::TexImage2D(
|
gl::BindTexture(gl::TEXTURE_2D, self.gl_framebuffer.texture_id());
|
||||||
|
|
||||||
|
gl::TexSubImage2D(
|
||||||
gl::TEXTURE_2D,
|
gl::TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
gl::RGBA as i32,
|
0,
|
||||||
|
0,
|
||||||
size.width as i32,
|
size.width as i32,
|
||||||
size.height as i32,
|
size.height as i32,
|
||||||
0,
|
gl::RGBA,
|
||||||
gl::RGBA_INTEGER,
|
gl::UNSIGNED_BYTE,
|
||||||
gl::UNSIGNED_INT_8_8_8_8,
|
|
||||||
self.software_framebuffer.get_buffer().as_mut_ptr() as *const _,
|
self.software_framebuffer.get_buffer().as_mut_ptr() as *const _,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.event_tx.blocking_send(RetroEvent::Frame);
|
let _ = self.event_tx.blocking_send(RetroEvent::Frame);
|
||||||
|
@ -350,12 +360,79 @@ impl FrontendInterface for RetroState {
|
||||||
.press_button(libretro_sys_new::DEVICE_ID_JOYPAD_R2, None);
|
.press_button(libretro_sys_new::DEVICE_ID_JOYPAD_R2, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Key::t => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_LEFT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_Y,
|
||||||
|
Some(-0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::g => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_LEFT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_Y,
|
||||||
|
Some(0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::f => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_LEFT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_X,
|
||||||
|
Some(-0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::h => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_LEFT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_X,
|
||||||
|
Some(0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::i => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_RIGHT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_Y,
|
||||||
|
Some(-0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::k => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_RIGHT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_Y,
|
||||||
|
Some(0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::j => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_RIGHT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_X,
|
||||||
|
Some(-0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::l => {
|
||||||
|
self.pad.press_analog_axis(
|
||||||
|
libretro_sys_new::DEVICE_INDEX_ANALOG_RIGHT,
|
||||||
|
libretro_sys_new::DEVICE_ID_ANALOG_X,
|
||||||
|
Some(0x7fff),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hw_gl_init(&mut self) -> Option<HwGlInitData> {
|
fn hw_gl_init(&mut self) -> Option<HwGlInitData> {
|
||||||
|
// test SW-only even if a core wants to upgrade
|
||||||
|
//return None;
|
||||||
|
|
||||||
// Only create a new EGL/OpenGL context if we have to.
|
// Only create a new EGL/OpenGL context if we have to.
|
||||||
let context = self.egl_context.lock().expect("fuck you!");
|
let context = self.egl_context.lock().expect("fuck you!");
|
||||||
let extensions = gpu::egl_helpers::get_extensions(context.get_display());
|
let extensions = gpu::egl_helpers::get_extensions(context.get_display());
|
||||||
|
@ -375,6 +452,8 @@ impl FrontendInterface for RetroState {
|
||||||
self.software_framebuffer.clear();
|
self.software_framebuffer.clear();
|
||||||
self.gl_rendering = true;
|
self.gl_rendering = true;
|
||||||
|
|
||||||
|
tracing::info!("Rendering with OpenGL via EGL");
|
||||||
|
|
||||||
return Some(HwGlInitData {
|
return Some(HwGlInitData {
|
||||||
get_proc_address: gpu::egl::GetProcAddress as *mut std::ffi::c_void,
|
get_proc_address: gpu::egl::GetProcAddress as *mut std::ffi::c_void,
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,16 +6,12 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use rand::distributions::DistString;
|
use rand::distributions::DistString;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use tokio::sync::{
|
use tokio::sync::broadcast;
|
||||||
broadcast,
|
|
||||||
mpsc::{self, error::TryRecvError},
|
|
||||||
Mutex as TokioMutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{
|
extract::{
|
||||||
connect_info::ConnectInfo,
|
connect_info::ConnectInfo,
|
||||||
ws::{self, Message, WebSocket, WebSocketUpgrade},
|
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||||
State,
|
State,
|
||||||
},
|
},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
|
@ -34,7 +30,7 @@ async fn ws_handler<T: TransportReciever + Sync + Send + 'static>(
|
||||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||||
|
|
||||||
State(state): State<Arc<T>>,
|
State(state): State<Arc<T>>,
|
||||||
mut broadcast_rx: broadcast::Receiver<TransportMessage>,
|
broadcast_rx: broadcast::Receiver<TransportMessage>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
// finalize the upgrade process by returning upgrade callback.
|
// finalize the upgrade process by returning upgrade callback.
|
||||||
// we can customize the callback by sending additional info such as address.
|
// we can customize the callback by sending additional info such as address.
|
||||||
|
@ -44,7 +40,7 @@ async fn ws_handler<T: TransportReciever + Sync + Send + 'static>(
|
||||||
/// Actual websocket statemachine (one will be spawned per connection)
|
/// Actual websocket statemachine (one will be spawned per connection)
|
||||||
async fn handle_socket<T: TransportReciever + Sync + Send + 'static>(
|
async fn handle_socket<T: TransportReciever + Sync + Send + 'static>(
|
||||||
socket: WebSocket,
|
socket: WebSocket,
|
||||||
who: SocketAddr,
|
_who: SocketAddr, // FIXME
|
||||||
state: Arc<T>,
|
state: Arc<T>,
|
||||||
mut broadcast_rx: broadcast::Receiver<TransportMessage>,
|
mut broadcast_rx: broadcast::Receiver<TransportMessage>,
|
||||||
) {
|
) {
|
||||||
|
@ -56,7 +52,7 @@ async fn handle_socket<T: TransportReciever + Sync + Send + 'static>(
|
||||||
|
|
||||||
let recv_clone = Arc::clone(&state);
|
let recv_clone = Arc::clone(&state);
|
||||||
|
|
||||||
state.on_connect(&username).await;
|
let _ = state.on_connect(&username).await;
|
||||||
|
|
||||||
let mut send_task = tokio::spawn(async move {
|
let mut send_task = tokio::spawn(async move {
|
||||||
while let Ok(msg) = broadcast_rx.recv().await {
|
while let Ok(msg) = broadcast_rx.recv().await {
|
||||||
|
@ -80,7 +76,7 @@ async fn handle_socket<T: TransportReciever + Sync + Send + 'static>(
|
||||||
while let Some(Ok(msg)) = receiver.next().await {
|
while let Some(Ok(msg)) = receiver.next().await {
|
||||||
match msg {
|
match msg {
|
||||||
Message::Text(msg) => {
|
Message::Text(msg) => {
|
||||||
recv_clone.on_message(&username_clone, &msg).await;
|
let _ = recv_clone.on_message(&username_clone, &msg).await;
|
||||||
}
|
}
|
||||||
Message::Close(_) => break,
|
Message::Close(_) => break,
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -98,7 +94,7 @@ async fn handle_socket<T: TransportReciever + Sync + Send + 'static>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.on_leave(&username).await;
|
let _ = state.on_leave(&username).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebsocketTransport {
|
impl WebsocketTransport {
|
||||||
|
|
|
@ -8,11 +8,11 @@ pub struct Rect {
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
//#[derive(Debug)]
|
||||||
pub struct Point {
|
//pub struct Point {
|
||||||
pub x: u32,
|
// pub x: u32,
|
||||||
pub y: u32,
|
// pub y: u32,
|
||||||
}
|
//}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
|
|
|
@ -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
|
|
|
@ -1 +0,0 @@
|
||||||
#include "cudaGL.h"
|
|
|
@ -1,14 +0,0 @@
|
||||||
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.");
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
use cudarc::driver::{result as cuda_result, safe as cuda_safe, sys as cuda_sys, CudaDevice};
|
|
||||||
|
|
||||||
use super::sys;
|
|
||||||
|
|
||||||
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 {}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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(30);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// FIXME: this currently breaks the avbufferref system a bit
|
|
||||||
(*video_encoder_context.as_mut_ptr()).hw_frames_ctx = hw_frame_context.as_raw_mut();
|
|
||||||
(*video_encoder_context.as_mut_ptr()).hw_device_ctx =
|
|
||||||
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)?;
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +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 }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
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 }
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -1,109 +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 {
|
|
||||||
unsafe {
|
|
||||||
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 {}
|
|
||||||
|
|
||||||
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) }
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -1,21 +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;
|
|
||||||
|
|
||||||
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(())
|
|
||||||
}
|
|
Loading…
Reference in a new issue