239 lines
6.8 KiB
Rust
239 lines
6.8 KiB
Rust
//! Game capture source configuration using libobs-simple.
|
|
//!
|
|
//! This module provides capture sources for recording game footage,
|
|
//! including game capture, window capture, and monitor capture.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Game capture source for recording game footage.
|
|
///
|
|
/// Uses OBS game capture for efficient GPU-based capture.
|
|
#[derive(Debug, Clone)]
|
|
pub struct GameCapture {
|
|
/// Source name.
|
|
pub name: String,
|
|
/// Window title to capture (if specific window).
|
|
pub window: Option<String>,
|
|
/// Process name to capture.
|
|
pub process_name: Option<String>,
|
|
/// Capture mode.
|
|
pub mode: CaptureMode,
|
|
/// Whether to capture cursor.
|
|
pub capture_cursor: bool,
|
|
/// Window class (Windows only).
|
|
pub window_class: Option<String>,
|
|
}
|
|
|
|
impl Default for GameCapture {
|
|
fn default() -> Self {
|
|
Self {
|
|
name: "Game Capture".to_string(),
|
|
window: None,
|
|
process_name: Some("League of Legends.exe".to_string()),
|
|
mode: CaptureMode::Process,
|
|
capture_cursor: false,
|
|
window_class: Some("RiotWindowClass".to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl GameCapture {
|
|
/// Create a new game capture source.
|
|
pub fn new(name: &str) -> Self {
|
|
Self {
|
|
name: name.to_string(),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// Create a game capture configured for League of Legends.
|
|
pub fn for_league_of_legends() -> Self {
|
|
Self {
|
|
name: "League of Legends Capture".to_string(),
|
|
window: Some("League of Legends (TM) Client".to_string()),
|
|
process_name: Some("League of Legends.exe".to_string()),
|
|
mode: CaptureMode::Window,
|
|
capture_cursor: false,
|
|
window_class: Some("RiotWindowClass".to_string()),
|
|
}
|
|
}
|
|
|
|
/// Set the window to capture.
|
|
pub fn with_window(mut self, window: &str) -> Self {
|
|
self.window = Some(window.to_string());
|
|
self.mode = CaptureMode::Window;
|
|
self
|
|
}
|
|
|
|
/// Set the process name to capture.
|
|
pub fn with_process(mut self, process: &str) -> Self {
|
|
self.process_name = Some(process.to_string());
|
|
self.mode = CaptureMode::Process;
|
|
self
|
|
}
|
|
|
|
/// Set the window class (Windows only).
|
|
pub fn with_window_class(mut self, class: &str) -> Self {
|
|
self.window_class = Some(class.to_string());
|
|
self
|
|
}
|
|
|
|
/// Set whether to capture the cursor.
|
|
pub fn with_cursor(mut self, capture: bool) -> Self {
|
|
self.capture_cursor = capture;
|
|
self
|
|
}
|
|
|
|
/// Get the window string for OBS in the format "Title:Class:Executable".
|
|
pub fn window_string(&self) -> Option<String> {
|
|
match (&self.window, &self.window_class, &self.process_name) {
|
|
(Some(window), Some(class), Some(process)) => {
|
|
Some(format!("{}:{}:{}", window, class, process))
|
|
}
|
|
(Some(window), None, Some(process)) => Some(format!("{}::{}", window, process)),
|
|
(Some(window), Some(class), None) => Some(format!("{}:{}:", window, class)),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Capture mode.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
#[derive(Default)]
|
|
pub enum CaptureMode {
|
|
/// Capture any fullscreen application.
|
|
Any,
|
|
/// Capture a specific window.
|
|
Window,
|
|
/// Capture a specific process.
|
|
#[default]
|
|
Process,
|
|
}
|
|
|
|
/// Window capture source (alternative to game capture).
|
|
///
|
|
/// Uses OBS window capture which works with more applications
|
|
/// but may have slightly higher overhead than game capture.
|
|
#[derive(Debug, Clone)]
|
|
pub struct WindowCapture {
|
|
/// Source name.
|
|
pub name: String,
|
|
/// Window title.
|
|
pub window_title: String,
|
|
/// Window class (X11/Windows).
|
|
pub window_class: Option<String>,
|
|
/// Whether to capture cursor.
|
|
pub capture_cursor: bool,
|
|
}
|
|
|
|
impl WindowCapture {
|
|
/// Create a new window capture source.
|
|
pub fn new(name: &str, window_title: &str) -> Self {
|
|
Self {
|
|
name: name.to_string(),
|
|
window_title: window_title.to_string(),
|
|
window_class: None,
|
|
capture_cursor: false,
|
|
}
|
|
}
|
|
|
|
/// Set the window class (for X11/Windows).
|
|
pub fn with_class(mut self, class: &str) -> Self {
|
|
self.window_class = Some(class.to_string());
|
|
self
|
|
}
|
|
|
|
/// Set whether to capture the cursor.
|
|
pub fn with_cursor(mut self, capture: bool) -> Self {
|
|
self.capture_cursor = capture;
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Monitor capture source (fallback).
|
|
///
|
|
/// Captures an entire monitor. Useful as a fallback when
|
|
/// game capture or window capture don't work.
|
|
#[derive(Debug, Clone)]
|
|
pub struct MonitorCapture {
|
|
/// Source name.
|
|
pub name: String,
|
|
/// Monitor index (0-based).
|
|
pub monitor: u32,
|
|
/// Whether to capture cursor.
|
|
pub capture_cursor: bool,
|
|
}
|
|
|
|
impl MonitorCapture {
|
|
/// Create a new monitor capture source.
|
|
pub fn new(name: &str, monitor: u32) -> Self {
|
|
Self {
|
|
name: name.to_string(),
|
|
monitor,
|
|
capture_cursor: false,
|
|
}
|
|
}
|
|
|
|
/// Set whether to capture the cursor.
|
|
pub fn with_cursor(mut self, capture: bool) -> Self {
|
|
self.capture_cursor = capture;
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Capture source type.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum SourceType {
|
|
/// Game capture source.
|
|
GameCapture,
|
|
/// Window capture source.
|
|
WindowCapture,
|
|
/// Monitor capture source.
|
|
MonitorCapture,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_game_capture_creation() {
|
|
let capture = GameCapture::new("Test Capture");
|
|
assert_eq!(capture.name, "Test Capture");
|
|
assert_eq!(capture.mode, CaptureMode::Process);
|
|
}
|
|
|
|
#[test]
|
|
fn test_game_capture_with_process() {
|
|
let capture = GameCapture::new("Test").with_process("League of Legends.exe");
|
|
|
|
assert_eq!(
|
|
capture.process_name,
|
|
Some("League of Legends.exe".to_string())
|
|
);
|
|
assert_eq!(capture.mode, CaptureMode::Process);
|
|
}
|
|
|
|
#[test]
|
|
fn test_league_capture_config() {
|
|
let capture = GameCapture::for_league_of_legends();
|
|
assert_eq!(capture.name, "League of Legends Capture");
|
|
assert_eq!(
|
|
capture.process_name,
|
|
Some("League of Legends.exe".to_string())
|
|
);
|
|
assert_eq!(capture.window_class, Some("RiotWindowClass".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_window_string() {
|
|
let capture = GameCapture::for_league_of_legends();
|
|
let window_str = capture.window_string();
|
|
assert!(window_str.is_some());
|
|
let s = window_str.unwrap();
|
|
assert!(s.contains("League of Legends"));
|
|
assert!(s.contains("RiotWindowClass"));
|
|
}
|
|
}
|