Max - the AlsaInputPlugin now consumes 120% CPU on my 2-core processor, roughly
60% on each processor. I think the alsa poll event is firing too often.
Since my last update, I've been thinking that the alsa poll is really just a
timer, it fires every time a fixed number of frames have been written to its
internal buffer, and as a real-time stream this equates to a fixed number of
milli-seconds. So the MPD code would be much simpler if we could just use a
TimeoutMonitor to produce the wake-up instead of trying to integrate with the
alsa lib here.
The attached patch implements this idea. I get CPU load ~0.7%, which is the
same as playing line-in via arecord | aplay, so I think there is little wasted
overhead here. There is also an advantage (I think) that the alsa handle is now
only referenced from one thread, I was unsure about using it in both threads.
There is still a bit of guesswork, which is the exact length of timeout to use.
I have selected twice the time it takes to fill the read buffer which works
well on my laptop - I don't have the experience to know if this is a good
general solution.
Do you think this implementation is OK in principle, or should I look again at
the polling solution?
Thanks
Steven
From a69a801b5adb2c82dee8addadaf36962f92b0688 Mon Sep 17 00:00:00 2001
From: Steven O'Brien <steven_obri...@yahoo.co.uk>
Date: Tue, 7 Jan 2014 12:46:23 +0000
Subject: [PATCH] input/AlsaInputPlugin: use TimeoutMonitor in place of
SocketMonitor
---
src/input/AlsaInputPlugin.cxx | 121 ++++++------------------------------------
1 file changed, 17 insertions(+), 104 deletions(-)
diff --git a/src/input/AlsaInputPlugin.cxx b/src/input/AlsaInputPlugin.cxx
index 5aa0a69..999c74b 100644
--- a/src/input/AlsaInputPlugin.cxx
+++ b/src/input/AlsaInputPlugin.cxx
@@ -31,12 +31,10 @@
#include "util/Domain.hxx"
#include "util/Error.hxx"
#include "util/StringUtil.hxx"
-#include "util/ReusableArray.hxx"
#include "util/Cast.hxx"
#include "Log.hxx"
-#include "event/MultiSocketMonitor.hxx"
+#include "event/TimeoutMonitor.hxx"
#include "event/DeferredMonitor.hxx"
-#include "event/Call.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "IOThread.hxx"
@@ -64,11 +62,12 @@ static constexpr unsigned int default_rate = 44100; // cd quality
*/
static constexpr size_t read_buffer_size = 4096;
-class AlsaInputStream final : MultiSocketMonitor, DeferredMonitor {
+class AlsaInputStream final : TimeoutMonitor, DeferredMonitor {
InputStream base;
snd_pcm_t *capture_handle;
size_t frame_size;
int frames_to_read;
+ int timeout_ms;
bool eof;
/**
@@ -77,13 +76,11 @@ class AlsaInputStream final : MultiSocketMonitor, DeferredMonitor {
*/
std::atomic_bool waiting;
- ReusableArray<pollfd> pfd_buffer;
-
public:
AlsaInputStream(EventLoop &loop,
const char *uri, Mutex &mutex, Cond &cond,
- snd_pcm_t *_handle, int _frame_size)
- :MultiSocketMonitor(loop),
+ snd_pcm_t *_handle, int _frame_size, int rate)
+ :TimeoutMonitor(loop),
DeferredMonitor(loop),
base(input_plugin_alsa, uri, mutex, cond),
capture_handle(_handle),
@@ -101,7 +98,7 @@ public:
base.size = -1;
base.ready = true;
frames_to_read = read_buffer_size / frame_size;
-
+ timeout_ms = frames_to_read * 1000 * 2 / rate;
snd_pcm_start(capture_handle);
DeferredMonitor::Schedule();
@@ -133,9 +130,7 @@ public:
if (snd_pcm_avail(capture_handle) > frames_to_read)
return true;
- if (!waiting.exchange(true))
- SafeInvalidateSockets();
-
+ waiting.store(true, std::memory_order_relaxed);
return false;
}
@@ -152,16 +147,18 @@ private:
int Recover(int err);
- void SafeInvalidateSockets() {
- DeferredMonitor::Schedule();
+ virtual void OnTimeout() override {
+ if (waiting.exchange(false, std::memory_order_relaxed)) {
+ const ScopeLock protect(base.mutex);
+ /* wake up the thread that is waiting for more data */
+ base.cond.broadcast();
+ }
+ TimeoutMonitor::Schedule(timeout_ms);
}
virtual void RunDeferred() override {
- InvalidateSockets();
+ TimeoutMonitor::Schedule(timeout_ms);
}
-
- virtual int PrepareSockets() override;
- virtual void DispatchSockets() override;
};
inline InputStream *
@@ -190,8 +187,8 @@ AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond,
int frame_size = snd_pcm_format_width(format) / 8 * channels;
AlsaInputStream *stream = new AlsaInputStream(io_thread_get(),
- uri, mutex, cond,
- handle, frame_size);
+ uri, mutex, cond, handle,
+ frame_size, rate);
return &stream->base;
}
@@ -216,40 +213,6 @@ AlsaInputStream::Read(void *ptr, size_t size, Error &error)
return nbytes;
}
-int
-AlsaInputStream::PrepareSockets()
-{
- if (!waiting) {
- ClearSocketList();
- return -1;
- }
-
- int count = snd_pcm_poll_descriptors_count(capture_handle);
- if (count < 0) {
- ClearSocketList();
- return -1;
- }
-
- struct pollfd *pfds = pfd_buffer.Get(count);
-
- count = snd_pcm_poll_descriptors(capture_handle, pfds, count);
- if (count < 0)
- count = 0;
-
- ReplaceSocketList(pfds, count);
- return -1;
-}
-
-void
-AlsaInputStream::DispatchSockets()
-{
- waiting = false;
-
- const ScopeLock protect(base.mutex);
- /* wake up the thread that is waiting for more data */
- base.cond.broadcast();
-}
-
inline int
AlsaInputStream::Recover(int err)
{
@@ -323,23 +286,6 @@ AlsaInputStream::OpenDevice(const char *device,
return nullptr;
}
- /* period needs to be big enough so that poll() doesn't fire too often,
- * but small enough that buffer overruns don't occur if Read() is not
- * invoked often enough.
- * the calculation here is empirical; however all measurements were
- * done using 44100:16:2. When we extend this plugin to support
- * other audio formats then this may need to be revisited */
- snd_pcm_uframes_t period = read_buffer_size * 2;
- int direction = -1;
- if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params,
- &period, &direction)) < 0) {
- error.Format(alsa_input_domain, "Cannot set period size (%s)",
- snd_strerror(err));
- snd_pcm_hw_params_free(hw_params);
- snd_pcm_close(capture_handle);
- return nullptr;
- }
-
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) {
error.Format(alsa_input_domain, "Cannot set parameters (%s)",
snd_strerror(err));
@@ -350,39 +296,6 @@ AlsaInputStream::OpenDevice(const char *device,
snd_pcm_hw_params_free (hw_params);
- snd_pcm_sw_params_t *sw_params;
-
- snd_pcm_sw_params_malloc(&sw_params);
- snd_pcm_sw_params_current(capture_handle, sw_params);
-
- if ((err = snd_pcm_sw_params_set_start_threshold(capture_handle, sw_params,
- period)) < 0) {
- error.Format(alsa_input_domain,
- "unable to set start threshold (%s)", snd_strerror(err));
- snd_pcm_sw_params_free(sw_params);
- snd_pcm_close(capture_handle);
- return nullptr;
- }
-
- if ((err = snd_pcm_sw_params_set_period_event(capture_handle, sw_params,
- 1)) < 0) {
- error.Format(alsa_input_domain,
- "unable to set period event (%s)", snd_strerror(err));
- snd_pcm_sw_params_free(sw_params);
- snd_pcm_close(capture_handle);
- return nullptr;
- }
-
- if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0) {
- error.Format(alsa_input_domain,
- "unable to install sw params (%s)", snd_strerror(err));
- snd_pcm_sw_params_free(sw_params);
- snd_pcm_close(capture_handle);
- return nullptr;
- }
-
- snd_pcm_sw_params_free(sw_params);
-
snd_pcm_prepare(capture_handle);
return capture_handle;
--
1.8.3.2
------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT
organizations don't have a clear picture of how application performance
affects their revenue. With AppDynamics, you get 100% visibility into your
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
Musicpd-dev-team mailing list
Musicpd-dev-team@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team