l1npengtul / nokhwa

Cross Platform Rust Library for Powerful Webcam/Camera Capture
Apache License 2.0
502 stars 121 forks source link

Use fair mutex for CallbackCamera #112

Closed RReverser closed 1 year ago

RReverser commented 1 year ago

This fixes lock contention that prevented any operations on the camera once capture loop is started by using a fair mutex instead of a regular one.

It's slightly more expensive, but this usecase - trying to access a shared resource that is repeatedly locked/unlocked by someone else - is exactly what it's designed for, and indeed it fixes the deadlock issue.

Fixes #111.

RReverser commented 1 year ago

Note: I know 0.10 branch is not the current one, but senpai branch is currently in an incompilable state on Windows (there are various errors) and I really just want to unblock myself, so hoping this can be backported at least into 0.10 for now.

UPD: also looks like cherry-pick applies almost cleanly onto senpai branch too, so porting to newer version won't be a problem either.

RReverser commented 1 year ago

The only problem I'm having even after this PR is that my first example from #111 now outputs:

Querying...
Opening camera...
Waiting 1 sec...
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Trying to change format...
Got frame!
Changed format successfully!
Got frame!

And program exits and the callback is never called again. I thought maybe I need to camera.open_stream()? again after changing parameters, but that fails with:

Error: Could not open device stream: Stream Already Open

I don't know what's going on here.

l1npengtul commented 1 year ago

Slight coding oversight. Ill push a change to your PR to fix it to make it easier for me to cherry-pick later

l1npengtul commented 1 year ago

I am away from my PC, unforunately making the edit hard. What you need to do is close the stream before you do any format operation than restart the stream.

RReverser commented 1 year ago

What you need to do is close the stream before you do any format operation than restart the stream.

I tried that too, but same error (FWIW this is on Windows):

use nokhwa::pixel_format::RgbFormat;
use nokhwa::utils::{RequestedFormat, RequestedFormatType};
use nokhwa::CallbackCamera;

fn main() -> anyhow::Result<()> {
    println!("Querying...");
    let cameras = nokhwa::query(nokhwa::utils::ApiBackend::Auto)?;
    println!("Opening camera...");
    let mut camera = CallbackCamera::new(
        cameras[0].index().clone(),
        RequestedFormat::new::<RgbFormat>(RequestedFormatType::AbsoluteHighestResolution),
        |_frame| {
            println!("Got frame!");
        },
    )?;
    camera.open_stream()?;
    println!("Waiting 1 sec...");
    std::thread::sleep(std::time::Duration::from_secs(1));
    println!("Closing stream...");
    camera.stop_stream()?;
    println!("Trying to change format...");
    camera.set_camera_requset(RequestedFormat::new::<RgbFormat>(
        RequestedFormatType::AbsoluteHighestFrameRate,
    ))?;
    println!("Changed format successfully!");
    println!("Opening stream again...");
    camera.open_stream()?;
    Ok(())
}
Querying...
Opening camera...
Waiting 1 sec...
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Got frame!
Closing stream...
Got frame!
Trying to change format...
Got frame!
Changed format successfully!
Opening stream again...
Got frame!
Error: Could not open device stream: Stream Already Open
RReverser commented 1 year ago

In fact, even if I don't do any config change but simply open stream - close stream - open stream again, it fails:

use nokhwa::pixel_format::RgbFormat;
use nokhwa::utils::{RequestedFormat, RequestedFormatType};
use nokhwa::CallbackCamera;

fn main() -> anyhow::Result<()> {
    println!("Querying...");
    let cameras = nokhwa::query(nokhwa::utils::ApiBackend::Auto)?;
    println!("Opening camera...");
    let mut camera = CallbackCamera::new(
        cameras[0].index().clone(),
        RequestedFormat::new::<RgbFormat>(RequestedFormatType::AbsoluteHighestResolution),
        |_frame| {
            println!("Got frame!");
        },
    )?;
    camera.open_stream()?;
    println!("Closing stream...");
    camera.stop_stream()?;
    println!("Opening stream again...");
    camera.open_stream()?;
    Ok(())
}
Querying...
Opening camera...
Closing stream...
Opening stream again...
Got frame!
Error: Could not open device stream: Stream Already Open

Looks like closing stream is not doing anything?

l1npengtul commented 1 year ago

Probably some spaghetti stuck in thr closing stream code in nokhwa-bindings-windows...

I'll take a further look

-------- Original Message -------- On Apr. 17, 2023, 7:07 p.m., Ingvar Stepanyan wrote:

In fact, even if I don't do any config change but simply open stream - close stream - open stream again, it fails:

use

nokhwa

::

pixel_format

::

RgbFormat

;

use

nokhwa

::

utils

::

{

RequestedFormat

,

RequestedFormatType

}

;

use

nokhwa

::

CallbackCamera

;

fn

main

(

)

-> anyhow

::

Result

<

(

)

{

println

!

(

"Querying..."

)

;

let

cameras = nokhwa

::

query

(

nokhwa

::

utils

::

ApiBackend

::

Auto

)

?

;

println

!

(

"Opening camera..."

)

;

let

mut

camera =

CallbackCamera

::

new

(

cameras

[

0

]

.

index

(

)

.

clone

(

)

,

RequestedFormat

::

new

::

<

RgbFormat

(

RequestedFormatType

::

AbsoluteHighestResolution

)

,

|_frame|

{

println

!

(

"Got frame!"

)

;

}

,

)

?

;

camera

.

open_stream

(

)

?

;

println

!

(

"Closing stream..."

)

;

camera

.

stop_stream

(

)

?

;

println

!

(

"Opening stream again..."

)

;

camera

.

open_stream

(

)

?

;

Ok

(

(

)

)

}

Querying... Opening camera... Closing stream... Opening stream again... Got frame! Error: Could not open device stream: Stream Already Open

Looks like closing stream is not doing anything?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

RReverser commented 1 year ago

Looks like closing stream is not doing anything?

Yeah I think this is the problem: https://github.com/l1npengtul/nokhwa/blob/3f570ee0e5b72ec7957d8bc02fb83aae2392ff07/nokhwa-bindings-windows/src/lib.rs#L1187-L1189

This only sets a flag instead of really closing the stream, so when I try to open stream again, it tries to actually open it and fails.

RReverser commented 1 year ago

As a temporary workaround, found that I can at least throw away the existing Camera and create a new one with desired format.

OlivierLDff commented 1 year ago

Any update on this, I'm experiencing the same issues with v4l2?

l1npengtul commented 1 year ago

Too busy with other things lately, I think I'll restart development of 0.11 next week and hopefully it will be done by the end of summer. I'll merge this for now so you can point at git for Cargo.