-
Notifications
You must be signed in to change notification settings - Fork 54
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Describe the bug 🐛
Recording fails to start, seeing this error: New handler error: Windows API Error: A valid type has not been set for this stream or a stream that it depends on. (0xC00D6D60)
Here is the complete output:
Starting capture for display:
Creating encoder with dimensions: 1596x827
Failed to start recording: New handler error: Windows API Error: A valid type has not been set for this stream or a stream that it depends on. (0xC00D6D60)
Expected behavior 📝
Recording begins without errors .
OS 🤖
- Version: 11
- Build[e.g. 22H2]
Additional context ➕
Code:
use serde::{Deserialize, Serialize};
use std::{
io::{self, Write},
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
time::Instant,
};
use windows_capture::{
capture::{Context, GraphicsCaptureApiHandler},
encoder::{AudioSettingsBuilder, ContainerSettingsBuilder, VideoEncoder, VideoSettingsBuilder},
frame::Frame,
graphics_capture_api::InternalCaptureControl,
monitor::Monitor,
settings::{ColorFormat, CursorCaptureSettings, DrawBorderSettings, Settings},
window::Window,
};
use crate::common::{CaptureTarget, DisplayItem, WindowItem};
static IS_RECORDING: AtomicBool = AtomicBool::new(false);
struct CaptureFlags {
output_path: String,
width: u32,
height: u32,
}
struct Capture {
encoder: Option<VideoEncoder>,
start: Instant,
output_path: String,
}
impl GraphicsCaptureApiHandler for Capture {
type Flags = CaptureFlags;
type Error = Box<dyn std::error::Error + Send + Sync>;
fn new(ctx: Context<Self::Flags>) -> Result<Self, Self::Error> {
IS_RECORDING.store(true, Ordering::SeqCst);
println!("Creating encoder with dimensions: {}x{}", ctx.flags.width, ctx.flags.height);
let encoder = VideoEncoder::new(
VideoSettingsBuilder::new(ctx.flags.width, ctx.flags.height),
AudioSettingsBuilder::default().disabled(true),
ContainerSettingsBuilder::default(),
ctx.flags.output_path.clone(),
)?;
Ok(Self {
encoder: Some(encoder),
start: Instant::now(),
output_path: ctx.flags.output_path.clone(),
})
}
fn on_frame_arrived(
&mut self,
frame: &mut Frame,
capture_control: InternalCaptureControl,
) -> Result<(), Self::Error> {
if !IS_RECORDING.load(Ordering::SeqCst) {
println!("\nStop signal received, finalizing video...");
if let Some(encoder) = self.encoder.take() {
encoder.finish()?;
}
capture_control.stop();
return Ok(());
}
print!(
"\rRecording for: {} seconds",
self.start.elapsed().as_secs()
);
io::stdout().flush()?;
if let Some(encoder) = self.encoder.as_mut() {
encoder.send_frame(frame)?;
}
Ok(())
}
fn on_closed(&mut self) -> Result<(), Self::Error> {
IS_RECORDING.store(false, Ordering::SeqCst);
println!("\nCapture session ended (e.g., window closed).");
if let Some(encoder) = self.encoder.take() {
encoder.finish()?;
}
Ok(())
}
}
// --- END: Refactored State Management ---
#[cfg(target_os = "windows")]
#[tauri::command]
pub fn list_windows() -> Result<Vec<WindowItem>, String> {
Window::enumerate()
.map(|windows| {
windows
.into_iter()
.map(|window| WindowItem {
id: window.process_id().unwrap_or(0),
title: window.title().unwrap_or_default(),
app_name: window.process_name().unwrap_or_default(),
app_pid: window.process_id().unwrap_or(0) as i32,
})
.collect()
})
.map_err(|e| e.to_string())
}
#[cfg(target_os = "windows")]
#[tauri::command]
pub fn list_displays() -> Result<Vec<DisplayItem>, String> {
Monitor::enumerate()
.map(|monitors| {
monitors
.into_iter()
.map(|monitor| DisplayItem {
id: monitor.index().unwrap_or(0) as u32,
name: monitor.name().unwrap_or_else(|_| "Unknown Display".to_string()),
width: monitor.width().unwrap_or(0),
height: monitor.height().unwrap_or(0),
})
.collect()
})
.map_err(|e| e.to_string())
}
#[tauri::command]
pub fn start_recording(target: CaptureTarget, audioInputId: Option<String>, outputPathStr: String) -> Result<(), String> {
if IS_RECORDING.load(Ordering::SeqCst) {
return Err("A recording is already in progress.".to_string());
}
tauri::async_runtime::spawn(async move {
let result: Result<(), String> = (async {
match target {
CaptureTarget { window_id: Some(window_id), .. } => {
let window = Window::enumerate()
.map_err(|e| e.to_string())?
.into_iter()
.find(|w| w.process_id().unwrap_or(0) == window_id)
.ok_or_else(|| "Window not found".to_string())?;
println!("Starting capture for window: {}", window.title().unwrap_or_default());
let settings = Settings::new(
window,
CursorCaptureSettings::Default,
DrawBorderSettings::Default,
ColorFormat::Bgra8,
CaptureFlags {
output_path: outputPathStr,
width: (window.rect().unwrap().right - window.rect().unwrap().left) as u32,
height: (window.rect().unwrap().bottom - window.rect().unwrap().top) as u32,
},
);
Capture::start_free_threaded(settings).map(|_| ()).map_err(|e| e.to_string())
}
CaptureTarget { display_id: Some(display_id), .. } => {
let monitor = Monitor::enumerate()
.map_err(|e| e.to_string())?
.into_iter()
.find(|m| m.index().unwrap_or(0) as u32 == display_id)
.ok_or_else(|| "Monitor not found".to_string())?;
println!("Starting capture for display: {}", monitor.name().unwrap_or_else(|_| "Unknown Display".to_string()));
let settings = Settings::new(
monitor,
CursorCaptureSettings::Default,
DrawBorderSettings::Default,
ColorFormat::Rgba8,
CaptureFlags {
output_path: outputPathStr,
width: monitor.width().unwrap_or(0),
height: monitor.height().unwrap_or(0),
},
);
Capture::start_free_threaded(settings).map(|_| ()).map_err(|e| e.to_string())
}
_ => Err("Either window_id or display_id must be provided".to_string()),
}
}).await;
if let Err(e) = result {
eprintln!("Failed to start recording: {}", e);
IS_RECORDING.store(false, Ordering::SeqCst);
}
});
Ok(())
}
#[tauri::command]
pub fn stop_recording() -> Result<(), String> {
if !IS_RECORDING.load(Ordering::SeqCst) {
return Err("No recording is currently in progress.".to_string());
}
IS_RECORDING.store(false, Ordering::SeqCst);
Ok(())
}
#[tauri::command]
pub fn list_audio_inputs() -> Result<Vec<String>, String>{
Ok(vec![])
}
I've tried adjusting the frame width/height based on the selected display. I've also tried using ColorFormat:Bgra8
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working