i-rinat / apulse

PulseAudio emulation for ALSA
MIT License
609 stars 35 forks source link

Capture PCM output of apulse #106

Closed Crypto90 closed 4 years ago

Crypto90 commented 4 years ago

I have a really strange issue, on which I stuck for quiet a long time now.

My goal is to capture the apulse output stream to a file, without the need of alsa.

I run into two issues:

  1. pa_mainloop runs way too fast which causes high cpu load.
  2. audio callbacks which get requested inside data_available_for_stream gets empty or partial empty buffers as result. I debugged this as far as I could, it comes from firefox AudioStream.cpp class, if Audio underruns, the callback buffer gets filled with zeros. But why does it underrun? I should be able to fifo the pcm audio output and play the fifo at the same time.

Set in firefox about:config: security.sandbox.content.level = 0

which will allow the MOZ_DUMP_AUDIO to work (you can instead add an fwrite into the data_available_for_stream function, to write the byte buffer into a file [in binary mode fopen]).

Run Firefox and dump the audio playback: APULSE_PLAYBACK_DEVICE=null APULSE_CAPTURE_DEVICE=null MOZ_DUMP_AUDIO=1 LD_LIBRARY_PATH=/path/to/apulse/libs /path/to/firefox/firefox

Produced AudioStream-1.wav in working directory: RlMDTcV

As you can see, firefox does fill the callbacks with zeros which is problematic in my case.

I want to fwrite the audio callback buffers into a file. I managed to filter the zeros out, but the high cpu and the underruns still apear A LOT, so its not possible for me right now to fifo the audio and send it into a player at the same time without lags/buffering issues, because of underruns.

Does anyone have some hint? It should be possible to let apulse act as a capturing layer instead of an alsa layer.

*The testcase was generated with the original unmodified apulse code.

Crypto90 commented 4 years ago

I noticed if I use sleep or usleep to prevent high cpu mainloop cycles, firefox stops processing more data which will result in empty callbacks too.

I managed to filter out normal underruns, but I need to find a way to clock/sleep inside the mainloop to keep the cpu usage low without thread blocking firefox internal buffer fill code.

So normal sleep or usleep functions do not work, because they prevent loading new data on firefox side.

Update 1: So I found out, that the pa_threaded_mainloop gets locked and unlocked. While we are inside "data_available_for_stream", the thread is locked as far I can see. If the thread is locked and a sleep gets called, the sleep gets executed in the parent thread (which is firefox audio processing thread) instead of the current thread. If I comment out all lock/unlock logic, sleep does work and firefox does always provide data.

But its a bad idea to disable all locking and unlocking logic, can someone tell me how I can sleep inside data_available_for_stream() while the thread is locked without sleeping in the firefox audio thread? I just want to sleep inside the mainloop thread.

Crypto90 commented 4 years ago

Got it working.