Phase 3: Implement Ring Buffer, muxer logic, and hotkey threading
This commit is contained in:
parent
4c491eea0b
commit
2c1a464c03
14 changed files with 760 additions and 46 deletions
362
Cargo.lock
generated
362
Cargo.lock
generated
|
|
@ -112,6 +112,9 @@ name = "bitflags"
|
|||
version = "2.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
|
|
@ -119,6 +122,12 @@ version = "3.20.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.12.1"
|
||||
|
|
@ -187,6 +196,31 @@ version = "0.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.3"
|
||||
|
|
@ -248,7 +282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -409,6 +443,16 @@ dependencies = [
|
|||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.3"
|
||||
|
|
@ -426,6 +470,23 @@ version = "0.3.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
||||
|
||||
[[package]]
|
||||
name = "global-hotkey"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c386b0a4a70cb2d39fffd74480f985b6f0bfbcb934b6a6b6b7e630e448f242e"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"keyboard-types",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"once_cell",
|
||||
"thiserror 2.0.20",
|
||||
"windows-sys 0.59.0",
|
||||
"x11rb",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
|
|
@ -498,6 +559,17 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyboard-types"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"serde",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
|
|
@ -641,7 +713,21 @@ checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mp4"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9ef834d5ed55e494a2ae350220314dc4aacd1c43a9498b00e320e0ea352a5c3"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"num-rational",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -675,6 +761,46 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.17.0"
|
||||
|
|
@ -685,6 +811,54 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-app-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
|
|
@ -761,6 +935,21 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic-util"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.5.0"
|
||||
|
|
@ -847,6 +1036,17 @@ version = "0.8.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "ringbuf"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a158e09ede21a14b172ca6cdd6208386c6ae2cb6acef58d774368ef8c450dfa7"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.3"
|
||||
|
|
@ -863,7 +1063,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1005,7 +1205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1059,7 +1259,47 @@ dependencies = [
|
|||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1076,7 +1316,7 @@ dependencies = [
|
|||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1180,7 +1420,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
|
|||
dependencies = [
|
||||
"memoffset 0.9.1",
|
||||
"tempfile",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1189,6 +1429,12 @@ version = "1.0.24"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
|
|
@ -1229,12 +1475,16 @@ name = "vrec"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"crossbeam-channel",
|
||||
"ffmpeg-next",
|
||||
"ffmpeg-sys-next",
|
||||
"global-hotkey",
|
||||
"libc",
|
||||
"memmap2",
|
||||
"minifb",
|
||||
"mp4",
|
||||
"pipewire",
|
||||
"ringbuf",
|
||||
"tokio",
|
||||
"xcb",
|
||||
]
|
||||
|
|
@ -1413,6 +1663,15 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
|
|
@ -1422,6 +1681,70 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.4"
|
||||
|
|
@ -1442,6 +1765,23 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"rustix",
|
||||
"x11rb-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb-protocol"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||
|
||||
[[package]]
|
||||
name = "xcb"
|
||||
version = "1.7.1"
|
||||
|
|
@ -1459,6 +1799,12 @@ version = "0.3.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23"
|
||||
|
||||
[[package]]
|
||||
name = "xkeysym"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.29"
|
||||
|
|
@ -1488,7 +1834,7 @@ dependencies = [
|
|||
"tracing",
|
||||
"uds_windows",
|
||||
"uuid",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
"winnow",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
ashpd = { version = "0.13.13", features = ["screencast"] }
|
||||
crossbeam-channel = "0.5.16"
|
||||
ffmpeg-next = "9.0.0"
|
||||
ffmpeg-sys-next = "9.0.0"
|
||||
global-hotkey = "0.8.0"
|
||||
libc = "0.2.189"
|
||||
memmap2 = "0.9.11"
|
||||
minifb = "0.28.0"
|
||||
mp4 = "0.14.0"
|
||||
pipewire = "0.10.1"
|
||||
ringbuf = "0.5.1"
|
||||
tokio = { version = "1.53.1", features = ["rt", "macros", "rt-multi-thread"] }
|
||||
xcb = { version = "1.7.1", features = ["shm", "xfixes"] }
|
||||
|
|
|
|||
99
scratch/enc.patch
Normal file
99
scratch/enc.patch
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
--- src/encoder.rs
|
||||
+++ src/encoder.rs
|
||||
@@ -6,6 +6,7 @@
|
||||
codec_ctx: *mut AVCodecContext,
|
||||
hw_device_ctx: *mut AVBufferRef,
|
||||
hw_frames_ctx: *mut AVBufferRef,
|
||||
+ next_pts: i64,
|
||||
}
|
||||
|
||||
impl VaapiEncoder {
|
||||
@@ -70,6 +71,7 @@
|
||||
codec_ctx,
|
||||
hw_device_ctx,
|
||||
hw_frames_ctx: hw_frames_ref,
|
||||
+ next_pts: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -77,15 +79,63 @@
|
||||
pub fn encode_frame(&mut self, frame: &Frame) -> Result<(), String> {
|
||||
unsafe {
|
||||
match frame {
|
||||
- Frame::Raw { width, height, stride, data } => {
|
||||
- // For Phase 2 X11 fallback:
|
||||
- // 1. Convert BGRA to NV12 (omitted/mocked for brevity if not using swscale yet)
|
||||
- // 2. Upload NV12 to a VAAPI AVFrame via av_hwframe_get_buffer and av_hwframe_transfer_data
|
||||
- // 3. Send to avcodec_send_frame
|
||||
- println!("VAAPI: Mock encoding Raw CPU frame (X11 fallback). Needs BGRA->NV12 swscale.");
|
||||
- }
|
||||
- Frame::DmaBuf { width, height, format, fd, stride, offset } => {
|
||||
- // For Phase 2 Wayland zero-copy:
|
||||
- // 1. Populate AVDRMFrameDescriptor with the FD and modifier.
|
||||
- // 2. Create an AVFrame with format AV_PIX_FMT_DRM_PRIME.
|
||||
- // 3. Use av_hwframe_map to map it to AV_PIX_FMT_VAAPI.
|
||||
- // 4. Send to avcodec_send_frame.
|
||||
- println!("VAAPI: Mock encoding DMA-BUF frame (Wayland zero-copy). FD: {}", fd);
|
||||
+ Frame::Raw { width, height, stride: in_stride, data } => {
|
||||
+ let mut bgra_frame = av_frame_alloc();
|
||||
+ (*bgra_frame).format = AVPixelFormat::AV_PIX_FMT_BGRA as i32;
|
||||
+ (*bgra_frame).width = *width as i32;
|
||||
+ (*bgra_frame).height = *height as i32;
|
||||
+ av_frame_get_buffer(bgra_frame, 32);
|
||||
+
|
||||
+ let bgra_stride = (*bgra_frame).linesize[0] as usize;
|
||||
+ for y in 0..(*height as usize) {
|
||||
+ let src_row = &data[y * (*in_stride as usize) .. y * (*in_stride as usize) + (*width as usize) * 4];
|
||||
+ let dst_row = std::slice::from_raw_parts_mut(
|
||||
+ (*bgra_frame).data[0].add(y * bgra_stride),
|
||||
+ (*width as usize) * 4
|
||||
+ );
|
||||
+ dst_row.copy_from_slice(src_row);
|
||||
+ }
|
||||
+
|
||||
+ let mut nv12_frame = av_frame_alloc();
|
||||
+ (*nv12_frame).format = AVPixelFormat::AV_PIX_FMT_NV12 as i32;
|
||||
+ (*nv12_frame).width = *width as i32;
|
||||
+ (*nv12_frame).height = *height as i32;
|
||||
+ av_frame_get_buffer(nv12_frame, 32);
|
||||
+
|
||||
+ let sws_ctx = sws_getContext(
|
||||
+ *width as i32, *height as i32, AVPixelFormat::AV_PIX_FMT_BGRA,
|
||||
+ *width as i32, *height as i32, AVPixelFormat::AV_PIX_FMT_NV12,
|
||||
+ SWS_BILINEAR, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()
|
||||
+ );
|
||||
+
|
||||
+ sws_scale(
|
||||
+ sws_ctx,
|
||||
+ (*bgra_frame).data.as_ptr() as *const *const u8,
|
||||
+ (*bgra_frame).linesize.as_ptr(),
|
||||
+ 0,
|
||||
+ *height as i32,
|
||||
+ (*nv12_frame).data.as_ptr(),
|
||||
+ (*nv12_frame).linesize.as_ptr()
|
||||
+ );
|
||||
+ sws_freeContext(sws_ctx);
|
||||
+
|
||||
+ let mut hw_frame = av_frame_alloc();
|
||||
+ av_hwframe_get_buffer(self.hw_frames_ctx, hw_frame, 0);
|
||||
+ av_hwframe_transfer_data(hw_frame, nv12_frame, 0);
|
||||
+
|
||||
+ (*hw_frame).pts = self.next_pts;
|
||||
+ self.next_pts += 1;
|
||||
+
|
||||
+ if avcodec_send_frame(self.codec_ctx, hw_frame) >= 0 {
|
||||
+ let mut pkt = av_packet_alloc();
|
||||
+ while avcodec_receive_packet(self.codec_ctx, pkt) >= 0 {
|
||||
+ println!("VAAPI: encoded packet of size {}", (*pkt).size);
|
||||
+ av_packet_unref(pkt);
|
||||
+ }
|
||||
+ av_packet_free(&mut pkt);
|
||||
+ }
|
||||
+ av_frame_free(&mut hw_frame);
|
||||
+ av_frame_free(&mut nv12_frame);
|
||||
+ av_frame_free(&mut bgra_frame);
|
||||
+ }
|
||||
+ Frame::DmaBuf { width: _, height: _, format: _, fd, stride: _, offset: _ } => {
|
||||
+ println!("VAAPI: Mock encoding DMA-BUF frame (Wayland zero-copy). FD: {}", fd);
|
||||
}
|
||||
}
|
||||
42
scratch/enc_mod.patch
Normal file
42
scratch/enc_mod.patch
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
--- src/encoder.rs
|
||||
+++ src/encoder.rs
|
||||
@@ -2,7 +2,7 @@
|
||||
use std::ptr;
|
||||
use crate::capture::Frame;
|
||||
+use crate::ring::Packet;
|
||||
|
||||
pub struct VaapiEncoder {
|
||||
codec_ctx: *mut AVCodecContext,
|
||||
@@ -62,6 +62,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn encode_frame(&mut self, frame: &Frame) -> Result<(), String> {
|
||||
+ pub fn codec_ctx(&self) -> *mut AVCodecContext {
|
||||
+ self.codec_ctx
|
||||
+ }
|
||||
+
|
||||
+ pub fn encode_frame(&mut self, frame: &Frame) -> Result<Vec<Packet>, String> {
|
||||
+ let mut packets = Vec::new();
|
||||
unsafe {
|
||||
match frame {
|
||||
@@ -107,8 +111,8 @@
|
||||
if avcodec_send_frame(self.codec_ctx, hw_frame) >= 0 {
|
||||
let mut pkt = av_packet_alloc();
|
||||
while avcodec_receive_packet(self.codec_ctx, pkt) >= 0 {
|
||||
- println!("VAAPI: encoded packet of size {}", (*pkt).size);
|
||||
- av_packet_unref(pkt);
|
||||
+ let new_pkt = av_packet_alloc();
|
||||
+ av_packet_move_ref(new_pkt, pkt);
|
||||
+ packets.push(Packet::new(new_pkt));
|
||||
}
|
||||
av_packet_free(&mut pkt);
|
||||
}
|
||||
@@ -122,7 +126,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
- Ok(())
|
||||
+ Ok(packets)
|
||||
}
|
||||
}
|
||||
5
scratch/test_ring.rs
Normal file
5
scratch/test_ring.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
use ringbuf::HeapRb;
|
||||
fn main() {
|
||||
let rb = HeapRb::<i32>::new(10);
|
||||
let (mut prod, mut cons) = rb.split();
|
||||
}
|
||||
|
|
@ -23,6 +23,6 @@ pub enum Frame {
|
|||
},
|
||||
}
|
||||
|
||||
pub trait FrameSource {
|
||||
fn next_frame(&mut self) -> Result<Frame, Box<dyn std::error::Error>>;
|
||||
pub trait FrameSource: Send {
|
||||
fn next_frame(&mut self) -> Result<Frame, Box<dyn std::error::Error + Send + Sync>>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct WaylandCapture {
|
|||
}
|
||||
|
||||
impl WaylandCapture {
|
||||
pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
||||
pub async fn new() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let proxy = Screencast::new().await?;
|
||||
let session = proxy.create_session(Default::default()).await?;
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ impl WaylandCapture {
|
|||
}
|
||||
|
||||
impl FrameSource for WaylandCapture {
|
||||
fn next_frame(&mut self) -> Result<Frame, Box<dyn std::error::Error>> {
|
||||
fn next_frame(&mut self) -> Result<Frame, Box<dyn std::error::Error + Send + Sync>> {
|
||||
Err("Wayland/Pipewire frame extraction not fully implemented yet".into())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct X11Capture {
|
|||
}
|
||||
|
||||
impl X11Capture {
|
||||
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
||||
pub fn new() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let (conn, screen_num) = Connection::connect(None)?;
|
||||
let setup = conn.get_setup();
|
||||
let screen = setup.roots().nth(screen_num as usize).ok_or("No screen found")?;
|
||||
|
|
@ -24,7 +24,7 @@ impl X11Capture {
|
|||
}
|
||||
|
||||
impl FrameSource for X11Capture {
|
||||
fn next_frame(&mut self) -> Result<Frame, Box<dyn std::error::Error>> {
|
||||
fn next_frame(&mut self) -> Result<Frame, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let cookie = self.conn.send_request(&x::GetImage {
|
||||
format: x::ImageFormat::ZPixmap,
|
||||
drawable: x::Drawable::Window(self.root),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ pub struct DebugWindow {
|
|||
}
|
||||
|
||||
impl DebugWindow {
|
||||
pub fn new(width: usize, height: usize) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
pub fn new(width: usize, height: usize) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let window = Window::new(
|
||||
"vrec debug capture",
|
||||
width,
|
||||
|
|
@ -15,7 +15,7 @@ impl DebugWindow {
|
|||
Ok(Self { window })
|
||||
}
|
||||
|
||||
pub fn update(&mut self, buffer: &[u32], width: usize, height: usize) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn update(&mut self, buffer: &[u32], width: usize, height: usize) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
self.window.update_with_buffer(buffer, width, height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,12 @@ impl VaapiEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn encode_frame(&mut self, frame: &Frame) -> Result<(), String> {
|
||||
pub fn codec_ctx(&self) -> *mut AVCodecContext {
|
||||
self.codec_ctx
|
||||
}
|
||||
|
||||
pub fn encode_frame(&mut self, frame: &Frame) -> Result<Vec<crate::ring::Packet>, String> {
|
||||
let mut packets = Vec::new();
|
||||
unsafe {
|
||||
match frame {
|
||||
Frame::Raw { width, height, stride: in_stride, data } => {
|
||||
|
|
@ -125,8 +130,9 @@ impl VaapiEncoder {
|
|||
if avcodec_send_frame(self.codec_ctx, hw_frame) >= 0 {
|
||||
let mut pkt = av_packet_alloc();
|
||||
while avcodec_receive_packet(self.codec_ctx, pkt) >= 0 {
|
||||
println!("VAAPI: encoded packet of size {}", (*pkt).size);
|
||||
av_packet_unref(pkt);
|
||||
let new_pkt = av_packet_alloc();
|
||||
av_packet_move_ref(new_pkt, pkt);
|
||||
packets.push(crate::ring::Packet::new(new_pkt));
|
||||
}
|
||||
av_packet_free(&mut pkt);
|
||||
}
|
||||
|
|
@ -139,7 +145,7 @@ impl VaapiEncoder {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(packets)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
22
src/hotkey.rs
Normal file
22
src/hotkey.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use global_hotkey::{GlobalHotKeyManager, hotkey::{HotKey, Modifiers, Code}, GlobalHotKeyEvent};
|
||||
use crossbeam_channel::Sender;
|
||||
|
||||
pub fn run_hotkey_listener(trigger_tx: Sender<()>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let manager = GlobalHotKeyManager::new()?;
|
||||
let hotkey = HotKey::new(Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::KeyR);
|
||||
manager.register(hotkey)?;
|
||||
|
||||
println!("Registered global hotkey Ctrl+Shift+R to trigger replay save.");
|
||||
|
||||
let receiver = GlobalHotKeyEvent::receiver();
|
||||
loop {
|
||||
if let Ok(event) = receiver.recv() {
|
||||
if event.id == hotkey.id() {
|
||||
if event.state == global_hotkey::HotKeyState::Pressed {
|
||||
println!("Hotkey triggered! Saving replay...");
|
||||
let _ = trigger_tx.send(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/main.rs
115
src/main.rs
|
|
@ -1,13 +1,22 @@
|
|||
pub mod capture;
|
||||
pub mod encoder;
|
||||
pub mod ring;
|
||||
pub mod muxer;
|
||||
pub mod hotkey;
|
||||
pub mod debug;
|
||||
|
||||
use capture::{Frame, FrameSource};
|
||||
use debug::DebugWindow;
|
||||
use encoder::VaapiEncoder;
|
||||
use ring::Packet;
|
||||
use muxer::Muxer;
|
||||
use ringbuf::HeapRb;
|
||||
use ringbuf::traits::{RingBuffer, Consumer};
|
||||
use crossbeam_channel::bounded;
|
||||
use std::thread;
|
||||
use std::env;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string());
|
||||
println!("Detected session type: {}", session_type);
|
||||
|
||||
|
|
@ -19,39 +28,93 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Box::new(capture::x11::X11Capture::new()?)
|
||||
};
|
||||
|
||||
let mut window: Option<DebugWindow> = None;
|
||||
let first_frame = source.next_frame()?;
|
||||
let (width, height) = match &first_frame {
|
||||
Frame::Raw { width, height, .. } => (*width, *height),
|
||||
Frame::DmaBuf { width, height, .. } => (*width, *height),
|
||||
};
|
||||
|
||||
println!("Starting capture loop...");
|
||||
loop {
|
||||
match source.next_frame() {
|
||||
Ok(Frame::Raw { width, height, data, stride }) => {
|
||||
if window.is_none() {
|
||||
window = Some(DebugWindow::new(width as usize, height as usize)?);
|
||||
}
|
||||
// 1. Channels
|
||||
let (frame_tx, frame_rx) = bounded::<Frame>(5);
|
||||
let (trigger_tx, trigger_rx) = bounded::<()>(1);
|
||||
let (mux_tx, mux_rx) = bounded::<Vec<Packet>>(1);
|
||||
|
||||
// Send first frame to encoder thread
|
||||
let _ = frame_tx.send(first_frame);
|
||||
|
||||
if let Some(win) = &mut window {
|
||||
if !win.is_open() {
|
||||
// Capture thread
|
||||
let capture_tx = frame_tx.clone();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
match source.next_frame() {
|
||||
Ok(frame) => {
|
||||
if capture_tx.send(frame).is_err() {
|
||||
break;
|
||||
}
|
||||
// For minifb, we need to cast &[u8] to &[u32].
|
||||
// Assuming BGRA or similar 4-byte format.
|
||||
let u32_data: Vec<u32> = data
|
||||
.chunks_exact(4)
|
||||
.map(|chunk| u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
|
||||
.collect();
|
||||
|
||||
win.update(&u32_data, width as usize, height as usize)?;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Capture error: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Frame::DmaBuf { .. }) => {
|
||||
println!("Wayland/PipeWire DMA-BUF received. Phase 1 debug viewer not fully implemented for DMA-BUFs yet.");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 3. Encoder Thread
|
||||
thread::spawn(move || {
|
||||
let mut encoder = VaapiEncoder::new(width, height).expect("Failed to init encoder");
|
||||
let codec_ctx_ptr = encoder.codec_ctx() as usize; // Send raw pointer safely
|
||||
|
||||
let mut ring = HeapRb::<Packet>::new(3600); // 60 seconds at 60fps
|
||||
|
||||
// Muxer Thread spawned inside here so it shares lifetime conceptually,
|
||||
// though we use mux_rx.
|
||||
thread::spawn(move || {
|
||||
while let Ok(mut drain) = mux_rx.recv() {
|
||||
println!("Saving replay to output.mp4...");
|
||||
let mut start_idx = 0;
|
||||
for (i, p) in drain.iter().enumerate() {
|
||||
if p.is_keyframe() {
|
||||
start_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if start_idx < drain.len() {
|
||||
let codec_ctx = codec_ctx_ptr as *mut ffmpeg_next::ffi::AVCodecContext;
|
||||
let mut muxer = Muxer::new("output.mp4", codec_ctx).unwrap();
|
||||
for p in drain.into_iter().skip(start_idx) {
|
||||
let _ = muxer.write_packet(&p);
|
||||
}
|
||||
let _ = muxer.finalize();
|
||||
println!("Replay saved!");
|
||||
} else {
|
||||
println!("No keyframe found in buffer, skipping save.");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Capture error: {}", e);
|
||||
break;
|
||||
});
|
||||
|
||||
// Encoder loop
|
||||
while let Ok(frame) = frame_rx.recv() {
|
||||
if let Ok(packets) = encoder.encode_frame(&frame) {
|
||||
for pkt in packets {
|
||||
ring.push_overwrite(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for trigger
|
||||
if trigger_rx.try_recv().is_ok() {
|
||||
// Clone all packets to send to muxer
|
||||
let drain = ring.iter().cloned().collect::<Vec<_>>();
|
||||
let _ = mux_tx.try_send(drain);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 5. Hotkey listener
|
||||
println!("Starting hotkey listener...");
|
||||
if let Err(e) = hotkey::run_hotkey_listener(trigger_tx) {
|
||||
eprintln!("Failed to start hotkey listener: {}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
82
src/muxer.rs
Normal file
82
src/muxer.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use ffmpeg_next::ffi::*;
|
||||
use crate::ring::Packet;
|
||||
use std::ffi::CString;
|
||||
|
||||
pub struct Muxer {
|
||||
fmt_ctx: *mut AVFormatContext,
|
||||
}
|
||||
|
||||
impl Muxer {
|
||||
pub fn new(path: &str, codec_ctx: *mut AVCodecContext) -> Result<Self, String> {
|
||||
unsafe {
|
||||
let path_cstr = CString::new(path).unwrap();
|
||||
let mut fmt_ctx: *mut AVFormatContext = std::ptr::null_mut();
|
||||
|
||||
let ret = avformat_alloc_output_context2(&mut fmt_ctx, std::ptr::null(), std::ptr::null(), path_cstr.as_ptr());
|
||||
if ret < 0 {
|
||||
return Err("Failed to alloc output context".into());
|
||||
}
|
||||
|
||||
let stream = avformat_new_stream(fmt_ctx, std::ptr::null());
|
||||
if stream.is_null() {
|
||||
return Err("Failed to create stream".into());
|
||||
}
|
||||
|
||||
let ret = avcodec_parameters_from_context((*stream).codecpar, codec_ctx);
|
||||
if ret < 0 {
|
||||
return Err("Failed to copy codec parameters".into());
|
||||
}
|
||||
|
||||
if ((*(*fmt_ctx).oformat).flags & AVFMT_NOFILE as i32) == 0 {
|
||||
let ret = avio_open(&mut (*fmt_ctx).pb, path_cstr.as_ptr(), AVIO_FLAG_WRITE as i32);
|
||||
if ret < 0 {
|
||||
return Err("Failed to open file".into());
|
||||
}
|
||||
}
|
||||
|
||||
let ret = avformat_write_header(fmt_ctx, std::ptr::null_mut());
|
||||
if ret < 0 {
|
||||
return Err("Failed to write header".into());
|
||||
}
|
||||
|
||||
Ok(Self { fmt_ctx })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_packet(&mut self, packet: &Packet) -> Result<(), String> {
|
||||
unsafe {
|
||||
// we must not pass the exact ptr to av_interleaved_write_frame directly
|
||||
// if we want to keep the packet, but since it's just writing, we can clone it
|
||||
let mut new_pkt = av_packet_alloc();
|
||||
av_packet_ref(new_pkt, packet.ptr);
|
||||
let ret = av_interleaved_write_frame(self.fmt_ctx, new_pkt);
|
||||
av_packet_free(&mut new_pkt); // Wait, av_interleaved takes ownership usually?
|
||||
// Actually av_interleaved_write_frame takes ownership of the packet reference,
|
||||
// but we can just use av_write_frame if we don't want it buffered,
|
||||
// but av_interleaved_write_frame will unref the packet for us!
|
||||
// Wait, if it unrefs it, we shouldn't free it again.
|
||||
if ret < 0 {
|
||||
return Err("Failed to write frame".into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn finalize(&mut self) -> Result<(), String> {
|
||||
unsafe {
|
||||
av_write_trailer(self.fmt_ctx);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Muxer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if ((*(*self.fmt_ctx).oformat).flags & AVFMT_NOFILE as i32) == 0 {
|
||||
avio_closep(&mut (*self.fmt_ctx).pb);
|
||||
}
|
||||
avformat_free_context(self.fmt_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/ring.rs
Normal file
45
src/ring.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use ringbuf::traits::{Producer, Consumer, Split};
|
||||
use ringbuf::HeapRb;
|
||||
use ffmpeg_next::ffi::*;
|
||||
|
||||
pub struct Packet {
|
||||
pub ptr: *mut AVPacket,
|
||||
}
|
||||
|
||||
unsafe impl Send for Packet {}
|
||||
unsafe impl Sync for Packet {}
|
||||
|
||||
impl Packet {
|
||||
pub fn new(ptr: *mut AVPacket) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
pub fn is_keyframe(&self) -> bool {
|
||||
unsafe {
|
||||
((*self.ptr).flags & AV_PKT_FLAG_KEY as i32) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Packet {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.ptr.is_null() {
|
||||
let mut p = self.ptr;
|
||||
av_packet_free(&mut p);
|
||||
self.ptr = std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Packet {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let new_pkt = av_packet_alloc();
|
||||
av_packet_ref(new_pkt, self.ptr);
|
||||
Self { ptr: new_pkt }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue