I'm just working on a very simple video recording proof of concept, structured on the jpeg_capture example. Here is the code:
use std::{process::exit, time::Duration};
use libcamera_rs::{
camera::CameraConfigurationStatus, camera_manager::CameraManager, framebuffer_allocator::FrameBufferAllocator,
framebuffer_map::MemoryMappedFrameBuffer, pixel_format::PixelFormat, properties, stream::StreamRole,
};
// drm-fourcc does not have MJPEG type yet, construct it from raw fourcc identifier
const PIXEL_FORMAT_MJPEG: PixelFormat = PixelFormat::new(u32::from_le_bytes([b'M', b'J', b'P', b'G']), 0);
fn main() {
let filename = match std::env::args().nth(1) {
Some(f) => f,
None => {
println!("Error: missing file output parameter");
println!("Usage: ./video_capture </path/to/output.mjpeg>");
exit(1);
}
};
let mgr = CameraManager::new().unwrap();
let cameras = mgr.cameras();
let cam = cameras.get(0).expect("No cameras found");
println!(
"Using camera: {}",
*cam.properties().get::<properties::Model>().unwrap()
);
let mut cam = cam.acquire().expect("Unable to acquire camera");
// This will generate default configuration for each specified role
let mut cfgs = cam.generate_configuration(&[StreamRole::VideoRecording]).unwrap();
cfgs.get_mut(0).unwrap().set_pixel_format(PIXEL_FORMAT_MJPEG);
println!("Generated config: {:#?}", cfgs);
match cfgs.validate() {
CameraConfigurationStatus::Valid => println!("Camera configuration valid!"),
CameraConfigurationStatus::Adjusted => println!("Camera configuration was adjusted: {:#?}", cfgs),
CameraConfigurationStatus::Invalid => panic!("Error validating camera configuration"),
}
// Ensure that pixel format was unchanged
assert_eq!(
cfgs.get(0).unwrap().get_pixel_format(),
PIXEL_FORMAT_MJPEG,
"MJPEG is not supported by the camera"
);
cam.configure(&mut cfgs).expect("Unable to configure camera");
let mut alloc = FrameBufferAllocator::new(&cam);
// Allocate frame buffers for the stream
let cfg = cfgs.get(0).unwrap();
let stream = cfg.stream().unwrap();
let buffers = alloc.alloc(&stream).unwrap();
println!("Allocated {} buffers", buffers.len());
// Convert FrameBuffer to MemoryMappedFrameBuffer, which allows reading &[u8]
let buffers = buffers
.into_iter()
.map(|buf| MemoryMappedFrameBuffer::new(buf).unwrap())
.collect::<Vec<_>>();
// Create capture requests and attach buffers
let reqs = buffers
.into_iter()
.enumerate()
.map(|(i, buf)| {
let mut req = cam.create_request(Some(i as u64)).unwrap();
req.add_buffer(&stream, buf).unwrap();
req
})
.collect::<Vec<_>>();
// Completed capture requests are returned as a callback
let (tx, rx) = std::sync::mpsc::channel();
cam.on_request_completed(move |req| {
tx.send(req).unwrap();
});
// TODO: Set `Control::FrameDuration()` here. Blocked on https://github.com/lit-robotics/libcamera-rs/issues/2
cam.start(None).unwrap();
// Enqueue all requests to the camera
for req in reqs {
println!("Request queued for execution: {req:#?}");
cam.queue_request(req).unwrap();
}
loop {
println!("Waiting for camera request execution");
let req = rx.recv_timeout(Duration::from_secs(2)).expect("Camera request failed");
println!("Received frame {} from request {}", req.sequence(), req.cookie());
// Recycle the request back to the camera for execution
cam.queue_request(req).unwrap();
}
// Everything is cleaned up automatically by Drop implementations
}
The output is as follows:
[22:52:00.786683276] [53183] INFO Camera camera_manager.cpp:299 libcamera v0.0.4+17-3f8bcc1a
Using camera: Integrated_Webcam_FHD: Integrat
Generated config: [
StreamConfigurationRef {
pixel_format: MJPEG,
size: Size {
width: 1920,
height: 1080,
},
stride: 0,
frame_size: 4147789,
buffer_count: 4,
},
]
Camera configuration valid!
[22:52:00.926189027] [53183] INFO Camera camera.cpp:1028 configuring streams: (0) 1920x1080-MJPEG
Allocated 4 buffers
Request queued for execution: Request {
seq: 0,
status: Pending,
cookie: 0,
}
Request queued for execution: Request {
seq: 0,
status: Pending,
cookie: 1,
}
Request queued for execution: Request {
seq: 0,
status: Pending,
cookie: 2,
}
Request queued for execution: Request {
seq: 0,
status: Pending,
cookie: 3,
}
Waiting for camera request execution
Received frame 0 from request 0
Waiting for camera request execution
Received frame 1 from request 1
Waiting for camera request execution
Received frame 2 from request 2
Waiting for camera request execution
Received frame 3 from request 3
Waiting for camera request execution
Received frame 4 from request 0
Waiting for camera request execution
Received frame 5 from request 1
Waiting for camera request execution
Received frame 6 from request 2
Waiting for camera request execution
[22:52:01.551288812] [53300] FATAL default request.cpp:155 assertion "request->status() == RequestPending" failed in cancel()
Backtrace:
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera7Request7Private6cancelEv+0xfb) [0x7f8835532d1d]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera15PipelineHandler14doQueueRequestEPNS_7RequestE+0xef) [0x7f8835526b71]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera15PipelineHandler15doQueueRequestsEv+0x55) [0x7f8835526bdf]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera17BoundMethodMemberINS_15PipelineHandlerEvJEE6invokeEv+0x74) [0x7f883552e026]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera15BoundMethodArgsIvJEE10invokePackIJEvEENSt9enable_ifIXsrSt7is_voidIT0_E5valueEvE4typeEPNS_19BoundMethodPackBaseESt16integer_sequenceImJXspT_EEE+0x33) [0x7f883573cf47]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera15BoundMethodArgsIvJEE10invokePackEPNS_19BoundMethodPackBaseE+0x27) [0x7f883573ce51]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera15BoundMethodBase12activatePackESt10shared_ptrINS_19BoundMethodPackBaseEEb+0x109) [0x7f883571f199]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera17BoundMethodMemberINS_15PipelineHandlerEvJEE8activateEb+0xd7) [0x7f883552df33]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera6SignalIJEE4emitEv+0x8c) [0x7f8835722b5e]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera7Request7Private20emitPrepareCompletedEv+0x28) [0x7f8835532df8]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera7Request7Private7prepareENSt6chrono8durationIlSt5ratioILl1ELl1000EEEE+0x17c) [0x7f8835532fa2]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera15PipelineHandler12queueRequestEPNS_7RequestE+0x5a) [0x7f8835526a7a]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera17BoundMethodMemberINS_15PipelineHandlerEvJPNS_7RequestEEE6invokeES3_+0x7f) [0x7f88354b4497]
target/debug/examples/video_capture(_ZN9libcamera15BoundMethodArgsIvJPNS_7RequestEEE10invokePackIJLm0EEvEENSt9enable_ifIXsrSt7is_voidIT0_E5valueEvE4typeEPNS_19BoundMethodPackBaseESt16integer_sequenceImJXspT_EEE+0x4a) [0x55e79a75d3d0]
target/debug/examples/video_capture(_ZN9libcamera15BoundMethodArgsIvJPNS_7RequestEEE10invokePackEPNS_19BoundMethodPackBaseE+0x27) [0x55e79a75d1b9]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera13InvokeMessage6invokeEv+0x46) [0x7f883573012e]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera6Object7messageEPNS_7MessageE+0x7a) [0x7f8835730876]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera6Thread16dispatchMessagesENS_7Message4TypeE+0x317) [0x7f8835737a8d]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera19EventDispatcherPoll13processEventsEv+0x38) [0x7f8835720d40]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera6Thread4execEv+0x70) [0x7f8835736d9e]
/usr/local/lib/x86_64-linux-gnu/libcamera.so.0.0.4(_ZN9libcamera13CameraManager7Private3runEv+0x112) [0x7f88354b65e8]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZN9libcamera6Thread11startThreadEv+0xf2) [0x7f8835736d22]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZSt13__invoke_implIvMN9libcamera6ThreadEFvvEPS1_JEET_St21__invoke_memfun_derefOT0_OT1_DpOT2_+0x6a) [0x7f883573b640]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZSt8__invokeIMN9libcamera6ThreadEFvvEJPS1_EENSt15__invoke_resultIT_JDpT0_EE4typeEOS6_DpOS7_+0x3b) [0x7f883573b593]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEE9_M_invokeIJLm0ELm1EEEEvSt12_Index_tupleIJXspT_EEE+0x47) [0x7f883573b4f3]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEEclEv+0x1c) [0x7f883573b4a8]
/usr/local/lib/x86_64-linux-gnu/libcamera-base.so.0.0.4(_ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS4_EEEEE6_M_runEv+0x20) [0x7f883573b488]
/lib/x86_64-linux-gnu/libstdc++.so.6(+0xdc2b3) [0x7f8834fb62b3]
Aborted (core dumped)
I believe the error is because the Requests are not getting cleared before re-queuing. The libcamera example cam app uses the Request::reuse method prior to re-queuing already-used Requests.
I think it would be handy if that method were exposed in these rust bindings.
I'm just working on a very simple video recording proof of concept, structured on the
jpeg_capture
example. Here is the code:The output is as follows:
I believe the error is because the
Request
s are not getting cleared before re-queuing. Thelibcamera
examplecam
app uses the Request::reuse method prior to re-queuing already-usedRequest
s.I think it would be handy if that method were exposed in these rust bindings.