NiiightmareXD / windows-capture

Fastest Windows Screen Capture Library For Rust And Python 🔥
MIT License
205 stars 27 forks source link

How to Start Recording? I'm facing a lot of errors #26

Open themujahidkhan opened 6 months ago

themujahidkhan commented 6 months ago

Hey @NiiightmareXD

I'm using Tauri + NextJS to build a Screen Recorder application using the windows-capture library; I'm new to the Rust language. I created a new file called screen_capture.rs file inside the directory, like this 👉 src-tauri/src/screen_capture.rs

I copied the code that you've provided, pasted as it is, and created 2 new functions to "Start the recording" and "Stop the recording"

main.rs

#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

mod screen_capture;

use tauri::Builder;

fn main() {
    Builder::default()
        .invoke_handler(tauri::generate_handler![
            screen_capture::start_capture, 
            screen_capture::stop_capture
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

screen_capture.rs

use std::{
    io::{self, Write},
    time::Instant,
};

use windows_capture::{
    capture::GraphicsCaptureApiHandler,
    encoder::{VideoEncoder, VideoEncoderQuality, VideoEncoderType},
    frame::Frame,
    graphics_capture_api::InternalCaptureControl,
    monitor::Monitor,
    settings::{ColorFormat, CursorCaptureSettings, DrawBorderSettings, Settings},
};

// This struct will be used to handle the capture events.
struct Capture {
    // The video encoder that will be used to encode the frames.
    encoder: Option<VideoEncoder>,
    // To measure the time the capture has been running
    start: Instant,
}

impl GraphicsCaptureApiHandler for Capture {
    // The type of flags used to get the values from the settings.
    type Flags = String;

    // The type of error that can occur during capture, the error will be returned from `CaptureControl` and `start` functions.
    type Error = Box<dyn std::error::Error + Send + Sync>;

    // Function that will be called to create the struct. The flags can be passed from settings.
    fn new(message: Self::Flags) -> Result<Self, Self::Error> {
        println!("Got The Flag: {message}");

        let encoder = VideoEncoder::new(
            VideoEncoderType::Mp4,
            VideoEncoderQuality::HD1080p,
            1920,
            1080,
            "video.mp4",
        )?;

        Ok(Self {
            encoder: Some(encoder),
            start: Instant::now(),
        })
    }

    // Called every time a new frame is available.
    fn on_frame_arrived(
        &mut self,
        frame: &mut Frame,
        capture_control: InternalCaptureControl,
    ) -> Result<(), Self::Error> {
        print!(
            "\rRecording for: {} seconds",
            self.start.elapsed().as_secs()
        );
        io::stdout().flush()?;

        // Send the frame to the video encoder
        self.encoder.as_mut().unwrap().send_frame(frame)?;

        // Note: The frame has other uses too for example you can save a single for to a file like this:
        // frame.save_as_image("frame.png", ImageFormat::Png)?;
        // Or get the raw data like this so you have full control:
        // let data = frame.buffer()?;

        // Stop the capture after 6 seconds
        if self.start.elapsed().as_secs() >= 6 {
            // Finish the encoder and save the video.
            self.encoder.take().unwrap().finish()?;

            capture_control.stop();

            // Because there wasn't any new lines in previous prints
            println!();
        }

        Ok(())
    }

    // Optional handler called when the capture item (usually a window) closes.
    fn on_closed(&mut self) -> Result<(), Self::Error> {
        println!("Capture Session Closed");

        Ok(())
    }
}

fn main() {
    // Gets The Foreground Window, Checkout The Docs For Other Capture Items
    let primary_monitor = Monitor::primary().expect("There is no primary monitor");

    let settings = Settings::new(
        // Item To Captue
        primary_monitor,
        // Capture Cursor Settings
        CursorCaptureSettings::Default,
        // Draw Borders Settings
        DrawBorderSettings::Default,
        // The desired color format for the captured frame.
        ColorFormat::Rgba8,
        // Additional flags for the capture settings that will be passed to user defined `new` function.
        "Yea This Works".to_string(),
    )
    .unwrap();

    // Starts the capture and takes control of the current thread.
    // The errors from handler trait will end up here
    Capture::start(settings).expect("Screen Capture Failed");
}

Below code is for frontend

import {invoke} from "@tauri-apps/api/tauri";

export default function Home() {
   const startRecording = () => {
     invoke("start_capture").catch(console.error);
   };

   const stopRecording = () => {
     invoke("stop_capture").catch(console.error);
   };

  return (
    <div className="flex flex-col items-center justify-center h-screen">
      <div>
        <button onClick={startRecording}>Start Recording</button>
        <button onClick={stopRecording}>Stop Recording</button>
      </div>
    </div>
  );
};

I'm getting a lot of errors in my terminal,

the method `blocking_kind` exists for reference `&Result<(), Box<dyn Error>>`, but its trait bounds were not satisfied
the following trait bounds were not satisfied:
`Box<dyn std::error::Error>: Into<InvokeError>`
which is required by `Result<(), Box<dyn std::error::Error>>: tauri::command::private::ResultKind`
`Result<(), Box<dyn std::error::Error>>: serde::ser::Serialize`
which is required by `&Result<(), Box<dyn std::error::Error>>: tauri::command::private::SerializeKind`

mismatched types
  expected enum `Result<screen_capture::Capture, Box<(dyn std::error::Error + Send + Sync + 'static)>>`
found unit type `()`

unused imports: `Write`, `self`
`#[warn(unused_imports)]` on by default

Thank You

NiiightmareXD commented 6 months ago

where do you call blocking_kind method? the error is from there

themujahidkhan commented 6 months ago

Now, I'm getting different errors.

Can you tell me how to start the recording with a library? Which module or function is responsible for recording? How do I access them inside mymain.rs file to call it from the front end?

NiiightmareXD commented 6 months ago

Now, I'm getting different errors.

Can you tell me how to start the recording with a library? Which module or function is responsible for recording? How do I access them inside mymain.rs file to call it from the front end?

there is a example in readme

eythaann commented 5 months ago

Hi, I'm also having problems in tauri, I literally copy and paste the init example and make it a fn and does not works, but my error is: [2024-03-29][11:13:16][ERROR][komorebi_ui] Screen Capture Failed: FailedToInitWinRT https://learn.microsoft.com/en-us/windows/win32/api/roapi/nf-roapi-roinitialize

use std::{
    io::{self, Write},
    time::Instant,
};

use windows::Win32::Foundation::HWND;
use windows_capture::{
    capture::GraphicsCaptureApiHandler,
    encoder::{VideoEncoder, VideoEncoderQuality, VideoEncoderType},
    frame::Frame,
    graphics_capture_api::InternalCaptureControl,
    monitor::Monitor,
    settings::{ColorFormat, CursorCaptureSettings, DrawBorderSettings, Settings},
};

// This struct will be used to handle the capture events.
struct Capture {
    // The video encoder that will be used to encode the frames.
    encoder: Option<VideoEncoder>,
    // To measure the time the capture has been running
    start: Instant,
}

impl GraphicsCaptureApiHandler for Capture {
    // The type of flags used to get the values from the settings.
    type Flags = String;

    // The type of error that can occur during capture, the error will be returned from `CaptureControl` and `start` functions.
    type Error = Box<dyn std::error::Error + Send + Sync>;

    // Function that will be called to create the struct. The flags can be passed from settings.
    fn new(message: Self::Flags) -> Result<Self, Self::Error> {
        println!("Got The Flag: {message}");

        let encoder = VideoEncoder::new(
            VideoEncoderType::Mp4,
            VideoEncoderQuality::HD1080p,
            1920,
            1080,
            "video.mp4",
        )?;

        Ok(Self {
            encoder: Some(encoder),
            start: Instant::now(),
        })
    }

    // Called every time a new frame is available.
    fn on_frame_arrived(
        &mut self,
        frame: &mut Frame,
        capture_control: InternalCaptureControl,
    ) -> Result<(), Self::Error> {
        print!(
            "\rRecording for: {} seconds",
            self.start.elapsed().as_secs()
        );
        io::stdout().flush()?;

        // Send the frame to the video encoder
        self.encoder.as_mut().unwrap().send_frame(frame)?;

        // Note: The frame has other uses too for example you can save a single for to a file like this:
        // frame.save_as_image("frame.png", ImageFormat::Png)?;
        // Or get the raw data like this so you have full control:
        // let data = frame.buffer()?;

        // Stop the capture after 6 seconds
        if self.start.elapsed().as_secs() >= 6 {
            // Finish the encoder and save the video.
            self.encoder.take().unwrap().finish()?;

            capture_control.stop();

            // Because there wasn't any new lines in previous prints
            println!();
        }

        Ok(())
    }

    // Optional handler called when the capture item (usually a window) closes.
    fn on_closed(&mut self) -> Result<(), Self::Error> {
        println!("Capture Session Closed");

        Ok(())
    }
}

pub fn capture_window(_hwnd: HWND) -> Result<(), ()> {
    /* let window = Window::from_raw_hwnd(hwnd); */
    let primary_monitor = Monitor::primary().unwrap();

    let settings = Settings::new(
        // Item To Captue
        primary_monitor,
        // Capture Cursor Settings
        CursorCaptureSettings::Default,
        // Draw Borders Settings
        DrawBorderSettings::Default,
        // The desired color format for the captured frame.
        ColorFormat::Rgba8,
        // Additional flags for the capture settings that will be passed to user defined `new` function.
        "Yea This Works".to_string(),
    )
    .unwrap();

    // Starts the capture and takes control of the current thread.
    // The errors from handler trait will end up here
    Capture::start(settings).expect("Screen Capture Failed");
    Ok(())
}
NiiightmareXD commented 5 months ago

Hey, can you check if the start_free_threaded function works or not? also, what is your Windows version?

eythaann commented 5 months ago

I'm using win11 23h2, and let me see what I found related to start_free_threaded

eythaann commented 5 months ago

hmmm start_free_threaded isn't called in any part of the code I see, maybe is my editor not matching the reference or maybe a macro? but I don't find where start_free_threaded is called.

NiiightmareXD commented 5 months ago

no I mean't instead of Capture::start use Capture::start_free_threaded.

because I think Tauri initializes the WinRT in the main thread itself so you should run the capture in another thread.

Capture::start_free_threaded returns a handle to the capturing thread which you can use to call the struct or get the thread handle and etc.

eythaann commented 5 months ago

ohh, let me try this

eythaann commented 5 months ago

yeap that is, the thread. but now it freeze on send_frame "2" was never printed and error is not emitted:

self.encoder.as_mut().unwrap().send_frame(frame).expect("Failed to send frame");
println!("\r\n2");
NiiightmareXD commented 5 months ago

Ok... thats weird I have to try to do the same in Tauri