diff --git a/Cargo.lock b/Cargo.lock index e8e60aed..37ec1371 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -844,6 +844,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "uuid", ] [[package]] @@ -8050,9 +8051,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" dependencies = [ "getrandom 0.2.15", "serde", diff --git a/apps/cli/Cargo.toml b/apps/cli/Cargo.toml index 9bdb13d0..83d068d7 100644 --- a/apps/cli/Cargo.toml +++ b/apps/cli/Cargo.toml @@ -19,3 +19,4 @@ tokio.workspace = true editor = "0.1.1" scap.workspace = true core-graphics = "0.24.0" +uuid = { version = "1.11.1", features = ["v4"] } diff --git a/apps/cli/src/main.rs b/apps/cli/src/main.rs index 67b9ada5..2230225e 100644 --- a/apps/cli/src/main.rs +++ b/apps/cli/src/main.rs @@ -1,10 +1,13 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{env::current_dir, path::PathBuf, sync::Arc}; use cap_editor::create_segments; use cap_media::sources::{get_target_fps, ScreenCaptureTarget}; use cap_project::{RecordingMeta, XY}; +use cap_recording::RecordingOptions; use cap_rendering::RenderVideoConstants; use clap::{Args, Parser, Subcommand, ValueEnum}; +use tokio::io::{AsyncBufReadExt, BufReader}; +use uuid::Uuid; #[derive(Parser)] struct Cli { @@ -54,6 +57,12 @@ struct RecordStartArgs { /// ID of the microphone to record #[arg(long)] mic: Option, + #[arg(long)] + /// Path to save the '.cap' project to + path: Option, + /// Maximum fps to record at (max 60) + #[arg(long)] + fps: Option, } #[derive(Args)] @@ -183,7 +192,39 @@ window {}: }) .ok_or("No target specified".to_string())??; - dbg!(target_info); + dbg!(&target_info); + + let id = Uuid::new_v4().to_string(); + let path = args + .path + .unwrap_or_else(|| current_dir().unwrap().join(format!("{id}.cap"))); + + let actor = cap_recording::spawn_recording_actor( + id, + path, + RecordingOptions { + capture_target: target_info, + camera_label: None, + audio_input_name: None, + fps: 30, + output_resolution: None, + }, + None, + None, + ) + .await + .map_err(|e| e.to_string())?; + + println!("Recording starting, press Enter to stop"); + + tokio::io::BufReader::new(tokio::io::stdin()) + .read_line(&mut String::new()) + .await + .unwrap(); + + println!("Recording stopped"); + + actor.stop().await.unwrap(); } _ => {} }, diff --git a/crates/media/src/encoders/h264_avassetwriter.rs b/crates/media/src/encoders/h264_avassetwriter.rs index bb49e4d3..b2bc13fb 100644 --- a/crates/media/src/encoders/h264_avassetwriter.rs +++ b/crates/media/src/encoders/h264_avassetwriter.rs @@ -58,7 +58,7 @@ impl H264AVAssetWriterEncoder { let mut video_input = av::AssetWriterInput::with_media_type_and_output_settings( av::MediaType::video(), - Some(output_settings.as_ref()), + Some(dbg!(output_settings.as_ref())), ) .map_err(|_| MediaError::Any("Failed to create AVAssetWriterInput"))?; video_input.set_expects_media_data_in_real_time(true); diff --git a/crates/media/src/lib.rs b/crates/media/src/lib.rs index 306423f8..bbd88691 100644 --- a/crates/media/src/lib.rs +++ b/crates/media/src/lib.rs @@ -24,7 +24,7 @@ pub fn init() -> Result<(), MediaError> { #[derive(Error, Debug)] pub enum MediaError { - #[error("Media error: {0}")] + #[error("{0}")] Any(&'static str), #[error("Cannot build a pipeline without any tasks")] diff --git a/crates/media/src/sources/screen_capture.rs b/crates/media/src/sources/screen_capture.rs index 0d4fedf3..3b3ca7ea 100644 --- a/crates/media/src/sources/screen_capture.rs +++ b/crates/media/src/sources/screen_capture.rs @@ -130,7 +130,8 @@ impl ScreenCaptureSource { let options = this.create_options(); - let [frame_width, frame_height] = get_output_frame_size(&options); + let [frame_width, frame_height] = get_output_frame_size(&dbg!(options)); + dbg!(frame_width, frame_height); this.video_info = VideoInfo::from_raw(RawVideoFormat::Bgra, frame_width, frame_height, MAX_FPS); @@ -147,7 +148,7 @@ impl ScreenCaptureSource { } fn create_options(&self) -> Options { - let targets = dbg!(scap::get_all_targets()); + let targets = scap::get_all_targets(); let excluded_targets: Vec = targets .iter() @@ -173,10 +174,36 @@ impl ScreenCaptureSource { }; let target = match &self.target { - ScreenCaptureTarget::Window(w) => targets.into_iter().find(|t| match &t { - Target::Window(window) if window.id == w.id => true, - _ => false, - }), + ScreenCaptureTarget::Window(w) => { + let window_target = targets + .iter() + .find_map(|t| match t { + Target::Window(window) if window.id == w.id => Some(window), + _ => None, + }) + .unwrap(); + + #[cfg(target_os = "macos")] + { + platform::display_for_window(window_target.raw_handle).and_then(|display| { + targets.into_iter().find(|t| match t { + Target::Display(d) => d.raw_handle.id == display.id, + _ => false, + }) + }) + } + #[cfg(target_os = "windows")] + { + platform::display_for_window(window_target.raw_handle).and_then(|display| { + targets.into_iter().find(|t| match t { + Target::Display(d) => d.raw_handle == display, + _ => false, + }) + }) + } + #[cfg(not(any(target_os = "windows", target_os = "macos")))] + None + } ScreenCaptureTarget::Screen(capture_screen) => targets .iter() .find(|t| match t { diff --git a/crates/recording/src/actor.rs b/crates/recording/src/actor.rs index d85ebfe8..5327114e 100644 --- a/crates/recording/src/actor.rs +++ b/crates/recording/src/actor.rs @@ -6,6 +6,7 @@ use std::{ use cap_flags::FLAGS; use cap_media::{ + data::Pixel, encoders::{H264Encoder, MP3Encoder, Output}, feeds::{AudioInputFeed, CameraFeed}, filters::VideoFilter, @@ -542,7 +543,7 @@ impl MakeCapturePipeline for cap_media::sources::CMSampleBufferCapture { let screen_config = source.info(); let screen_encoder = cap_media::encoders::H264AVAssetWriterEncoder::init( "screen", - screen_config, + dbg!(screen_config), Output::File(output_path.into()), )?; @@ -561,8 +562,11 @@ impl MakeCapturePipeline for cap_media::sources::AVFrameCapture { where Self: Sized, { - let screen_config = source.info(); - let screen_filter = VideoFilter::init("screen", screen_config, screen_config)?; + let mut screen_config = source.info(); + let screen_filter = VideoFilter::init("screen", screen_config, { + screen_config.pixel_format = Pixel::NV12; + screen_config + })?; let screen_encoder = H264Encoder::init("screen", screen_config, Output::File(output_path.into()))?; Ok(builder