Skip to content

Commit

Permalink
feat: windows 支持屏幕录制 (#168)
Browse files Browse the repository at this point in the history
* feat: windows 支持屏幕录制

* fix: 修复不支持平台提示

* chore: 更新文档

---------

Co-authored-by: nashaofu <[email protected]>
  • Loading branch information
nashaofu and nashaofu authored Dec 11, 2024
1 parent e3edc28 commit c7d47ee
Show file tree
Hide file tree
Showing 21 changed files with 558 additions and 41 deletions.
16 changes: 10 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "xcap"
version = "0.0.15"
version = "0.1.0"
edition = "2021"
description = "XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (to be implemented)."
description = "XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (WIP)."
license = "Apache-2.0"
documentation = "https://docs.rs/xcap"
homepage = "https://github.com/nashaofu/xcap"
Expand All @@ -15,10 +15,10 @@ keywords = ["screen", "monitor", "window", "capture", "image"]
vendored = ["dbus/vendored"]

[dependencies]
image = "0.24"
image = "0.25"
log = "0.4"
sysinfo = "0.32"
thiserror = "1.0"
sysinfo = "0.33"
thiserror = "2.0"

[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.10"
Expand All @@ -34,11 +34,15 @@ windows = { version = "0.58", features = [
"Win32_System_Threading",
"Win32_System_ProcessStatus",
"Win32_Storage_FileSystem",
"Win32_Graphics_Dxgi",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D11",
"Win32_Graphics_Dxgi_Common",
] }

[target.'cfg(target_os="linux")'.dependencies]
percent-encoding = "2.3"
xcb = { version = "1.4", features = ["randr"] }
xcb = { version = "1.5", features = ["randr"] }
dbus = { version = "0.9" }

[dev-dependencies]
Expand Down
54 changes: 46 additions & 8 deletions README-zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

[English](README.md) | 简体中文

XCap 是一个使用 Rust 编写的跨平台的屏幕捕获库,它支持 Linux(X11,Wayland)、MacOS 与 Windows。XCap 支持截图与视频录制(待实现)。
XCap 是一个使用 Rust 编写的跨平台的屏幕捕获库,它支持 Linux(X11,Wayland)、MacOS 与 Windows。XCap 支持截图与视频录制(实现中)。

## 功能

- 跨平台: 支持 Linux(X11,Wayland)、MacOS 与 Windows。
- 支持多种截图模式: 可以对屏幕与窗口进行截图。
- 支持视频录制:支持对屏幕或窗口进行录制(待实现)。
- 支持视频录制:支持对屏幕或窗口进行录制(实现中)。

### 实现状态

| 功能 | Linux(X11) | Linux(Wayland) | MacOS | Windows |
| -------- | ---------- | -------------- | ----- | ------- |
| 屏幕截图 |||||
| 窗口截图 |||||
| 屏幕录制 | 🛠️ | 🛠️ | 🛠️ | 🛠️ |
| 窗口录制 | 🛠️ | 🛠️ | 🛠️ | 🛠️ |
| 功能 | Linux(X11) | Linux(Wayland) | MacOS | Windows(>=Windows 8.1) |
| -------- | ---------- | -------------- | ----- | ---------------------- |
| 屏幕截图 |||| |
| 窗口截图 |||| |
| 屏幕录制 | 🛠️ | 🛠️ | 🛠️ | |
| 窗口录制 | 🛠️ | 🛠️ | 🛠️ | 🛠️ |

- ✅: 功能可用
- ⛔: 功能可用,但在一些特殊场景下未完全支持
Expand Down Expand Up @@ -55,6 +55,42 @@ fn main() {
}
```

- 屏幕录制

```rust
use std::{sync::Arc, thread, time::Duration};
use xcap::Monitor;

fn main() {
let monitor = Monitor::from_point(100, 100).unwrap();

let video_recorder = Arc::new(monitor.video_recorder().unwrap());

let video_recorder_clone = video_recorder.clone();
thread::spawn(move || {
video_recorder_clone
.on_frame(|frame| {
println!("frame: {:?}", frame.width);
Ok(())
})
.unwrap();
});

println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
thread::sleep(Duration::from_secs(2));
println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
}

```

- 窗口截图

```rust
Expand Down Expand Up @@ -104,6 +140,8 @@ fn main() {
}
```

更多例子可以在 [examples](./examples) 目录中找到。

## Linux 系统要求

在 Linux 上,需要安装 `libxcb`, `libxrandr``dbus`.
Expand Down
54 changes: 46 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

English | [简体中文](README-zh_CN.md)

XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (to be implemented).
XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (WIP).

## Features

- Cross-platform: Supports Linux (X11, Wayland), MacOS, and Windows.
- Supports multiple screenshot modes: Can take screenshots of the screen and windows.
- Supports video recording: Supports recording of the screen or window (to be implemented).
- Supports video recording: Supports recording of the screen or window (WIP).

### Implementation Status

| Feature | Linux(X11) | Linux(Wayland) | MacOS | Windows |
| ---------------- | ---------- | -------------- | ----- | ------- |
| Screen Capture |||||
| Window Capture |||||
| Screen Recording | 🛠️ | 🛠️ | 🛠️ | 🛠️ |
| Window Recording | 🛠️ | 🛠️ | 🛠️ | 🛠️ |
| Feature | Linux(X11) | Linux(Wayland) | MacOS | Windows(>=Windows 8.1) |
| ---------------- | ---------- | -------------- | ----- | ---------------------- |
| Screen Capture |||| |
| Window Capture |||| |
| Screen Recording | 🛠️ | 🛠️ | 🛠️ | |
| Window Recording | 🛠️ | 🛠️ | 🛠️ | 🛠️ |

- ✅: Feature available
- ⛔: Feature available, but not fully supported in some special scenarios
Expand Down Expand Up @@ -55,6 +55,42 @@ fn main() {
}
```

- Screen Record

```rust
use std::{sync::Arc, thread, time::Duration};
use xcap::Monitor;

fn main() {
let monitor = Monitor::from_point(100, 100).unwrap();

let video_recorder = Arc::new(monitor.video_recorder().unwrap());

let video_recorder_clone = video_recorder.clone();
thread::spawn(move || {
video_recorder_clone
.on_frame(|frame| {
println!("frame: {:?}", frame.width);
Ok(())
})
.unwrap();
});

println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
thread::sleep(Duration::from_secs(2));
println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
}

```

- Window Capture

```rust
Expand Down Expand Up @@ -104,6 +140,8 @@ fn main() {
}
```

More examples in [examples](./examples)

## Linux System Requirements

On Linux, you need to install `libxcb`, `libxrandr`, and `dbus`.
Expand Down
47 changes: 47 additions & 0 deletions examples/monitor_record.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use fs_extra::dir;
use std::{
thread,
time::{Duration, Instant},
};
use xcap::Monitor;

fn main() {
let monitors = Monitor::all().unwrap();

dir::create_all("target/monitors", true).unwrap();

let monitor = monitors.get(0).unwrap().clone();

let mut i = 0;
let frame = 20;
let start = Instant::now();
let fps = 1000 / frame;

loop {
i += 1;
let time = Instant::now();
let image = monitor.capture_image().unwrap();
image
.save(format!("target/monitors/monitor-{}.png", i,))
.unwrap();
let sleep_time = fps * i - start.elapsed().as_millis() as i128;
println!(
"sleep_time: {:?} current_step_time: {:?}",
sleep_time,
time.elapsed()
);
if sleep_time > 0 {
thread::sleep(Duration::from_millis(sleep_time as u64));
}

if i >= 900 {
break;
}
}

println!("time {:?}", start.elapsed());
let actual_fps = 900 / start.elapsed().as_secs();
println!("actual fps: {}", actual_fps);

// ffmpeg -framerate {actual_fps} -i monitor-%d.png -c:v libx264 -pix_fmt yuv420p output.mp4
}
30 changes: 30 additions & 0 deletions examples/windows_monitor_record.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use std::{sync::Arc, thread, time::Duration};
use xcap::Monitor;

fn main() {
let monitor = Monitor::from_point(100, 100).unwrap();

let video_recorder = Arc::new(monitor.video_recorder().unwrap());

let video_recorder_clone = video_recorder.clone();
thread::spawn(move || {
video_recorder_clone
.on_frame(|frame| {
println!("frame: {:?}", frame.width);
Ok(())
})
.unwrap();
});

println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
thread::sleep(Duration::from_secs(2));
println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
}
10 changes: 10 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use std::sync::PoisonError;

use thiserror::Error;

#[derive(Debug, Error)]
pub enum XCapError {
#[error("{0}")]
Error(String),
#[error("StdSyncPoisonError {0}")]
StdSyncPoisonError(String),

#[cfg(target_os = "linux")]
#[error(transparent)]
Expand Down Expand Up @@ -53,3 +57,9 @@ impl From<core_graphics::display::CGError> for XCapError {
}

pub type XCapResult<T> = Result<T, XCapError>;

impl<T> From<PoisonError<T>> for XCapError {
fn from(value: PoisonError<T>) -> Self {
XCapError::StdSyncPoisonError(value.to_string())
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod error;
mod monitor;
mod video_recorder;
mod window;

#[cfg(target_os = "macos")]
Expand All @@ -19,3 +20,5 @@ pub use image;
pub use error::{XCapError, XCapResult};
pub use monitor::Monitor;
pub use window::Window;

pub use video_recorder::VideoRecorder;
6 changes: 5 additions & 1 deletion src/linux/impl_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use xcb::{

use crate::error::{XCapError, XCapResult};

use super::capture::capture_monitor;
use super::{capture::capture_monitor, impl_video_recorder::ImplVideoRecorder};

#[derive(Debug, Clone)]
pub(crate) struct ImplMonitor {
Expand Down Expand Up @@ -236,4 +236,8 @@ impl ImplMonitor {
pub fn capture_image(&self) -> XCapResult<RgbaImage> {
capture_monitor(self)
}

pub fn video_recorder(&self) -> XCapResult<ImplVideoRecorder> {
ImplVideoRecorder::new()
}
}
25 changes: 25 additions & 0 deletions src/linux/impl_video_recorder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![allow(unused)]

use crate::{video_recorder::Frame, XCapResult};

#[derive(Debug, Clone)]
pub struct ImplVideoRecorder {}

impl ImplVideoRecorder {
pub fn new() -> XCapResult<Self> {
unimplemented!()
}

pub fn on_frame<F>(&self, on_frame: F) -> XCapResult<()>
where
F: Fn(Frame) -> XCapResult<()> + Send + 'static,
{
unimplemented!()
}
pub fn start(&self) -> XCapResult<()> {
unimplemented!()
}
pub fn stop(&self) -> XCapResult<()> {
unimplemented!()
}
}
1 change: 1 addition & 0 deletions src/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ mod wayland_capture;
mod xorg_capture;

pub mod impl_monitor;
pub mod impl_video_recorder;
pub mod impl_window;
4 changes: 2 additions & 2 deletions src/linux/wayland_capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fn org_gnome_shell_screenshot(

let filename = path.to_string_lossy().to_string();

proxy.method_call(
proxy.method_call::<(), (i32, i32, i32, i32, bool, &String), &str, &str>(
"org.gnome.Shell.Screenshot",
"ScreenshotArea",
(x, y, width, height, false, &filename),
Expand Down Expand Up @@ -124,7 +124,7 @@ fn org_freedesktop_portal_screenshot(
options.insert(String::from("modal"), Variant(Box::new(true)));
options.insert(String::from("interactive"), Variant(Box::new(false)));

proxy.method_call(
proxy.method_call::<(), (&str, PropMap), &str, &str>(
"org.freedesktop.portal.Screenshot",
"Screenshot",
("", options),
Expand Down
Loading

0 comments on commit c7d47ee

Please sign in to comment.