mirror of
https://github.com/xlmnxp/blue-recorder.git
synced 2025-03-31 14:54:54 +03:00
Merge pull request #14 from ochibani/restore-wayland-support
read wayland stream width and height
This commit is contained in:
commit
9c4d8c6b82
270
Cargo.lock
generated
270
Cargo.lock
generated
@ -496,10 +496,12 @@ dependencies = [
|
||||
"chrono",
|
||||
"ffmpeg-sidecar",
|
||||
"glib 0.10.3",
|
||||
"gstreamer",
|
||||
"libadwaita",
|
||||
"open",
|
||||
"subprocess",
|
||||
"tempfile",
|
||||
"zbus 5.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -620,6 +622,16 @@ dependencies = [
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
@ -1486,7 +1498,7 @@ version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016"
|
||||
dependencies = [
|
||||
"gio-sys",
|
||||
"gio-sys 0.16.3",
|
||||
"glib-sys 0.16.3",
|
||||
"gobject-sys 0.16.3",
|
||||
"libc",
|
||||
@ -1517,7 +1529,7 @@ checksum = "de55cb49432901fe2b3534177fa06844665b9b0911d85d8601a8d8b88b7791db"
|
||||
dependencies = [
|
||||
"cairo-sys-rs",
|
||||
"gdk-pixbuf-sys",
|
||||
"gio-sys",
|
||||
"gio-sys 0.16.3",
|
||||
"glib-sys 0.16.3",
|
||||
"gobject-sys 0.16.3",
|
||||
"libc",
|
||||
@ -1568,7 +1580,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"gio-sys",
|
||||
"gio-sys 0.16.3",
|
||||
"glib 0.16.9",
|
||||
"libc",
|
||||
"once_cell",
|
||||
@ -1590,6 +1602,19 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gio-sys"
|
||||
version = "0.20.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8446d9b475730ebef81802c1738d972db42fde1c5a36a627ebc4d665fc87db04"
|
||||
dependencies = [
|
||||
"glib-sys 0.20.7",
|
||||
"gobject-sys 0.20.7",
|
||||
"libc",
|
||||
"system-deps 7.0.3",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib"
|
||||
version = "0.10.3"
|
||||
@ -1621,7 +1646,7 @@ dependencies = [
|
||||
"futures-executor",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
"gio-sys",
|
||||
"gio-sys 0.16.3",
|
||||
"glib-macros 0.16.8",
|
||||
"glib-sys 0.16.3",
|
||||
"gobject-sys 0.16.3",
|
||||
@ -1631,6 +1656,27 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib"
|
||||
version = "0.20.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f969edf089188d821a30cde713b6f9eb08b20c63fc2e584aba2892a7984a8cc0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
"gio-sys 0.20.8",
|
||||
"glib-macros 0.20.7",
|
||||
"glib-sys 0.20.7",
|
||||
"gobject-sys 0.20.7",
|
||||
"libc",
|
||||
"memchr",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-macros"
|
||||
version = "0.10.1"
|
||||
@ -1662,6 +1708,19 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-macros"
|
||||
version = "0.20.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-sys"
|
||||
version = "0.10.1"
|
||||
@ -1682,6 +1741,16 @@ dependencies = [
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-sys"
|
||||
version = "0.20.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b360ff0f90d71de99095f79c526a5888c9c92fc9ee1b19da06c6f5e75f0c2a53"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"system-deps 7.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
@ -1722,6 +1791,17 @@ dependencies = [
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gobject-sys"
|
||||
version = "0.20.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a56235e971a63bfd75abb13ef70064e1346388723422a68580d8a6fbac6423"
|
||||
dependencies = [
|
||||
"glib-sys 0.20.7",
|
||||
"libc",
|
||||
"system-deps 7.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "graphene-rs"
|
||||
version = "0.16.3"
|
||||
@ -1777,6 +1857,43 @@ dependencies = [
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "700cb1b2e86dda424f85eb728102a111602317e40b4dd71cf1c0dc04e0cc5d95"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"glib 0.20.7",
|
||||
"gstreamer-sys",
|
||||
"itertools 0.13.0",
|
||||
"libc",
|
||||
"muldiv",
|
||||
"num-integer",
|
||||
"num-rational",
|
||||
"once_cell",
|
||||
"option-operations",
|
||||
"paste",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-sys"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16cf1ae0a869aa7066ce3c685b76053b4b4f48f364a5b18c4b1f36ef57469719"
|
||||
dependencies = [
|
||||
"glib-sys 0.20.7",
|
||||
"gobject-sys 0.20.7",
|
||||
"libc",
|
||||
"system-deps 7.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk4"
|
||||
version = "0.5.5"
|
||||
@ -1823,7 +1940,7 @@ dependencies = [
|
||||
"cairo-sys-rs",
|
||||
"gdk-pixbuf-sys",
|
||||
"gdk4-sys",
|
||||
"gio-sys",
|
||||
"gio-sys 0.16.3",
|
||||
"glib-sys 0.16.3",
|
||||
"gobject-sys 0.16.3",
|
||||
"graphene-sys",
|
||||
@ -2325,7 +2442,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de902982372b454a0081d7fd9dd567b37b73ae29c8f6da1820374d345fd95d5b"
|
||||
dependencies = [
|
||||
"gdk4-sys",
|
||||
"gio-sys",
|
||||
"gio-sys 0.16.3",
|
||||
"glib-sys 0.16.3",
|
||||
"gobject-sys 0.16.3",
|
||||
"gtk4-sys",
|
||||
@ -2511,6 +2628,12 @@ dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "muldiv"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0"
|
||||
|
||||
[[package]]
|
||||
name = "nb-connect"
|
||||
version = "1.2.0"
|
||||
@ -2724,6 +2847,15 @@ dependencies = [
|
||||
"pathdiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-operations"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0"
|
||||
dependencies = [
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.3.1"
|
||||
@ -3589,7 +3721,20 @@ version = "6.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
|
||||
dependencies = [
|
||||
"cfg-expr",
|
||||
"cfg-expr 0.15.8",
|
||||
"heck 0.5.0",
|
||||
"pkg-config",
|
||||
"toml 0.8.19",
|
||||
"version-compare 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "7.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005"
|
||||
dependencies = [
|
||||
"cfg-expr 0.17.2",
|
||||
"heck 0.5.0",
|
||||
"pkg-config",
|
||||
"toml 0.8.19",
|
||||
@ -4734,10 +4879,46 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"xdg-home",
|
||||
"zbus_macros 4.4.0",
|
||||
"zbus_names",
|
||||
"zbus_names 3.0.0",
|
||||
"zvariant 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "192a0d989036cd60a1e91a54c9851fb9ad5bd96125d41803eed79d2e2ef74bd7"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
"async-fs",
|
||||
"async-io 2.4.0",
|
||||
"async-lock 3.4.0",
|
||||
"async-process",
|
||||
"async-recursion",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
"blocking",
|
||||
"enumflags2 0.7.11",
|
||||
"event-listener 5.4.0",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"nix 0.29.0",
|
||||
"ordered-stream",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"static_assertions",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.59.0",
|
||||
"winnow 0.6.24",
|
||||
"xdg-home",
|
||||
"zbus_macros 5.3.0",
|
||||
"zbus_names 4.1.1",
|
||||
"zvariant 5.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "1.9.3"
|
||||
@ -4760,7 +4941,22 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"zvariant_utils",
|
||||
"zvariant_utils 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3685b5c81fce630efc3e143a4ded235b107f1b1cdf186c3f115529e5e5ae4265"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"zbus_names 4.1.1",
|
||||
"zvariant 5.2.0",
|
||||
"zvariant_utils 3.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4774,6 +4970,18 @@ dependencies = [
|
||||
"zvariant 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "4.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"winnow 0.6.24",
|
||||
"zvariant 5.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
@ -4980,6 +5188,21 @@ dependencies = [
|
||||
"zvariant_derive 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55e6b9b5f1361de2d5e7d9fd1ee5f6f7fcb6060618a1f82f3472f58f2b8d4be9"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2 0.7.11",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"winnow 0.6.24",
|
||||
"zvariant_derive 5.2.0",
|
||||
"zvariant_utils 3.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "2.10.0"
|
||||
@ -5002,7 +5225,20 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"zvariant_utils",
|
||||
"zvariant_utils 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "573a8dd76961957108b10f7a45bac6ab1ea3e9b7fe01aff88325dc57bb8f5c8b"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"zvariant_utils 3.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5015,3 +5251,17 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_utils"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd46446ea2a1f353bfda53e35f17633afa79f4fe290a611c94645c69fe96a50"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"syn 2.0.96",
|
||||
"winnow 0.6.24",
|
||||
]
|
||||
|
@ -14,6 +14,8 @@ async-std = {version = "1.12.0", features = ["attributes"]}
|
||||
chrono = { version = "0.4.19", optional = true }
|
||||
ffmpeg-sidecar = "2.0.5"
|
||||
glib = { version = "0.10.3", optional = true }
|
||||
gstreamer = "0.23.4"
|
||||
open = "5.1.4"
|
||||
subprocess = {version = "0.2.6", optional = true }
|
||||
tempfile = "3.10.1"
|
||||
zbus = "5.3.0"
|
||||
|
@ -3,6 +3,7 @@ use adw::gtk::{CheckButton, ComboBoxText, Entry, FileChooserNative, SpinButton};
|
||||
#[cfg(feature = "gtk")]
|
||||
use adw::gtk::prelude::*;
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use chrono::Timelike;
|
||||
#[cfg(feature = "gtk")]
|
||||
use chrono::Utc;
|
||||
use ffmpeg_sidecar::child::FfmpegChild;
|
||||
@ -18,11 +19,12 @@ use std::time::Duration;
|
||||
#[cfg(feature = "cmd")]
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::utils::{is_video_record, RecordMode};
|
||||
use crate::utils::{is_video_record, is_wayland, RecordMode};
|
||||
#[cfg(feature = "cmd")]
|
||||
use crate::utils::{is_input_audio_record, is_output_audio_record, is_valid};
|
||||
#[cfg(feature = "gtk")]
|
||||
use crate::utils::validate_video_file;
|
||||
use crate::wayland_linux::{CursorModeTypes, RecordTypes, WaylandRecorder};
|
||||
|
||||
#[cfg(feature = "cmd")]
|
||||
#[derive(Clone)]
|
||||
@ -56,6 +58,7 @@ pub struct Ffmpeg {
|
||||
pub output: String,
|
||||
pub temp_video_filename: String,
|
||||
pub saved_filename: String,
|
||||
pub width: Option<u16>,
|
||||
pub height: Option<u16>,
|
||||
pub input_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
|
||||
pub output_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
|
||||
@ -70,6 +73,7 @@ pub struct Ffmpeg {
|
||||
pub record_mouse: CheckButton,
|
||||
pub show_area: CheckButton,
|
||||
pub video_switch: CheckButton,
|
||||
pub wayland_recorder: WaylandRecorder,
|
||||
}
|
||||
|
||||
#[cfg(feature = "cmd")]
|
||||
@ -87,6 +91,7 @@ impl Ffmpeg {
|
||||
);
|
||||
let mut ffmpeg_command = FfmpegCommand::new();
|
||||
let format = "x11grab";
|
||||
self.width = Some(width);
|
||||
self.height = Some(height);
|
||||
|
||||
// Record video to tmp if audio record enabled
|
||||
@ -483,6 +488,35 @@ impl Ffmpeg {
|
||||
//if mode == RecordMode::Window && !self.follow_mouse.is_active() {
|
||||
// TODO pulse = gstreamer for video && add to cmd linux + add convert function to gstreamer ouput
|
||||
//} else {
|
||||
if is_wayland() {
|
||||
let folder_path = Path::new(&self.saved_filename)
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("Failed to get parent path."))?;
|
||||
self.temp_video_filename = folder_path
|
||||
.join(format!(".blue-recorder-{}.webm", Utc::now().nanosecond()))
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
// Start recording
|
||||
let stream = glib::MainContext::default().block_on(self.wayland_recorder.start(
|
||||
self.temp_video_filename.clone(),
|
||||
match mode {
|
||||
RecordMode::Screen => RecordTypes::Monitor,
|
||||
RecordMode::Window => RecordTypes::Window,
|
||||
_ => RecordTypes::MonitorOrWindow,
|
||||
},
|
||||
if self.record_mouse.is_active() {
|
||||
CursorModeTypes::Show
|
||||
} else {
|
||||
CursorModeTypes::Hidden
|
||||
},
|
||||
));
|
||||
|
||||
self.width = Some(width);
|
||||
self.height = Some(height);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let display = format!("{}+{},{}",
|
||||
std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string())
|
||||
.as_str(),
|
||||
@ -491,6 +525,7 @@ impl Ffmpeg {
|
||||
);
|
||||
let mut ffmpeg_command = FfmpegCommand::new();
|
||||
let format = "x11grab";
|
||||
self.width = Some(width);
|
||||
self.height = Some(height);
|
||||
let filename = self.saved_filename.clone();
|
||||
self.output = Path::new(&filename).extension()
|
||||
@ -637,6 +672,17 @@ impl Ffmpeg {
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
} else if self.video_switch.is_active() && is_wayland() {
|
||||
glib::MainContext::default().block_on(self.wayland_recorder.stop());
|
||||
match self.merge() {
|
||||
Ok(_) => {
|
||||
self.clean()?;
|
||||
},
|
||||
Err(error) => {
|
||||
self.clean()?;
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
pub mod wayland_linux;
|
||||
|
||||
pub mod ffmpeg_linux;
|
||||
|
||||
pub mod ffmpeg_windows;
|
||||
|
||||
pub mod utils;
|
||||
pub mod utils;
|
300
core/src/wayland_linux.rs
Normal file
300
core/src/wayland_linux.rs
Normal file
@ -0,0 +1,300 @@
|
||||
use anyhow::{Error, Result};
|
||||
use gst::prelude::*;
|
||||
use gstreamer as gst;
|
||||
use std::collections::HashMap;
|
||||
use zbus::{
|
||||
export::futures_util::TryStreamExt,
|
||||
message, proxy,
|
||||
zvariant::{Dict, ObjectPath, OwnedObjectPath, Str, Structure, Value},
|
||||
Connection, MessageStream,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum RecordTypes {
|
||||
Default,
|
||||
Monitor,
|
||||
Window,
|
||||
MonitorOrWindow,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CursorModeTypes {
|
||||
Default,
|
||||
Hidden,
|
||||
Show,
|
||||
}
|
||||
|
||||
#[proxy(
|
||||
interface = "org.freedesktop.portal.ScreenCast",
|
||||
default_service = "org.freedesktop.portal.Desktop",
|
||||
default_path = "/org/freedesktop/portal/desktop"
|
||||
)]
|
||||
trait ScreenCast {
|
||||
async fn create_session(&self, options: HashMap<&str, Value<'_>>) -> Result<OwnedObjectPath>;
|
||||
async fn select_sources(
|
||||
&self,
|
||||
session_handle: ObjectPath<'_>,
|
||||
options: HashMap<&str, Value<'_>>,
|
||||
) -> Result<OwnedObjectPath>;
|
||||
async fn start(
|
||||
&self,
|
||||
session_handle: ObjectPath<'_>,
|
||||
parent_window: &str,
|
||||
options: HashMap<&str, Value<'_>>,
|
||||
) -> Result<OwnedObjectPath>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WaylandRecorder {
|
||||
connection: Connection,
|
||||
screen_cast_proxy: ScreenCastProxy<'static>,
|
||||
session_path: String,
|
||||
pipeline: Option<gst::Pipeline>,
|
||||
filename: String,
|
||||
}
|
||||
|
||||
impl WaylandRecorder {
|
||||
pub async fn new() -> Self {
|
||||
let connection = Connection::session()
|
||||
.await
|
||||
.expect("failed to connect to session bus");
|
||||
let screen_cast_proxy = ScreenCastProxy::new(&connection)
|
||||
.await
|
||||
.expect("failed to create dbus proxy for screen-cast");
|
||||
gst::init().expect("failed to initialize gstreamer");
|
||||
|
||||
WaylandRecorder {
|
||||
connection,
|
||||
screen_cast_proxy,
|
||||
session_path: String::new(),
|
||||
filename: String::from("blue_recorder.webm"),
|
||||
pipeline: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(
|
||||
&mut self,
|
||||
filename: String,
|
||||
record_type: RecordTypes,
|
||||
cursor_mode_type: CursorModeTypes,
|
||||
) -> (i32, i32) {
|
||||
self.screen_cast_proxy
|
||||
.create_session(HashMap::from([
|
||||
("handle_token", Value::from("blue_recorder_1")),
|
||||
("session_handle_token", Value::from("blue_recorder_1")),
|
||||
]))
|
||||
.await
|
||||
.expect("failed to create session");
|
||||
|
||||
let (mut height, mut width) = (0, 0);
|
||||
|
||||
let mut message_stream = MessageStream::from(self.connection.clone());
|
||||
|
||||
self.filename = filename.clone();
|
||||
|
||||
while let Some(msg) = message_stream
|
||||
.try_next()
|
||||
.await
|
||||
.expect("failed to get message")
|
||||
{
|
||||
match msg.message_type() {
|
||||
message::Type::Signal => {
|
||||
let body = msg.body();
|
||||
let (response_num, response): (u32, HashMap<&str, Value>) =
|
||||
body.deserialize().unwrap();
|
||||
|
||||
if response_num > 0 {
|
||||
return (height, width);
|
||||
}
|
||||
|
||||
if response.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if response.contains_key("session_handle") {
|
||||
self.handle_session(
|
||||
self.screen_cast_proxy.clone(),
|
||||
response.clone(),
|
||||
record_type,
|
||||
cursor_mode_type,
|
||||
)
|
||||
.await
|
||||
.expect("failed to handle session");
|
||||
continue;
|
||||
}
|
||||
|
||||
if response.contains_key("streams") {
|
||||
let stream = self.record_screen_cast(response.clone())
|
||||
.await
|
||||
.expect("failed to record screen cast");
|
||||
|
||||
width = stream.0;
|
||||
height = stream.1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("\n\nUnkown message: {:?}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(height, width)
|
||||
}
|
||||
|
||||
pub async fn stop(&mut self) {
|
||||
if let Some(pipeline) = self.pipeline.clone() {
|
||||
pipeline
|
||||
.set_state(gst::State::Null)
|
||||
.expect("failed to stop pipeline");
|
||||
}
|
||||
|
||||
if self.session_path.len() > 0 {
|
||||
println!(
|
||||
"Closing session...: {:?}",
|
||||
self.session_path.replace("request", "session")
|
||||
);
|
||||
self.connection
|
||||
.clone()
|
||||
.call_method(
|
||||
Some("org.freedesktop.portal.Desktop"),
|
||||
self.session_path.clone().replace("request", "session"),
|
||||
Some("org.freedesktop.portal.Session"),
|
||||
"Close",
|
||||
&(),
|
||||
)
|
||||
.await
|
||||
.expect("failed to close session");
|
||||
self.session_path = String::new();
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_session(
|
||||
&mut self,
|
||||
screen_cast_proxy: ScreenCastProxy<'_>,
|
||||
response: HashMap<&str, Value<'_>>,
|
||||
record_type: RecordTypes,
|
||||
cursor_mode_type: CursorModeTypes,
|
||||
) -> Result<()> {
|
||||
let response_session_handle = response
|
||||
.get("session_handle")
|
||||
.expect("cannot get session_handle")
|
||||
.clone()
|
||||
.downcast::<String>()
|
||||
.expect("cannot down cast session_handle");
|
||||
|
||||
self.session_path = response_session_handle.clone();
|
||||
|
||||
screen_cast_proxy
|
||||
.select_sources(
|
||||
ObjectPath::try_from(response_session_handle.clone())?,
|
||||
HashMap::from([
|
||||
("handle_token", Value::from("blue_recorder_1")),
|
||||
(
|
||||
"types",
|
||||
Value::from(match record_type {
|
||||
RecordTypes::Monitor => 1u32,
|
||||
RecordTypes::Window => 2u32,
|
||||
RecordTypes::MonitorOrWindow => 3u32,
|
||||
_ => 0u32,
|
||||
}),
|
||||
),
|
||||
(
|
||||
"cursor_mode",
|
||||
Value::from(match cursor_mode_type {
|
||||
CursorModeTypes::Hidden => 1u32,
|
||||
CursorModeTypes::Show => 2u32,
|
||||
_ => 0u32,
|
||||
}),
|
||||
),
|
||||
]),
|
||||
)
|
||||
.await?;
|
||||
|
||||
screen_cast_proxy
|
||||
.start(
|
||||
ObjectPath::try_from(response_session_handle.clone())?,
|
||||
"parent_window",
|
||||
HashMap::from([("handle_token", Value::from("blue_recorder_1"))]),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn record_screen_cast(&mut self, response: HashMap<&str, Value<'_>>) -> Result<(i32, i32)> {
|
||||
let streams: &Value<'_> = response.get("streams").expect("cannot get streams");
|
||||
|
||||
let (mut width, mut height) = (0, 0);
|
||||
|
||||
let stream_fields = streams
|
||||
.clone()
|
||||
.downcast::<Vec<Value>>()
|
||||
.expect("cannot down cast streams to vec array")
|
||||
.first()
|
||||
.expect("cannot get first object from streams array")
|
||||
.clone()
|
||||
.downcast::<Structure>()
|
||||
.expect("cannot down cast first object to structure");
|
||||
|
||||
if let Some(field) = stream_fields.fields().get(1) {
|
||||
let dict = field
|
||||
.clone()
|
||||
.downcast::<Dict>()
|
||||
.expect("cannot down cast field to value");
|
||||
|
||||
let size_str = Str::from("size");
|
||||
let size = dict
|
||||
.get::<Str, Structure>(&size_str)
|
||||
.expect("cannot get size")
|
||||
.expect("cannot get size structure");
|
||||
|
||||
let fields = size.fields();
|
||||
|
||||
let size = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
field
|
||||
.clone()
|
||||
.downcast::<i32>()
|
||||
.expect("cannot down cast width to i32")
|
||||
})
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
let [stream_width, stream_height] = size.as_slice() else {
|
||||
return Err(Error::msg("cannot get width and height"));
|
||||
};
|
||||
|
||||
width = *stream_width;
|
||||
height = *stream_height;
|
||||
}
|
||||
|
||||
// get fields from nested structure inside elements
|
||||
// NOTICE: this is not the best way to get node_id, but it works for now
|
||||
let stream_node_id: u32 = stream_fields.fields()
|
||||
.first()
|
||||
.expect("cannot get first field from structure")
|
||||
.clone()
|
||||
.downcast::<u32>()
|
||||
.expect("cannot down cast first field to u32");
|
||||
|
||||
// launch gstreamer pipeline
|
||||
let gst_element: gst::Element = gst::parse::launch(&format!(
|
||||
"pipewiresrc path={stream_node_id} ! videorate ! video/x-raw,framerate=60/1 ! videoconvert ! vp8enc min-quantizer=0 max-quantizer=1 keyframe-mode=disabled buffer-size=20000 ! webmmux ! filesink location={filename}",
|
||||
filename = self.filename
|
||||
)).expect("failed to launch gstreamer pipeline");
|
||||
|
||||
// start pipeline
|
||||
let pipeline: gst::Pipeline = gst_element
|
||||
.dynamic_cast::<gst::Pipeline>()
|
||||
.expect("pipeline error");
|
||||
|
||||
self.pipeline = Some(pipeline.clone());
|
||||
|
||||
pipeline
|
||||
.set_state(gst::State::Playing)
|
||||
.expect("failed to start pipeline");
|
||||
|
||||
println!("Recording Wayland screen cast...");
|
||||
Ok((width, height))
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ use blue_recorder_core::utils::{disable_input_widgets, enable_input_widgets,
|
||||
is_overwrite, is_wayland, play_record, RecordMode, validate_video_file};
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
use blue_recorder_core::utils::{audio_output_source, sources_descriptions_list};
|
||||
use blue_recorder_core::wayland_linux::WaylandRecorder;
|
||||
#[cfg(target_os = "windows")]
|
||||
use cpal::traits::{DeviceTrait, HostTrait};
|
||||
use std::cell::RefCell;
|
||||
@ -791,6 +792,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
show_area: area_switch,
|
||||
video_switch: video_switch.clone()
|
||||
}));
|
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
let ffmpeg_record_interface: Rc<RefCell<Ffmpeg>> = Rc::new(RefCell::new(Ffmpeg {
|
||||
audio_input_id: audio_source_combobox.clone(),
|
||||
@ -803,6 +805,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
output: String::new(),
|
||||
temp_video_filename: String::new(),
|
||||
saved_filename: String::new(),
|
||||
width: None,
|
||||
height: None,
|
||||
input_audio_process: None,
|
||||
output_audio_process: None,
|
||||
@ -817,6 +820,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
record_mouse: mouse_switch.clone(),
|
||||
show_area: area_switch,
|
||||
video_switch: video_switch.clone(),
|
||||
wayland_recorder: glib::MainContext::default().block_on(WaylandRecorder::new())
|
||||
}));
|
||||
|
||||
// Record button
|
||||
@ -832,8 +836,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
let _error_dialog = error_dialog.clone();
|
||||
let _error_message = error_message.clone();
|
||||
let _follow_mouse_switch = follow_mouse_switch.clone();
|
||||
let mut _input_widgets = input_widgets.clone();
|
||||
//let main_context = glib::MainContext::default();
|
||||
let mut _input_widgets: Vec<Widget> = input_widgets.clone();
|
||||
let _main_window = main_window.clone();
|
||||
let _mouse_switch = mouse_switch.clone();
|
||||
let _play_button = play_button.clone();
|
||||
@ -841,7 +844,6 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
let _record_time_label = record_time_label.clone();
|
||||
let _stop_button = stop_button.clone();
|
||||
let _video_switch = video_switch.clone();
|
||||
//let wayland_record = main_context.block_on(WaylandRecorder::new());
|
||||
let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone();
|
||||
let second_click: Rc<RefCell<RecordClick>> = Rc::new(RefCell::new(RecordClick {
|
||||
is_record_button_clicked: false,
|
||||
@ -900,7 +902,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
second_click.clone(),
|
||||
);
|
||||
}
|
||||
} else if _delay_spin.value() as u16 == 0 && !is_wayland() {
|
||||
} else if _delay_spin.value() as u16 == 0 {
|
||||
let _area_capture = area_capture.borrow_mut();
|
||||
disable_input_widgets(_input_widgets.clone());
|
||||
start_timer(record_time_label.clone());
|
||||
|
Loading…
Reference in New Issue
Block a user