From f49985f02d8d1e49a4e2075f3b9f2d651c571770 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Mon, 29 Mar 2021 14:17:04 +0900 Subject: [PATCH 01/16] aec-manager: Add audio AEC manager added new feature [Version] 13.0.61 [Issue Type] New feature Change-Id: I401390836ee34ea4444180e9725febd1720ac4a4 Signed-off-by: Jaechul Lee --- Makefile.am | 9 ++ configure.ac | 12 ++ packaging/pulseaudio-modules-tizen.spec | 6 +- src/aec-manager.c | 267 ++++++++++++++++++++++++++++++++ src/aec-manager.h | 38 +++++ src/device-manager.c | 24 ++- src/module-tizenaudio-sink.c | 75 ++++++++- src/module-tizenaudio-source.c | 75 ++++++++- 8 files changed, 492 insertions(+), 14 deletions(-) create mode 100644 src/aec-manager.c create mode 100644 src/aec-manager.h diff --git a/Makefile.am b/Makefile.am index e66660b..495578e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,10 @@ AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS) AM_LDFLAGS = $(NODELETE_LDFLAGS) MODULE_CFLAGS = $(AM_CFLAGS) $(PACORE_CFLAGS) $(PA_CFLAGS) -D__TIZEN__ +if ENABLE_AEC +MODULE_CFLAGS += -DSUPPORT_AEC +endif + MODULE_LDFLAGS = $(AM_LDFLAGS) $(PACORE_LDFLAGS) $(PA_LDFLAGS) -module -disable-static -avoid-version MODULE_LIBADD = $(AM_LIBADD) $(PACORE_LIBS) $(PA_LIBS) @@ -94,6 +98,11 @@ module_tizenaudio_policy_la_SOURCES = \ src/device-manager-db.c src/device-manager-db-priv.h \ src/tizen-device.c src/tizen-device.h src/tizen-device-def.c src/tizen-device-def.h \ src/subscribe-observer.c src/subscribe-observer.h + +if ENABLE_AEC +module_tizenaudio_policy_la_SOURCES += src/aec-manager.c src/aec-manager.h +endif + module_tizenaudio_policy_la_LDFLAGS = $(MODULE_LDFLAGS) -L$(pulsemodlibexecdir) module_tizenaudio_policy_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(VCONF_LIBS) $(INIPARSER_LIBS) $(LIBJSON_LIBS) libhal-interface.la libcommunicator.la module_tizenaudio_policy_la_CFLAGS = $(MODULE_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS) $(INIPARSER_CFLAGS) $(LIBJSON_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_policy diff --git a/configure.ac b/configure.ac index ac631fa..d1720a3 100644 --- a/configure.ac +++ b/configure.ac @@ -393,6 +393,18 @@ AC_ARG_ENABLE(acm, AC_HELP_STRING([--enable-acm], [using acm]), AM_CONDITIONAL(ENABLE_ACM, test "x$ENABLE_ACM" = "xyes") dnl end -------------------------------------------------------------------- +dnl use aec ---------------------------------------------------------------- +AC_ARG_ENABLE(aec, AC_HELP_STRING([--enable-aec], [using aec]), +[ + case "${enableval}" in + yes) ENABLE_AEC=yes ;; + no) ENABLE_AEC=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-aec) ;; + esac + ],[USE_AEC=no]) +AM_CONDITIONAL(ENABLE_AEC, test "x$ENABLE_AEC" = "xyes") +dnl end -------------------------------------------------------------------- + #### D-Bus support (optional) #### AC_ARG_ENABLE([dbus], diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 3310887..13add09 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.60 +Version: 13.0.61 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ @@ -46,7 +46,9 @@ export LD_AS_NEEDED=0 %reconfigure --prefix=%{_prefix} \ --disable-static \ --enable-acm \ -%if "%{tizen_profile_name}" == "tv" +%if "%{tizen_profile_name}" != "tv" + --enable-aec +%else --enable-vconf-helper %endif # --enable-haltc diff --git a/src/aec-manager.c b/src/aec-manager.c new file mode 100644 index 0000000..8ed96b5 --- /dev/null +++ b/src/aec-manager.c @@ -0,0 +1,267 @@ +/*** + This file is part of PulseAudio. + + Copyright 2021 Jaechul Lee + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "aec-manager.h" + +#define AEC_MANAGER_OBJECT_PATH "/org/pulseaudio/AecManager" +#define AEC_MANAGER_INTERFACE "org.pulseaudio.AecManager" + +#define AEC_MANAGER_METHOD_NAME_ON "On" +#define AEC_MANAGER_METHOD_NAME_OFF "Off" +#define AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS "GetSinkParam" +#define AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS "GetSourceParam" + +#define AEC_UNIX_SOCKET_PATH "/tmp/.aec.socket" + +static struct aec_manager { + pa_dbus_connection *dbus_conn; + pa_source *source; /* builtin-mic */ + pa_sink *sink; /* builtin-spk */ +} am; + +enum { + SINK_MESSAGE_SET_AEC_STATE = PA_SINK_MESSAGE_MAX, + SINK_MESSAGE_GET_AEC_PARAMS, +}; + +enum { + SOURCE_MESSAGE_SET_AEC_STATE = PA_SOURCE_MESSAGE_MAX, + SOURCE_MESSAGE_GET_AEC_PARAMS, +}; + +#define AEC_MANAGER_INTROSPECT_XML \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "" \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + "" + +enum method_handler_index { + METHOD_HANDLER_ON, + METHOD_HANDLER_OFF, + METHOD_HANDLER_GET_SINK_PARAMS, + METHOD_HANDLER_GET_SOURCE_PARAMS, + METHOD_HANDLER_MAX +}; + +static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_ON] = { + .method_name = AEC_MANAGER_METHOD_NAME_ON, + .receive_cb = handle_method_on }, + [METHOD_HANDLER_OFF] = { + .method_name = AEC_MANAGER_METHOD_NAME_OFF, + .receive_cb = handle_method_off }, + [METHOD_HANDLER_GET_SINK_PARAMS] = { + .method_name = AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS, + .receive_cb = handle_method_get_sink_params }, + [METHOD_HANDLER_GET_SOURCE_PARAMS] = { + .method_name = AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS, + .receive_cb = handle_method_get_source_params }, +}; + +static void send_get_param_reply(DBusConnection *conn, DBusMessage *msg, aec_params_t* param) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + const char *ptr1 = param->card; + const char *ptr2 = param->device; + + reply = dbus_message_new_method_return(msg); + if (!reply) { + pa_log_error("Failed to alloc reply"); + return; + } + + dbus_message_iter_init_append(reply, &msg_iter); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->rate); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->channels); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->format); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->frag_size); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->nfrags); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr1); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr2); + dbus_connection_send(conn, reply, NULL); + + dbus_message_unref(reply); +} + +static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink), + SINK_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL); + pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source), + SOURCE_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink), + SINK_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL); + pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source), + SOURCE_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata) { + aec_params_t param; + + pa_asyncmsgq_send(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink), + SINK_MESSAGE_GET_AEC_PARAMS, ¶m, 0, NULL); + + send_get_param_reply(conn, msg, ¶m); +} + +static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata) { + aec_params_t param; + + pa_asyncmsgq_send(am.source->asyncmsgq, PA_MSGOBJECT(am.source), + SOURCE_MESSAGE_GET_AEC_PARAMS, ¶m, 0, NULL); + + send_get_param_reply(conn, msg, ¶m); +} + +static DBusHandlerResult method_handler_for_vt(DBusConnection *conn, DBusMessage *m, void *userdata) { + const char *path, *interface, *member; + DBusMessage *r = NULL; + + pa_assert(conn); + pa_assert(m); + + path = dbus_message_get_path(m); + interface = dbus_message_get_interface(m); + member = dbus_message_get_member(m); + + pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); + + if (!pa_safe_streq(path, AEC_MANAGER_OBJECT_PATH)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { + r = dbus_message_new_method_return(m); + if (r) { + const char *xml = AEC_MANAGER_INTROSPECT_XML; + dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID); + dbus_connection_send((conn), r, NULL); + dbus_message_unref(r); + } + } else { + int i; + for (i = 0; i < METHOD_HANDLER_MAX; i++) { + if (dbus_message_is_method_call(m, AEC_MANAGER_INTERFACE, + method_handlers[i].method_name)) + method_handlers[i].receive_cb(conn, m, NULL); + } + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +int aec_manager_init(pa_core *core, pa_source *source, pa_sink *sink) { + DBusError err; + pa_dbus_connection *conn = NULL; + static const DBusObjectPathVTable vtable = { + .message_function = method_handler_for_vt, + }; + + if (!source || !sink) { + pa_log_error("AEC init failed. source(%p)/sink(%p) is null.", source, sink); + return -1; + } + + dbus_error_init(&err); + if (!(conn = pa_dbus_bus_get(core, DBUS_BUS_SYSTEM, &err)) || dbus_error_is_set(&err)) { + if (conn) + pa_dbus_connection_unref(conn); + + dbus_error_free(&err); + pa_log_error("Unable to contact D-Bus system bus: %s: %s", err.name, err.message); + return -1; + } + + am.dbus_conn = conn; + if (!dbus_connection_register_object_path(pa_dbus_connection_get(conn), + AEC_MANAGER_OBJECT_PATH, &vtable, NULL)) { + pa_dbus_connection_unref(conn); + pa_log_error("Failed to register object path"); + return -1; + } + + am.source = source; + am.sink = sink; + + pa_log_info("AEC init success"); + + return 0; +} + +void aec_manager_deinit(void) { + if (am.dbus_conn) + pa_dbus_connection_unref(am.dbus_conn); + + pa_log_info("AEC deinit success"); +} diff --git a/src/aec-manager.h b/src/aec-manager.h new file mode 100644 index 0000000..d30f788 --- /dev/null +++ b/src/aec-manager.h @@ -0,0 +1,38 @@ +/*** + This file is part of PulseAudio. + + Copyright 2021 Jaechul Lee + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifndef footizenaecmanagerfoo +#define footizenaecmanagerfoo + +typedef struct aec_params { + int rate; + int channels; + int format; + int frag_size; + int nfrags; + char card[32]; + char device[32]; +} aec_params_t; + +int aec_manager_init(pa_core *core, pa_source *source, pa_sink *sink); +void aec_manager_deinit(); + +#endif diff --git a/src/device-manager.c b/src/device-manager.c index 14b2b89..aef1eee 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -49,6 +49,10 @@ #include "device-manager-dbus-priv.h" #include "device-manager-db-priv.h" +#ifdef SUPPORT_AEC +#include "aec-manager.h" +#endif + #define SHARED_DEVICE_MANAGER "tizen-device-manager" #define DEVICE_MAP_FILE "/etc/pulse/device-map.json" @@ -2905,6 +2909,8 @@ static pa_hook_result_t device_running_changed_hook_cb(pa_core *c, pa_tz_device_ pa_device_manager* pa_device_manager_get(pa_core *c) { pa_device_manager *dm; + pa_source *source; + pa_sink *sink; pa_assert(c); @@ -2961,12 +2967,17 @@ pa_device_manager* pa_device_manager_get(pa_core *c) { } /* Just for convenience when test*/ - if (!_device_manager_set_default_sink(dm, DEVICE_TYPE_SPEAKER, "normal")) { + sink = _device_manager_set_default_sink(dm, DEVICE_TYPE_SPEAKER, "normal"); + if (!sink) pa_log_warn("Set default sink with speaker(normal) failed"); - } - if (!_device_manager_set_default_source(dm, DEVICE_TYPE_MIC, "normal")) { + + source = _device_manager_set_default_source(dm, DEVICE_TYPE_MIC, "normal"); + if (!source) pa_log_warn("Set default source with mic(normal) failed"); - } + +#ifdef SUPPORT_AEC + aec_manager_init(dm->core, source, sink); +#endif pa_shared_set(c, SHARED_DEVICE_MANAGER, dm); @@ -3044,4 +3055,9 @@ void pa_device_manager_unref(pa_device_manager *dm) { pa_shared_remove(dm->core, SHARED_DEVICE_MANAGER); pa_xfree(dm); + +#ifdef SUPPORT_AEC + aec_manager_deinit(); +#endif + } diff --git a/src/module-tizenaudio-sink.c b/src/module-tizenaudio-sink.c index 03f1e4d..c8f1d35 100644 --- a/src/module-tizenaudio-sink.c +++ b/src/module-tizenaudio-sink.c @@ -47,6 +47,10 @@ #include "hal-interface.h" +#ifdef SUPPORT_AEC +#include "aec-manager.h" +#endif + PA_MODULE_AUTHOR("Tizen"); PA_MODULE_DESCRIPTION("Tizen Audio Sink"); PA_MODULE_VERSION(PACKAGE_VERSION); @@ -74,6 +78,13 @@ PA_MODULE_USAGE( #define DEVICE_NAME_MAX 30 +#ifdef SUPPORT_AEC +enum { + PA_SINK_MESSAGE_SET_AEC_STATE = PA_SINK_MESSAGE_MAX, + PA_SINK_MESSAGE_GET_AEC_PARAMS, +}; +#endif + struct userdata { pa_core *core; pa_module *module; @@ -100,6 +111,11 @@ struct userdata { uint64_t write_count; pa_hal_interface *hal_interface; + +#ifdef SUPPORT_AEC + bool aec_enable; + pa_sample_spec ss; +#endif }; static const char* const valid_modargs[] = { @@ -159,7 +175,7 @@ static int suspend(struct userdata *u) { u->rtpoll_item = NULL; } - pa_log_info("Device suspended...[%s,%s]", u->card, u->device); + pa_log_info("Device suspended..."); return 0; } @@ -170,6 +186,9 @@ static int unsuspend(struct userdata *u) { int32_t ret; size_t frame_size; + char *card = u->card; + char *device = u->device; + pa_assert(u); pa_assert(!u->pcm_handle); @@ -182,9 +201,16 @@ static int unsuspend(struct userdata *u) { goto fail; } +#ifdef SUPPORT_AEC + if (u->aec_enable) { + card = "Loopback"; + device = "0,0"; + } +#endif + ret = pa_hal_interface_pcm_open(u->hal_interface, - u->card, - u->device, + card, + device, DIRECTION_OUT, &sample_spec, u->frag_size / frame_size, @@ -201,7 +227,7 @@ static int unsuspend(struct userdata *u) { u->write_count = 0; u->first = true; - pa_log_info("Resumed successfully..."); + pa_log_info("Resumed successfully...device(%s:%s)", card, device); return 0; @@ -278,6 +304,36 @@ static int sink_process_msg( *((pa_usec_t*)data) = latency; return 0; } +#ifdef SUPPORT_AEC + case PA_SINK_MESSAGE_SET_AEC_STATE: { + pa_sink *s = PA_SINK(o); + bool enable = (bool)data; + + if (u->aec_enable == enable) + return 0; + + pa_log_info("AEC enable(%d)", enable); + + u->aec_enable = enable; + if (s->thread_info.state == PA_SINK_RUNNING) { + suspend(u); + unsuspend(u); + } + + return 0; + } + case PA_SINK_MESSAGE_GET_AEC_PARAMS: { + aec_params_t *params = (aec_params_t *)data; + params->rate = u->ss.rate; + params->channels = u->ss.channels; + params->format = u->ss.format; + params->frag_size = u->frag_size; + params->nfrags = u->nfrags; + snprintf(params->card, DEVICE_NAME_MAX, "%s", u->card); + snprintf(params->device, DEVICE_NAME_MAX, "%s", u->device); + return 0; + } +#endif } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -519,6 +575,7 @@ int pa__init(pa_module*m) { const char *modarg_device; char card[DEVICE_NAME_MAX]; char device[DEVICE_NAME_MAX]; + size_t frame_size, buffer_size, period_frames, buffer_frames; pa_assert(m); @@ -546,6 +603,9 @@ int pa__init(pa_module*m) { u->first = true; u->hal_interface = pa_hal_interface_get(u->core); u->rtpoll = pa_rtpoll_new(); +#ifdef SUPPORT_AEC + u->ss = ss; +#endif pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) { @@ -595,6 +655,13 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, "tizen.device", u->device); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen"); + frame_size = pa_frame_size(&ss); + buffer_size = u->frag_size * u->nfrags; + buffer_frames = buffer_size / frame_size; + period_frames = u->frag_size / frame_size; + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size); + if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log_error("Invalid properties."); pa_sink_new_data_done(&data); diff --git a/src/module-tizenaudio-source.c b/src/module-tizenaudio-source.c index c0337e0..4ec1330 100644 --- a/src/module-tizenaudio-source.c +++ b/src/module-tizenaudio-source.c @@ -47,6 +47,10 @@ #include "hal-interface.h" +#ifdef SUPPORT_AEC +#include "aec-manager.h" +#endif + PA_MODULE_AUTHOR("Tizen"); PA_MODULE_DESCRIPTION("Tizen Audio Source"); PA_MODULE_VERSION(PACKAGE_VERSION); @@ -70,6 +74,13 @@ PA_MODULE_USAGE( #define DEVICE_NAME_MAX 30 +#ifdef SUPPORT_AEC +enum { + PA_SOURCE_MESSAGE_SET_AEC_STATE = PA_SOURCE_MESSAGE_MAX, + PA_SOURCE_MESSAGE_GET_AEC_PARAMS, +}; +#endif + struct userdata { pa_core *core; pa_module *module; @@ -95,6 +106,11 @@ struct userdata { uint64_t read_count; pa_usec_t latency_time; pa_hal_interface *hal_interface; + +#ifdef SUPPORT_AEC + bool aec_enable; + pa_sample_spec ss; +#endif }; static const char* const valid_modargs[] = { @@ -152,7 +168,7 @@ static int suspend(struct userdata *u) { u->rtpoll_item = NULL; } - pa_log_info("Device suspended...[%s,%s]", u->card, u->device); + pa_log_info("Device suspended..."); return 0; } @@ -163,6 +179,9 @@ static int unsuspend(struct userdata *u) { int32_t ret; size_t frame_size; + char *card = u->card; + char *device = u->device; + pa_assert(u); pa_assert(!u->pcm_handle); @@ -175,9 +194,16 @@ static int unsuspend(struct userdata *u) { goto fail; } +#ifdef SUPPORT_AEC + if (u->aec_enable) { + card = "Loopback"; + device = "1,1"; + } +#endif + ret = pa_hal_interface_pcm_open(u->hal_interface, - u->card, - u->device, + card, + device, DIRECTION_IN, &sample_spec, u->frag_size / frame_size, @@ -194,7 +220,7 @@ static int unsuspend(struct userdata *u) { u->read_count = 0; u->first = true; - pa_log_info("Resumed successfully..."); + pa_log_info("Resumed successfully...device(%s:%s)", card, device); return 0; @@ -265,6 +291,36 @@ static int source_process_msg( *((pa_usec_t*)data) = u->timestamp > now ? 0ULL : now - u->timestamp; return 0; } +#ifdef SUPPORT_AEC + case PA_SOURCE_MESSAGE_SET_AEC_STATE: { + pa_source *s = PA_SOURCE(o); + bool enable = (bool)data; + + if (u->aec_enable == enable) + return 0; + + pa_log_info("AEC enable(%d)", enable); + + u->aec_enable = enable; + if (s->thread_info.state == PA_SOURCE_RUNNING) { + suspend(u); + unsuspend(u); + } + + return 0; + } + case PA_SOURCE_MESSAGE_GET_AEC_PARAMS: { + aec_params_t *params = (aec_params_t *)data; + params->rate = u->ss.rate; + params->channels = u->ss.channels; + params->format = u->ss.format; + params->frag_size = u->frag_size; + params->nfrags = u->nfrags; + snprintf(params->card, DEVICE_NAME_MAX, "%s", u->card); + snprintf(params->device, DEVICE_NAME_MAX, "%s", u->device); + return 0; + } +#endif } return pa_source_process_msg(o, code, data, offset, chunk); @@ -463,6 +519,7 @@ int pa__init(pa_module*m) { const char *modarg_device; char card[DEVICE_NAME_MAX]; char device[DEVICE_NAME_MAX]; + size_t frame_size, buffer_size, period_frames, buffer_frames; pa_assert(m); @@ -490,6 +547,9 @@ int pa__init(pa_module*m) { u->first = true; u->hal_interface = pa_hal_interface_get(u->core); u->rtpoll = pa_rtpoll_new(); +#ifdef SUPPORT_AEC + u->ss = ss; +#endif pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) { @@ -526,6 +586,13 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, "tizen.device", u->device); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen"); + frame_size = pa_frame_size(&ss); + buffer_size = u->frag_size * u->nfrags; + buffer_frames = buffer_size / frame_size; + period_frames = u->frag_size / frame_size; + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size); + if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log_error("Invalid properties."); pa_source_new_data_done(&data); -- 2.7.4 From 6ee4ef64b2d71bedd17136d044868d7ea4b4db2e Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Tue, 27 Apr 2021 20:12:24 +0900 Subject: [PATCH 02/16] device-manager: enhance some if-else cascaded statements [Version] 13.0.62 [Issue Type] Revise Change-Id: I9f8ac0a13e3ee0ed92530f466537e7290e4e16e0 --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/device-manager.c | 174 ++++++++++++++++++-------------- 2 files changed, 98 insertions(+), 78 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 13add09..1c4f294 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.61 +Version: 13.0.62 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/device-manager.c b/src/device-manager.c index aef1eee..fbefe47 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -227,6 +227,48 @@ static internal_codec_device internal_codec_devices[DEVICE_INDEX_MAX] = { .is_running[1] = false }, }; +typedef struct _device_module { + const char *module_name[2]; /* in/out */ + const char *device_string; + int (*custom_device_get_func)(pa_object *, char *); +} device_module_t; + +static int pulse_device_get_alsa_device_name(pa_object *pdevice, char *device_name); +static int pulse_device_get_tizen_device_name(pa_object *pdevice, char *device_name); + +static const device_module_t module_table[] = { + [DM_DEVICE_CLASS_NONE] = { + .module_name = { NULL, NULL }, + .device_string = NULL, + .custom_device_get_func = NULL + }, + [DM_DEVICE_CLASS_ALSA] = { + .module_name = { "module-alsa-source", "module-alsa-sink" }, + .device_string = NULL, + .custom_device_get_func = pulse_device_get_alsa_device_name + }, + [DM_DEVICE_CLASS_TIZEN] = { + .module_name = { "module-tizenaudio-source", "module-tizenaudio-sink" }, + .device_string = NULL, + .custom_device_get_func = pulse_device_get_tizen_device_name + }, + [DM_DEVICE_CLASS_BT] = { + .module_name = { NULL, "module-bluez5-device" }, + .device_string = "bt", + .custom_device_get_func = NULL + }, + [DM_DEVICE_CLASS_NULL] = { + .module_name = { "module-null-source", "module-null-sink" }, + .device_string = "null", + .custom_device_get_func = NULL + }, + [DM_DEVICE_CLASS_ACM] = { + .module_name = { NULL, "module-acm-sink" }, + .device_string = "acm", + .custom_device_get_func = NULL + } +}; + void simple_device_dump(pa_log_level_t level, const char *prefix, int id, const char *type, const char *name, int direction, int state) { pa_logl(level, "%s device id(%d) type(%s) name (%s) direction(%d) state(%d)", pa_strempty(prefix), id, pa_strnull(type), pa_strnull(name), direction, state); @@ -555,69 +597,18 @@ static bool pulse_device_is_monitor(pa_object *pdevice) { prop = pulse_device_get_proplist(pdevice); if ((device_class = pa_proplist_gets(prop, PA_PROP_DEVICE_CLASS))) { - if (pa_safe_streq(device_class, DEVICE_CLASS_MONITOR)) { + if (pa_safe_streq(device_class, DEVICE_CLASS_MONITOR)) return true; - } else { - return false; - } - } else { - return false; } -} - -/* Actually this device_name is not exactly same with alsa's form (ex. "hw:0,1") - * this is part of that. (ex. "0,1") */ -static int pulse_device_get_device_name(pa_object *pdevice, char *device_name) { - pa_proplist *prop; - prop = pulse_device_get_proplist(pdevice); - - if (pulse_device_is_alsa(pdevice)) { - const char *device_string_prop; - char *name_p; - - if (!(device_string_prop = pa_proplist_gets(prop, PA_PROP_DEVICE_STRING))) { - pa_log_error("failed to get property 'device.string'"); - return -1; - } - if (!(name_p = strchr(device_string_prop, ':'))) { - pa_log_error("failed to parse device string"); - return -1; - } - strncpy(device_name, name_p + 1, DEVICE_NAME_MAX); - } else if (pulse_device_is_tizenaudio(pdevice)) { - const char *card, *device; - if (!(card = pa_proplist_gets(prop, "tizen.card"))) { - pa_log_error("failed to get property 'tizen.card'"); - return -1; - } - if (!(device = pa_proplist_gets(prop, "tizen.device"))) { - pa_log_error("failed to get property 'tizen.device'"); - return -1; - } - snprintf(device_name, DEVICE_NAME_MAX, "%s,%s", card, device); - } else { - return -1; - } - return 0; + return false; } static const char* device_class_get_module_name(dm_device_class_t device_class, bool is_sink) { - if (device_class == DM_DEVICE_CLASS_NONE) { + if (device_class >= DM_DEVICE_CLASS_MAX) return NULL; - } else if (device_class == DM_DEVICE_CLASS_ALSA) { - return is_sink ? "module-alsa-sink" : "module-alsa-source"; - } else if (device_class == DM_DEVICE_CLASS_TIZEN) { - return is_sink ? "module-tizenaudio-sink" : "module-tizenaudio-source"; - } else if (device_class == DM_DEVICE_CLASS_BT) { - return is_sink ? "module-bluez5-device" : NULL; - } else if (device_class == DM_DEVICE_CLASS_NULL) { - return is_sink ? "module-null-sink" : "module-null-source"; - } else if (device_class == DM_DEVICE_CLASS_ACM) { - return is_sink ? "module-acm-sink" : NULL; - } else { - return NULL; - } + + return module_table[device_class].module_name[is_sink]; } static device_type_info* _device_manager_get_type_info(pa_idxset *type_infos, const char *type, const char *role) { @@ -935,6 +926,45 @@ static bool pulse_device_params_is_equal(pa_object *pdevice, const char *params) return device_params_is_equal(params, removed_module_args); } +static int pulse_device_get_alsa_device_name(pa_object *pdevice, char *device_name) { + pa_proplist *prop; + const char *device_string_prop; + char *name_p; + + prop = pulse_device_get_proplist(pdevice); + + if (!(device_string_prop = pa_proplist_gets(prop, PA_PROP_DEVICE_STRING))) { + pa_log_error("failed to get property 'device.string'"); + return -1; + } + if (!(name_p = strchr(device_string_prop, ':'))) { + pa_log_error("failed to parse device string"); + return -1; + } + + snprintf(device_name, DEVICE_NAME_MAX, "alsa:%s", name_p + 1); + return 0; +} + +static int pulse_device_get_tizen_device_name(pa_object *pdevice, char *device_name) { + pa_proplist *prop; + const char *card, *device; + + prop = pulse_device_get_proplist(pdevice); + + if (!(card = pa_proplist_gets(prop, "tizen.card"))) { + pa_log_error("failed to get property 'tizen.card'"); + return -1; + } + if (!(device = pa_proplist_gets(prop, "tizen.device"))) { + pa_log_error("failed to get property 'tizen.device'"); + return -1; + } + + snprintf(device_name, DEVICE_NAME_MAX, "tizen:%s,%s", card, device); + return 0; +} + static int pulse_device_get_device_string(pa_object *pdevice, char *device_string) { dm_device_class_t device_class; char device_name[DEVICE_NAME_MAX]; @@ -943,29 +973,19 @@ static int pulse_device_get_device_string(pa_object *pdevice, char *device_strin pa_assert(device_string); device_class = pulse_device_get_class(pdevice); + if (device_class <= DM_DEVICE_CLASS_NONE || + device_class >= DM_DEVICE_CLASS_MAX) + return -1; - if (device_class == DM_DEVICE_CLASS_ALSA) { - if (pulse_device_get_device_name(pdevice, device_name) < 0) - return -1; - snprintf(device_string, DEVICE_STR_MAX, "alsa:%s", device_name); - return 0; - } else if (device_class == DM_DEVICE_CLASS_NULL) { - snprintf(device_string, DEVICE_STR_MAX, "null"); - return 0; - } else if (device_class == DM_DEVICE_CLASS_TIZEN) { - if (pulse_device_get_device_name(pdevice, device_name) < 0) - return -1; - snprintf(device_string, DEVICE_STR_MAX, "tizen:%s", device_name); - return 0; - } else if (device_class == DM_DEVICE_CLASS_BT) { - snprintf(device_string, DEVICE_STR_MAX, "bt"); - return 0; - } else if (device_class == DM_DEVICE_CLASS_ACM) { - snprintf(device_string, DEVICE_STR_MAX, "acm"); - return 0; - } else { + if (module_table[device_class].custom_device_get_func && + module_table[device_class].custom_device_get_func(pdevice, device_name) < 0) return -1; - } + + snprintf(device_string, DEVICE_STR_MAX, "%s", + module_table[device_class].device_string ? + module_table[device_class].device_string : device_name); + + return 0; } /* pdevice is sink or source */ -- 2.7.4 From a6bd7d4bde162a37fbfc3a37ddb88458d961fb03 Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Tue, 1 Jun 2021 16:55:06 +0900 Subject: [PATCH 03/16] stream/device-manager: use PA_DEFAULT_CONFIG_DIR instead of hardcoding for configuration path [Version] 13.0.63 [Issue Type] New HAL Change-Id: I9e5072c5db449a699b231275883608a55f77f288 --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/device-manager.c | 2 +- src/stream-manager.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 1c4f294..7b3abc2 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.62 +Version: 13.0.63 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/device-manager.c b/src/device-manager.c index fbefe47..ef81274 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -55,7 +55,7 @@ #define SHARED_DEVICE_MANAGER "tizen-device-manager" -#define DEVICE_MAP_FILE "/etc/pulse/device-map.json" +#define DEVICE_MAP_FILE PA_DEFAULT_CONFIG_DIR"/device-map.json" #define DEVICE_STR_MAX 40 #define DEVICE_DIRECTION_MAX 3 #define DEVICE_PARAM_STRING_MAX 150 diff --git a/src/stream-manager.c b/src/stream-manager.c index 8043287..fe45e37 100644 --- a/src/stream-manager.c +++ b/src/stream-manager.c @@ -104,7 +104,7 @@ static const char* notify_command_type_str[] = { [NOTIFY_COMMAND_INFORM_STREAM_DISCONNECTED] = "INFORM_STREAM_DISCONNECTED", }; -#define STREAM_MAP_FILE "/etc/pulse/stream-map.json" +#define STREAM_MAP_FILE PA_DEFAULT_CONFIG_DIR"/stream-map.json" #define STREAM_MAP_VOLUMES "volumes" #define STREAM_MAP_VOLUME_TYPE "type" #define STREAM_MAP_VOLUME_IS_FOR_HAL "is-hal-volume" -- 2.7.4 From e4458cd898d6f348101a5f054694484803e30e70 Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Thu, 10 Jun 2021 17:40:50 +0900 Subject: [PATCH 04/16] Fix SVACE defect (MEMORY_LEAK.EX) [Version] 13.0.64 [Issue Type] Svace Change-Id: I1535c4d0f48d2be2e6c4fc54c2b30a6c668dd59e --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/device-manager-dbus.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 7b3abc2..d72bf2c 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.63 +Version: 13.0.64 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/device-manager-dbus.c b/src/device-manager-dbus.c index 2bc5dbd..ccab821 100644 --- a/src/device-manager-dbus.c +++ b/src/device-manager-dbus.c @@ -478,6 +478,7 @@ static int handle_bluez_headset_property_changed(DBusConnection *c, DBusMessage } if (_translate_external_value(DEVICE_TYPE_BT_SCO, status, &detected) < 0) { pa_log_warn("failed to translate bt-sco detected value"); + pa_xfree(name); return -1; } handle_device_status_changed(dm, DEVICE_TYPE_BT_SCO, name, dbus_message_get_path(s), detected); -- 2.7.4 From 1fe59e21c6c8f5aac919b7e08dba449ad4ffb503 Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Mon, 17 May 2021 17:57:49 +0900 Subject: [PATCH 05/16] tizenaudio-sink/source: write or read by calculated period size instead of fixed time. As this code is workaround code for supporting various hw model such as qualcomm board, will be refactored soon by using parameters given at loading time. In addition, use _hal_sysconfdir instead of _sysconfdir macro in spec [Version] 13.0.65 [Issue Type] Bring-up Change-Id: I96fef562cd7af2a67d3bed90b5db9f4def3edad4 --- packaging/pulseaudio-modules-tizen.spec | 7 +++++-- src/module-tizenaudio-sink.c | 16 ++++++++++++++++ src/module-tizenaudio-source.c | 10 +++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index d72bf2c..067da12 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.64 +Version: 13.0.65 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ @@ -40,7 +40,10 @@ PulseAudio module-acm-sink for sending PCM data to ACM core. %setup -q %build -export CFLAGS="%{optflags} -fno-strict-aliasing -D__TIZEN__ -DSYSCONFDIR=\\\"%{_sysconfdir}\\\" " +export CFLAGS="%{optflags} -fno-strict-aliasing -D__TIZEN__ -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\" " +%if "%{tizen_profile_name}" != "tv" + export CFLAGS+=" -DTIZEN_TV"; +%endif export LD_AS_NEEDED=0 %reconfigure --prefix=%{_prefix} \ diff --git a/src/module-tizenaudio-sink.c b/src/module-tizenaudio-sink.c index c8f1d35..62a12d5 100644 --- a/src/module-tizenaudio-sink.c +++ b/src/module-tizenaudio-sink.c @@ -419,11 +419,19 @@ static int process_render(struct userdata *u, pa_usec_t now) { if (u->first) { pa_log_debug("Fill initial buffer"); +#ifdef TIZEN_TV frames_to_write = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec) / frame_size; +#else + frames_to_write = u->frag_size / frame_size; +#endif u->timestamp = u->timestamp_written = now; } else { +#ifdef TIZEN_TV /* Write pcm every 10ms */ frames_to_write = pa_usec_to_bytes((10 * PA_USEC_PER_MSEC), &u->sink->sample_spec) / frame_size; +#else + frames_to_write = (u->frag_size / u->nfrags); +#endif if (frames_to_write > avail) break; } @@ -480,14 +488,22 @@ static void thread_func(void *userdata) { goto fail; if (work_done == 0) { +#ifdef TIZEN_TV pa_rtpoll_set_timer_relative(u->rtpoll, (10 * PA_USEC_PER_MSEC)); +#else + pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); +#endif } else { if (u->first) { pa_log_info("Starting playback."); pa_hal_interface_pcm_start(u->hal_interface, u->pcm_handle); u->first = false; } +#ifdef TIZEN_TV pa_rtpoll_set_timer_relative(u->rtpoll, (10 * PA_USEC_PER_MSEC)); +#else + pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); +#endif } } else { pa_rtpoll_set_timer_disabled(u->rtpoll); diff --git a/src/module-tizenaudio-source.c b/src/module-tizenaudio-source.c index 4ec1330..4beabf5 100644 --- a/src/module-tizenaudio-source.c +++ b/src/module-tizenaudio-source.c @@ -365,9 +365,13 @@ static int process_render(struct userdata *u, pa_usec_t now) { break; } +#ifdef TIZEN_TV frames_to_read = pa_usec_to_bytes(u->block_usec, &u->source->sample_spec) / frame_size; - chunk.length = pa_usec_to_bytes(u->block_usec, &u->source->sample_spec); +#else + frames_to_read = (u->frag_size / u->nfrags); + chunk.length = frames_to_read * frame_size; +#endif chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); if (frames_to_read > (size_t)avail) @@ -431,7 +435,11 @@ static void thread_func(void *userdata) { goto fail; if (work_done == 0) { +#ifdef TIZEN_TV pa_rtpoll_set_timer_relative(u->rtpoll, (10 * PA_USEC_PER_MSEC)); +#else + pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); +#endif } else { pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); } -- 2.7.4 From 78efdc4a4b432f70f7b31cf5fdc2e83e9386f4ab Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Tue, 29 Jun 2021 21:35:46 +0900 Subject: [PATCH 06/16] fixup! tizenaudio-sink/source: write or read by calculated period size instead of fixed time. [Version] 13.0.66 [Issue Type] Bug Change-Id: I59f0fe0901e6f07a9a071cd29f08e681162dc527 --- packaging/pulseaudio-modules-tizen.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 067da12..aeedf99 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.65 +Version: 13.0.66 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ @@ -41,7 +41,7 @@ PulseAudio module-acm-sink for sending PCM data to ACM core. %build export CFLAGS="%{optflags} -fno-strict-aliasing -D__TIZEN__ -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\" " -%if "%{tizen_profile_name}" != "tv" +%if "%{tizen_profile_name}" == "tv" export CFLAGS+=" -DTIZEN_TV"; %endif -- 2.7.4 From b365eb4407a4f95d0959e6218a73929ee59756c7 Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Mon, 31 May 2021 16:23:04 +0900 Subject: [PATCH 07/16] stream-manager-dbus: Add rpi playback route method handler [Version] 13.0.67 [Issue Type] Internal feature Change-Id: Iff4048c39bb7601066f21b58a2176a50377c96b9 --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/stream-manager-dbus-priv.h | 2 ++ src/stream-manager-dbus.c | 37 ++++++++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index aeedf99..2d54e44 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.66 +Version: 13.0.67 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/stream-manager-dbus-priv.h b/src/stream-manager-dbus-priv.h index 940940a..47ea970 100644 --- a/src/stream-manager-dbus-priv.h +++ b/src/stream-manager-dbus-priv.h @@ -57,6 +57,7 @@ #define STREAM_MANAGER_METHOD_NAME_SET_REMOTE_PERMISSION "SetRemotePermission" #define STREAM_MANAGER_METHOD_NAME_DISCOVER_REMOTE_DEVICE "DiscoverRemoteDevice" #define STREAM_MANAGER_METHOD_NAME_PUBLISH_LOCAL_DEVICE "PublishLocalDevice" +#define STREAM_MANAGER_METHOD_NAME_SET_RPI_PLAYBACK_ROUTE "SetRpiPlaybackRoute" /* signal */ #define STREAM_MANAGER_SIGNAL_NAME_VOLUME_CHANGED "VolumeChanged" #define STREAM_MANAGER_SIGNAL_NAME_DUCKING_STATE_CHANGED "DuckingStateChanged" @@ -95,6 +96,7 @@ enum method_handler_index { METHOD_HANDLER_NAME_SET_REMOTE_PERMISSION, METHOD_HANDLER_DISCOVER_REMOTE_DEVICE, METHOD_HANDLER_PUBLISH_LOCAL_DEVICE, + METHOD_HANDLER_SET_RPI_PLAYBACK_ROUTE, METHOD_HANDLER_MAX }; diff --git a/src/stream-manager-dbus.c b/src/stream-manager-dbus.c index e68cda6..f10e221 100644 --- a/src/stream-manager-dbus.c +++ b/src/stream-manager-dbus.c @@ -74,6 +74,7 @@ static void handle_get_ducking_state(DBusConnection *conn, DBusMessage *msg, voi static void handle_set_remote_permission(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_discover_remote_device(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_publish_local_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_rpi_playback_route(DBusConnection *conn, DBusMessage *msg, void *userdata); static void send_volume_changed_signal(DBusConnection *conn, const char *direction, const char *volume_type, const uint32_t volume_level); static pa_dbus_arg_info get_stream_info_args[] = { { "stream_type", "s", "in" }, @@ -188,6 +189,7 @@ static pa_dbus_arg_info set_remote_permission_args[] = { { "type", "s", "in" }, { "allowed", "b", "in" } }; static pa_dbus_arg_info discover_remote_device_args[] = { { "enable", "b", "in" } }; static pa_dbus_arg_info publish_local_device_args[] = { { "enable", "b", "in" } }; +static pa_dbus_arg_info set_rpi_playback_route_args[] = { { "type", "u", "in" } }; static const char* signature_args_for_in[] = { "s", /* METHOD_HANDLER_GET_STREAM_INFO */ @@ -220,7 +222,8 @@ static const char* signature_args_for_in[] = { "u", /* METHOD_HANDLER_GET_DUCKING_STATE */ "sub", /* METHOD_HANDLER_SET_REMOTE_PERMISSION */ "b", /* METHOD_HANDLER_DISCOVER_REMOTE_DEVICE */ - "b" /* METHOD_HANDLER_PUBLISH_LOCAL_DEVICE */ + "b", /* METHOD_HANDLER_PUBLISH_LOCAL_DEVICE */ + "u", /* METHOD_HANDLER_SET_RPI_PLAYBACK_ROUTE */ }; static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { @@ -379,6 +382,11 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .arguments = publish_local_device_args, .n_arguments = sizeof(publish_local_device_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_publish_local_device }, + [METHOD_HANDLER_SET_RPI_PLAYBACK_ROUTE] = { + .method_name = STREAM_MANAGER_METHOD_NAME_SET_RPI_PLAYBACK_ROUTE, + .arguments = set_rpi_playback_route_args, + .n_arguments = sizeof(set_rpi_playback_route_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_set_rpi_playback_route } }; static int get_stream_type(const char *direction, stream_type_t *stream_type) { @@ -2393,6 +2401,33 @@ error: "org.tizen.multimedia.audio.Internal"); } +static void handle_set_rpi_playback_route(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_stream_manager *m = (pa_stream_manager *)userdata; + dbus_uint32_t type; + hal_route_option route_option; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_UINT32, &type, + DBUS_TYPE_INVALID)); + + pa_log_info("setting rpi playback route(%u)", type); + + memset(&route_option, 0, sizeof(hal_route_option)); + route_option.role = STREAM_ROLE_MEDIA; + route_option.name = "rpi playback route"; + route_option.value = (int32_t)type; + + if (pa_hal_interface_update_route_option(m->hal, &route_option) == 0) + pa_dbus_send_empty_reply(conn, msg); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "%s", + "org.tizen.multimedia.audio.Internal"); +} + static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata) { int idx = 0; pa_stream_manager *m = (pa_stream_manager *)userdata; -- 2.7.4 From 56e3c9a339710449345ad2dd72b9070c1f48336f Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Mon, 2 Aug 2021 14:47:18 +0900 Subject: [PATCH 08/16] Add module-tizenaudio-sink2/source2 module-tizenaudio-sink2/source2 that are copies of tizenaudio-sink/source without AEC parts were added for a experimental functionality such as mmap, echo cancel, and non-interleave. Change-Id: I09588a5d90737da6e2ee9c8b3fd574db012e71f5 Signed-off-by: Jaechul Lee --- Makefile.am | 12 + packaging/pulseaudio-modules-tizen.spec | 2 + src/module-tizenaudio-sink2.c | 704 ++++++++++++++++++++++++++++++++ src/module-tizenaudio-source2.c | 636 +++++++++++++++++++++++++++++ 4 files changed, 1354 insertions(+) create mode 100644 src/module-tizenaudio-sink2.c create mode 100644 src/module-tizenaudio-source2.c diff --git a/Makefile.am b/Makefile.am index 495578e..6869eaa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,6 +39,8 @@ pulsemodlibexec_LTLIBRARIES = \ libcommunicator.la \ module-tizenaudio-sink.la \ module-tizenaudio-source.la \ + module-tizenaudio-sink2.la \ + module-tizenaudio-source2.la \ module-tizenaudio-policy.la \ module-tizenaudio-discover.la \ module-tizenaudio-publish.la \ @@ -76,6 +78,16 @@ module_tizenaudio_source_la_LDFLAGS = $(MODULE_LDFLAGS) module_tizenaudio_source_la_LIBADD = $(MODULE_LIBADD) libhal-interface.la module_tizenaudio_source_la_CFLAGS = $(MODULE_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_source +module_tizenaudio_sink2_la_SOURCES = src/module-tizenaudio-sink2.c +module_tizenaudio_sink2_la_LDFLAGS = $(MODULE_LDFLAGS) +module_tizenaudio_sink2_la_LIBADD = $(MODULE_LIBADD) libhal-interface.la +module_tizenaudio_sink2_la_CFLAGS = $(MODULE_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_sink2 + +module_tizenaudio_source2_la_SOURCES = src/module-tizenaudio-source2.c +module_tizenaudio_source2_la_LDFLAGS = $(MODULE_LDFLAGS) +module_tizenaudio_source2_la_LIBADD = $(MODULE_LIBADD) libhal-interface.la +module_tizenaudio_source2_la_CFLAGS = $(MODULE_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_source2 + module_sound_player_la_SOURCES = src/module-sound-player.c module_sound_player_la_LDFLAGS = $(MODULE_LDFLAGS) module_sound_player_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 2d54e44..df5e515 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -80,6 +80,8 @@ install -m 0644 %SOURCE1 %{buildroot}%{_tmpfilesdir}/pulseaudio.conf %{_libdir}/pulse-13.0/modules/module-tizenaudio-policy.so %{_libdir}/pulse-13.0/modules/module-tizenaudio-sink.so %{_libdir}/pulse-13.0/modules/module-tizenaudio-source.so +%{_libdir}/pulse-13.0/modules/module-tizenaudio-sink2.so +%{_libdir}/pulse-13.0/modules/module-tizenaudio-source2.so %{_libdir}/pulse-13.0/modules/module-tizenaudio-discover.so %{_libdir}/pulse-13.0/modules/module-tizenaudio-publish.so %{_libdir}/pulse-13.0/modules/libhal-interface.so diff --git a/src/module-tizenaudio-sink2.c b/src/module-tizenaudio-sink2.c new file mode 100644 index 0000000..f8a6059 --- /dev/null +++ b/src/module-tizenaudio-sink2.c @@ -0,0 +1,704 @@ +/*** + This file is part of PulseAudio. + + Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hal-interface.h" + +PA_MODULE_AUTHOR("Tizen"); +PA_MODULE_DESCRIPTION("Tizen Audio Sink"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(false); +PA_MODULE_USAGE( + "sink_name= " + "sink_properties= " + "device= " + "format= " + "rate= " + "channels= " + "channel_map=" + "fragments= " + "fragment_size= " + "block_msec= " + "max_request_msec= "); + +#define DEFAULT_SINK_NAME "tizenaudio-sink" + +/* block_usec should be less than amount of fragment_size * fragments */ +#define DEFAULT_BLOCK_USEC (PA_USEC_PER_SEC * 0.05) + +/* Sink device consumes that amount of maximum buffer at every request */ +#define DEFAULT_MAX_REQUEST_USEC (PA_USEC_PER_SEC * 0.032) + +#define DEVICE_NAME_MAX 30 + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink; + + pa_thread *thread; + pa_thread_mq thread_mq; + pa_rtpoll *rtpoll; + + void *pcm_handle; + uint32_t nfrags; + uint32_t frag_size; + + pa_usec_t block_usec; + pa_usec_t max_request_usec; + pa_usec_t timestamp; + pa_usec_t timestamp_written; + + char* card; + char* device; + bool first; + + pa_rtpoll_item *rtpoll_item; + + uint64_t write_count; + pa_hal_interface *hal_interface; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "sink_properties", + "device", + "format", + "rate", + "channels", + "channel_map", + "fragments", + "fragment_size", + "block_msec", + "max_request_msec", + NULL +}; + +static int build_pollfd(struct userdata *u) { + int32_t ret; + struct pollfd *pollfd; + int fd = -1; + + pa_assert(u); + pa_assert(u->pcm_handle); + pa_assert(u->rtpoll); + + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + + u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + ret = pa_hal_interface_pcm_get_fd(u->hal_interface, u->pcm_handle, &fd); + if (ret < 0 || fd < 0) { + pa_log_error("Failed to get fd(%d) of PCM device %d", fd, ret); + return -1; + } + pollfd->fd = fd; + pollfd->events = /* POLLOUT | */ POLLERR | POLLNVAL; + + return 0; +} + +/* Called from IO context */ +static int suspend(struct userdata *u) { + int32_t ret; + pa_assert(u); + pa_assert(u->pcm_handle); + + ret = pa_hal_interface_pcm_close(u->hal_interface, u->pcm_handle); + if (ret) { + pa_log_error("Error closing PCM device %x", ret); + } + u->pcm_handle = NULL; + + if (u->rtpoll_item) { + pa_rtpoll_item_free(u->rtpoll_item); + u->rtpoll_item = NULL; + } + + pa_log_info("Device suspended..."); + + return 0; +} + +/* Called from IO context */ +static int unsuspend(struct userdata *u) { + pa_sample_spec sample_spec; + int32_t ret; + size_t frame_size; + + pa_assert(u); + pa_assert(!u->pcm_handle); + + pa_log_info("Trying resume..."); + + sample_spec = u->sink->sample_spec; + frame_size = pa_frame_size(&sample_spec); + if (frame_size == 0) { + pa_log_error("Unexpected frame size zero!"); + goto fail; + } + + ret = pa_hal_interface_pcm_open(u->hal_interface, + u->card, + u->device, + DIRECTION_OUT, + &sample_spec, + u->frag_size / frame_size, + u->nfrags, + (void **)&u->pcm_handle); + if (ret) { + pa_log_error("Error opening PCM device %x", ret); + goto fail; + } + + if (build_pollfd(u) < 0) + goto fail; + + u->write_count = 0; + u->first = true; + + pa_log_info("Resumed successfully..."); + + return 0; + +fail: + if (u->pcm_handle) { + pa_hal_interface_pcm_close(u->hal_interface, u->pcm_handle); + u->pcm_handle = NULL; + } + return -PA_ERR_IO; +} + +/* Called from the IO thread. */ +static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) { + struct userdata *u; + int r; + + pa_assert(s); + pa_assert_se(u = s->userdata); + + /* It may be that only the suspend cause is changing, in which case there's + * nothing to do. */ + if (new_state == s->thread_info.state) + return 0; + + switch (new_state) { + case PA_SINK_SUSPENDED: { + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); + if ((r = suspend(u)) < 0) + return r; + break; + } + + case PA_SINK_IDLE: + case PA_SINK_RUNNING: { + if (s->thread_info.state == PA_SINK_INIT) { + if (build_pollfd(u) < 0) + return -PA_ERR_IO; + } + + if (s->thread_info.state == PA_SINK_SUSPENDED) { + if ((r = unsuspend(u)) < 0) + return r; + } + break; + } + + case PA_SINK_UNLINKED: + case PA_SINK_INIT: + case PA_SINK_INVALID_STATE: + break; + } + + return 0; +} + +static int sink_process_msg( + pa_msgobject *o, + int code, + void *data, + int64_t offset, + pa_memchunk *chunk) { + + struct userdata *u = PA_SINK(o)->userdata; + + switch (code) { + case PA_SINK_MESSAGE_GET_LATENCY: { + pa_usec_t now = pa_rtclock_now(); + pa_usec_t latency = 0ULL; + if (u->timestamp > now) { + if ((u->timestamp - now) > (now - u->timestamp_written)) { + latency = (u->timestamp - now) + (u->timestamp_written - now); + } + } + *((pa_usec_t*)data) = latency; + return 0; + } + } + + return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static void sink_update_requested_latency_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se((u = s->userdata)); + + u->block_usec = pa_sink_get_requested_latency_within_thread(s); + + if (u->block_usec == (pa_usec_t)-1) + u->block_usec = s->thread_info.max_latency; + + pa_sink_set_max_rewind_within_thread(s, pa_usec_to_bytes(u->block_usec, &s->sample_spec)); + pa_sink_set_max_request_within_thread(s, pa_usec_to_bytes(u->max_request_usec, &s->sample_spec)); +} + +static void process_rewind(struct userdata *u, pa_usec_t now) { +#if 1 + /* Rewind not supported */ + pa_sink_process_rewind(u->sink, 0); +#else + size_t rewind_nbytes, in_buffer; + pa_usec_t delay; + + pa_assert(u); + + rewind_nbytes = u->sink->thread_info.rewind_nbytes; + + if (!PA_SINK_IS_OPENED(u->sink->thread_info.state) || rewind_nbytes <= 0) + goto do_nothing; + + pa_log_debug("Requested to rewind %lu bytes.", (unsigned long)rewind_nbytes); + + if (u->timestamp <= now) + goto do_nothing; + + delay = u->timestamp - now; + in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec); + + if (in_buffer <= 0) + goto do_nothing; + + if (rewind_nbytes > in_buffer) + rewind_nbytes = in_buffer; + + pa_sink_process_rewind(u->sink, rewind_nbytes); + u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec); + + pa_log_debug("Rewound %lu bytes.", (unsigned long)rewind_nbytes); + return; + +do_nothing: + pa_sink_process_rewind(u->sink, 0); +#endif +} + +static int process_render(struct userdata *u, pa_usec_t now) { + int work_done = 0; + size_t ate = 0; + void *p; + size_t frames_to_write, frame_size; + uint32_t avail = 0; + + pa_assert(u); + + /* Fill the buffer up the latency size */ + while (u->timestamp < now + u->block_usec) { + pa_memchunk chunk; + frame_size = pa_frame_size(&u->sink->sample_spec); + if (frame_size == 0) { + pa_log_error("Unexpected frame size zero!"); + break; + } + + pa_hal_interface_pcm_available(u->hal_interface, u->pcm_handle, &avail); + if ((avail == 0) && !(u->first)) { + break; + } + + if (u->first) { + pa_log_debug("Fill initial buffer"); + + frames_to_write = u->frag_size / frame_size; + u->timestamp = u->timestamp_written = now; + } else { + frames_to_write = (u->frag_size / u->nfrags); + + if (frames_to_write > avail) + break; + } + + pa_sink_render_full(u->sink, frames_to_write * frame_size, &chunk); + p = pa_memblock_acquire(chunk.memblock); + + pa_hal_interface_pcm_write(u->hal_interface, u->pcm_handle, (const char*)p + chunk.index, (uint32_t)frames_to_write); + + pa_memblock_release(chunk.memblock); + pa_memblock_unref(chunk.memblock); + u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); + u->timestamp_written = pa_rtclock_now(); + + work_done = 1; + + ate += chunk.length; + if (ate >= u->sink->thread_info.max_request) { + break; + } + } + + return work_done; +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + unsigned short revents = 0; + + pa_assert(u); + + pa_log_debug("Thread starting up"); + + if (u->core->realtime_scheduling) + pa_thread_make_realtime(u->core->realtime_priority); + + pa_thread_mq_install(&u->thread_mq); + + for (;;) { + pa_usec_t now = 0; + int ret; + + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) + now = pa_rtclock_now(); + + if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) + process_rewind(u, now); + + /* Render some data and drop it immediately */ + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { + int work_done = process_render(u, now); + + if (work_done < 0) + goto fail; + + if (work_done == 0) { + pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); + } else { + if (u->first) { + pa_log_info("Starting playback."); + pa_hal_interface_pcm_start(u->hal_interface, u->pcm_handle); + u->first = false; + } + pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); + } + } else { + pa_rtpoll_set_timer_disabled(u->rtpoll); + } + + /* Hmm, nothing to do. Let's sleep */ + if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) + goto fail; + + if (ret == 0) + goto finish; + + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { + struct pollfd *pollfd; + if (u->rtpoll_item) { + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + revents = pollfd->revents; + if (revents & ~POLLOUT) { + pa_log_debug("Poll error 0x%x occured, try recover.", revents); + pa_hal_interface_pcm_recover(u->hal_interface, u->pcm_handle, revents); + u->first = true; + revents = 0; + } else { + //pa_log_debug("Poll wakeup.", revents); + } + } + } + } + +fail: + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread shutting down"); +} + +static int parse_to_get_card(const char *modarg_device, char *card) { + const char *name_p; + char *card_p; + + if (!strchr(modarg_device, ',')) { + pa_log_error("Failed to parse device argument : no comma"); + return -1; + } + + name_p = modarg_device; + card_p = card; + while (*name_p != ',') + *(card_p++) = *(name_p++); + *card_p = '\0'; + + return 0; +} + +static int parse_to_get_device(const char *modarg_device, char *device) { + const char *comma_p; + char *device_p; + + if (!(comma_p = strchr(modarg_device, ','))) { + pa_log_error("Failed to parse device argument : no comma"); + return -1; + } + + comma_p++; + device_p = device; + while (*comma_p != '\0') + *(device_p++) = *(comma_p++); + *device_p = '\0'; + + return 0; +} + +int pa__init(pa_module*m) { + struct userdata *u = NULL; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma = NULL; + pa_sink_new_data data; + uint32_t alternate_sample_rate; + uint32_t block_msec; + uint32_t max_request_msec; + const char *modarg_device; + char card[DEVICE_NAME_MAX]; + char device[DEVICE_NAME_MAX]; + size_t frame_size, buffer_size, period_frames, buffer_frames; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log_error("Failed to parse module arguments."); + goto fail; + } + + ss = m->core->default_sample_spec; + map = m->core->default_channel_map; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log_error("Invalid sample format specification or channel map"); + goto fail; + } + + alternate_sample_rate = m->core->alternate_sample_rate; + if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) { + pa_log_error("Failed to parse alternate sample rate"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->first = true; + u->hal_interface = pa_hal_interface_get(u->core); + u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + + if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) { + pa_log_error("device is invalid"); + goto fail; + } + + if (parse_to_get_card(modarg_device, card) || parse_to_get_device(modarg_device, device)) { + pa_log_error("failed to parse device module argument, %s", modarg_device); + goto fail; + } + + u->card = pa_xstrdup(card); + u->device = pa_xstrdup(device); + + u->frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss); + u->nfrags = m->core->default_n_fragments; + if (pa_modargs_get_value_u32(ma, "fragment_size", &u->frag_size) < 0 || + pa_modargs_get_value_u32(ma, "fragments", &u->nfrags) < 0) { + pa_log_error("fragment_size or fragments are invalid."); + goto fail; + } + pa_log_info("card(%s) device(%s) fragment_size(%u), fragments(%u)", u->card, u->device, u->frag_size, u->nfrags); + + /* Optional */ + block_msec = DEFAULT_BLOCK_USEC / PA_USEC_PER_MSEC; + pa_modargs_get_value_u32(ma, "block_msec", &block_msec); + u->block_usec = (pa_usec_t) block_msec * PA_USEC_PER_MSEC; + + /* Optional */ + max_request_msec = DEFAULT_MAX_REQUEST_USEC / PA_USEC_PER_MSEC; + pa_modargs_get_value_u32(ma, "max_request_msec", &max_request_msec); + u->max_request_usec = (pa_usec_t) max_request_msec * PA_USEC_PER_MSEC; + + u->timestamp = 0ULL; + u->timestamp_written = 0ULL; + + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, _("Tizen audio sink")); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); + pa_proplist_sets(data.proplist, "tizen.card", u->card); + pa_proplist_sets(data.proplist, "tizen.device", u->device); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen"); + + frame_size = pa_frame_size(&ss); + buffer_size = u->frag_size * u->nfrags; + buffer_frames = buffer_size / frame_size; + period_frames = u->frag_size / frame_size; + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size); + + if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log_error("Invalid properties."); + pa_sink_new_data_done(&data); + goto fail; + } + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); + pa_sink_new_data_done(&data); + + if (!u->sink) { + pa_log_error("Failed to create sink object."); + goto fail; + } + + u->sink->parent.process_msg = sink_process_msg; + u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; + u->sink->update_requested_latency = sink_update_requested_latency_cb; + u->sink->userdata = u; + + if (pa_hal_interface_pcm_open(u->hal_interface, + u->card, + u->device, + DIRECTION_OUT, + &u->sink->sample_spec, + u->frag_size / pa_frame_size(&u->sink->sample_spec), + u->nfrags, + (void **)&u->pcm_handle)) { + pa_log_error("Error opening PCM device"); + goto fail; + } + + pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); + pa_sink_set_rtpoll(u->sink, u->rtpoll); + + pa_sink_set_max_rewind(u->sink, 0); + pa_sink_set_max_request(u->sink, pa_usec_to_bytes(u->max_request_usec, &u->sink->sample_spec)); + + if (!(u->thread = pa_thread_new("tizenaudio-sink", thread_func, u))) { + pa_log_error("Failed to create thread."); + goto fail; + } + pa_sink_set_fixed_latency(u->sink, u->block_usec); + pa_sink_put(u->sink); + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se((u = m->userdata)); + + return pa_sink_linked_by(u->sink); +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->sink) + pa_sink_unref(u->sink); + + pa_xfree(u->card); + pa_xfree(u->device); + + if (u->rtpoll) + pa_rtpoll_free(u->rtpoll); + + if (u->pcm_handle) { + pa_hal_interface_pcm_stop(u->hal_interface, u->pcm_handle); + pa_hal_interface_pcm_close(u->hal_interface, u->pcm_handle); + } + + pa_xfree(u); +} diff --git a/src/module-tizenaudio-source2.c b/src/module-tizenaudio-source2.c new file mode 100644 index 0000000..790e2b2 --- /dev/null +++ b/src/module-tizenaudio-source2.c @@ -0,0 +1,636 @@ +/*** + This file is part of PulseAudio. + + Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hal-interface.h" + +PA_MODULE_AUTHOR("Tizen"); +PA_MODULE_DESCRIPTION("Tizen Audio Source"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(false); +PA_MODULE_USAGE( + "source_name= " + "source_properties= " + "device= " + "format= " + "rate= " + "channels= " + "channel_map=" + "fragments= " + "fragment_size= "); + + +#define DEFAULT_SOURCE_NAME "tizenaudio-source" + +/* BLOCK_USEC should be less than amount of fragment_size * fragments */ +#define BLOCK_USEC (PA_USEC_PER_SEC * 0.032) + +#define DEVICE_NAME_MAX 30 + +struct userdata { + pa_core *core; + pa_module *module; + pa_source *source; + + pa_thread *thread; + pa_thread_mq thread_mq; + pa_rtpoll *rtpoll; + + void *pcm_handle; + uint32_t nfrags; + uint32_t frag_size; + + pa_usec_t block_usec; + pa_usec_t timestamp; + + char* card; + char* device; + bool first; + + pa_rtpoll_item *rtpoll_item; + + uint64_t read_count; + pa_usec_t latency_time; + pa_hal_interface *hal_interface; +}; + +static const char* const valid_modargs[] = { + "source_name", + "source_properties", + "device", + "format", + "rate", + "channels", + "channel_map", + "fragments", + "fragment_size", + NULL +}; + +static int build_pollfd(struct userdata *u) { + int32_t ret; + struct pollfd *pollfd; + int fd = -1; + + pa_assert(u); + pa_assert(u->pcm_handle); + pa_assert(u->rtpoll); + + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + + u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + ret = pa_hal_interface_pcm_get_fd(u->hal_interface, u->pcm_handle, &fd); + if (ret < 0 || fd < 0) { + pa_log_error("Failed to get fd(%d) of PCM device %d", fd, ret); + return -1; + } + pollfd->fd = fd; + pollfd->events = /* POLLIN | */ POLLERR | POLLNVAL; + + return 0; +} + +/* Called from IO context */ +static int suspend(struct userdata *u) { + int32_t ret; + pa_assert(u); + pa_assert(u->pcm_handle); + + ret = pa_hal_interface_pcm_close(u->hal_interface, u->pcm_handle); + if (ret) { + pa_log_error("Error closing PCM device %x", ret); + } + u->pcm_handle = NULL; + + if (u->rtpoll_item) { + pa_rtpoll_item_free(u->rtpoll_item); + u->rtpoll_item = NULL; + } + + pa_log_info("Device suspended..."); + + return 0; +} + +/* Called from IO context */ +static int unsuspend(struct userdata *u) { + pa_sample_spec sample_spec; + int32_t ret; + size_t frame_size; + + pa_assert(u); + pa_assert(!u->pcm_handle); + + pa_log_info("Trying resume..."); + + sample_spec = u->source->sample_spec; + frame_size = pa_frame_size(&sample_spec); + if (frame_size == 0) { + pa_log_error("Unexpected frame size zero!"); + goto fail; + } + + ret = pa_hal_interface_pcm_open(u->hal_interface, + u->card, + u->device, + DIRECTION_IN, + &sample_spec, + u->frag_size / frame_size, + u->nfrags, + (void **)&u->pcm_handle); + if (ret) { + pa_log_error("Error opening PCM device %x", ret); + goto fail; + } + + if (build_pollfd(u) < 0) + goto fail; + + u->read_count = 0; + u->first = true; + + pa_log_info("Resumed successfully..."); + + return 0; + +fail: + if (u->pcm_handle) { + pa_hal_interface_pcm_close(u->hal_interface, u->pcm_handle); + u->pcm_handle = NULL; + } + return -PA_ERR_IO; +} + +/* Called from the IO thread. */ +static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) { + struct userdata *u; + int r; + + pa_assert(s); + pa_assert_se(u = s->userdata); + + /* It may be that only the suspend cause is changing, in which case there's + * nothing more to do. */ + if (new_state == s->thread_info.state) + return 0; + + switch (new_state) { + case PA_SOURCE_SUSPENDED: { + pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); + if ((r = suspend(u)) < 0) + return r; + break; + } + + case PA_SOURCE_IDLE: + case PA_SOURCE_RUNNING: { + if (s->thread_info.state == PA_SOURCE_INIT) { + if (build_pollfd(u) < 0) + return -PA_ERR_IO; + } + + if (s->thread_info.state == PA_SOURCE_SUSPENDED) { + if ((r = unsuspend(u)) < 0) + return r; + } + break; + } + + case PA_SOURCE_UNLINKED: + case PA_SOURCE_INIT: + case PA_SOURCE_INVALID_STATE: + break; + } + + return 0; +} + +static int source_process_msg( + pa_msgobject *o, + int code, + void *data, + int64_t offset, + pa_memchunk *chunk) { + + struct userdata *u = PA_SOURCE(o)->userdata; + + switch (code) { + case PA_SOURCE_MESSAGE_GET_LATENCY: { + pa_usec_t now = pa_rtclock_now(); + *((pa_usec_t*)data) = u->timestamp > now ? 0ULL : now - u->timestamp; + return 0; + } + } + + return pa_source_process_msg(o, code, data, offset, chunk); +} + +static void source_update_requested_latency_cb(pa_source *s) { + struct userdata *u; + size_t nbytes; + + pa_source_assert_ref(s); + pa_assert_se((u = s->userdata)); + + u->block_usec = pa_source_get_requested_latency_within_thread(s); + + if (u->block_usec == (pa_usec_t)-1) + u->block_usec = s->thread_info.max_latency; + + nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec); + pa_source_set_max_rewind_within_thread(s, nbytes); +} + +static int process_render(struct userdata *u, pa_usec_t now) { + int work_done = 0; + size_t ate = 0; + void *p; + size_t frames_to_read, frame_size; + uint32_t avail = 0; + + pa_assert(u); + + /* Fill the buffer up the latency size */ + while (u->timestamp < now + u->block_usec) { + pa_memchunk chunk; + frame_size = pa_frame_size(&u->source->sample_spec); + if (frame_size == 0) { + pa_log_error("Unexpected frame size zero!"); + break; + } + + pa_hal_interface_pcm_available(u->hal_interface, u->pcm_handle, &avail); + if (avail == 0) { + break; + } + + frames_to_read = (u->frag_size / u->nfrags); + chunk.length = frames_to_read * frame_size; + chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); + + if (frames_to_read > (size_t)avail) + frames_to_read = (size_t)avail; + + p = pa_memblock_acquire(chunk.memblock); + pa_hal_interface_pcm_read(u->hal_interface, u->pcm_handle, p, (uint32_t)frames_to_read); + pa_memblock_release(chunk.memblock); + + chunk.index = 0; + chunk.length = (size_t)frames_to_read * frame_size; + pa_source_post(u->source, &chunk); + pa_memblock_unref(chunk.memblock); + + u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec); + + work_done = 1; + + ate += chunk.length; + if (ate >= pa_usec_to_bytes(u->block_usec, &u->source->sample_spec)) { + break; + } + } + + return work_done; +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + unsigned short revents = 0; + + pa_assert(u); + pa_log_debug("Thread starting up"); + + if (u->core->realtime_scheduling) + pa_thread_make_realtime(u->core->realtime_priority); + + pa_thread_mq_install(&u->thread_mq); + + for (;;) { + pa_usec_t now = 0; + int ret; + + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) + now = pa_rtclock_now(); + + /* Render some data and drop it immediately */ + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { + int work_done; + + if (u->first) { + pa_log_info("Starting capture."); + pa_hal_interface_pcm_start(u->hal_interface, u->pcm_handle); + u->first = false; + u->timestamp = now; + } + + work_done = process_render(u, now); + + if (work_done < 0) + goto fail; + + if (work_done == 0) { + pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); + } else { + pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); + } + } else { + pa_rtpoll_set_timer_disabled(u->rtpoll); + } + + /* Hmm, nothing to do. Let's sleep */ + if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) + goto fail; + + if (ret == 0) + goto finish; + + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { + struct pollfd *pollfd; + if (u->rtpoll_item) { + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + revents = pollfd->revents; + if (revents & ~POLLIN) { + pa_log_debug("Poll error 0x%x occured, try recover.", revents); + pa_hal_interface_pcm_recover(u->hal_interface, u->pcm_handle, revents); + u->first = true; + revents = 0; + } else { + //pa_log_debug("Poll wakeup.", revents); + } + } + } + } + +fail: + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread shutting down"); +} + +static int parse_to_get_card(const char *modarg_device, char *card) { + const char *name_p; + char *card_p; + + if (!strchr(modarg_device, ',')) { + pa_log_error("Failed to parse device argument : no comma"); + return -1; + } + + name_p = modarg_device; + card_p = card; + while (*name_p != ',') + *(card_p++) = *(name_p++); + *card_p = '\0'; + + return 0; +} + +static int parse_to_get_device(const char *modarg_device, char *device) { + const char *comma_p; + char *device_p; + + if (!(comma_p = strchr(modarg_device, ','))) { + pa_log_error("Failed to parse device argument : no comma"); + return -1; + } + + comma_p++; + device_p = device; + while (*comma_p != '\0') + *(device_p++) = *(comma_p++); + *device_p = '\0'; + + return 0; +} + +int pa__init(pa_module*m) { + struct userdata *u = NULL; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma = NULL; + pa_source_new_data data; + uint32_t alternate_sample_rate; + const char *modarg_device; + char card[DEVICE_NAME_MAX]; + char device[DEVICE_NAME_MAX]; + size_t frame_size, buffer_size, period_frames, buffer_frames; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log_error("Failed to parse module arguments."); + goto fail; + } + + ss = m->core->default_sample_spec; + map = m->core->default_channel_map; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log_error("Invalid sample format specification or channel map"); + goto fail; + } + + alternate_sample_rate = m->core->alternate_sample_rate; + if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) { + pa_log_error("Failed to parse alternate sample rate"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->first = true; + u->hal_interface = pa_hal_interface_get(u->core); + u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + + if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) { + pa_log_error("device is invalid"); + goto fail; + } + + if (parse_to_get_card(modarg_device, card) || parse_to_get_device(modarg_device, device)) { + pa_log_error("failed to parse device module argument, %s", modarg_device); + goto fail; + } + + u->card = pa_xstrdup(card); + u->device = pa_xstrdup(device); + + u->frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss); + u->nfrags = m->core->default_n_fragments; + if (pa_modargs_get_value_u32(ma, "fragment_size", &u->frag_size) < 0 || + pa_modargs_get_value_u32(ma, "fragments", &u->nfrags) < 0) { + pa_log_error("fragment_size or fragments are invalid."); + goto fail; + } + pa_log_info("card(%s) device(%s) fragment_size(%u), fragments(%u)", u->card, u->device, u->frag_size, u->nfrags); + + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, _("Tizen audio source")); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); + pa_proplist_sets(data.proplist, "tizen.card", u->card); + pa_proplist_sets(data.proplist, "tizen.device", u->device); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen"); + + frame_size = pa_frame_size(&ss); + buffer_size = u->frag_size * u->nfrags; + buffer_frames = buffer_size / frame_size; + period_frames = u->frag_size / frame_size; + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size); + + if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log_error("Invalid properties."); + pa_source_new_data_done(&data); + goto fail; + } + + u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY); + pa_source_new_data_done(&data); + + if (!u->source) { + pa_log_error("Failed to create source object."); + goto fail; + } + + u->source->parent.process_msg = source_process_msg; + u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; + u->source->update_requested_latency = source_update_requested_latency_cb; + u->source->userdata = u; + + if (pa_hal_interface_pcm_open(u->hal_interface, + u->card, + u->device, + DIRECTION_IN, + &u->source->sample_spec, + u->frag_size / pa_frame_size(&u->source->sample_spec), + u->nfrags, + (void **)&u->pcm_handle)) { + pa_log_error("Error opening PCM device"); + goto fail; + } + + pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); + pa_source_set_rtpoll(u->source, u->rtpoll); + + u->block_usec = BLOCK_USEC; + u->latency_time = u->block_usec; + u->timestamp = 0ULL; + + pa_source_set_max_rewind(u->source, 0); + + if (!(u->thread = pa_thread_new("tizenaudio-source", thread_func, u))) { + pa_log_error("Failed to create thread."); + goto fail; + } + pa_source_set_fixed_latency(u->source, u->block_usec); + pa_source_put(u->source); + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se((u = m->userdata)); + + return pa_source_linked_by(u->source); +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->source) + pa_source_unlink(u->source); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->source) + pa_source_unref(u->source); + + pa_xfree(u->card); + pa_xfree(u->device); + + if (u->rtpoll) + pa_rtpoll_free(u->rtpoll); + + if (u->pcm_handle) { + pa_hal_interface_pcm_stop(u->hal_interface, u->pcm_handle); + pa_hal_interface_pcm_close(u->hal_interface, u->pcm_handle); + } + + pa_xfree(u); +} -- 2.7.4 From da998d9560653f48e71f04c2b0adde8061d4f7c5 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Fri, 30 Jul 2021 13:45:30 +0900 Subject: [PATCH 09/16] device-manager: remove checking module arguments The comparison of the mod args would be failed if unknown args in device-manager are added like 'echo=1' and it doesn't need to check module args validation because each module already checks it. All arguments would be parsed in case of passing NULL. [Version] 13.0.68 [Issue Type] Improvement Change-Id: I2be900ef82ecdedc5c181bdc919cd439d0a4b67d Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/device-manager.c | 36 ++------------------------------- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index df5e515..3986972 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.67 +Version: 13.0.68 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/device-manager.c b/src/device-manager.c index ef81274..a1a717e 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -91,38 +91,6 @@ #define DEVICE_CLASS_SOUND "sound" #define DEVICE_CLASS_MONITOR "monitor" -static const char* const valid_alsa_device_modargs[] = { - "name", - "sink_name", - "sink_properties", - "source_name", - "source_properties", - "namereg_fail", - "device", - "device_id", - "format", - "rate", - "alternate_rate", - "channels", - "channel_map", - "fragments", - "fragment_size", - "mmap", - "tsched", - "tsched_buffer_size", - "tsched_buffer_watermark", - "ignore_dB", - "control", - "rewind_safeguard", - "deferred_volume", - "deferred_volume_safety_margin", - "deferred_volume_extra_delay", - "fixed_latency_range", - "need_audio_pm", - "start_threshold", - NULL -}; - typedef enum dm_device_class_type { DM_DEVICE_CLASS_NONE, DM_DEVICE_CLASS_ALSA, @@ -864,8 +832,8 @@ static bool device_params_is_equal(const char *params1, const char *params2) { if (!params1 || !params2) return false; - modargs1 = pa_modargs_new(params1, valid_alsa_device_modargs); - modargs2 = pa_modargs_new(params2, valid_alsa_device_modargs); + modargs1 = pa_modargs_new(params1, NULL); + modargs2 = pa_modargs_new(params2, NULL); if (!modargs1 || !modargs2) { equal = false; -- 2.7.4 From cafa82a93f92ffd6e9505db143a03ea781822ccb Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Wed, 4 Aug 2021 10:01:56 +0900 Subject: [PATCH 10/16] tizenaudio-sink2: Use hardware interrupt for playback * Remove unused arguments (block_msec, max_request_msec) * Use fixed latency * Use poll event [Version] 13.0.69 [Issue Type] Improvement Change-Id: I404474e0da9837df6fc202cc793af0b5d2c6d799 Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tizenaudio-sink2.c | 171 +++++++++----------------------- 2 files changed, 46 insertions(+), 127 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 3986972..6f220f2 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.68 +Version: 13.0.69 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-sink2.c b/src/module-tizenaudio-sink2.c index f8a6059..e4e61e9 100644 --- a/src/module-tizenaudio-sink2.c +++ b/src/module-tizenaudio-sink2.c @@ -48,7 +48,7 @@ #include "hal-interface.h" PA_MODULE_AUTHOR("Tizen"); -PA_MODULE_DESCRIPTION("Tizen Audio Sink"); +PA_MODULE_DESCRIPTION("Tizen Audio Sink2"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(false); PA_MODULE_USAGE( @@ -60,17 +60,9 @@ PA_MODULE_USAGE( "channels= " "channel_map=" "fragments= " - "fragment_size= " - "block_msec= " - "max_request_msec= "); + "fragment_size= "); -#define DEFAULT_SINK_NAME "tizenaudio-sink" - -/* block_usec should be less than amount of fragment_size * fragments */ -#define DEFAULT_BLOCK_USEC (PA_USEC_PER_SEC * 0.05) - -/* Sink device consumes that amount of maximum buffer at every request */ -#define DEFAULT_MAX_REQUEST_USEC (PA_USEC_PER_SEC * 0.032) +#define DEFAULT_SINK_NAME "tizenaudio-sink2" #define DEVICE_NAME_MAX 30 @@ -82,16 +74,12 @@ struct userdata { pa_thread *thread; pa_thread_mq thread_mq; pa_rtpoll *rtpoll; + pa_usec_t timestamp; void *pcm_handle; uint32_t nfrags; uint32_t frag_size; - pa_usec_t block_usec; - pa_usec_t max_request_usec; - pa_usec_t timestamp; - pa_usec_t timestamp_written; - char* card; char* device; bool first; @@ -112,8 +100,6 @@ static const char* const valid_modargs[] = { "channel_map", "fragments", "fragment_size", - "block_msec", - "max_request_msec", NULL }; @@ -137,7 +123,7 @@ static int build_pollfd(struct userdata *u) { return -1; } pollfd->fd = fd; - pollfd->events = /* POLLOUT | */ POLLERR | POLLNVAL; + pollfd->events = POLLOUT | POLLERR | POLLNVAL; return 0; } @@ -200,6 +186,7 @@ static int unsuspend(struct userdata *u) { u->write_count = 0; u->first = true; + u->timestamp = pa_rtclock_now(); pa_log_info("Resumed successfully..."); @@ -268,14 +255,13 @@ static int sink_process_msg( switch (code) { case PA_SINK_MESSAGE_GET_LATENCY: { - pa_usec_t now = pa_rtclock_now(); - pa_usec_t latency = 0ULL; - if (u->timestamp > now) { - if ((u->timestamp - now) > (now - u->timestamp_written)) { - latency = (u->timestamp - now) + (u->timestamp_written - now); - } - } - *((pa_usec_t*)data) = latency; + int64_t r = 0; + + if (u->pcm_handle) + r = u->timestamp + pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - pa_rtclock_now(); + + *((int64_t *) data) = r; + return 0; } } @@ -283,22 +269,7 @@ static int sink_process_msg( return pa_sink_process_msg(o, code, data, offset, chunk); } -static void sink_update_requested_latency_cb(pa_sink *s) { - struct userdata *u; - - pa_sink_assert_ref(s); - pa_assert_se((u = s->userdata)); - - u->block_usec = pa_sink_get_requested_latency_within_thread(s); - - if (u->block_usec == (pa_usec_t)-1) - u->block_usec = s->thread_info.max_latency; - - pa_sink_set_max_rewind_within_thread(s, pa_usec_to_bytes(u->block_usec, &s->sample_spec)); - pa_sink_set_max_request_within_thread(s, pa_usec_to_bytes(u->max_request_usec, &s->sample_spec)); -} - -static void process_rewind(struct userdata *u, pa_usec_t now) { +static void process_rewind(struct userdata *u) { #if 1 /* Rewind not supported */ pa_sink_process_rewind(u->sink, 0); @@ -338,60 +309,35 @@ do_nothing: #endif } -static int process_render(struct userdata *u, pa_usec_t now) { - int work_done = 0; - size_t ate = 0; +static int process_render(struct userdata *u) { void *p; - size_t frames_to_write, frame_size; + size_t frame_size = pa_frame_size(&u->sink->sample_spec); + size_t frames_to_write = u->frag_size / u->nfrags; uint32_t avail = 0; + pa_memchunk chunk; pa_assert(u); - /* Fill the buffer up the latency size */ - while (u->timestamp < now + u->block_usec) { - pa_memchunk chunk; - frame_size = pa_frame_size(&u->sink->sample_spec); - if (frame_size == 0) { - pa_log_error("Unexpected frame size zero!"); - break; - } - - pa_hal_interface_pcm_available(u->hal_interface, u->pcm_handle, &avail); - if ((avail == 0) && !(u->first)) { - break; - } - - if (u->first) { - pa_log_debug("Fill initial buffer"); - - frames_to_write = u->frag_size / frame_size; - u->timestamp = u->timestamp_written = now; - } else { - frames_to_write = (u->frag_size / u->nfrags); - - if (frames_to_write > avail) - break; - } - - pa_sink_render_full(u->sink, frames_to_write * frame_size, &chunk); - p = pa_memblock_acquire(chunk.memblock); + pa_hal_interface_pcm_available(u->hal_interface, u->pcm_handle, &avail); + if (frames_to_write > avail) { + pa_log_debug("not enough avail size. frames_to_write(%d), avail(%d)", frames_to_write, avail); + return 0; + } - pa_hal_interface_pcm_write(u->hal_interface, u->pcm_handle, (const char*)p + chunk.index, (uint32_t)frames_to_write); + pa_sink_render_full(u->sink, frames_to_write * frame_size, &chunk); + p = pa_memblock_acquire(chunk.memblock); - pa_memblock_release(chunk.memblock); - pa_memblock_unref(chunk.memblock); - u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); - u->timestamp_written = pa_rtclock_now(); + if (pa_hal_interface_pcm_write(u->hal_interface, u->pcm_handle, (const char*)p + chunk.index, (uint32_t)frames_to_write)) { + pa_log_error("failed to write pcm. p(%p), size(%d)", p, frames_to_write); + return -1; + } - work_done = 1; + pa_memblock_release(chunk.memblock); + pa_memblock_unref(chunk.memblock); - ate += chunk.length; - if (ate >= u->sink->thread_info.max_request) { - break; - } - } + u->write_count += chunk.length; - return work_done; + return 0; } static void thread_func(void *userdata) { @@ -407,35 +353,23 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); + u->timestamp = pa_rtclock_now(); + for (;;) { - pa_usec_t now = 0; int ret; - if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) - now = pa_rtclock_now(); - if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) - process_rewind(u, now); + process_rewind(u); - /* Render some data and drop it immediately */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { - int work_done = process_render(u, now); - - if (work_done < 0) + if (process_render(u)) goto fail; - if (work_done == 0) { - pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); - } else { - if (u->first) { - pa_log_info("Starting playback."); - pa_hal_interface_pcm_start(u->hal_interface, u->pcm_handle); - u->first = false; - } - pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); + if (u->first) { + pa_log_info("Starting playback."); + pa_hal_interface_pcm_start(u->hal_interface, u->pcm_handle); + u->first = false; } - } else { - pa_rtpoll_set_timer_disabled(u->rtpoll); } /* Hmm, nothing to do. Let's sleep */ @@ -515,8 +449,6 @@ int pa__init(pa_module*m) { pa_modargs *ma = NULL; pa_sink_new_data data; uint32_t alternate_sample_rate; - uint32_t block_msec; - uint32_t max_request_msec; const char *modarg_device; char card[DEVICE_NAME_MAX]; char device[DEVICE_NAME_MAX]; @@ -546,6 +478,7 @@ int pa__init(pa_module*m) { u->core = m->core; u->module = m; u->first = true; + u->timestamp = 0ULL; u->hal_interface = pa_hal_interface_get(u->core); u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); @@ -572,19 +505,6 @@ int pa__init(pa_module*m) { } pa_log_info("card(%s) device(%s) fragment_size(%u), fragments(%u)", u->card, u->device, u->frag_size, u->nfrags); - /* Optional */ - block_msec = DEFAULT_BLOCK_USEC / PA_USEC_PER_MSEC; - pa_modargs_get_value_u32(ma, "block_msec", &block_msec); - u->block_usec = (pa_usec_t) block_msec * PA_USEC_PER_MSEC; - - /* Optional */ - max_request_msec = DEFAULT_MAX_REQUEST_USEC / PA_USEC_PER_MSEC; - pa_modargs_get_value_u32(ma, "max_request_msec", &max_request_msec); - u->max_request_usec = (pa_usec_t) max_request_msec * PA_USEC_PER_MSEC; - - u->timestamp = 0ULL; - u->timestamp_written = 0ULL; - pa_sink_new_data_init(&data); data.driver = __FILE__; data.module = m; @@ -620,7 +540,6 @@ int pa__init(pa_module*m) { u->sink->parent.process_msg = sink_process_msg; u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; - u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->userdata = u; if (pa_hal_interface_pcm_open(u->hal_interface, @@ -638,14 +557,14 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_max_rewind(u->sink, 0); - pa_sink_set_max_request(u->sink, pa_usec_to_bytes(u->max_request_usec, &u->sink->sample_spec)); + pa_sink_set_max_request(u->sink, buffer_size); + pa_sink_set_max_rewind(u->sink, buffer_size); if (!(u->thread = pa_thread_new("tizenaudio-sink", thread_func, u))) { pa_log_error("Failed to create thread."); goto fail; } - pa_sink_set_fixed_latency(u->sink, u->block_usec); + pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(buffer_size, &ss)); pa_sink_put(u->sink); pa_modargs_free(ma); -- 2.7.4 From 44dc6302b568f02afb2b20ff316659e0b7c13dc2 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Mon, 9 Aug 2021 12:41:48 +0900 Subject: [PATCH 11/16] tizenaudio-source2: Use hardware interrupt for capture * Remove unused arguments * Use fixed latency * Use poll event [Version] 13.0.70 [Issue Type] Improvement Change-Id: I8be80005a1f8f5ec0e126c76fc54253e2510ea17 Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tizenaudio-source2.c | 125 ++++++++++---------------------- 2 files changed, 39 insertions(+), 88 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 6f220f2..9935f28 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.69 +Version: 13.0.70 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-source2.c b/src/module-tizenaudio-source2.c index 790e2b2..333bdbb 100644 --- a/src/module-tizenaudio-source2.c +++ b/src/module-tizenaudio-source2.c @@ -63,10 +63,7 @@ PA_MODULE_USAGE( "fragment_size= "); -#define DEFAULT_SOURCE_NAME "tizenaudio-source" - -/* BLOCK_USEC should be less than amount of fragment_size * fragments */ -#define BLOCK_USEC (PA_USEC_PER_SEC * 0.032) +#define DEFAULT_SOURCE_NAME "tizenaudio-source2" #define DEVICE_NAME_MAX 30 @@ -83,7 +80,6 @@ struct userdata { uint32_t nfrags; uint32_t frag_size; - pa_usec_t block_usec; pa_usec_t timestamp; char* card; @@ -130,7 +126,7 @@ static int build_pollfd(struct userdata *u) { return -1; } pollfd->fd = fd; - pollfd->events = /* POLLIN | */ POLLERR | POLLNVAL; + pollfd->events = POLLIN | POLLERR | POLLNVAL; return 0; } @@ -193,6 +189,7 @@ static int unsuspend(struct userdata *u) { u->read_count = 0; u->first = true; + u->timestamp = pa_rtclock_now(); pa_log_info("Resumed successfully..."); @@ -261,8 +258,13 @@ static int source_process_msg( switch (code) { case PA_SOURCE_MESSAGE_GET_LATENCY: { - pa_usec_t now = pa_rtclock_now(); - *((pa_usec_t*)data) = u->timestamp > now ? 0ULL : now - u->timestamp; + uint64_t r = 0; + + if (u->pcm_handle) + r = pa_rtclock_now() - (u->timestamp + pa_bytes_to_usec(u->read_count, &u->source->sample_spec)); + + *((int64_t *) data) = r; + return 0; } } @@ -270,72 +272,41 @@ static int source_process_msg( return pa_source_process_msg(o, code, data, offset, chunk); } -static void source_update_requested_latency_cb(pa_source *s) { - struct userdata *u; - size_t nbytes; - - pa_source_assert_ref(s); - pa_assert_se((u = s->userdata)); - - u->block_usec = pa_source_get_requested_latency_within_thread(s); - - if (u->block_usec == (pa_usec_t)-1) - u->block_usec = s->thread_info.max_latency; - - nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec); - pa_source_set_max_rewind_within_thread(s, nbytes); -} - -static int process_render(struct userdata *u, pa_usec_t now) { - int work_done = 0; - size_t ate = 0; +static int process_render(struct userdata *u) { void *p; - size_t frames_to_read, frame_size; + size_t frame_size = pa_frame_size(&u->source->sample_spec); + size_t frames_to_read = u->frag_size / u->nfrags; uint32_t avail = 0; + pa_memchunk chunk; pa_assert(u); /* Fill the buffer up the latency size */ - while (u->timestamp < now + u->block_usec) { - pa_memchunk chunk; - frame_size = pa_frame_size(&u->source->sample_spec); - if (frame_size == 0) { - pa_log_error("Unexpected frame size zero!"); - break; - } - - pa_hal_interface_pcm_available(u->hal_interface, u->pcm_handle, &avail); - if (avail == 0) { - break; - } - - frames_to_read = (u->frag_size / u->nfrags); - chunk.length = frames_to_read * frame_size; - chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); - - if (frames_to_read > (size_t)avail) - frames_to_read = (size_t)avail; - p = pa_memblock_acquire(chunk.memblock); - pa_hal_interface_pcm_read(u->hal_interface, u->pcm_handle, p, (uint32_t)frames_to_read); - pa_memblock_release(chunk.memblock); + pa_hal_interface_pcm_available(u->hal_interface, u->pcm_handle, &avail); + if (frames_to_read > avail) { + pa_log_debug("not enough avail size. frames_to_read(%d), avail(%d)", frames_to_read, avail); + return 0; + } - chunk.index = 0; - chunk.length = (size_t)frames_to_read * frame_size; - pa_source_post(u->source, &chunk); - pa_memblock_unref(chunk.memblock); + chunk.length = frames_to_read * frame_size; + chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); - u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec); + p = pa_memblock_acquire(chunk.memblock); + if (pa_hal_interface_pcm_read(u->hal_interface, u->pcm_handle, p, (uint32_t)frames_to_read)) { + pa_log_error("failed to read pcm. p(%p), size(%d)", p, frames_to_read); + return -1; + } + pa_memblock_release(chunk.memblock); - work_done = 1; + chunk.index = 0; + chunk.length = (size_t)frames_to_read * frame_size; + pa_source_post(u->source, &chunk); + pa_memblock_unref(chunk.memblock); - ate += chunk.length; - if (ate >= pa_usec_to_bytes(u->block_usec, &u->source->sample_spec)) { - break; - } - } + u->read_count += chunk.length; - return work_done; + return 0; } static void thread_func(void *userdata) { @@ -350,36 +321,21 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); + u->timestamp = pa_rtclock_now(); + for (;;) { - pa_usec_t now = 0; int ret; - if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) - now = pa_rtclock_now(); - /* Render some data and drop it immediately */ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { - int work_done; + if (process_render(u)) + goto fail; if (u->first) { pa_log_info("Starting capture."); pa_hal_interface_pcm_start(u->hal_interface, u->pcm_handle); u->first = false; - u->timestamp = now; - } - - work_done = process_render(u, now); - - if (work_done < 0) - goto fail; - - if (work_done == 0) { - pa_rtpoll_set_timer_relative(u->rtpoll, (20 * PA_USEC_PER_MSEC)); - } else { - pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); } - } else { - pa_rtpoll_set_timer_disabled(u->rtpoll); } /* Hmm, nothing to do. Let's sleep */ @@ -549,7 +505,6 @@ int pa__init(pa_module*m) { u->source->parent.process_msg = source_process_msg; u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; - u->source->update_requested_latency = source_update_requested_latency_cb; u->source->userdata = u; if (pa_hal_interface_pcm_open(u->hal_interface, @@ -567,17 +522,13 @@ int pa__init(pa_module*m) { pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); - u->block_usec = BLOCK_USEC; - u->latency_time = u->block_usec; u->timestamp = 0ULL; - pa_source_set_max_rewind(u->source, 0); - if (!(u->thread = pa_thread_new("tizenaudio-source", thread_func, u))) { pa_log_error("Failed to create thread."); goto fail; } - pa_source_set_fixed_latency(u->source, u->block_usec); + pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(buffer_size, &ss)); pa_source_put(u->source); pa_modargs_free(ma); -- 2.7.4 From 2114b2b49894bb46358229a1b814ec7447e56efa Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Thu, 12 Aug 2021 14:45:46 +0900 Subject: [PATCH 12/16] device-manager: Support to load sink2/source2 pulseaudio tries to load tizenaudio-sink2/source2 instead of tizenaudio-sink/source if 'tizen2' keyword is found in device-map file. [Version] 13.0.71 [Issue Type] Improvement Change-Id: Ie9c67f4888fb628961811d906e4d6c396700b14e Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/device-manager.c | 75 ++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 9935f28..3445223 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.70 +Version: 13.0.71 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/device-manager.c b/src/device-manager.c index a1a717e..c5491e0 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -58,6 +58,7 @@ #define DEVICE_MAP_FILE PA_DEFAULT_CONFIG_DIR"/device-map.json" #define DEVICE_STR_MAX 40 #define DEVICE_DIRECTION_MAX 3 +#define DEVICE_MODULE_STRING_MAX 256 #define DEVICE_PARAM_STRING_MAX 150 #define DEVICE_AVAIL_COND_NUM_MAX 2 #define DEVICE_AVAIL_COND_STR_MAX 6 @@ -424,16 +425,18 @@ static bool pulse_device_is_tunnel(pa_object *pdevice) { } static bool pulse_device_is_tizenaudio(pa_object *pdevice) { + char name[DEVICE_NAME_MAX]; + if (!pdevice) return false; - if (pa_sink_isinstance(pdevice)) { - pa_sink *sink = PA_SINK(pdevice); - return pa_safe_streq(sink->module->name, "module-tizenaudio-sink"); - } else { - pa_source *source = PA_SOURCE(pdevice); - return pa_safe_streq(source->module->name, "module-tizenaudio-source"); - } + if (!module_table[DM_DEVICE_CLASS_TIZEN].custom_device_get_func) + return false; + + if (module_table[DM_DEVICE_CLASS_TIZEN].custom_device_get_func(pdevice, name)) + return false; + + return true; } static bool pulse_device_is_usb(pa_object *pdevice) { @@ -572,11 +575,40 @@ static bool pulse_device_is_monitor(pa_object *pdevice) { return false; } -static const char* device_class_get_module_name(dm_device_class_t device_class, bool is_sink) { +static char* device_class_get_module_name(dm_device_class_t device_class, const char *device_string, bool is_sink) { + char version[8] = ""; + char module_name[DEVICE_MODULE_STRING_MAX]; + const char *_module_name; + if (device_class >= DM_DEVICE_CLASS_MAX) return NULL; - return module_table[device_class].module_name[is_sink]; + if (!device_string) + return NULL; + + _module_name = module_table[device_class].module_name[is_sink]; + + /* supports tizen[2-9] keyword in device-map */ + if (device_class == DM_DEVICE_CLASS_TIZEN) { + int v; + char *class; + const char *state = NULL, *prefix = "tizen"; + + class = pa_split(device_string, ":", &state); + + if (!pa_streq(class, prefix)) { + if (!pa_atoi(class + strlen(prefix), &v)) + snprintf(version, sizeof(version), "%d", v); + else + pa_log_warn("failed to get a version from class(%s)", class); + } + + pa_xfree(class); + } + + snprintf(module_name, DEVICE_MODULE_STRING_MAX, "%s%s", _module_name, version); + + return pa_xstrdup(module_name); } static device_type_info* _device_manager_get_type_info(pa_idxset *type_infos, const char *type, const char *role) { @@ -916,7 +948,8 @@ static int pulse_device_get_alsa_device_name(pa_object *pdevice, char *device_na static int pulse_device_get_tizen_device_name(pa_object *pdevice, char *device_name) { pa_proplist *prop; - const char *card, *device; + const char *card, *device, *version; + char class[8] = "tizen"; prop = pulse_device_get_proplist(pdevice); @@ -924,12 +957,17 @@ static int pulse_device_get_tizen_device_name(pa_object *pdevice, char *device_n pa_log_error("failed to get property 'tizen.card'"); return -1; } + if (!(device = pa_proplist_gets(prop, "tizen.device"))) { pa_log_error("failed to get property 'tizen.device'"); return -1; } - snprintf(device_name, DEVICE_NAME_MAX, "tizen:%s,%s", card, device); + if ((version = pa_proplist_gets(prop, "tizen.version"))) + snprintf(class, sizeof(class), "tizen%s", version); + + snprintf(device_name, DEVICE_NAME_MAX, "%s:%s,%s", class, card, device); + return 0; } @@ -1844,7 +1882,7 @@ static pa_hook_result_t sink_proplist_changed(pa_core *core, pa_sink *sink, pa_d Build params for load sink or source, and load it. */ static void* load_module(pa_core *c, bool is_sink, const char *device_string, const char *device_params, const char *device_role) { - const char *module_name; + char *module_name; pa_module *module; pa_sink *sink; pa_source *source; @@ -1865,7 +1903,7 @@ static void* load_module(pa_core *c, bool is_sink, const char *device_string, co } pa_log_info("device class : %d", device_class); - if (!(module_name = device_class_get_module_name(device_class, is_sink))) { + if (!(module_name = device_class_get_module_name(device_class, device_string, is_sink))) { pa_log_error("Get proper module name to load failed"); return NULL; } @@ -1873,16 +1911,19 @@ static void* load_module(pa_core *c, bool is_sink, const char *device_string, co if (build_params_to_load_module(device_string, device_params, device_class, full_params) < 0) { pa_log_error("build param to load module failed"); - return NULL; + goto exit; } pa_log_info("argument : %s", full_params); if (pa_module_load(&module, c, module_name, full_params)) { pa_log_error("Load module with name '%s' argument '%s' failed", module_name, full_params); - return NULL; + goto exit; } pa_log_info("module loaded : %s %u with '%s'", module->name, module->index, module->argument); + pa_xfree(module_name); + module_name = NULL; + if (is_sink) { PA_IDXSET_FOREACH(sink, c->sinks, device_idx) { if (sink->module == module) { @@ -1905,6 +1946,10 @@ static void* load_module(pa_core *c, bool is_sink, const char *device_string, co pa_log_warn("Failed to find matching %s after load module", is_sink ? "sink" : "source"); +exit: + if (module_name) + pa_xfree(module_name); + return NULL; } -- 2.7.4 From f3c72b7ab94bc6dd0eeb0b90eae8fdb793dadc89 Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Mon, 15 Mar 2021 16:45:23 +0900 Subject: [PATCH 13/16] support external bt-sco device for tv [Version] 13.0.71 [Issue Type] Improvement Change-Id: Ic9d3d00c5a48e319203468550efa5fb40f6f243d --- packaging/pulseaudio-modules-tizen.spec | 4 +- src/device-manager-dbus.c | 10 +++- src/device-manager.c | 82 +++++++++++++++++++++++++++++++++ src/module-tizenaudio-policy.c | 47 +++++++++++++++++++ src/tizen-device.c | 6 +++ src/tizen-device.h | 4 ++ 6 files changed, 150 insertions(+), 3 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 9935f28..741064d 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.70 +Version: 13.0.71 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ @@ -43,6 +43,8 @@ PulseAudio module-acm-sink for sending PCM data to ACM core. export CFLAGS="%{optflags} -fno-strict-aliasing -D__TIZEN__ -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\" " %if "%{tizen_profile_name}" == "tv" export CFLAGS+=" -DTIZEN_TV"; +%else + export CFLAGS+=" -D__TIZEN_INTERNAL_BT_SCO__" %endif export LD_AS_NEEDED=0 diff --git a/src/device-manager-dbus.c b/src/device-manager-dbus.c index ccab821..359d4f4 100644 --- a/src/device-manager-dbus.c +++ b/src/device-manager-dbus.c @@ -219,7 +219,9 @@ static void handle_dump_device_list(DBusConnection *conn, DBusMessage *msg, void static void handle_test_device_status_change(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_set_acm_mode(DBusConnection *conn, DBusMessage *msg, void *userdata); +#ifdef __TIZEN_INTERNAL_BT_SCO__ static int method_call_bt_get_name(DBusConnection *conn, const char *device_path, char **name); +#endif enum method_handler_index { METHOD_HANDLER_GET_CONNECTED_DEVICE_LIST, @@ -431,6 +433,7 @@ static int _translate_external_value(const char *type, int value, device_detecte return 0; } +#ifdef __TIZEN_INTERNAL_BT_SCO__ static int handle_bluez_headset_property_changed(DBusConnection *c, DBusMessage *s, pa_device_manager *dm) { DBusMessageIter msg_iter, variant_iter; char *property_name; @@ -504,6 +507,7 @@ static int handle_bluez_headset_property_changed(DBusConnection *c, DBusMessage return 0; } +#endif /* __TIZEN_INTERNAL_BT_SCO__ */ static DBusHandlerResult dbus_filter_device_detect_handler(DBusConnection *c, DBusMessage *s, void *userdata) { DBusError error; @@ -550,11 +554,11 @@ static DBusHandlerResult dbus_filter_device_detect_handler(DBusConnection *c, DB goto fail; } handle_device_status_changed(dm, DEVICE_TYPE_FORWARDING, NULL, NULL, detected); - +#ifdef __TIZEN_INTERNAL_BT_SCO__ } else if (dbus_message_is_signal(s, DBUS_INTERFACE_BLUEZ_HEADSET, "PropertyChanged")) { if (handle_bluez_headset_property_changed(c, s, dm) < 0) goto fail; - +#endif } else { pa_log_debug("Unknown message, not handle it"); dbus_error_free(&error); @@ -635,6 +639,7 @@ static bool device_is_match_with_mask(pa_tz_device *device, int mask) { device_is_match_type(device, mask & DEVICE_TYPE_FLAGS)); } +#ifdef __TIZEN_INTERNAL_BT_SCO__ static int method_call_bt_get_name(DBusConnection *conn, const char *device_path, char **name) { const char *intf = DBUS_INTERFACE_BLUEZ_DEVICE, *prop = "Alias"; DBusMessage *msg, *reply; @@ -682,6 +687,7 @@ static int method_call_bt_get_name(DBusConnection *conn, const char *device_path dbus_message_unref(reply); return 0; } +#endif /* __TIZEN_INTERNAL_BT_SCO__ */ static void handle_get_connected_device_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_device_manager *dm = (pa_device_manager *)userdata; diff --git a/src/device-manager.c b/src/device-manager.c index a1a717e..114f28f 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -87,6 +87,9 @@ #define DEVICE_API_RAOP "raop" #define DEVICE_API_TUNNEL "tunnel" #define DEVICE_API_RTSP "rtsp" +#ifndef __TIZEN_INTERNAL_BT_SCO__ +#define DEVICE_API_BTSCO "btsco" +#endif #define DEVICE_BUS_USB "usb" #define DEVICE_CLASS_SOUND "sound" #define DEVICE_CLASS_MONITOR "monitor" @@ -480,6 +483,17 @@ static bool pulse_device_is_rtsp(pa_object *pdevice) { return pa_safe_streq(pa_proplist_gets(prop, PA_PROP_DEVICE_API), DEVICE_API_RTSP); } +#ifndef __TIZEN_INTERNAL_BT_SCO__ +static bool pulse_device_is_btsco(pa_object *pdevice) { + pa_proplist *prop = pulse_device_get_proplist(pdevice); + + if (!prop) + return false; + + return pa_safe_streq(pa_proplist_gets(prop, PA_PROP_DEVICE_API), DEVICE_API_BTSCO); +} +#endif + static const char* pulse_device_get_device_string_removed_argument(pa_object *pdevice) { static char removed_param[DEVICE_PARAM_STRING_MAX] = {0,}; char *device_string_p = NULL; @@ -1156,6 +1170,10 @@ static const char* pulse_device_get_system_id(pa_object *pdevice) { return pa_proplist_gets(prop, PA_PROP_DEVICE_DESCRIPTION); else if (pulse_device_is_rtsp(pdevice)) return pa_proplist_gets(prop, PA_PROP_DEVICE_DESCRIPTION); +#ifndef __TIZEN_INTERNAL_BT_SCO__ + else if (pulse_device_is_btsco(pdevice)) + return pa_proplist_gets(prop, PA_PROP_DEVICE_DESCRIPTION); +#endif else return NULL; } @@ -1578,6 +1596,50 @@ static void handle_rtsp_pulse_device(pa_object *pdevice, bool is_loaded, pa_devi } } +#ifndef __TIZEN_INTERNAL_BT_SCO__ +static void handle_external_btsco_pulse_device(pa_object *pdevice, bool is_loaded, pa_device_manager *dm) { + const char *name, *system_id; + dm_device_direction_t direction; + pa_tz_device *device; + + pa_assert(pdevice); + pa_assert(dm); + + pa_log_info("Handle btsco pulse device"); + + system_id = pulse_device_get_system_id(pdevice); + direction = pulse_device_get_direction(pdevice); + + if (is_loaded) { + pa_tz_device_new_data data; + pa_proplist *prop; + + prop = pulse_device_get_proplist(pdevice); + name = pa_proplist_gets(prop, PA_PROP_DEVICE_DESCRIPTION); + + pa_tz_device_new_data_init(&data, dm->device_list, dm->comm, NULL); + pa_tz_device_new_data_set_type(&data, DEVICE_TYPE_BT_SCO); + pa_tz_device_new_data_set_name(&data, name); + pa_tz_device_new_data_set_direction(&data, direction); + pa_tz_device_new_data_set_system_id(&data, system_id); + pa_tz_device_new_data_set_use_internal_codec(&data, false); + if (direction == DM_DEVICE_DIRECTION_OUT) { + pa_tz_device_new_data_add_sink(&data, DEVICE_ROLE_NORMAL, PA_SINK(pdevice)); + } else { + pa_tz_device_new_data_add_source(&data, DEVICE_ROLE_NORMAL, PA_SOURCE(pdevice)); + } + + pa_tz_device_new(&data); + pa_tz_device_new_data_done(&data); + } else { + if (!(device = device_list_get_device(dm, DEVICE_TYPE_BT_SCO, NULL, system_id))) + pa_log_warn("Can't get btsco device for %s", system_id); + else + pa_tz_device_free(device); + } +} +#endif /* __TIZEN_INTERNAL_BT_SCO__ */ + static void handle_internal_pulse_device(pa_object *pdevice, bool is_loaded, pa_device_manager *dm) { pa_tz_device *device; struct composite_type *ctype; @@ -1661,6 +1723,12 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, pa_dev pulse_device_set_use_internal_codec(PA_OBJECT(sink), true); handle_internal_pulse_device(PA_OBJECT(sink), true, dm); return PA_HOOK_OK; +#ifndef __TIZEN_INTERNAL_BT_SCO__ + } else if (pulse_device_is_btsco(PA_OBJECT(sink))) { + pulse_device_set_use_internal_codec(PA_OBJECT(sink), false); + handle_external_btsco_pulse_device(PA_OBJECT(sink), true, dm); + return PA_HOOK_OK; +#endif } else { pa_log_debug("Don't care this sink"); } @@ -1705,6 +1773,11 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, pa_ } else if (pulse_device_is_alsa(PA_OBJECT(sink)) || pulse_device_is_tizenaudio(PA_OBJECT(sink))) { handle_internal_pulse_device(PA_OBJECT(sink), false, dm); return PA_HOOK_OK; +#ifndef __TIZEN_INTERNAL_BT_SCO__ + } else if (pulse_device_is_btsco(PA_OBJECT(sink))) { + handle_external_btsco_pulse_device(PA_OBJECT(sink), false, dm); + return PA_HOOK_OK; +#endif } else { pa_log_debug("Don't care this sink"); } @@ -1738,6 +1811,11 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, pulse_device_set_use_internal_codec(PA_OBJECT(source), false); handle_rtsp_pulse_device(PA_OBJECT(source), true, dm); return PA_HOOK_OK; +#ifndef __TIZEN_INTERNAL_BT_SCO__ + } else if (pulse_device_is_btsco(PA_OBJECT(source))) { + pulse_device_set_use_internal_codec(PA_OBJECT(source), false); + handle_external_btsco_pulse_device(PA_OBJECT(source), true, dm); +#endif } else { pa_log_debug("Don't care this source"); } @@ -1778,7 +1856,11 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc return PA_HOOK_OK; } else if (pulse_device_is_rtsp(PA_OBJECT(source))) { handle_rtsp_pulse_device(PA_OBJECT(source), false, dm); +#ifndef __TIZEN_INTERNAL_BT_SCO__ + } else if (pulse_device_is_btsco(PA_OBJECT(source))) { + handle_external_btsco_pulse_device(PA_OBJECT(source), false, dm); return PA_HOOK_OK; +#endif } else { pa_log_debug("Don't care this source"); } diff --git a/src/module-tizenaudio-policy.c b/src/module-tizenaudio-policy.c index 2efbc89..44b1894 100644 --- a/src/module-tizenaudio-policy.c +++ b/src/module-tizenaudio-policy.c @@ -85,7 +85,9 @@ struct userdata; #define LOOPBACK_DEFAULT_LATENCY_MSEC 40 #define LOOPBACK_DEFAULT_ADJUST_SEC 3 +#ifdef __TIZEN_INTERNAL_BT_SCO__ #define TIMED_BT_SCO_CLOSE_USEC 3000000 +#endif /* Macros */ #define CONVERT_TO_HAL_DIRECTION(stream_type) \ @@ -183,7 +185,9 @@ struct userdata { int32_t latency_msec; int32_t adjust_sec; } loopback_args; +#ifdef __TIZEN_INTERNAL_BT_SCO__ pa_time_event *time_event_bt_sco_close; +#endif }; static void __load_dump_config(struct userdata *u) @@ -208,6 +212,7 @@ static void __load_dump_config(struct userdata *u) iniparser_freedict(dict); } +#ifdef __TIZEN_INTERNAL_BT_SCO__ /* threre is only one sco connected device */ static pa_tz_device* _get_sco_connected_device(pa_device_manager *dm) { pa_idxset *device_list; @@ -450,6 +455,7 @@ static void update_bt_sco_option(struct userdata *u, const char* role) { pa_log_warn("Failed to get property for wideband / nrec...."); } } +#endif static void update_loopback_module_args(struct userdata* u, int32_t parent_id, pa_sink *sink, pa_source *source) { @@ -690,6 +696,7 @@ static bool skip_device(const char *stream_role, const char *device_type) return false; } +#ifdef __TIZEN_INTERNAL_BT_SCO__ static bool skip_bt_sco_device(struct userdata *u, const char *stream_role, const char *device_type) { pa_assert(u); pa_assert(stream_role); @@ -719,6 +726,7 @@ static bool skip_bt_sco_device(struct userdata *u, const char *stream_role, cons return false; } +#endif static bool is_supported_stream_role_for_usb(const char *role) { pa_assert(role); @@ -819,8 +827,10 @@ static void select_device_by_auto_or_auto_all_routing(struct userdata *u, pa_str if (!pa_safe_streq(device_type, dm_device_type) || !IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) continue; pa_log_info(" ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction); +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) continue; +#endif if (skip_usb_device(data->stream_role, device)) continue; @@ -891,8 +901,10 @@ static void select_device_by_auto_last_connected_routing(struct userdata *u, pa_ dm_device_type, dm_device_direction, dm_device_id, creation_time); if (!pa_safe_streq(device_type, dm_device_type) || !IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) continue; +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) continue; +#endif if (skip_usb_device(data->stream_role, device)) continue; @@ -1137,8 +1149,10 @@ static void reset_route(struct userdata *u, stream_type_t stream_type) { pa_assert(u); +#ifdef __TIZEN_INTERNAL_BT_SCO__ /* update BT SCO: close */ update_bt_sco_state(u, false, true, NULL); +#endif /* unload combine sink */ if (stream_type == STREAM_SINK_INPUT) { @@ -1291,9 +1305,15 @@ static void fill_device_info(hal_route_info *route_info, const char *type, uint3 route_info->num_of_devices -1, type, direction, id); } +#ifdef __TIZEN_INTERNAL_BT_SCO__ static pa_hook_result_t update_combine_sink_and_bt_sco(struct userdata *u, pa_stream_manager_hook_data_for_route *data, pa_tz_device *device, const char *stream_role, const char *dm_device_type, pa_sink **combine_sink_arg1, pa_sink **combine_sink_arg2) { +#else +static pa_hook_result_t update_combine_sink(struct userdata *u, pa_stream_manager_hook_data_for_route *data, + pa_tz_device *device, const char *stream_role, const char *dm_device_type, + pa_sink **combine_sink_arg1, pa_sink **combine_sink_arg2) { +#endif pa_sink *sink = NULL; pa_assert(u); @@ -1309,6 +1329,7 @@ static pa_hook_result_t update_combine_sink_and_bt_sco(struct userdata *u, pa_st else pa_log_error("[ROUTE][AUTO] could not get sink"); } +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (pa_safe_streq(dm_device_type, DEVICE_TYPE_BT_SCO)) { if (IS_ROLE_AVAILABLE_BT_SCO_OPEN(stream_role)) { if (update_bt_sco_state(u, true, false, stream_role)) { @@ -1320,6 +1341,7 @@ static pa_hook_result_t update_combine_sink_and_bt_sco(struct userdata *u, pa_st return PA_HOOK_OK; } update_bt_sco_state(u, false, false, NULL); +#endif break; } case STREAM_ROUTE_TYPE_AUTO_ALL: { @@ -1328,7 +1350,9 @@ static pa_hook_result_t update_combine_sink_and_bt_sco(struct userdata *u, pa_st void *s = NULL; stream_route_type_t route_type; +#ifdef __TIZEN_INTERNAL_BT_SCO__ update_bt_sco_state(u, false, false, NULL); +#endif /* find the proper sink/source */ /* currently, we support two sinks for combining */ @@ -1412,8 +1436,10 @@ static int32_t update_route_by_preemptive_device(struct userdata *u, pa_stream_m /* if it needs to skip it, keep going to next device for proper UCM setting */ if (skip_device(data->stream_role, dm_device_type)) return -1; +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) return -1; +#endif fill_device_info(route_info, dm_device_type, CONVERT_TO_HAL_DIRECTION(data->stream_type), dm_device_id); return 0; } @@ -1466,8 +1492,10 @@ static pa_hook_result_t handle_auto_or_auto_all_routing(struct userdata *u, pa_s /* if it needs to skip it, keep going to next device for proper UCM setting */ if (skip_device(data->stream_role, dm_device_type)) continue; +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) continue; +#endif fill_device_info(route_info, dm_device_type, CONVERT_TO_HAL_DIRECTION(data->stream_type), dm_device_id); break; } @@ -1485,10 +1513,18 @@ update_auto_active_dev: else pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type); +#ifdef __TIZEN_INTERNAL_BT_SCO__ return update_combine_sink_and_bt_sco(u, data, *device, route_info->role, dm_device_type, NULL, NULL); +#else + return update_combine_sink(u, data, *device, route_info->role, dm_device_type, NULL, NULL); +#endif } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) { +#ifdef __TIZEN_INTERNAL_BT_SCO__ update_combine_sink_and_bt_sco(u, data, *device, NULL, NULL, &combine_sink_arg1, &combine_sink_arg2); +#else + update_combine_sink(u, data, *device, NULL, NULL, &combine_sink_arg1, &combine_sink_arg2); +#endif } } return PA_HOOK_OK; @@ -1539,8 +1575,12 @@ static pa_hook_result_t handle_auto_last_connected_routing(struct userdata *u, p continue; if ((use_internal_codec = pa_tz_device_is_use_internal_codec(*device))) { /* if it needs to skip it, keep going to next device for proper UCM setting */ +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (skip_device(data->stream_role, dm_device_type) || skip_bt_sco_device(u, data->stream_role, dm_device_type)) +#else + if (skip_device(data->stream_role, dm_device_type)) +#endif continue; } if (!latest_device || (latest_creation_time <= creation_time)) { @@ -1582,6 +1622,7 @@ update_auto_active_dev: pa_log_error("[ROUTE][AUTO_LAST_CONN] could not get sink"); } +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (pa_safe_streq(dm_device_type, DEVICE_TYPE_BT_SCO)) { if (IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info->role)) { if (update_bt_sco_state(u, true, false, route_info->role)) { @@ -1593,6 +1634,7 @@ update_auto_active_dev: } else { update_bt_sco_state(u, false, false, NULL); } +#endif /* Update device with latest_device to use be used later in this function */ *device = latest_device; @@ -1631,6 +1673,8 @@ static pa_hook_result_t handle_manual_routing(struct userdata *u, pa_stream_mana dm_device_direction = pa_tz_device_get_direction(*device); pa_log_debug(" ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction); + +#ifdef __TIZEN_INTERNAL_BT_SCO__ /* Check for availability for opening Bluetooth SCO */ if (pa_safe_streq(dm_device_type, DEVICE_TYPE_BT_SCO) && IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info->role)) { /* update BT SCO: open */ @@ -1643,6 +1687,7 @@ static pa_hook_result_t handle_manual_routing(struct userdata *u, pa_stream_mana /* update BT SCO: close */ update_bt_sco_state(u, false, false, NULL); } +#endif if (IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) { if ((use_internal_codec = pa_tz_device_is_use_internal_codec(*device))) @@ -1971,7 +2016,9 @@ void pa__done(pa_module *m) if (!(u = m->userdata)) return; +#ifdef __TIZEN_INTERNAL_BT_SCO__ bt_sco_close(u, false); +#endif if (u->loopback_modules) { pa_hashmap_remove_all(u->loopback_modules); diff --git a/src/tizen-device.c b/src/tizen-device.c index 996098a..cbeae10 100644 --- a/src/tizen-device.c +++ b/src/tizen-device.c @@ -145,8 +145,10 @@ static char* _device_get_info_str(pa_tz_device *device) { pa_strbuf_printf(buf, " Product ID : %04x\n", device->product_id); pa_strbuf_printf(buf, " Specified Stream role : %s\n", device->specified_stream_role); } +#ifdef __TIZEN_INTERNAL_BT_SCO__ if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO)) pa_strbuf_printf(buf, " SCO opened : %s\n", pa_yes_no(device->sco_opened)); +#endif playback_str = get_playback_list_str(device->playback_devices); capture_str = get_capture_list_str(device->capture_devices); @@ -492,7 +494,9 @@ pa_tz_device* pa_tz_device_new(pa_tz_device_new_data *data) { device->state = DM_DEVICE_STATE_DEACTIVATED; device->creation_time = pa_rtclock_now(); device->use_internal_codec = data->use_internal_codec; +#ifdef __TIZEN_INTERNAL_BT_SCO__ device->sco_opened = false; +#endif device->is_running = false; device->specified_stream_role = pa_xstrdup("none"); @@ -832,6 +836,7 @@ pa_intset* pa_tz_device_get_stream_list(pa_tz_device *device) { return stream_id_set; } +#ifdef __TIZEN_INTERNAL_BT_SCO__ static int method_call_bt_sco_enable_pcm(pa_dbus_connection *conn, bool enable) { DBusMessage *msg, *reply; DBusError err; @@ -1081,4 +1086,5 @@ int pa_tz_device_sco_get_status(pa_tz_device *device, dm_device_bt_sco_status_t pa_log_info("BT SCO (%u) Get Status, %d", device->id, *status); return 0; } +#endif /* __TIZEN_INTERNAL_BT_SCO__ */ diff --git a/src/tizen-device.h b/src/tizen-device.h index 916a819..a7649ae 100644 --- a/src/tizen-device.h +++ b/src/tizen-device.h @@ -60,7 +60,9 @@ struct pa_tz_device { * false, if this device uses external card(bt-a2dp, usb */ bool use_internal_codec; /* If this is sco device, this can be used to */ +#ifdef __TIZEN_INTERNAL_BT_SCO__ bool sco_opened; +#endif /* Devices are contained in this list */ pa_idxset *list; @@ -153,12 +155,14 @@ bool pa_tz_device_is_use_internal_codec(pa_tz_device *device); bool pa_tz_device_is_all_suspended(pa_tz_device *device); pa_intset* pa_tz_device_get_stream_list(pa_tz_device *device); +#ifdef __TIZEN_INTERNAL_BT_SCO__ /* Only for BT SCO device */ int pa_tz_device_sco_enable_pcm(pa_tz_device *device, bool enable); int pa_tz_device_sco_open(pa_tz_device *device); int pa_tz_device_sco_close(pa_tz_device *device); int pa_tz_device_sco_get_property(pa_tz_device *device, bool *is_wide_band, bool *nrec); int pa_tz_device_sco_get_status(pa_tz_device *device, dm_device_bt_sco_status_t *status); +#endif /* Only for USB device */ int pa_tz_device_get_vendor_id(pa_tz_device *device); -- 2.7.4 From 324cb705d7953ea4f7a23c5d0ac08d1bbdd961a1 Mon Sep 17 00:00:00 2001 From: jungsup lee Date: Wed, 8 Sep 2021 13:54:40 +0900 Subject: [PATCH 14/16] device-manager-dbus: always place bt-sco device at first in device list [Version] 13.0.72 [Issue Type] Improvement Change-Id: I15efd7b12c48a3278ad64e9fdb7a2f409cf4183f --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/device-manager-dbus.c | 94 ++++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 741064d..b58e363 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.71 +Version: 13.0.72 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/device-manager-dbus.c b/src/device-manager-dbus.c index 359d4f4..8c067af 100644 --- a/src/device-manager-dbus.c +++ b/src/device-manager-dbus.c @@ -689,35 +689,22 @@ static int method_call_bt_get_name(DBusConnection *conn, const char *device_path } #endif /* __TIZEN_INTERNAL_BT_SCO__ */ -static void handle_get_connected_device_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { - pa_device_manager *dm = (pa_device_manager *)userdata; - DBusMessage *reply = NULL; - DBusMessageIter msg_iter, array_iter, device_iter; - pa_tz_device *device; - dm_device_state_t state; +static void array_iter_append(DBusMessageIter *array_iter, pa_idxset *device_list, int mask) { uint32_t device_idx; + dm_device_state_t state; dbus_int32_t device_id, direction; - int mask; char *type, *name; dbus_int32_t vendor_id, product_id; dbus_bool_t is_running; + pa_tz_device *device; + DBusMessageIter device_iter; - pa_assert(conn); - pa_assert(msg); - pa_assert(dm); - - pa_assert_se((reply = dbus_message_new_method_return(msg))); - - pa_assert_se(dbus_message_get_args(msg, NULL, - DBUS_TYPE_INT32, &mask, - DBUS_TYPE_INVALID)); - - pa_log_info("Get connected device list (mask : %d)", mask); + pa_assert(array_iter); - dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "(isiisiib)", &array_iter)); + if (!device_list) + return; - PA_IDXSET_FOREACH(device, dm->device_list, device_idx) { + PA_IDXSET_FOREACH(device, device_list, device_idx) { device_id = (dbus_int32_t)pa_tz_device_get_id(device); state = pa_tz_device_get_state(device); direction = pa_tz_device_get_direction(device); @@ -727,9 +714,9 @@ static void handle_get_connected_device_list(DBusConnection *conn, DBusMessage * product_id = (dbus_int32_t) pa_tz_device_get_product_id(device); product_id = (dbus_int32_t) pa_tz_device_get_product_id(device); is_running = (dbus_bool_t) pa_tz_device_is_running(device); - if (device_is_match_with_mask(device, mask)) { + if (device_is_match_with_mask(device, mask)) { simple_device_dump(PA_LOG_INFO, "[MATCH]", device_id, type, name, direction, state); - pa_assert_se(dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &device_iter)); + pa_assert_se(dbus_message_iter_open_container(array_iter, DBUS_TYPE_STRUCT, NULL, &device_iter)); dbus_message_iter_append_basic(&device_iter, DBUS_TYPE_INT32, &device_id); dbus_message_iter_append_basic(&device_iter, DBUS_TYPE_STRING, &type); dbus_message_iter_append_basic(&device_iter, DBUS_TYPE_INT32, &direction); @@ -738,11 +725,70 @@ static void handle_get_connected_device_list(DBusConnection *conn, DBusMessage * dbus_message_iter_append_basic(&device_iter, DBUS_TYPE_INT32, &vendor_id); dbus_message_iter_append_basic(&device_iter, DBUS_TYPE_INT32, &product_id); dbus_message_iter_append_basic(&device_iter, DBUS_TYPE_BOOLEAN, &is_running); - pa_assert_se(dbus_message_iter_close_container(&array_iter, &device_iter)); + pa_assert_se(dbus_message_iter_close_container(array_iter, &device_iter)); } else { simple_device_dump(PA_LOG_INFO, "[UNMATCH]", device_id, type, name, direction, state); } } +} + +#ifndef __TIZEN_INTERNAL_BT_SCO__ +static int include_device_filter_func(const void *d, const void *include_device_type) { + pa_tz_device *device = (pa_tz_device *)d; + + pa_assert(device); + pa_assert(include_device_type); + + return (int)pa_safe_streq(pa_tz_device_get_type(device), (const char *)include_device_type); +} + +static int exclude_device_filter_func(const void *d, const void *exclude_device_type) { + pa_tz_device *device = (pa_tz_device *)d; + + pa_assert(device); + pa_assert(exclude_device_type); + + return (int)!pa_safe_streq(pa_tz_device_get_type(device), (const char *)exclude_device_type); +} +#endif /* __TIZEN_INTERNAL_BT_SCO__ */ + +static void handle_get_connected_device_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_device_manager *dm = (pa_device_manager *)userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, array_iter; + int mask; +#ifndef __TIZEN_INTERNAL_BT_SCO__ + pa_idxset *idxset1, *idxset2; +#endif + + pa_assert(conn); + pa_assert(msg); + pa_assert(dm); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_INT32, &mask, + DBUS_TYPE_INVALID)); + + pa_log_info("Get connected device list (mask : %d)", mask); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "(isiisiib)", &array_iter)); + +#ifdef __TIZEN_INTERNAL_BT_SCO__ + array_iter_append(&array_iter, dm->device_list, mask); +#else + /* divide into two groups and merge them because dbus message doesn't support sorting or prepend */ + idxset1 = pa_idxset_filtered_copy(dm->device_list, NULL, include_device_filter_func, DEVICE_TYPE_BT_SCO); + idxset2 = pa_idxset_filtered_copy(dm->device_list, NULL, exclude_device_filter_func, DEVICE_TYPE_BT_SCO); + + array_iter_append(&array_iter, idxset1, mask); + array_iter_append(&array_iter, idxset2, mask); + + pa_idxset_free(idxset1, NULL); + pa_idxset_free(idxset2, NULL); +#endif /* __TIZEN_INTERNAL_BT_SCO__ */ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &array_iter)); -- 2.7.4 From 64302e3895000bd0d1d1e9c6a068463ab049f1be Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Mon, 13 Sep 2021 09:16:49 +0900 Subject: [PATCH 15/16] device-manager: Fix svace defect (DEREF_OF_NULL.RET.PROC.STAT) [Version] 13.0.73 [Issue Type] Svace Change-Id: If07027bfc1f37a296aedf440e03fa04040c42ad1 --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/device-manager.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index b58e363..2092eeb 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.72 +Version: 13.0.73 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/device-manager.c b/src/device-manager.c index 4e3d132..16e5641 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -610,7 +610,7 @@ static char* device_class_get_module_name(dm_device_class_t device_class, const class = pa_split(device_string, ":", &state); - if (!pa_streq(class, prefix)) { + if (!pa_safe_streq(class, prefix)) { if (!pa_atoi(class + strlen(prefix), &v)) snprintf(version, sizeof(version), "%d", v); else -- 2.7.4 From 5a9800395039ec6f9f11ddac6d2e8d9dfaad8307 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Wed, 8 Sep 2021 13:49:32 +0900 Subject: [PATCH 16/16] tizenaudio-sink2/source2: Fix frames_to_write calculation [Version] 13.0.74 [Issue Type] Bug fix Change-Id: Ida9fcab0c380fc71125f64dc90d8a186840f40ae Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tizenaudio-sink.c | 2 +- src/module-tizenaudio-sink2.c | 4 ++-- src/module-tizenaudio-source2.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 2092eeb..39724b8 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 13.0.73 +Version: 13.0.74 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-sink.c b/src/module-tizenaudio-sink.c index 62a12d5..de01053 100644 --- a/src/module-tizenaudio-sink.c +++ b/src/module-tizenaudio-sink.c @@ -430,7 +430,7 @@ static int process_render(struct userdata *u, pa_usec_t now) { /* Write pcm every 10ms */ frames_to_write = pa_usec_to_bytes((10 * PA_USEC_PER_MSEC), &u->sink->sample_spec) / frame_size; #else - frames_to_write = (u->frag_size / u->nfrags); + frames_to_write = u->frag_size / frame_size; #endif if (frames_to_write > avail) break; diff --git a/src/module-tizenaudio-sink2.c b/src/module-tizenaudio-sink2.c index e4e61e9..fe064fb 100644 --- a/src/module-tizenaudio-sink2.c +++ b/src/module-tizenaudio-sink2.c @@ -312,7 +312,7 @@ do_nothing: static int process_render(struct userdata *u) { void *p; size_t frame_size = pa_frame_size(&u->sink->sample_spec); - size_t frames_to_write = u->frag_size / u->nfrags; + size_t frames_to_write = u->frag_size / frame_size; uint32_t avail = 0; pa_memchunk chunk; @@ -324,7 +324,7 @@ static int process_render(struct userdata *u) { return 0; } - pa_sink_render_full(u->sink, frames_to_write * frame_size, &chunk); + pa_sink_render_full(u->sink, u->frag_size, &chunk); p = pa_memblock_acquire(chunk.memblock); if (pa_hal_interface_pcm_write(u->hal_interface, u->pcm_handle, (const char*)p + chunk.index, (uint32_t)frames_to_write)) { diff --git a/src/module-tizenaudio-source2.c b/src/module-tizenaudio-source2.c index 333bdbb..55bc60a 100644 --- a/src/module-tizenaudio-source2.c +++ b/src/module-tizenaudio-source2.c @@ -275,7 +275,7 @@ static int source_process_msg( static int process_render(struct userdata *u) { void *p; size_t frame_size = pa_frame_size(&u->source->sample_spec); - size_t frames_to_read = u->frag_size / u->nfrags; + size_t frames_to_read = u->frag_size / frame_size; uint32_t avail = 0; pa_memchunk chunk; @@ -289,7 +289,7 @@ static int process_render(struct userdata *u) { return 0; } - chunk.length = frames_to_read * frame_size; + chunk.length = u->frag_size; chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); p = pa_memblock_acquire(chunk.memblock); -- 2.7.4