[Feature request] Rust lang wrapper?
overheat opened this issue · 11 comments
Is it on the roadmap?
I'd love to see rust bindings or a wrapper, yes. But we don't have anyone on the team who particularly knows Rust. As you're asking, that suggests you have sufficient knowledge on the language. Would you be willing to build up the bindings?
Good to know you love to see it. Actrually I have tried, but it beyond my Rust(or C++) knowleadge right now.
Hope other Rust & C++ guru see this issue and help on this. :)
For a while I've been using gstreamer plugin to interface libcamera with Rust, which is quite simple to setup with gstreamer-rs
, but it only supports basic streaming without any configuration options (i.e. framerate, exposure control is missing).
I've tried implementing Rust wrapper for libcamera before going the gstreamer route, but unfortunatelly C++ is not very FFI friendly due to templates, polymorphism and other quirks. It would be much simpler if libcamera had C API as an intermediate layer between C++ and Rust, but afaik that does not exist.
Anyway, I now see that gstreamer plugin is not really working for me and I will try a second shot at C++ wrapper. I will report if I make any progress.
Indeed, perhaps adding a C API will give you easy rust bindings?
Presumably - that's a matter of wrapping all the C++ API with C wrappers ? That would give both C / Rust language options.
For gstreamer, the framerate control support should get in soon, but otherwise, please consider looking at adding features to the gstreamer plugin too!
Yes, having a C API would make Rust bindings much easier as most of the FFI bridge can be auto-generated with bindgen. This produces a list of unsafe functions that can be called directly from Rust (usually packaged in a separate crate with -sys
or -bind
suffix). Then a safe wrapper is written around these to satisfy all Rust safety rules.
I've had great success with NCNN this way. The library is written in C++, but they provide a C wrapper. If anyone is interested, here is a Rust wrapper for it https://github.com/lit-robotics/rust-ncnn.
Unfortunatelly I don't think I can help in writting C API for libcamera. It's been quite a while since I did anything in C/C++ and writting a good API for such library is a bit of a challenge. I can, however, contribute a Rust wrapper when C API is done. In the meantime, I will experiment with interfacing with C++ directly, maybe it's not as bad as it looks.
I tried wrapping C++ into Rust directly with cxx but it turned out to be way more boilerplate than writing a C wrapper.
So I went ahead with C wrapper route and it turned out surprisingly well. I am already able to list cameras from both C and Rust! You can check my initial C API here. Any comments or help is welcome.
@chemicstry Thank you for kicking this off! It will be very interesting to see how it goes, and grows!
Please consider posting work to the libcamera development mailing list for review and discussion with the whole project, (early as an RFC even) to make sure we can figure out how to integrate this!
I finally got to the point of capturing a JPEG image using Rust: https://github.com/lit-robotics/libcamera-rs. It is still missing some less common functions, but general functionality like all control and property types is fully implemented.
I'm quite short on time (startups...) and I find the patchwork process a bit cumbersome (never used) so I opted to implement C wrapper outside the libcamera tree for now. It is dynamically linked to libcamera using pkg-config while building libcamera-sys
so can be used with unmodified libcamera installation.
You can find the C API here. It turned out a bit larger than I initially anticipated. Since there is very little information on wrapping C++ API with pure C (I guess it's only useful for FFI?), here are some insights:
- Most of the C++ classes can be wrapped with opaque pointers:
object_t *object_create()
,int object_member_fn(object_t *obj)
andvoid object_destroy(object_t *obj)
. This works quite well except for the huge amount of accessor functions. - Simple data classes can be wrapped by redefining identical layout struct and adding static asserts to match C++ class size and member offsets.
- Probably the hardest part is dealing with STL containers. All return by value containers must be reallocated to the heap with an opaque pointer. Accessor functions have to be duplicated for each item type if you want to avoid dealing with
void*
. For example, iterating a control list map looks like this:
libcamera_control_list_iter_t* iter = libcamera_control_list_iter(list);
while (!libcamera_control_list_iter_end(iter)) {
unsigned int id = libcamera_control_list_iter_id(iter);
const libcamera_control_value_t *val = libcamera_control_list_iter_value(iter);
libcamera_control_list_iter_next(iter);
}
libcamera_control_list_iter_destroy(iter);
- API is missing documentation and I don't think that it would be possible to auto generate it from C++ docs, because some functions have wildly different signatures. Maybe it could only have C-specific usage docs and refer to C++ for any functional information.
Again sorry for not initiating this on the mailing list, maybe once I free up some time I will try to start a discussion there. Meanwhile, maybe someone will find this useful.
Thank you for sharing your findings and development progress. I'll share it with the core team and see if there's anything we can do to help progress it as well.
I see your project is licenced as MIT/Apache - will that prevent us from ever being able to upstream this work into libcamera in the future?
I used MIT/Apache as that is standard for Rust projects and I think it is fine since I'm dynamic linking with LGPL library.
Regarding integrating with libcamera, I think that is also possible, because MIT/Apache are the most permissible licenses. I.e. you can relicense forks of MIT project to LGPL without permission of original author. If there are any objections I can relicense it under LGPL.
Oh, I didn't know it could be relicensed ... without permission of the original author?! It sounds like we can figure that out when we have a plan for upstreaming anyway. It would be good to make this an upstream supported feature in libcamera, so it can be directly pacakged/integrated etc ... I bet others would already like to use it.
Thank you for making this public though - I've skimmed through - and there's a lot of work gone into it, even if it's figuring out all the boilerplates etc...