Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
themujahidkhan opened this issue Mar 8, 2024 · 12 comments
Open

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

themujahidkhan opened this issue Mar 8, 2024 · 12 comments

Comments

@themujahidkhan
Copy link

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
Copy link
Owner

NiiightmareXD commented Mar 12, 2024

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

@themujahidkhan
Copy link
Author

themujahidkhan commented Mar 12, 2024

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 my main.rs file to call it from the front end?

@NiiightmareXD
Copy link
Owner

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 my main.rs file to call it from the front end?

there is a example in readme

@themujahidkhan themujahidkhan closed this as not planned Won't fix, can't repro, duplicate, stale Mar 12, 2024
@eythaann
Copy link

eythaann commented Mar 29, 2024

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 NiiightmareXD reopened this Mar 29, 2024
@NiiightmareXD
Copy link
Owner

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

@eythaann
Copy link

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

@eythaann
Copy link

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
Copy link
Owner

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
Copy link

ohh, let me try this

@eythaann
Copy link

eythaann commented Mar 30, 2024

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
Copy link
Owner

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

@angelbests
Copy link

angelbests commented Sep 30, 2024

you must use this fn tauri::async_runtime::spawn


#[tauri::command]
fn start_capture(app: AppHandle){
    tauri::async_runtime::spawn(async move {
        // Gets The Foreground Window, Checkout The Docs For Other Capture Items
        let flagset = Flagset {
            app:app,
            flag:true,
        };
        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.
            flagset
        );
        // 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");
    });

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants