maxicarlos08 / gphoto2-rs Goto Github PK
View Code? Open in Web Editor NEWRust wrapper for gphoto2
License: GNU Lesser General Public License v2.1
Rust wrapper for gphoto2
License: GNU Lesser General Public License v2.1
From comment docs on gp_file_get_data_and_size
:
* For regular CameraFiles, the pointer to data that is returned is
* still owned by libgphoto2 and its lifetime is the same as the #file.
*
* For filedescriptor or handler based CameraFile types, the returned
* data pointer is owned by the caller and needs to be free()d to avoid
* memory leaks.
*
**/
All of the gp_camera_file_*
and gp_camera_folder_*
functions are thin wrappers around the corresponding gp_filesystem_get_*
.
I wonder if it would be better to avoid exposing those bindings on Camera
and instead focus only on CameraFS
.
While libgphoto2 itself has to maintain both at least for backward compatibility, there's no need to bind both categories for us. On the other hand, exposing a single, consistent, interface for all file manipulation could be better from ergonomics perspective.
WDYT?
Currently, there's no way to create a CameraFile
from existing in-memory data.
Looks like right now Context
, Camera
and Task
is Send
but not Sync
, which makes it hard to use in many APIs that expect being able to check the future status from an arbitrary thread.
For example, trying to use such future inside a tokio::spawn
API:
tokio::task::spawn(async move {
let camera = Context::new()?.autodetect_camera().await?;
// TODO
Ok(())
});
will fail with something like:
error: future cannot be sent between threads safely
--> examples/drop_camera.rs:8:22
|
8 | tokio::task::spawn(async move {
| ______________________^
9 | | let camera = Context::new()?.autodetect_camera().await?;
10 | | // TODO
11 | | Ok(())
12 | | });
| |___^ future created by async block is not `Send`
|
= help: the trait `std::marker::Sync` is not implemented for `(dyn gphoto2::context::ProgressHandler + std::marker::Send + 'static)`
note: future is not `Send` as this value is used across an await
--> examples/drop_camera.rs:9:53
|
9 | let camera = Context::new()?.autodetect_camera().await?;
| --------------- ^^^^^^ await occurs here, with `Context::new()?` maybe used later
| |
| has type `&gphoto2::Context` which is not `Send`
note: `Context::new()?` is later dropped here
--> examples/drop_camera.rs:9:60
|
9 | let camera = Context::new()?.autodetect_camera().await?;
| ^
help: consider moving this into a `let` binding to create a shorter lived borrow
--> examples/drop_camera.rs:9:18
|
9 | let camera = Context::new()?.autodetect_camera().await?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `tokio::spawn`
--> /home/rreverser/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.25.0/src/task/spawn.rs:163:21
|
163 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
Seems the only reason those APIs are not Sync
is because of progress handlers, which are only constrained by Send
. It makes sense, since progress handlers are usually mutating the environment, but I wonder if it would make sense to turn them into channels similarly to waker
field and other operations, so that background gphoto2 thread sends progress updates via Sender
, and receivers on any thread handle them.
As a follow-up to #26, it's also worth hooking up the global logging function gp_log_add_func
to the log
crate too as it provides even more info.
libgphoto2
allows you to set progress functions to a context.
These cannot be used to eg. track the download progress of a specific file being downloaded from the camera (you only get a display message). But you can use them to show some progress bars on your frontend (if you are building one) whenever libgphoto2
wants to report it
I've been looking a bit into how libgphoto2 deals with encodings, and found that it actually respects system localisation + some undocumented APIs for overriding localisation/encodings. What worries me there is that I'm not certain that non-English strings are guaranteed to be encoded in UTF-8 by libgphoto2, in which case perhaps we don't have a right to return them as String, or perhaps we at least have to customize encoding upon startup to enforce UTF-8 or something... As I said, I only started looking at this and there's a bit of uncertainty here. Now that I added test framework, I think it would be helpful to try and verify those assumptions and check what happens with non-ASCII characters.
Originally posted by @RReverser in #27 (comment)
If I'm reading code correctly, it seems that right now these two methods would conflict with each other - Task::set_progress_handler
sets its own set of handlers on the context and unsets them at the end of the task, so it would always override any global handlers set by Context::set_progress_handlers
.
If so... maybe it's better to make Context::set_progress_handlers
private rather than part of the public API as it seems pretty easy to misuse?
In the process of writing tests, I've noticed that the internal pointer of camera.port_info()
seems to be tied to the Camera
's own lifetime, and is freed when the camera is.
As a result, it's very easy to run into memory corruption issues in safe code. Example:
let camera = Context::new()?.autodetect_camera()?;
let port_info = camera.port_info()?;
println!("1) {:?}", port_info);
drop(camera);
println!("2) {:?}", port_info);
1) PortInfo { name: "Universal Serial Bus", path: "usb:001,001", port_type: Some(Usb) }
2) PortInfo { name: "pG�m�U", path: "��m�U", port_type: Some(Usb) }
PortInfo
should probably hold reference to Camera
(either as a Rust reference, or via libgphoto2's reference counting), but also, where is one there might be others.
It's worth walking through other data structures that have pointers and are returned from camera / context / filesystem / etc. and making sure they are not tied similarly to their parents.
The example code to take a picture in the README.md
gives an error message: Error: Path not absolute
.
The file and folder arguments in camera_fs.download_to
are reversed.
camera_fs.download_to(&file_path.name(), &file_path.folder(), Path::new(&file_path.name().to_string())).wait()?;
Should be:
camera_fs.download_to(&file.folder(), &file.name(), Path::new(&file.name().to_string())).wait()?;
The code in the examples/capture.rs
runs fine.
hi there! when compiling a rust project using gphoto2-rs under linux mint i get the following error:
$ cargo run
Compiling libgphoto2_sys v1.2.0
error: failed to run custom build command for `libgphoto2_sys v1.2.0`
Caused by:
process didn't exit successfully: `/home/<project-folder>/target/debug/build/libgphoto2_sys-7987dc01ee9af43d/build-script-build` (exit status: 101)
--- stdout
cargo:rerun-if-env-changed=LIBGPHOTO2_NO_PKG_CONFIG
cargo:rerun-if-env-changed=PKG_CONFIG_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG
cargo:rerun-if-env-changed=PKG_CONFIG
cargo:rerun-if-env-changed=LIBGPHOTO2_STATIC
cargo:rerun-if-env-changed=LIBGPHOTO2_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG_PATH
cargo:rerun-if-env-changed=PKG_CONFIG_PATH
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG_LIBDIR
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG_SYSROOT_DIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
cargo:rerun-if-env-changed=SYSROOT
cargo:rerun-if-env-changed=LIBGPHOTO2_STATIC
cargo:rerun-if-env-changed=LIBGPHOTO2_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu
cargo:rustc-link-lib=gphoto2
cargo:rustc-link-lib=m
cargo:rustc-link-lib=gphoto2_port
cargo:rustc-link-lib=m
cargo:rerun-if-env-changed=PKG_CONFIG_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG
cargo:rerun-if-env-changed=PKG_CONFIG
cargo:rerun-if-env-changed=LIBGPHOTO2_STATIC
cargo:rerun-if-env-changed=LIBGPHOTO2_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG_PATH
cargo:rerun-if-env-changed=PKG_CONFIG_PATH
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG_LIBDIR
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64-unknown-linux-gnu
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64_unknown_linux_gnu
cargo:rerun-if-env-changed=HOST_PKG_CONFIG_SYSROOT_DIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
--- stderr
/usr/include/gphoto2/gphoto2-port-log.h:24:10: fatal error: 'stdarg.h' file not found
thread 'main' panicked at 'Unable to generate bindings: ClangDiagnostic("/usr/include/gphoto2/gphoto2-port-log.h:24:10: fatal error: 'stdarg.h' file not found\n")', /home/<user>/.cargo/registry/src/github.com-1ecc6299db9ec823/libgphoto2_sys-1.2.0/src/build.rs:35:6
stack backtrace:
0: 0x55a4fc4b1a5a - std::backtrace_rs::backtrace::libunwind::trace::hba70c054c9cdbd74
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
1: 0x55a4fc4b1a5a - std::backtrace_rs::backtrace::trace_unsynchronized::hfff24a4d77b00fef
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: 0x55a4fc4b1a5a - std::sys_common::backtrace::_print_fmt::h6fb3e9652d3b4f4e
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/sys_common/backtrace.rs:65:5
3: 0x55a4fc4b1a5a - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h254ba81a1e20fed0
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/sys_common/backtrace.rs:44:22
4: 0x55a4fc4d6ffe - core::fmt::write::h232ccd94259bfe24
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/fmt/mod.rs:1213:17
5: 0x55a4fc4aebe5 - std::io::Write::write_fmt::h963cfaecfdd596f7
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/io/mod.rs:1682:15
6: 0x55a4fc4b1825 - std::sys_common::backtrace::_print::h6fbc4343523214ce
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/sys_common/backtrace.rs:47:5
7: 0x55a4fc4b1825 - std::sys_common::backtrace::print::h55ab07cec21aacd5
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/sys_common/backtrace.rs:34:9
8: 0x55a4fc4b322f - std::panicking::default_hook::{{closure}}::hc10df65206eee69e
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:267:22
9: 0x55a4fc4b2f6b - std::panicking::default_hook::hdd684731d8d78925
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:286:9
10: 0x55a4fc4b3939 - std::panicking::rust_panic_with_hook::h58681788b2d08dc0
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:688:13
11: 0x55a4fc4b36d9 - std::panicking::begin_panic_handler::{{closure}}::he6d9da406579493c
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:579:13
12: 0x55a4fc4b1f0c - std::sys_common::backtrace::__rust_end_short_backtrace::h5b1f3b233c047d47
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/sys_common/backtrace.rs:137:18
13: 0x55a4fc4b33e2 - rust_begin_unwind
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:575:5
14: 0x55a4fbfb5c13 - core::panicking::panic_fmt::hea602a2467b5109d
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/panicking.rs:64:14
15: 0x55a4fbfb60c3 - core::result::unwrap_failed::he3f6a4db4030a3f8
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/result.rs:1790:5
16: 0x55a4fbfb9d67 - core::result::Result<T,E>::expect::hbaab93758227f04c
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/result.rs:1069:23
17: 0x55a4fbfb9815 - build_script_build::main::h4895ba51fb0920c7
at /home/js/.cargo/registry/src/github.com-1ecc6299db9ec823/libgphoto2_sys-1.2.0/src/build.rs:24:18
18: 0x55a4fbfb72bb - core::ops::function::FnOnce::call_once::h37466a4baa549378
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/ops/function.rs:250:5
19: 0x55a4fbfb752e - std::sys_common::backtrace::__rust_begin_short_backtrace::h8d2f616e44d46b44
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/sys_common/backtrace.rs:121:18
20: 0x55a4fbfb6f91 - std::rt::lang_start::{{closure}}::hf44732de27ff8381
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/rt.rs:166:18
21: 0x55a4fc4aaebc - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h2dd1a24ae3e0569f
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/ops/function.rs:287:13
22: 0x55a4fc4aaebc - std::panicking::try::do_call::h71e38d3ed05d0919
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:483:40
23: 0x55a4fc4aaebc - std::panicking::try::h9dd8fea17c119511
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:447:19
24: 0x55a4fc4aaebc - std::panic::catch_unwind::h073a10d358958706
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panic.rs:140:14
25: 0x55a4fc4aaebc - std::rt::lang_start_internal::{{closure}}::h0cf5d9b5652f6b98
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/rt.rs:148:48
26: 0x55a4fc4aaebc - std::panicking::try::do_call::hc59ab1d339fa21e7
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:483:40
27: 0x55a4fc4aaebc - std::panicking::try::h40dd3124b394a6da
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:447:19
28: 0x55a4fc4aaebc - std::panic::catch_unwind::hff10c6c48e0fc17d
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panic.rs:140:14
29: 0x55a4fc4aaebc - std::rt::lang_start_internal::h7868f0ffe3ad1ec2
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/rt.rs:148:20
30: 0x55a4fbfb6f6a - std::rt::lang_start::h22ae034c20e50bce
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/rt.rs:165:17
31: 0x55a4fbfb9aae - main
32: 0x7f70e08be083 - __libc_start_main
at /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
33: 0x55a4fbfb62ae - _start
34: 0x0 - <unknown>
rust ist up to date, it is installed via rustup. maybe i'm missing a linux package or there is a file linking issue. libgphoto2-dev is installed.
my system:
System: Kernel: 5.15.0-67-generic x86_64 bits: 64 Desktop: Cinnamon 5.2.7
Distro: Linux Mint 20.3 Una
Machine: Type: Laptop System: Dell product: Latitude 3520 v: N/A serial: <filter>
Mobo: Dell model: 03VVMC v: A00 serial: <filter> UEFI: Dell v: 1.23.2
date: 10/27/2022
Battery: ID-1: BAT0 charge: 54.0 Wh condition: 54.0/54.0 Wh (100%)
CPU: Topology: Quad Core model: 11th Gen Intel Core i5-1135G7 bits: 64 type: MT MCP
L2 cache: 8192 KiB
Speed: 1041 MHz min/max: 400/4200 MHz Core speeds (MHz): 1: 937 2: 888 3: 633 4: 941
5: 1004 6: 1007 7: 900 8: 1090
Graphics: Device-1: Intel driver: i915 v: kernel
Display: server: X.Org 1.20.13 driver: modesetting unloaded: fbdev,vesa
resolution: 1920x1080~60Hz
OpenGL: renderer: Mesa Intel Xe Graphics (TGL GT2) v: 4.6 Mesa 21.2.6
Audio: Device-1: Intel driver: sof-audio-pci-intel-tgl
Device-2: Logitech HD Pro Webcam C920 type: USB driver: snd-usb-audio,uvcvideo
Sound Server: ALSA v: k5.15.0-67-generic
Network: Device-1: Intel driver: iwlwifi
IF: wlp0s20f3 state: down mac: <filter>
Device-2: Realtek RTL8111/8168/8411 PCI Express Gigabit Ethernet driver: r8169
IF: enp43s0 state: up speed: 1000 Mbps duplex: full mac: <filter>
Drives: Local Storage: total: 238.47 GiB used: 300.88 GiB (126.2%)
ID-1: /dev/nvme0n1 model: KBG40ZNS256G NVMe KIOXIA 256GB size: 238.47 GiB
RAID: Hardware-1: Intel Volume Management Device NVMe RAID Controller driver: vmd
Partition: ID-1: / size: 159.84 GiB used: 150.40 GiB (94.1%) fs: ext4 dev: /dev/nvme0n1p6
Sensors: System Temperatures: cpu: 37.0 C mobo: N/A
Fan Speeds (RPM): N/A
Info: Processes: 283 Uptime: 3h 55m Memory: 7.51 GiB used: 4.76 GiB (63.3%) Shell: bash
inxi: 3.0.38
Hi! The creation of this repo / crate is very well timed. I actually forked gphoto2 myself a month or two to add extra functionality as @dcuddeback's repos are dead. Are we able to go over the code I wrote / possibly clean it up and get it merged??
Edit It looks like you have added most features! Great work! I have two requests below Edit
.
.
Below is a summary of my features:
Added in-memory file downloads: add-in-memory-filemedia
Line 154 in 6649fdb
Added ability to query and update widgets: add-camera-widget
Added ability to stream video out of device: add-camera-video
Post edit: Since It looks like you have implemented most features I had (and your code better written than mine), there is no point integrating any of my code mentioned above. The only two potential requests I have are to implement the video streaming (video.rs), and the Widget::set_value_string function. That would make this crate 100% usable for my needs, and I can archive my github fork :)
Thanks!
Looks like libgphoto2 is not thread-safe (gphoto/libgphoto2#166) even in the sense that you can't instantiate separate Context
s in different threads, because it uses libltdl for loading iolibs & camlibs, which is not thread safe.
Given how common multithreading is in Rust, and how it can only protect against explicit access to the same resource, but knows nothing about implicit C statics, it would be good to walk over all the libgphoto2 methods from the patch linked in that issue (or just over all libgphoto2 functions that use lt_*
APIs) and add internal mutex on Rust side and lock it wherever we use those functions.
In theory, that should only include loading/destroying/managing abilities list and port list, but there might be other transitive dependencies.
If the download fail (it seems that I often lose the USB connection on my camera) using CameraFs::download_file
, it leave behind a 0 bytes file. IMHO it should clean it up at least if it is zero size, since no download occured.
I tried to use lib, however i spent long period of time on this error:
thread 'main' panicked at 'called
Result::unwrap()on an
Err value: Unspecified error [Expected GroupWidget but got RadioWidget { id: 2, name: "shutterspeed", label: "Shutter Speed", readonly: false, choices: [_; 53], choice: "1/250" }]'
however changing the type to GroupWidget:
let mut shutterspeed = camera.config_key::<gphoto2::widget::GroupWidget>("shutterspeed").wait().unwrap();
is not helping, however if i will change it to:
let mut shutterspeed = camera.config_key::<gphoto2::widget::RadioWidget>("shutterspeed").wait().unwrap();
Error is gone. It seems that in error message GroupWidget and RadioWidget are mixed.
Here is simple example that will help you to figure out:
use gphoto2::{Camera, Context, Result};
use gphoto2::widget::{RadioWidget, Widget};
fn get_iso(camera: Camera) {
let mut shutterspeed = camera
.config_key::<gphoto2::widget::GroupWidget>("shutterspeed")
.wait()
.unwrap();
let mut camera_model = camera
.config_key::<gphoto2::widget::TextWidget>("cameramodel")
.wait()
.unwrap();
}
fn main() -> Result<()> {
env_logger::init();
let camera = Context::new()?.autodetect_camera().wait()?;
get_iso(camera);
Ok(())
}
libgphoto2 itself is licensed under LGPL-v2.1, which allows it to be used in many more contexts than GPL does.
Currently, the Rust wrapper is licensed under GPL-v3.0, which makes it unnecessarily more restrictive than the library itself.
Would it be possible to change license of the Rust wrapper to make it at least as permissive as the underlying library?
Hey there, pretty stumped and don't know how else to try to move forward.
I have a basic application that uses these bindings and it successfully compiles and runs when executed from within mingw64 and ucrt64, however running ntldd -R
and copying over the dll's for my application (just libgphoto2-6.dll
, libgphoto2_port-12.dll
and their non-system dependencies) to the target/debug
directory where the executable sits, and running through cmd
, I get the error: Error loading a library
. It is not more descriptive than that and I'm not sure how to get a more verbose output. I've tried increased logging, backtraces, etc, Windows & Visual Studio debugging tools to no avail.
I believe it has to do with libgphoto2 as it happens when trying to initialize the camera handler in my application, but i'd love to have some additional input as to what to try next.
The full list of DLL's I have in the same directory as the compiled executable:
libexif-12.dll
libgphoto2_port-12.dll
libgphoto2-6.dll
libintl-8.dll
libiconv-2.dll
libsystre-0.dll
libtre-5.dll
libwinpthread-1.dll
Happy to provide any other information that might be useful to help get to the bottom of this.
It seems the API to download previews is missing.
FileInfo
doesn't have the data.
In C, you use gp_camera_file_get()
with GP_FILE_TYPE_PREVIEW
.
Looks like ecosystem is increasingly moving towards tracing
for logging. log
is still supported via backward compatibility, but tracing
provides even more features including structural logging of individual values in addition to the message, compatibility with more backends, nested spans for grouping messages and neat attribute that allows to get all of that by simply instrumenting functions.
While not a high priority, I think for debugging it would be useful to integrate with tracing although it might require some care in the Task
struct to make sure that it works correctly in asynchronous contexts like tracing's own Instrumented helper.
After lifetime cleanup in #6, I've decided to take a look at other potential instances of unbounded lifetimes now that there's not much to look at.
I've noticed that string helpers have such unbounded lifetimes, but, unlike with heap pointers in #6, those can actually be harmful. For example:
pub fn camera_text_to_str<'a>(text: libgphoto2_sys::CameraText) -> Cow<'a, str> {
unsafe { String::from_utf8_lossy(ffi::CStr::from_ptr(text.text.as_ptr()).to_bytes()) }
}
Here, CameraText
contains on-stack array of chars. The helper takes a pointer to that stack array, passes via CStr & String conversions, and potentially gets a Cow
back with that same pointer. Then, it claims that the returned pointer is valid and borrowed (from unbounded lifetime 'a
), and returns upwards, even though the actual data is no longer reachable once the function returns.
Same applies to usages of chars_to_cow
whenever input pointer points to temporary on-stack data.
It's pretty hard to trigger this to show that strings might contain garbage once overridden by other stack data, but, as far as I can tell, it works only by accident.
Those unbounded lifetimes should be eliminated, and instead there should probably be a couple of helpers:
&'a [c_char]
and returns Cow<'a, str>
- this would be most popular (there's lots of [c_char; 123]
data in the codebase, including in CameraText
) and safe as the lifetime would be clearly bound to the input.chars_to_cow
, but marked unsafe
so that it's clear it should be used only if you can pass explicit lifetime (e.g. strings returned from Widget::name
etc. that are constructed from *const c_char
that is valid as long as Widget
itself).chars_to_string
for the rest of the cases, to immediately convert into String
and not expect the input pointer to live after the function call.Right now, Context
creates an unbounded lifetime (see Unbounded Lifetimes tied to whatever scope it was created in via Context::new
. This is not very useful and doesn't provide any extra guarantees, since the pointers returned from libgphoto2 are heap-allocated anyway. As such, the lifetime from Context
and most of other data structures that inherit the same lifetime, can be safely removed.
@maxicarlos08 do you want to take this one?
The gphoto2 feature live view might be missing in this rust binding.
With this feature of gphoto2 you can capture a live preview video stream from a supported camera.
To approach feature completeness of this binding with the original gphoto2, I think this feature should be added, if possible.
As there is no std video stream API in rust I know of, my suggestion would be to provide some interface that could, for example, be connected to the official rust binding for gstreamer (maybe as a video-source for gst-pipelines) or other popular media framework.
When creating from an existing File
, we use gp_file_new_from_fd
, which says:
* This function takes ownership of the filedescriptor and will close it when closing the CameraFile.
That is, we actually shouldn't keep the original File
around as its ownership has already been transferred, so it must not be closed on drop by Rust.
Continuation of the multithreading shenanigans, and I found out that libgphoto2 is actually non-reentrant - not safe to use from multiple threads even if you create separate Contexts. We'll need to wrap around this unsafety somehow (see my suggestion at the end of the quote).
Now that I'm looking a bit deeper, I'm actually not sure that libgphoto2 is thread-safe at all, so fixing ltdl codepaths won't help much... There seems to be a lot of global mutable variables throughout the codebase, which are not protected by mutexes or atomics, and as such are inherently prone to corruption via race conditions.
So even if one takes care and creates separate gphoto2 contexts per-thread, all those internal data structures can be still corrupted by racing calls.
I guess the only safe way to use gphoto2 in multithreaded environment is to actually pin it to a single thread and make sure all work, for all devices, is done there. (at least until it's refactored in some future to avoid global variables, or to have mutex around all of them)
Originally posted by @RReverser in gphoto/libgphoto2#848 (comment)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.