Making applications with libobscura
Importing
The main crate of libobscura is libvidi. This crate controls the basic properties of cameras. To include it as a dependency in your application, add this to your Cargo.toml
file:
[dependencies]
libvidi = { git = "https://codeberg.org/libobscura/libobscura.git", branch = "master" }
Caution! Libvidi is still developing rapidly and may introduce breaking changes at any time.
Usage
The basic objects in libobscura are:
- the camera list
- the camera (unacquired and acquired for exclusive use
- the stream
- the buffers
You need to use them all to get a picture from the camera:
use vidi;
// The tracker is notified about all supported cameras on the system.
let cameras_tracker = vidi::actors::camera_list::spawn()?;
// The list of cameras present at the moment
let cameras = cameras_tracker.cameras();
// The info for the first camera on the list
let camera_info = &cameras[0].0;
// Create a camera device
let camera = cameras_list.create(camera_info.id)
.unwrap().unwrap();
The operations so far are unlikely to fail under normal circumstances. The next step, though, will fail if some other application is already using the camera:
// Take exclusive ownership of the camera on the entire system.
let mut camera = camera.acquire().unwrap();
Now you're ready to start streaming and receive pictures.
Streams and buffers
Libobscura exposes two APIs to start streaming and get pictures to application developers.
One is easy, but forces you to make a copy if you want to do anything complex: it's the "borrowing" API.
The "owning" API is more powerful: it avoids copies (zero-copy) and lets you send buffers across threads. As a downside, you must re-queue your buffers back in the camera manually, so you can cause dead locks and memory leaks.
Both APIs offer the same configuration options, so choose the appropriate one.
Borrowing
The "decode_frame.rs" example uses the easy Stream API.
// Start capturing
let mut stream = camera.start(
// Choose your preferred data format
Config{fourcc: FourCC::new(b"YUYV"), width: 640, height: 480},
4
).unwrap();
loop {
// Get next frame
let (buf, meta, _next) = stream.next().unwrap();
let mmap = buf.memory_map_ro().unwrap();
let data = mmap.as_slice();
// process the raw pixel data here
}
Note that the program will not get to the next frame if you spend too much time processing this one. This will cause frame dropping. The buffer is borrowed and it belongs to the Stream instance, so you can't send it to another thread for processing, either.
Figure: A buffer borrowing API was chosen, among others, to prevent losing buffers.
Owning
The owning API gives you ownership of buffers, and expects you to return them when you're done. Because you own the buffers, you can send them between threads and even lose and leak them – in safe code (leaking is not, strictly speaking, unsafe).
See the example "glium.rs".
Figure: The owning API does not attach buffers to the owner (stream), but the user is responsible for returning them to prevent having unuseable resources (leaks).
Examples
There are more examples in the vidi-examples crate.