use std::io::{self, Write};
use std::time::Instant;
use windows::Storage::Streams::InMemoryRandomAccessStream;
use windows::core::Interface;
use windows_capture::capture::{Context, GraphicsCaptureApiHandler};
use windows_capture::encoder::{
AudioSettingsBuilder, ContainerSettingsBuilder, VideoEncoder, VideoSettingsBuilder,
};
use windows_capture::frame::Frame;
use windows_capture::graphics_capture_api::InternalCaptureControl;
use windows_capture::graphics_capture_picker::GraphicsCapturePicker;
use windows_capture::settings::{
ColorFormat, CursorCaptureSettings, DirtyRegionSettings, DrawBorderSettings,
MinimumUpdateIntervalSettings, SecondaryWindowSettings, Settings,
};
struct StreamCapture {
encoder: Option<VideoEncoder>,
stream: InMemoryRandomAccessStream,
start: Instant,
}
impl GraphicsCaptureApiHandler for StreamCapture {
type Flags = (i32, i32);
type Error = Box<dyn std::error::Error + Send + Sync>;
fn new(ctx: Context<Self::Flags>) -> Result<Self, Self::Error> {
let stream = InMemoryRandomAccessStream::new()?;
let encoder = VideoEncoder::new_from_stream(
VideoSettingsBuilder::new(ctx.flags.0 as u32, ctx.flags.1 as u32),
AudioSettingsBuilder::default().disabled(true),
ContainerSettingsBuilder::default(),
stream.cast()?,
)?;
Ok(Self {
encoder: Some(encoder),
stream,
start: Instant::now(),
})
}
fn on_frame_arrived(
&mut self,
frame: &mut Frame,
capture_control: InternalCaptureControl,
) -> Result<(), Self::Error> {
print!(
"\rStreaming for: {} seconds | Buffer size: {} bytes",
self.start.elapsed().as_secs(),
self.stream.Size()?
);
io::stdout().flush()?;
self.encoder.as_mut().unwrap().send_frame(frame)?;
if self.start.elapsed().as_secs() >= 6 {
self.encoder.take().unwrap().finish()?;
let size = self.stream.Size()?;
println!("\nCapture finished. Stream contains {size} bytes.");
let reader =
windows::Storage::Streams::DataReader::CreateDataReader(&self.stream.GetInputStreamAt(0)?)?;
reader.LoadAsync(size as u32)?.join()?;
let mut bytes = vec![0u8; size as usize];
reader.ReadBytes(&mut bytes)?;
std::fs::write("stream_output.mp4", &bytes)?;
println!("Saved stream to stream_output.mp4");
capture_control.stop();
}
Ok(())
}
fn on_closed(&mut self) -> Result<(), Self::Error> {
println!("Capture session ended");
Ok(())
}
}
fn main() {
let item = GraphicsCapturePicker::pick_item().expect("Failed to pick item");
let Some(item) = item else {
println!("No item selected");
return;
};
let size = item.size().expect("Failed to get item size");
let settings = Settings::new(
item,
CursorCaptureSettings::Default,
DrawBorderSettings::Default,
SecondaryWindowSettings::Default,
MinimumUpdateIntervalSettings::Default,
DirtyRegionSettings::Default,
ColorFormat::Rgba8,
size,
);
StreamCapture::start(settings).expect("Stream capture failed");
}