#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/poll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/core-rtclock.h>
#include "hal-interface.h"
#include "tizenaudio-util.h"
#define DEFAULT_FRAGMENTS 4
#define DEFAULT_HAL_READ_TIMEOUT 150
+#define SMOOTHER_MIN_INTERVAL (2 * PA_USEC_PER_MSEC)
+#define SMOOTHER_MAX_INTERVAL (200 * PA_USEC_PER_MSEC)
+#define SMOOTHER_WINDOW_USEC (10 * PA_USEC_PER_SEC)
+#define SMOOTHER_ADJUST_USEC (1 * PA_USEC_PER_SEC)
+
struct userdata {
pa_core *core;
pa_module *module;
uint32_t nfrags;
uint32_t frag_size;
- pa_usec_t timestamp;
-
char* card;
char* device;
bool first;
pa_usec_t latency_time;
pa_hal_interface *hal_interface;
+ pa_smoother *smoother;
+ pa_usec_t smoother_interval;
+ pa_usec_t last_smoother_update;
+
/* for preprocessor */
pa_msgobject *preprocessor;
pa_asyncmsgq *preprocessor_asyncmsgq;
};
+static int64_t source_get_latency(struct userdata *u) {
+ int64_t delay = 0ULL;
+ pa_usec_t now1, now2;
+ pa_usec_t read_usec;
+
+ pa_assert(u);
+
+ now1 = pa_rtclock_now();
+ now2 = pa_smoother_get(u->smoother, now1);
+
+ read_usec = pa_bytes_to_usec(u->read_count, &u->source->sample_spec);
+
+ if (now2 > read_usec)
+ delay = now2 - read_usec;
+
+ return delay;
+}
+
+static void update_smoother(struct userdata *u) {
+ pa_usec_t now1, now2;
+
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ now1 = pa_rtclock_now();
+
+ if (u->last_smoother_update > 0)
+ if (u->last_smoother_update + u->smoother_interval > now1)
+ return;
+
+ now2 = pa_bytes_to_usec(u->read_count, &u->source->sample_spec);
+
+ pa_smoother_put(u->smoother, now1, now2);
+
+ u->last_smoother_update = now1;
+ u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
+}
+
static void userdata_free(struct userdata *u);
static int build_pollfd(struct userdata *u) {
return 0;
}
+ pa_smoother_pause(u->smoother, pa_rtclock_now());
+
ret = pa_hal_interface_pcm_close(u->hal_interface, u->pcm_handle);
if (ret)
pa_log_error("Error closing PCM device. rtpoll will be stopped anyway");
u->read_count = 0;
u->first = true;
- u->timestamp = pa_rtclock_now();
+
+ pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
+ u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+ u->last_smoother_update = 0;
pa_log_info("Resumed successfully...");
switch (code) {
case PA_SOURCE_MESSAGE_GET_LATENCY: {
- uint64_t r = 0;
+ int64_t r = 0;
- if (u->pcm_handle) {
- pa_usec_t now = pa_rtclock_now();
- pa_usec_t read_latency = u->timestamp + pa_bytes_to_usec(u->read_count, &u->source->sample_spec);
-
- if (now > read_latency)
- r = now - read_latency;
- }
+ if (u->pcm_handle)
+ r = source_get_latency(u);
*((int64_t *) data) = r;
return 0;
}
+ chunk.index = 0;
chunk.length = u->frag_size;
chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
pa_memblock_release(chunk.memblock);
- chunk.index = 0;
- chunk.length = (size_t)frames_to_read * frame_size;
-
if (u->preprocess_on) {
pa_asyncmsgq_post(u->preprocessor_asyncmsgq, u->preprocessor,
PA_SOURCE_MESSAGE_PREPROCESSOR_PUSH_DATA, u->source, 0, &chunk, NULL);
pa_thread_mq_install(&u->thread_mq);
- u->timestamp = pa_rtclock_now();
-
for (;;) {
int ret;
if (u->first) {
pa_log_info("Starting capture.");
pa_hal_interface_pcm_start(u->hal_interface, u->pcm_handle);
+ pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
u->first = false;
}
+ update_smoother(u);
+
if (u->rtpoll_timeout)
pa_rtpoll_set_timer_relative(u->rtpoll, (u->rtpoll_timeout * PA_USEC_PER_MSEC));
} else
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ u->smoother = pa_smoother_new(
+ SMOOTHER_ADJUST_USEC,
+ SMOOTHER_WINDOW_USEC,
+ true,
+ true,
+ 5,
+ pa_rtclock_now(),
+ true);
+ u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+
modarg_device_id = pa_modargs_get_value(ma, "device_id", NULL);
modarg_device = pa_modargs_get_value(ma, "device", NULL);
if (!modarg_device_id && !modarg_device) {
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
- u->timestamp = 0ULL;
-
if (!(u->thread = pa_thread_new("tizenaudio-source2", thread_func, u))) {
pa_log_error("Failed to create thread.");
goto fail;
}
-#ifdef TIZEN_TV
- /* Use frag_size to reduce loopback latency */
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->frag_size, &ss));
-#else
- pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(buffer_size, &ss));
-#endif
+
pa_source_put(u->source);
return u->source;
if (u->hal_interface)
pa_hal_interface_unref(u->hal_interface);
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
pa_xfree(u);
}