record-daemon: initial commit
This commit is contained in:
293
record-daemon/src/recording/capture.rs
Normal file
293
record-daemon/src/recording/capture.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
//! Game capture source configuration.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
/// Game capture source for recording game footage.
|
||||
#[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,
|
||||
}
|
||||
|
||||
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::Any,
|
||||
capture_cursor: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GameCapture {
|
||||
/// Create a new game capture source.
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 whether to capture the cursor.
|
||||
pub fn with_cursor(mut self, capture: bool) -> Self {
|
||||
self.capture_cursor = capture;
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the OBS source.
|
||||
///
|
||||
/// Note: This would create the actual obs_source_t in libobs.
|
||||
pub fn create_source(&self) -> Result<CaptureSource> {
|
||||
info!("Creating game capture source: {}", self.name);
|
||||
|
||||
// Note: Actual libobs source creation would happen here
|
||||
// obs_source_create("game_capture", name, settings, nullptr)
|
||||
|
||||
let source = CaptureSource {
|
||||
name: self.name.clone(),
|
||||
source_type: SourceType::GameCapture,
|
||||
active: false,
|
||||
};
|
||||
|
||||
debug!("Game capture source created");
|
||||
Ok(source)
|
||||
}
|
||||
}
|
||||
|
||||
/// Capture mode.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum CaptureMode {
|
||||
/// Capture any fullscreen application.
|
||||
Any,
|
||||
/// Capture a specific window.
|
||||
Window,
|
||||
/// Capture a specific process.
|
||||
Process,
|
||||
}
|
||||
|
||||
/// Capture source abstraction.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CaptureSource {
|
||||
/// Source name.
|
||||
pub name: String,
|
||||
/// Source type.
|
||||
pub source_type: SourceType,
|
||||
/// Whether the source is active.
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
impl CaptureSource {
|
||||
/// Check if the source is active.
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.active
|
||||
}
|
||||
|
||||
/// Activate the source.
|
||||
pub fn activate(&mut self) -> Result<()> {
|
||||
if self.active {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug!("Activating capture source: {}", self.name);
|
||||
// Note: Actual activation would involve obs_source_set_enabled
|
||||
self.active = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deactivate the source.
|
||||
pub fn deactivate(&mut self) -> Result<()> {
|
||||
if !self.active {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug!("Deactivating capture source: {}", self.name);
|
||||
// Note: Actual deactivation would involve obs_source_set_enabled
|
||||
self.active = false;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Source type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SourceType {
|
||||
/// Game capture source.
|
||||
GameCapture,
|
||||
/// Window capture source.
|
||||
WindowCapture,
|
||||
/// Monitor capture source.
|
||||
MonitorCapture,
|
||||
}
|
||||
|
||||
/// Window capture source (alternative to game capture).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowCapture {
|
||||
/// Source name.
|
||||
pub name: String,
|
||||
/// Window title.
|
||||
pub window_title: String,
|
||||
/// Window class (X11).
|
||||
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).
|
||||
pub fn with_class(mut self, class: &str) -> Self {
|
||||
self.window_class = Some(class.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the OBS source.
|
||||
pub fn create_source(&self) -> Result<CaptureSource> {
|
||||
info!(
|
||||
"Creating window capture source: {} ({})",
|
||||
self.name, self.window_title
|
||||
);
|
||||
|
||||
let source = CaptureSource {
|
||||
name: self.name.clone(),
|
||||
source_type: SourceType::WindowCapture,
|
||||
active: false,
|
||||
};
|
||||
|
||||
Ok(source)
|
||||
}
|
||||
}
|
||||
|
||||
/// Monitor capture source (fallback).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorCapture {
|
||||
/// Source name.
|
||||
pub name: String,
|
||||
/// Monitor index.
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the OBS source.
|
||||
pub fn create_source(&self) -> Result<CaptureSource> {
|
||||
info!(
|
||||
"Creating monitor capture source: {} (monitor {})",
|
||||
self.name, self.monitor
|
||||
);
|
||||
|
||||
let source = CaptureSource {
|
||||
name: self.name.clone(),
|
||||
source_type: SourceType::MonitorCapture,
|
||||
active: false,
|
||||
};
|
||||
|
||||
Ok(source)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the League of Legends game window.
|
||||
pub fn find_league_window() -> Option<String> {
|
||||
// Note: Actual window finding would use platform-specific APIs
|
||||
// On Linux: X11/Wayland
|
||||
// On Windows: Win32 API
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// Would use x11rb or similar to find window
|
||||
// For now, return the expected window title
|
||||
Some("League of Legends (TM) Client".to_string())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Would use FindWindowW
|
||||
Some("League of Legends (TM) Client".to_string())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||
None
|
||||
}
|
||||
|
||||
#[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::Any);
|
||||
}
|
||||
|
||||
#[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_capture_source_activation() {
|
||||
let mut source = CaptureSource {
|
||||
name: "Test".to_string(),
|
||||
source_type: SourceType::GameCapture,
|
||||
active: false,
|
||||
};
|
||||
|
||||
assert!(!source.is_active());
|
||||
source.activate().unwrap();
|
||||
assert!(source.is_active());
|
||||
source.deactivate().unwrap();
|
||||
assert!(!source.is_active());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user