bryal / captrs

Cross-platform screen capture in Rust
GNU Affero General Public License v3.0
151 stars 16 forks source link

Empty pixel data on Windows #11

Open EmiliaTheGoddess opened 1 year ago

EmiliaTheGoddess commented 1 year ago

The example code(taking average color of screen, exactly what I need) is running on GNU/Linux without any problem but on windows every pixel just returns 0. Happens on both a daily use Windows with drivers installed and a new VM with nothing installed. Is there some sort of windows-specific dependency I'm missing?

yurtemre7 commented 1 year ago

Did you find a solution or another library?

EmiliaTheGoddess commented 1 year ago

@yurtemre7 I ended up using some code I found online. You can try other capture libraries like screenshot-rs.

TommyGymer commented 1 year ago

I've found that, for some reason, the first capture on Windows results in a blank (an entirely zero) frame with subsequent captures working correctly.

frame 1

frame1

frame 2

frame2

code used

let mut capturer = Capturer::new(1).unwrap();
let (w, h) = capturer.geometry();

// capture_frame_components is a modified version of capture_frame
// which returns a bgr Vec<u8> rather than a Vec<Bgr8>
let f1 = capturer.capture_frame_components().unwrap();
let f2 = capturer.capture_frame_components().unwrap();

// qoi has been converted to png for the images above
let p1 = Path::new("./frame1.qoi");
let p2 = Path::new("./frame2.qoi");

// using the bgr array directly for the qoi, causing the inverted colour as this expects rgb
let o1 = encode_to_vec(&f1, w, h).unwrap();
let o2 = encode_to_vec(&f2, w, h).unwrap();

File::create(p1).unwrap().write_all(&o1).unwrap();
File::create(p2).unwrap().write_all(&o2).unwrap();
0Ky commented 1 year ago

Could this be related to how AcquireNextFrame() works via Desktop Duplication API in Windows? Which implies that you can have a pointer information before the image.

The AccumulatedFrames, TotalMetadataBufferSize, and LastPresentTime are set to zero only when the pointer was updated (that is, the desktop image was not updated) based on FrameInfo output when calling AcquireNextFrame.

Would it be possible to include checks, something similar to:

// No image update, only cursor moved.
if (info.AccumulatedFrames == 0 || info.LastPresentTime.QuadPart == 0)
{
    RETURN_ERR(DXGI_ERROR_WAIT_TIMEOUT);
}

// not interested in just mouse updates, which can happen much faster than 60fps if you really shake the mouse
if (info.LastPresentTime.HighPart == 0) 
{
    hr = _lDeskDupl->ReleaseFrame();
    return false;
}