Skip to content

[BUG 🪲] Windows API Error (0xC00D6D60) #145

@rachellcarroll

Description

@rachellcarroll

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 working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions