Giter Site home page Giter Site logo

gphoto2-rs's Introduction

GPhoto2-rs

Rust bindings to libgphoto2

What about gphoto-rs?

I know about the other crate (gphoto and gphoto2-sys which was created by @dcuddeback, but it is missing a lot of features which make the crate unusable for me, most notably the ability to change camera settings and in memory file download.
The author hasn't been active since 2017 regardless of numerous pull- and feature requests, so I made a new project with a more up to date rust code and all the features from libgphoto2.

Features

  • Camera
    • Capture images
    • Capture preview images
    • Download images
    • Get port information
    • Get abilities (model, driver stability, permissions, ...)
    • Read configuration
    • Set configuration
    • Interact with filesystem on camera
    • Camera events
    • Usb port information
  • Context
    • Autodetect camera
    • Get list of available cameras
    • Get camera by model and port

Gettings started

Installation

Run cargo add gphoto2 to add gphoto2 to your project or add this to your Cargo.toml:

[dependencies]
gphoto2 = "1"

Install libgphoto2

The libgphoto2 library must be installed on your system to use this library.

To install libgphoto2 on Debian based systems run:

sudo apt install libgphoto2-dev

On Arch systems run:

sudo pacman -S libgphoto2

On MacOS systems with Homebrew run:

homebrew install libgphoto2
Windows

There is no official way to install libgphoto2 on windows, but you can install it with MSYS2 (link to the package: mingw-w64-libgphoto2).

Basic Usage

This example takes a picture and saves it to disk

use gphoto2::{Context, Result};
use std::path::Path;

fn main() -> Result<()> {
  // Create a new context and detect the first camera from it
  let camera = Context::new()?.autodetect_camera().wait().expect("Failed to autodetect camera");
  let camera_fs = camera.fs();


  // And take pictures
  let file_path = camera.capture_image().wait().expect("Could not capture image");
  camera_fs.download_to(&file_path.folder(), &file_path.name(), Path::new(&file_path.name().to_string())).wait()?;

  // For more advanced examples take a look at the examples/ folder

  Ok(())
}

You can find more examples here

Logging

To make your debugging life a bit easier, this crate hooks up the libgphoto2 log functions to the log crate.

To show the logs use a logging implementation like env_logger.

Additional logs

By default we use gp_context_set_log_func in a context to get the logs, but there is also gp_log_add_func which provides a lot more information and can be a lot more useful when debugging.

The reason this crate doesn't use gp_log_add_func by default is because it is disabled in most Linux distributions and Windows. You will have to check if your installed version does not disable this feature or build libgphoto2 yourself without passing the --disabled-debug flag to the configure command.

To use this feature, enable the extended_logs feature of this crate (the linker will fail if your version of libgphoto2 was not compiled without the --disabled-debug).

Testing

To run the tests of this crate the test feature must be enabled:

cargo test -F test

Note that test builds a very stripped down version of libgphoto2, which is only usable for testing (Don't enable this feature when using this crate).

Stability

In general all all APIs should be stable, I've tested the ones my camera supported and found no bugs so far.
If you encounter an error like BAD_PARAMETERS or found a bug, please create an issue on GitHub.

License

Copyright © 2022 Maxicarlos08 [email protected]

This library uses the libgphoto2 library, which is licensed under the LGPL version 2.1.

gphoto2-rs's People

Contributors

gkodes avatar hfiguiere avatar maxicarlos08 avatar rreverser avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

gphoto2-rs's Issues

Mixed Widget types in error message.

I tried to use lib, however i spent long period of time on this error:

thread 'main' panicked at 'called Result::unwrap()on anErr 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(())
}

Hook up gp_log_add_func

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.

CameraFile shouldn't keep ownership of the original File

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.

libgphoto2 is actually not thread-safe at all :(

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)

Consistent filesystem access?

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?

[Solved] Compilation error on Linux Mint: 'stdarg.h' file not found

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

String helpers have stack-use-after-return issues

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:

  1. Takes &'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.
  2. Something like the current 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).
  3. 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.

Relicensing to LGPL

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?

CameraFile::get_data is probably leaking memory

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.
 *
 **/

Example code in README.md does not work

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.

Missing Feature: Liveview

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.

Context & the rest shouldn't have a lifetime

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?

0 byte file left behind

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.

Task future is not multithreading-friendly

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.

Possibility of merging extra features?

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

  • Added means to save a FileMedia into a memory buffer instead of a file. Code was taken from various forks of the original @dcuddeback.
  • ********* Looks like you added this in (More idiomatic I might add - using new's):
    pub(crate) fn new() -> Result<Self> {
    please ignore *********

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!

Switch from log to tracing?

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.

Windows help: runs great in mingw64 & ucrt64, gets Error loading a library output when trying to run outside of msys2 env.

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 -Rand 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.

Add ability to set context progress functions

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

libgphoto2 has thread safety issues

Looks like libgphoto2 is not thread-safe (gphoto/libgphoto2#166) even in the sense that you can't instantiate separate Contexts 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.

@maxicarlos08

Interaction between Context::set_progress_handlers and Task::set_progress_handler

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?

Downloading previews

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.

Memory unsoundness in port_info (and maybe others)

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.

Problem with tauri

I try to use this wrapper on tauri, but face the following issue, is this a problem with gphoto2 or tauri?
image

Investigate encodings and localisation

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.