tizenaudio-source: Add smoother for the accurate latency 16/301516/2 accepted/tizen/unified/20231120.174018
authorJaechul Lee <jcsing.lee@samsung.com>
Fri, 17 Nov 2023 07:50:20 +0000 (16:50 +0900)
committerJaechul Lee <jcsing.lee@samsung.com>
Fri, 17 Nov 2023 07:53:10 +0000 (16:53 +0900)
[Version] 15.0.68
[Issue Type] Update

Change-Id: Ib9bfb0a3434075b66235939ce0fa49c48345727a
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
packaging/pulseaudio-modules-tizen.spec
src/tizenaudio-source2.c

index 2f15b81..8d6e842 100644 (file)
@@ -2,7 +2,7 @@
 
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          15.0.67
+Version:          15.0.68
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
index e6122f8..867ce7f 100644 (file)
@@ -44,6 +44,8 @@
 #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;
@@ -72,8 +79,6 @@ struct userdata {
     uint32_t nfrags;
     uint32_t frag_size;
 
-    pa_usec_t timestamp;
-
     char* card;
     char* device;
     bool first;
@@ -87,11 +92,53 @@ struct userdata {
     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) {
@@ -138,6 +185,8 @@ static int suspend(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");
@@ -190,7 +239,10 @@ static int unsuspend(struct userdata *u) {
 
     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...");
 
@@ -259,15 +311,10 @@ static int source_process_msg(
 
     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;
 
@@ -334,6 +381,7 @@ static int process_render(struct userdata *u) {
         return 0;
     }
 
+    chunk.index = 0;
     chunk.length = u->frag_size;
     chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
 
@@ -353,9 +401,6 @@ static int process_render(struct userdata *u) {
 
     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);
@@ -381,8 +426,6 @@ static void thread_func(void *userdata) {
 
     pa_thread_mq_install(&u->thread_mq);
 
-    u->timestamp = pa_rtclock_now();
-
     for (;;) {
         int ret;
 
@@ -394,9 +437,12 @@ static void thread_func(void *userdata) {
             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
@@ -502,6 +548,16 @@ pa_source *pa_tizenaudio_source2_new(pa_module *m,
     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) {
@@ -642,19 +698,13 @@ pa_source *pa_tizenaudio_source2_new(pa_module *m,
     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;
@@ -696,6 +746,9 @@ static void userdata_free(struct userdata *u) {
     if (u->hal_interface)
         pa_hal_interface_unref(u->hal_interface);
 
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
     pa_xfree(u);
 }