GithubHelp home page GithubHelp logo

jnqnfe / pulse-binding-rust Goto Github PK

View Code? Open in Web Editor NEW
65.0 3.0 20.0 1.47 MB

FFI and bindings for using PulseAudio from the Rust programming language.

License: Apache License 2.0

C 29.22% Rust 70.78%
pulseaudio rust

pulse-binding-rust's People

Contributors

agraven avatar berkus avatar danielchabrowski avatar frafra avatar fredszaq avatar jnqnfe avatar johnazoidberg avatar mitchhentges avatar mozgiii avatar timvisee avatar yatinmaan 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pulse-binding-rust's Issues

linker error: DSO missing from command line

I cloned the repo, this is what I get on cargo test:

---- src/mainloop/standard.rs - mainloop::standard (line 57) stdout ----
error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.0.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.1.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.10.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.11.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.12.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.13.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.14.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.15.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.2.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.3.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.4.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.5.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.6.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.7.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.8.rcgu.o" "/tmp/rustdoctestMs1Q5E/rust_out.rust_out.7rcbfp3g-cgu.9.rcgu.o" "-o" "/tmp/rustdoctestMs1Q5E/rust_out" "/tmp/rustdoctestMs1Q5E/rust_out.33dyzt1ekirinwy8.rcgu.o" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro" "-Wl,-znow" "-nodefaultlibs" "-L" "/home/mozgiii/Desktop/pulse-binding-rust/target/debug/deps" "-L" "/usr/lib/x86_64-linux-gnu" "-L" "/home/mozgiii/Desktop/pulse-binding-rust/target/debug/deps" "-L" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/home/mozgiii/Desktop/pulse-binding-rust/target/debug/deps/liblibpulse_binding-fff5c6144e0561bf.rlib" "/home/mozgiii/Desktop/pulse-binding-rust/target/debug/deps/liblibpulse_sys-7a0dcb80d455ec51.rlib" "/home/mozgiii/Desktop/pulse-binding-rust/target/debug/deps/liblibc-e30d7bd1ff40dae2.rlib" "-Wl,--start-group" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-e39317eb74365d3c.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-4d55a38564aae54a.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libbacktrace_sys-f8521075e248b627.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-7c91ffdc8da860d3.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-0ad27b9879d551d3.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-588f18eae3ea58be.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-4ebf5caee903d98f.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-8895b32baedb08c6.rlib" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-6a9d233d01acc350.rlib" "-Wl,--end-group" "/home/mozgiii/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-851bb3b5f6c4db49.rlib" "-Wl,-Bdynamic" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-l:libpulse.so.0" "-lutil" "-lutil" "-ldl" "-lrt" "-lpthread" "-lgcc_s" "-lc" "-lm" "-lrt" "-lpthread" "-lutil" "-lutil"
  = note: /usr/bin/ld: /home/mozgiii/Desktop/pulse-binding-rust/target/debug/deps/liblibpulse_binding-fff5c6144e0561bf.rlib(libpulse_binding-fff5c6144e0561bf.34aowjpr2n0d3j30.rcgu.o): undefined reference to symbol 'pa_encoding_from_string'
          //usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-11.1.so: error adding symbols: DSO missing from command line
          collect2: error: ld returned 1 exit status

How can I fix this?

Soundness issue

MainloopInner::get_api dereferences the user accessible raw pointer MainloopInner::api.

This is unsound because a user could safely modify MainloopInner::api and call MainloopInner::get_api to cause UB.

Possible tokio integration?

With the Futures and Async/Await features soon to be stabilized, is it possible to use this library asynchronously with Tokio? If so, could documentation and possibly a feature be added that allows for it so that actions to be taken after a sound is played can be done through future combinators? I haven't really dug into the library, but it didn't give us compilation issues, so we went with it for a hackathon. We'd like to integrate it into our production application, but we use a Tokio based framework and having this work with that natively would be really useful.

Documentation typo?

Doc link

In the Warning Section:

...
it does fit perfectly with the Rust borrow checking mechanism 
and 
thus you cannot rely upon the borrow checker to prevent unsafe use as much as is typical.

It fits with the borrow checking mechanism but because of that, you can't rely on the borrow checker?
That seems a bit contradictory.

Typing mismatch in mod.rs and timeval.rs on macOS

I'm working with the Pulse Audio binding to do a simple FFT visualizer. I'm getting the following traceback when trying to compile basic usage of the standard mainloop and context and stream stuff:

   Compiling libpulse-binding v2.2.0
error[E0308]: mismatched types
  --> /Users/noahbkim/.cargo/registry/src/github.com-1ecc6299db9ec823/libpulse-binding-2.2.0/src/time/mod.rs:55:35
   |
55 |         Timeval::new(secs as i64, usecs as i64)
   |                                   ^^^^^^^^^^^^ expected i32, found i64

error[E0308]: mismatched types
  --> /Users/noahbkim/.cargo/registry/src/github.com-1ecc6299db9ec823/libpulse-binding-2.2.0/src/time/mod.rs:74:42
   |
74 |         Timeval::new(t.as_secs() as i64, (t.subsec_nanos() / NANOS_PER_MILLI) as i64)
   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found i64

error[E0308]: mismatched types
   --> /Users/noahbkim/.cargo/registry/src/github.com-1ecc6299db9ec823/libpulse-binding-2.2.0/src/time/timeval.rs:100:41
    |
100 |             true => { self.0.tv_usec |= PA_TIMEVAL_RTCLOCK; },
    |                                         ^^^^^^^^^^^^^^^^^^ expected i32, found i64

error[E0277]: no implementation for `i32 |= i64`
   --> /Users/noahbkim/.cargo/registry/src/github.com-1ecc6299db9ec823/libpulse-binding-2.2.0/src/time/timeval.rs:100:38
    |
100 |             true => { self.0.tv_usec |= PA_TIMEVAL_RTCLOCK; },
    |                                      ^^ no implementation for `i32 |= i64`
    |
    = help: the trait `std::ops::BitOrAssign<i64>` is not implemented for `i32`

I'm working on the current version of macOS with a vanilla installation of Rust. Let me know if you need any other information.

Travis test failures

Having fixed Travis builds to actually run the tests (cargo test needed --all) and fixed the first problem - missing package dependency - now we're getting failures on two tests that pass perfectly fine on my local system.

Unfortunately the build script (and actually the local run) is pointing me at line numbers which make no sense - line #41 of src/mainloop/standard.rs and line #213 of src/mainloop/threaded.rs

With no idea where the problem is actually occurring, how to solve (without flooding Travis with attempts at pinpointing the real failure locations)...?

Any ideas anyone?

Record Stream never calls read callback.

Hi! I'm trying to create a volume mixer in Rust using your library, and I'm having an issue related to monitor streams. I'm trying to create a peak monitor, roughly derived from pavucontrol's C++ implementation, and as far as I can tell I've inputted the same properties faithfully to libpulse_binding, but the callback I supply to set_read_callback never triggers. None of the unwrap calls fail, and the program doesn't crash, I just get no response. I've tried querying synchronously using readable_size as well, and I only ever get None.

Side note -- when I was trying to debug I ran across Stream::set_state_callback, and I'm unsure how I'm actually supposed to use it. It doesn't pass the current state into the callback and I can't reference the stream from inside the callback as it's already being used outside of it, so I wasn't able to use it to help me test.

I'm a new Rust programmer, so if I'm making a simple mistake I'd really appreciate being pointed in the right direction. Thank you for your time, and this library!

// These values are dynamic in the actual code.
let input_index = 1; 
let sink_index = 0;

let mut attr = BufferAttr::default();
attr.fragsize = 4;
attr.maxlength = u32::MAX;

let spec = Spec { channels: 1, format: Format::F32le, rate: 25 };
assert!(spec.is_valid());

// self.context is an Rc<RefCell<...>> of the context.
let mut s = Stream::new(&mut self.context.borrow_mut(), "Peak Detect", &spec, None).unwrap();
s.set_monitor_stream(index).unwrap();

let tx = self.channel.tx.clone();
s.set_read_callback(Some(Box::new(move |peak| println!("Peak! {}", peak))));

s.connect_record(Some(&sink_index.to_string().as_str()), Some(&attr),
	StreamFlagSet::DONT_MOVE | StreamFlagSet::ADJUST_LATENCY | StreamFlagSet::PEAK_DETECT).unwrap();

How to disable `pa_encoding_from_string` feature?

I'm on Ubuntu 18.04 which comes with PulseAudio 11 as best as I can tell. I'm trying to write something that uses pulse-binding-simple, but I can't work out how to disable the pa_encoding_from_string feature which is on by default. I can disable it for pulse-binding but it seems to be enabled again as soon as I try to use pulse-simple.

Windows support

I'm using libpulse-simple-binding 2.14.0 with rustc 1.40 via rustup toolchain install stable-x86_64-pc-windows-gnu.

Any crate that depends on any crates from this repository where libpulse-sys > 0 is a dependency, fails to build inside libpulse-sys with this toolchain.

Line 17 of libpulse-sys/src/mainloop/standard.rs is use libc::pollfd;

For some reason, this toolchain doesn't include a pollfd in std despite the fact that the platform actually supports it in native code. In winsock2.h (from MinGW64):

typedef struct pollfd {
  SOCKET fd;
  short  events;
  short  revents;
} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD;

See also https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll

The expected functions that operate on this struct are easy to emulate. PulseAudio itself has the file poll-win32.c which implements pa_poll() using native Win32 API, and the core Pulse library and daemon use pa_poll(). Unfortunately, Rust just doesn't provide std::pollfd on Windows.

This is blocking me from being able to get work done, so if you have any idea how to proceed, it would be appreciated.

Potential unsafety in context?

The Context struct can be connected to an event loop and triggered from a separate thread. Passing in thread-unsafe values here would result in a potential data race, right? So it should probably set the generic bounds Box<dyn FnMut() + 'static + Send>?

Impossible to safely use set_state_callback with context

set_state_callback does not (unlike the C library) supply the Context as a parameter to the closure. That leaves us with passing the Context using an Rc+RefCell to the closure. Which fails at runtime, since the connect function takes a mutable reference which makes it borrowed twice at the same time when used inside the closure.

CI failure - floating point calculation assertions

I'm aware that with the latest automated monthly CI run the instance using the nightly compiler is now failing, as I can reproduce locally with the nightly compiler.

The following tests are failing:

  • src/time/microseconds.rs - time::microseconds::MicroSeconds::div_f32 (line 595)
  • src/time/microseconds.rs - time::microseconds::MicroSeconds::from_secs_f32 (line 257)
  • src/time/microseconds.rs - time::microseconds::MicroSeconds::from_secs_f64 (line 218)
  • src/time/microseconds.rs - time::microseconds::MicroSeconds::mul_f32 (line 508)

The error output is as follows:

---- src/time/microseconds.rs - time::microseconds::MicroSeconds::div_f32 (line 595) stdout ----
Test executable failed (exit code 101).

stderr:
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `MicroSeconds(859872558)`,
 right: `MicroSeconds(859872559)`', src/time/microseconds.rs:7:1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


---- src/time/microseconds.rs - time::microseconds::MicroSeconds::from_secs_f32 (line 257) stdout ----
Test executable failed (exit code 101).

stderr:
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `MicroSeconds(2299999)`,
 right: `MicroSeconds(2300000)`', src/time/microseconds.rs:6:1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


---- src/time/microseconds.rs - time::microseconds::MicroSeconds::from_secs_f64 (line 218) stdout ----
Test executable failed (exit code 101).

stderr:
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `MicroSeconds(2299999)`,
 right: `MicroSeconds(2300000)`', src/time/microseconds.rs:6:1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


---- src/time/microseconds.rs - time::microseconds::MicroSeconds::mul_f32 (line 508) stdout ----
Test executable failed (exit code 101).

stderr:
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `MicroSeconds(8478000000)`,
 right: `MicroSeconds(8478000152)`', src/time/microseconds.rs:8:1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I'll head now to the Rust compiler issue list to file an issue report...

Missing documentation

As a new user pulse audio I've been going through the documentation over on the freedesktop site but it's really helpful to see the more Rusty comments and I notice that this crate doesn't have a link to the documents on docs.rs.

This is a shame because the all the necessary doc comments are there they just don't seem to be recognised when doing a cargo publish. I don't know why this is the case, maybe because many of the files begin with /// comments and not //!, but that's just my guess.

I just fried running cargo doc --open on my cloned repo. It runs fine, so I've no idea why cargo publish doesn't do this.

Lifetime mismatch around .get()

The PulseAudio API description of pa_proplist_get says:

The data pointer returned will point to an internally allocated buffer. The caller should make a copy of the data before the property list is accessed again.

However, the get function has a signature of pub fn get(&self, key: &str) -> Option<&[u8]>, allowing safe Rust code to access the memory way after another access has happened.

The mismatch here is that get only takes a shared pointer, and thus limits the lifetime to as long as nothing "modifies" self, but the PA API already considers access a mutation.

I've found this tracking down why myxer doesn't display the name of an (admittedly broken, but hey it's a cheap Bluetooth gadget, and the name is untrusted data after all) amplifier. I didn't run into trouble with this precisely (the fault I was looking for is somewhere else), and chances are the names do regularly point into memory isn't really mutated by later accesses, but the PA API doesn't guarantee it, and may change at any time.

Options as I see them are:

  • Make get and anything else that "accesses" the proplist take a &mut self.
  • Return an owned copy of the data.

I don't see any easy way to do either of them without breaking the API. Worse, the latter (which may sound easier on API users) would require locking for all accesses because Proplist implements Sync, so another thread could perform a get at the same time and copying the data out just before get returns would not be enough.

load_module not displaying all errors

Hello,
I'm using load_module in libpulse-binding, which states in the docs

Returns None on error, i.e. invalid arguments or state. The callback is provided with the index.

But this never happens.
If you create a module-null-sink with invalid params IterateResult won't show you any errors, nor the Operation. And I don't see where any Option is used, to return any kind of None.
The doxygen from pa sadly isn't very helpful either..

Cannot send mainloop across threads

I would like to signal a threaded mainloop in the ctrl+c handler in order to make it exit as cleanly as possible.

The crate I am using for it leverages a thread to handle the command. I thought I could pass the mainloop using an Arc and a Mutex.

However, the Rc in the mainloop does not implement Sync and Send and seems to prevent passing the entire object.

Was it intended? If it is the case, is there another way to do that?

SinkInputInfo to SinkInputInfoInternal

Hi! I'm a Rust beginner, so I might be missing something obvious. I want to subscribe to the server and get notified when the volume of any sink input changes. I've set up a callback using get_sink_input_info and it's firing as intended, but the parameter of interest is of type SinkInputInfoInternal. How can I convert this to the proper Rust struct type?

Licensing change

Up to and including version 2.12 of the binding crates and version 1.10 of the sys crates available in this repository, they have been licensed with LGPL v2.1+, with a possible alternative of dual MIT and Apache-2.0 licensing under certain circumstances (as previously discussed in the project readme).

As of v2.13 (bindings) and v1.11 (sys) respectively, I have changed the licensing, dropping LGPL and promoting dual MIT and Apache-2.0 to be the new licensing model.

Why?

LGPL is a variant of GPL intended for use with libraries; it relaxes the restriction that forces projects using GPL code to also be GPL licensed. The requirement of LGPL licensed code is that if it is compiled into a separate object, i.e. a dynamically-loaded library (like a system library - .dll/.so) then such a requirement does not apply. It thus only applies if compiled statically, i.e. combined directly into an application or library object, whereby that object must be GPL/LGPL licensed.

This meant that for complying with the license, to make use of these sys and binding crates, you would be forced to either license anything using them as GPL/LGPL, or to compile them into shared libraries that sit alongside your project, which is not really ideal for a thin binding layer.

The dual MIT and Apache-2.0 licensing imposes no such requirements.

Note that the default way Rust crates are consumed, through simply adding dependencies to a Cargo.toml file, is static compiling! All of your dependencies, unless you explicitly compile them as a shared library, are being compiled statically. There is thus risk that users may blindly depend on these crates and violate the licensing in doing so.

Furthermore, I, the project author, have absolutely no interest in imposing such a restriction on use of these crates.

Why was LGPL ever chosen?

PulseAudio itself is LGPL 2.1+ licensed, and I had to consider this when I created these crates. I was concerned about the possibility of these crates being considered derivative works of PulseAudio through for instance the fact that they largely copy the documentation from the PulseAudio C header files.

I thus simply went with licensing this project under the same LGPL 2.1+ licensing to avoid any potential for compliance issues.

What has changed?

Nothing has really changed, but I have re-evaluated the situation.

I have discussed the issue with the PulseAudio project (here). Their stance is that although the LGPL license makes certain common sense exceptions, there is no actual exception for copying documentation like I am doing. The only way to get their official approval would be to build a list of all individuals who have ever contributed to the PulseAudio documentation and get their explicit approval, which of course would be a pain.

My feeling is that perhaps the LGPL license authors had not considered a situation like this when they wrote it, and that what I am doing is not in the spirit of what their license was trying to
restrict. They have a contact address for asking such questions, and I emailed them requesting clarification on their position on this. I have not yet received a response.

Of interest of course is that documentation does not actually get compiled into any resulting library or application. It does however become part of documentation generated by cargo doc.

Another factor of interest is that there is a FAQ available for the GPL/LGPL licenses (here), and specifically there is an entry on "fair-use" (here) which explicitly makes clear that fair-use does apply...

Decisions

What I have decided is that the nature of my project and use of the PA documentation within it
should reasonably be considered fair-use, and that I should thus not feel bound to using LGPL for my crates.

My project is simply providing a free Rust interface to PA; I'm maintaining it myself independently
largely because they told me previously that it was too burdensome for them to merge into the
official project; and providing a copy of the documentation as I do is extremely valuable to users
of this Rust interface to PA. It would be very unreasonable for users to have to consult the C
header files. It is just a matter of making the documentation available in the location most useful
to Rust users, where they would expect to find it. That is the purpose in having copied it. Not
doing so would make use of PA for Rust programmers significantly unpleasant.

We also come back to that issue of combining LGPL code within compiled objects. This documentation, being documentation, is never compiled into libraries or applications (ignoring doc-tests, which is fair-use anyway as being examples), so there is actually the licensing restriction should not even apply to projects depending on these crates so long as they just depend upon them and not copy and paste stuff.

The only applicable "derivative-work" related use of the documentation where it gets combined into something, if this is even any concern of LGPL, is the HTML documentation generated by cargo doc. Here I feel that personal generation of this HTML documentation is also surely a clear fair-use
situation.

Furthermore I feel that the copy of the HTML documentation hosted on docs.rs is again a fair-use situation - it is just providing a helpful free resource for Rust users.

I have thus decided to move forward with changing the licensing of these crates.

How does this affect me (the reader)?

You can now freely depend upon the crates, allowing them to be compiled-in statically, as natural, without worry of being being in violation of the LGPL license. You should also of course be able to generate and make use of the HTML based documentation with cargo doc without concern.

That is, as long as you are simply making normal use of these crates. If you're considering
doing something unusual like copying the documentation into something else like your own code or into a book you wish to publish, then you should evaluate whether my fair-use of that documentation reasonably would carry over to your use of it, or whether you must comply with the original LGPL license.

Suggested Cargo.toml entry for libpulse_binding doesn't work

If, as suggested, I:

Add this to your Cargo.toml:

[dependencies]
libpulse_binding = "2.0"

then this happens:

$ cargo build
    Updating crates.io index
error: no matching package named `libpulse_binding` found                                                                                                                                     
location searched: registry `https://github.com/rust-lang/crates.io-index`
did you mean: libpulse-binding
required by package `foo v0.1.0 (/home/josh/src/foo)`

Possible UB in `get_api` function

Hi,

In libpulse_binding::mainloop::api::standard::Mainloop (this line) and libpulse_binding::mainloop::api::threaded::Mainloop (this line), there is the function get_api which returns a borrow to the API vtable. I think the lifetime annotations of this function are incorrect and could lead to unexpected behavior.

pub fn get_api<'a>(&self) -> &'a ::mainloop::api::MainloopApi {
    ...
}

The returned borrow can live even after the Mainloop is freed and the Api is destroyed. As the function's docstring says, "No need to free the API as it is owned by the loop and is destroyed when the loop is freed."

I believe that something like this shouldn't be allowed to compile:

let mut mainloop = Mainloop::new().expect("Failed to create Mainoop");
let api: &MainloopApi = mainloop.get_api();
drop(mainloop); // Also frees API vtable
// Use api here

Changing the function signature to this would solve the issue and prevent the above code from compiling.

pub fn get_api(&self) -> & ::mainloop::api::MainloopApi

CI failure, again...

I'm aware that CI is failing after just having bumped the OS from ubuntu 20.04 to 22.04. I don't know why this is happening, so far I've failed to find a solution, and I'm rather frustrated, especially with docs.rs builds also failing. Help would be appreciated.

What is happening seems to be as follows - compiling works just fine, but when it comes to running tests, the doctests that try to run a basic example of using pulseaudio are failing to setup a pulseaudio context object, with the pulseaudio client library returning a -1 error, corresponding to an "access" error condition.

This isn't a code issue. The tests work fine locally, and worked just fine in the ubuntu 20.04 environment. Something is wrong with the new OS environment itself. 22.04 has pulseaudio version 15.99 rather than 13.99, but I don't expect that's somehow the issue.

Researching pulseaudio issues on 22.04 I've come across other people having issues with pulseaudio on their own machines after upgrading from 20.04, and I've tried some of the suggestions from here, but with no luck. In fact it seems that the CI ubuntu 22.04 environment does not even have pipewire installed. Installing pipewire (pipewire, pipewire-pulse, and pipewire-media-session) and then doing what's suggested in that thread did not help.

What on earth have the ubuntu guys done to break PA?

Can I get this fixed or must I go back to 20.04, which obviously can only be a temporary measure, not a permanent future-proof fix? :/

Make SinkInfo, ServerInfo, and similar types available outside of callbacks

The types listed in the title all share the issue that when you receive them in callbacks passed to methods like get_sink_info_by_name, you can't use those types outside of the callbacks due to lifetime constraints.

I first tried to address the issue by deriving Clone for the relevant structs, but I realized that won't solve the problem since cloning a Cow::Borrowed just produces a copy of the reference it contains. I next tried to see if the derive-into-owned crate would work, but the derive seems to fail on some types, for example SinkInfo.

Allowing send + sync for Context

I'm not sure whether it's safe to make Context send, but I'm currently failing to share an Arc<Mutex<Context>> due to

`*const extern "C" fn(*mut pulse_sys::pa_context, *const i8, *mut pulse_sys::pa_proplist, *mut std::ffi::c_void)` cannot be sent between threads safely

I'm not sure if it helps, this is the full log output when trying to do:
(move || ) the context Arc<Mutex<_>>

help: within `pulse::context::Context`, the trait `std::marker::Send` is not implemented for `*const extern "C" fn(*mut pulse_sys::pa_context, *const i8, *mut pulse_sys::pa_proplist, *mut std::ffi::c_void)`
   = note: required because it appears within the type `std::marker::PhantomData<*const extern "C" fn(*mut pulse_sys::pa_context, *const i8, *mut pulse_sys::pa_proplist, *mut std::ffi::c_void)>`
   = note: required because it appears within the type `pulse::callbacks::MultiUseCallback<(dyn std::ops::FnMut(std::string::String, pulse::proplist::Proplist) + 'static), extern "C" fn(*mut pulse_sys::pa_context, *const i8, *mut pulse_sys::pa_proplist, *mut std::ffi::c_void)>`
   = note: required because it appears within the type `pulse::context::CallbackPointers`
   = note: required because it appears within the type `pulse::context::Context`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<pulse::context::Context>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::Mutex<pulse::context::Context>>`

I'm trying to share a Context & Mainloop with a hyper thread, both wrapped inside an Arc<Mutex<_>>, so my only option for now would be to handle these in a new thread and use an mpsc channel to communicate between them.

Possible undefined behavior due to missing `Send` for callbacks

When using mainloop::threaded::Mainloop, the callbacks that one gives to functions like context::introspect::Introspector::get_server_info are executed on a different thread. I think that means that those callbacks must be required to be Send, just like a callback for std::thread::spawn is required to be Send.

Segfault occurs when calling `stream.set_write_callback(None)` inside write callback

Now, I have no idea if this is how the API was intended to be used, but as I was figuring out how to upload samples I discovered this issue.

First, I was using @danieldg's libpulse-tokio main loop implementation, but then I created a small repository which implemented creating a stream and uploading it as a sample with both libpulse-tokio as well as the standard threaded main loop implementations, and this is definitely an issue with both.

The repository I created is here: https://github.com/acheronfail/pulse-stream-segfault/.

The Problem

When calling stream.set_write_callback(None) inside a write callback, a segfault occurs.

Expected behaviour

I expected nothing to happen. In fact, if you call stream.set_state_callback(None) inside the state callback, then no error occurs. This is why I expected nothing to happen.

Small code example

This is basically the code:

let stream = Rc::new(RefCell::new({/* code to create a stream object */}));

let stream_ref = stream.clone();
stream.borrow_mut().set_write_callback(Some(Box::new(move |len| {
  // do the actual write...

  // SEGFAULT HERE:
  stream_ref.borrow_mut().set_write_callback(None);
})));

Have a look at the repository I created here:

Questions

  • Is this an issue with libpulse_binding?
  • Is this just me using the libpulse API incorrectly - does it not support this operation?

get_str documentation inaccurate

The documentation of Proplist::get_str says:

Will return None if the key does not exist or if data is not valid UTF-8.

However, the implementation performs .to_string_lossy(), returns Some(String) where the string just contains replacement characters.

I think the implementation behavior makes more sense than the documented one (If my soundcard is named b"Xpecial Audio \xff", I prefer to see "Xpecial Audio �" over "Unnamed card" or whatever the application makes of a None, just the documentation doesn't say that.

[libpulse-binding] how to manage mainloop in callbacks

Hey, thanks for creating this higher-level abstraction for pulse!
How would you recommend managing mainloop when I'm dealing with callbacks?
For example:

let mainloop: Rc<RefCell<Mainloop>> = ...
let context = Rc<RefCell<Context>> = ...

let op = {
    let mainloop = mainloop.clone();
    context.borrow().introspect().get_server_info(move |info| {
        let op = context.borrow().introspect().get_sink_info_by_name(&info.default_sink_name.unwrap(), move |_| {
            println!("neat");
        });
        # loop
        #     mainloop.iterate
        #     break if op.get_state() == Done
        #
    })
}

# loop
#     mainloop.iterate => at this point, callback is called
#     break if op.get_state() == Done
#

I'm running into an issue where mainloop is in use when my callback is called. So, when I need to wait for my nested operation (get_sink_info_by_name(...)), I can't borrow mainloop to iterate, since it's already in use.

How would you recommend handling this?

Travis testing failure

I am aware that recently that travis has been consistently reporting failure recently. Do not fret, there is nothing wrong with the crates!

The problem is simply some issue with the travis setup itself, relating it seems to the version of PA used within the test environment.

I'm working on it.

The simple fix to gets things working again it appeared to be to disable PA v12 features in the testing, since no travis environment yet has PA v12. Note that previously we only had success with that due to a mistake that I have now corrected in regards to use of feature flags with our Cargo workspace, the fix for which has helped highlight issues with our use of travis. However, having now tried disabling PA v12 support, it is still broken, and as of this minute, I'm still trying to understand the cause.

Currently, three specific tests are failing, with no immediately obvious reason. The PA v12 symbol being missing in the system lib (too old a version) is mentioned, though I don;t see why, and may be a read herring. I really don't know. Investigation continues...

Discussion ongoing here: https://travis-ci.community/t/failure-due-to-system-libs-now-being-older/4712

Allow using MainContext from "glib" crate

This is a mirror of the TODO in pulse-sys-mainloop-glib.

I'd like to be able to use MainContext from the glib crate as a parameter for Mainloop::new(...) in pulse-binding-glib.

I'd be interested in drawing up a PR for this, but I'm not sure how to peel the GMainContext out of the glib crate. MainContext is defined as:

pub struct MainContext(Shared<glib_sys::GMainContext>);

I'm not sure how to get at the Shared structure, since it's private. Once that's accomplished, though, I'm assuming I can just shared.borrow_mut() to get at the GMainContext inside?

How to use ListResult?

say i have a card, and i need to change the audio profile of that card, so i use get_card_info_by_name to get the info of that card to decide what profile to choose. however this returns the info in a ListResult<&CardInfo>'_>>, and it doesn't seem i can iterate over it or unwrap it to get the underlying CardInfo or anything, how would i get info about a card this way?

Error: invalid argument when calling Simple.read()

For the record, I have no experience with the PulseAudio C library, so this very well could be me just overlooking something.

The entirety of my code is:
[main.rs]

use anyhow::Result;
use wizja::pulse::PARecorder;

fn main() -> Result<()> {
    let args: Vec<String> = env::args().collect();

    let mut rec = PARecorder::new()?;

    println!("{:#?}", rec.buf);

    Ok(())
}

[pulse.rs]

extern crate libpulse_binding as pulse;
extern crate libpulse_simple_binding as psimple;

use anyhow::{anyhow, Result};
use psimple::Simple;
use pulse::sample;
use pulse::stream::Direction;

const SPEC: sample::Spec = sample::Spec {
    format: sample::SAMPLE_S16NE,
    channels: 2,
    rate: 44100,
};

pub struct PARecorder {
    srv: Simple,
    pub buf: Vec<u8>,
}

impl PARecorder {
    pub fn new() -> Result<PARecorder> {
        assert!(SPEC.is_valid());

        let srv = Simple::new(
            None,
            "wizja",
            Direction::Record,
            None,
            "Visualizer",
            &SPEC,
            None,
            None,
        )
        .map_err(|e| match e.to_string() {
            Some(s) => anyhow!(s),
            None => anyhow!("???"),
        })?;

        Ok(PARecorder {
            srv,
            buf: Vec::with_capacity(10000),
        })
    }

    pub fn update(&mut self) -> Result<()> {
        self.srv
            .read(&mut self.buf)
            .map_err(|e| match e.to_string() {
                Some(s) => anyhow!(s),
                None => anyhow!("???"),
            })?;
        Ok(())
    }
}

When running this, just to test if I get any microphone input, I get Error: Invalid argument (which I think is PAError(3)). What's wrong with this code? It seems similar enough to the parec-simple.c example from Pulse's Doxygen (which does run on my system).

Cross compile error

Hi, I try to cross compile my project that's depends on libpulse-binding and libpulse-simple-binding from archlinux (x86_64) targeting aarch64-unknown-linux-gnu, but I got this error.

cargo build --target aarch64-unknown-linux-gnu
   Compiling libpulse-sys v1.20.1
The following warnings were emitted during compilation:

warning: pkg-config has not been configured to support cross-compilation.

error: failed to run custom build command for `libpulse-sys v1.20.1`

Caused by:
  process didn't exit successfully: `/home/admur/Dev/rpulse/target/debug/build/libpulse-sys-d2947d344d65e7ef/build-script-build` (exit status: 1)
  --- stdout
  cargo:rerun-if-env-changed=LIBPULSE_NO_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS_aarch64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS_aarch64_unknown_linux_gnu
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG_ALLOW_CROSS
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS
  cargo:rerun-if-env-changed=PKG_CONFIG_aarch64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_aarch64_unknown_linux_gnu
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_aarch64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_aarch64_unknown_linux_gnu
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=LIBPULSE_NO_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS_aarch64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS_aarch64_unknown_linux_gnu
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG_ALLOW_CROSS
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS
  cargo:rerun-if-env-changed=PKG_CONFIG_aarch64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_aarch64_unknown_linux_gnu
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_aarch64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_aarch64_unknown_linux_gnu
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
  cargo:warning=pkg-config has not been configured to support cross-compilation.

  Install a sysroot for the target platform and configure it via
  PKG_CONFIG_SYSROOT_DIR and PKG_CONFIG_PATH, or install a
  cross-compiling wrapper for pkg-config and set it via
  PKG_CONFIG environment variable.

Assertion 'q->front' failed at pulsecore/queue.c:81, function pa_queue_push(). Aborting. Aborted (core dumped)

Sometimes, when running a program I'm developing with libpulse-binding, I get the following error in the terminal:

Assertion 'q->front' failed at pulsecore/queue.c:81, function pa_queue_push(). Aborting.
Aborted (core dumped)

Unfortunately, I can't seem to get any more information, since no stack traces are given, even when the flag is enabled.
A quick google search results in this issue, which suggests that the bug may have been fixed in a newer version of PulseAudio.
I was wondering if that's something that would need to be changed on your end, or on mine, and if so, what would have to be done? It seems that on other devices, this error will sometimes pop up randomly during the runtime of the application.

question, I'm stuck to just retrieve the sink infos

Hi,

First, thank for your amazing work!
I have no previous experience with the PulseAudio C library and I'm just trying to use your binding to retrieve the informations of the sink (volume, muted).
I started by using the standard loop (I have no clue if it's a good idea or not, for my simple needs).
What I have done successfully is to create a context and connect it to the pulseaudio running server.
Also I can subscribe to sink events and get the callback fired as expected. Now the problem is I fail to retrieve the sinks info. Either from the subscribe callback or from the context's introspection. In the doc there is no example for that (and I find it pretty complex for a beginner without some previous experience with PulseAudio).
Also the relationship between the main loop and the context introspection/subscribed events is pretty vague.

Can you help me ?

EDIT: After a couple more hours of diving in the doc and the code, I finally got it. So all my question above are not relevant anymore.

Here is my simple code:

use libpulse_binding as pulse;
use pulse::callbacks::ListResult;
use pulse::context::subscribe::subscription_masks;
use pulse::context::{flags, Context};
use pulse::def::Retval;
use pulse::mainloop::standard::IterateResult;
use pulse::mainloop::standard::Mainloop;
use pulse::proplist::Proplist;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;

pub fn run() {
    let mut proplist = Proplist::new().unwrap();
    proplist
        .set_str(pulse::proplist::properties::APPLICATION_NAME, "FooApp")
        .unwrap();

    let mainloop = Rc::new(RefCell::new(
        Mainloop::new().expect("Failed to create mainloop"),
    ));

    let mut context =
        Context::new_with_proplist(mainloop.borrow().deref(), "FooAppContext", &proplist)
            .expect("Failed to create new context");

    context.connect(None, flags::NOFAIL, None).unwrap();

    // Wait for context to be ready
    loop {
        match mainloop.borrow_mut().iterate(false) {
            IterateResult::Quit(_) | IterateResult::Err(_) => {
                eprintln!("Iterate state was not success, quitting...");
                return;
            }
            IterateResult::Success(_) => {}
        }
        println!("{:#?}", context.get_state());
        match context.get_state() {
            pulse::context::State::Ready => {
                break;
            }
            pulse::context::State::Failed | pulse::context::State::Terminated => {
                eprintln!("Context state failed/terminated, quitting...");
                return;
            }
            _ => {}
        }
    }

    let interest = subscription_masks::SINK;

    let mut subscription = context.subscribe(interest, |_| {});
    context.set_subscribe_callback(Some(Box::new(|facility_opt, operation_opt, index| {
        // here the callback is fired when I mute or change the volume, but how can I get the sink
        // info from inside the clojure ?
        println!("{:#?}\n{:#?}\n{:#?}\n", facility_opt, operation_opt, index);
    })));

    // Our main loop
    loop {
        // I don't know if this is useful or not ?
        match mainloop.borrow_mut().iterate(false) {
            IterateResult::Quit(_) | IterateResult::Err(_) => {
                eprintln!("Iterate state was not success, quitting...");
                return;
            }
            IterateResult::Success(_) => {}
        }

        let introspector = &context.introspect();
        introspector.get_sink_info_list(|i| println!("")); // I don't know what to do with that. How can I get back the result ?
    }

    // Clean shutdown
    mainloop.borrow_mut().quit(Retval(0));
}

Droping failed Stream results in a panic

When trying to delete a failed stream (from a HashMap) I get this error:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: PAErr(-15)', libcore/result.rs:1009:5
Stack trace

stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: std::panicking::rust_panic_with_hook
   5: std::panicking::continue_panic_fmt
   6: rust_begin_unwind
   7: core::panicking::panic_fmt
   8: core::result::unwrap_failed
             at /build/rust/src/rustc-1.30.1-src/src/libcore/macros.rs:26
   9: <core::result::Result<T, E>>::unwrap
             at /build/rust/src/rustc-1.30.1-src/src/libcore/result.rs:808
  10: <libpulse_binding::stream::Stream as core::ops::drop::Drop>::drop
             at /home/futpib/.cargo/registry/src/github.com-1ecc6299db9ec823/libpulse-binding-2.3.0/src/stream.rs:1613
  11: core::ptr::drop_in_place
             at /build/rust/src/rustc-1.30.1-src/src/libcore/ptr.rs:59
  12: core::ptr::drop_in_place
             at /build/rust/src/rustc-1.30.1-src/src/libcore/ptr.rs:59
  13: core::ptr::drop_in_place
             at /build/rust/src/rustc-1.30.1-src/src/libcore/ptr.rs:59
  14: core::ptr::drop_in_place
             at /build/rust/src/rustc-1.30.1-src/src/libcore/ptr.rs:59
  15: <alloc::rc::Rc<T> as core::ops::drop::Drop>::drop
             at /build/rust/src/rustc-1.30.1-src/src/liballoc/rc.rs:835
  16: core::ptr::drop_in_place
             at /build/rust/src/rustc-1.30.1-src/src/libcore/ptr.rs:59
  17: <std::collections::hash::map::HashMap<K, V, S>>::retain
             at /build/rust/src/rustc-1.30.1-src/src/libstd/collections/hash/map.rs:1471
  18: papeaks::main
             at src/main.rs:181
  19: std::rt::lang_start::{{closure}}
             at /build/rust/src/rustc-1.30.1-src/src/libstd/rt.rs:74
  20: std::panicking::try::do_call
  21: __rust_maybe_catch_panic
  22: std::rt::lang_start_internal
  23: std::rt::lang_start
             at /build/rust/src/rustc-1.30.1-src/src/libstd/rt.rs:74
  24: main
  25: __libc_start_main
  26: _start

I think Drop implementation should not call disconnect if the stream is not in PA_CONTEXT_READY as PulseAudio's pa_stream_disconnect returns PA_ERR_BADSTATE which looks like the reason behind PAErr(-15).

impl Drop for Stream {
fn drop(&mut self) {
self.disconnect().unwrap();
unsafe { capi::pa_stream_unref(self.ptr) };
self.ptr = null_mut::<StreamInternal>();
}
}

pub fn disconnect(&mut self) -> Result<(), PAErr> {
match unsafe { capi::pa_stream_disconnect(self.ptr) } {
0 => Ok(()),
e => Err(PAErr(e)),
}
}

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.