From cf37f9eacd4010e7fefa7fa2f97720b03fbb44bd Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Thu, 2 Apr 2020 08:43:22 +0900 Subject: [PATCH 01/16] stream-manager: Check role/route_type functions to skip process are revised check_role_to_skip()/check_route_type_to_skip() are renamed to is_invalid_role()/is_invalid_route_type() respectively. Specific conditions inside of the previous functions are moved out of the new renamed functions. [Version] 13.0.10 [Issue Type] Refactoring Change-Id: Ie6ec2e0a3b42078d1265e15ef35db240daad2111 Signed-off-by: Sangchul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/stream-manager.c | 73 ++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index b9b0c18..c465e25 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.10 +Version: 13.0.11 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/stream-manager.c b/src/stream-manager.c index 72e26bb..8ca0809 100644 --- a/src/stream-manager.c +++ b/src/stream-manager.c @@ -982,43 +982,35 @@ static bool check_name_to_skip(pa_stream_manager *m, process_command_type_t comm return ret; } -static bool check_role_to_skip(pa_stream_manager *m, process_command_type_t command, const char *role) { - bool ret = true; +static bool is_invalid_role(pa_stream_manager *m, const char *role) { stream_info *s = NULL; pa_assert(m); pa_assert(m->stream_infos); pa_assert(role); - if ((s = pa_hashmap_get(m->stream_infos, role))) - ret = false; - if (s && pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING) && - (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED || - command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)) - ret = true; - - pa_log_debug("role is [%s], skip(%d)", role, ret); + if (!(s = pa_hashmap_get(m->stream_infos, role))) { + pa_log_warn("invalid role[%s], skip it", role); + return true; + } - return ret; + return false; } -static bool check_route_type_to_skip(process_command_type_t command, const char *route_type_str) { - bool ret = false; - stream_route_type_t route_type; +static bool is_invalid_route_type(const char *route_type_str, stream_route_type_t *route_type) { + int32_t _route_type; pa_assert(route_type_str); - if (!pa_atoi(route_type_str, (int32_t*)&route_type)) { - if ((route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) && - (command > PROCESS_COMMAND_PREPARE && command < PROCESS_COMMAND_UPDATE_VOLUME)) - ret = true; /* this route route is for external device, need to skip */ - } else { + if (pa_atoi(route_type_str, &_route_type)) { pa_log_error("could not convert route_type_str(%s) to int", route_type_str); - ret = true; + return true; } - pa_log_debug("command is [%s], skip(%d) for route_type(%d)", process_command_type_str[command], ret, route_type); - return ret; + if (route_type) + *route_type = (stream_route_type_t)_route_type; + + return false; } static bool check_name_is_vstream(void *stream, stream_type_t type, bool is_new_data) { @@ -1999,7 +1991,7 @@ static process_stream_result_t handle_command_prepare(pa_stream_manager *m, void pa_log_warn("role is null, set default to [%s]", role); } else { /* skip roles */ - if (check_role_to_skip(m, PROCESS_COMMAND_PREPARE, role)) + if (is_invalid_role(m, role)) return PROCESS_STREAM_RESULT_SKIP; /* load forwarding device */ @@ -2022,7 +2014,7 @@ static process_stream_result_t handle_command_prepare(pa_stream_manager *m, void /* skip route types */ if ((route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) { - if (check_route_type_to_skip(PROCESS_COMMAND_PREPARE, route_type_str)) + if (is_invalid_route_type(route_type_str, NULL)) return PROCESS_STREAM_RESULT_SKIP; } @@ -2048,6 +2040,7 @@ static process_stream_result_t handle_command_change_route_by_stream_started(pa_ stream_type_t type, bool is_new_data) { const char *role = NULL; const char *route_type_str = NULL; + stream_route_type_t route_type; const char *preferred_device_role; bool need_update = false; @@ -2080,11 +2073,17 @@ static process_stream_result_t handle_command_change_route_by_stream_started(pa_ } /* skip roles */ - if (check_role_to_skip(m, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, role)) + if (is_invalid_role(m, role)) + return PROCESS_STREAM_RESULT_SKIP; + if (pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING)) { + pa_log_debug("role is [%s], skip it", role); return PROCESS_STREAM_RESULT_SKIP; + } /* skip route types */ - if (check_route_type_to_skip(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, route_type_str)) + if (is_invalid_route_type(route_type_str, &route_type)) + return PROCESS_STREAM_RESULT_SKIP; + if (route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) return PROCESS_STREAM_RESULT_SKIP; if (!is_new_data) { @@ -2165,12 +2164,19 @@ static process_stream_result_t handle_command_change_route_by_stream_ended(pa_st update_forwarding_device(m, false); /* skip roles */ - if (check_role_to_skip(m, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, role)) + if (is_invalid_role(m, role)) + return PROCESS_STREAM_RESULT_SKIP; + if (pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING)) { + pa_log_debug("role is [%s], skip it", role); return PROCESS_STREAM_RESULT_SKIP; + } /* skip route types */ if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) { - if (check_route_type_to_skip(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, route_type_str)) + stream_route_type_t route_type; + if (is_invalid_route_type(route_type_str, &route_type)) + return PROCESS_STREAM_RESULT_SKIP; + if (route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) return PROCESS_STREAM_RESULT_SKIP; } @@ -2213,12 +2219,15 @@ static process_stream_result_t handle_command_change_route_by_stream_focus_chang } /* skip roles */ - if (check_role_to_skip(m, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED, role)) + if (is_invalid_role(m, role)) return PROCESS_STREAM_RESULT_SKIP; /* skip route types */ if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) { - if (check_route_type_to_skip(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED, route_type_str)) + stream_route_type_t route_type; + if (is_invalid_route_type(route_type_str, &route_type)) + return PROCESS_STREAM_RESULT_SKIP; + if (route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) return PROCESS_STREAM_RESULT_SKIP; } @@ -2302,12 +2311,12 @@ static process_stream_result_t handle_command_add_remove_parent_id(pa_stream_man } /* skip roles */ - if (check_role_to_skip(m, command, role)) + if (is_invalid_role(m, role)) return PROCESS_STREAM_RESULT_SKIP; /* skip route types */ if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) { - if (check_route_type_to_skip(command, route_type_str)) + if (is_invalid_route_type(route_type_str, NULL)) return PROCESS_STREAM_RESULT_SKIP; } -- 2.7.4 From 948b1b5a54f7e1c69e5e0dff57f817ecf271703e Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Wed, 8 Apr 2020 15:06:48 +0900 Subject: [PATCH 02/16] sound-player: Adds error return for not supported media type added checking not support media type error and checking invalid handle in sound_stop function. in this case, sound_server returns org.tizen.multimedia.audio.InvalidState and it changes to INVALID_OPERATION [Version] 13.0.12 [Issue Type] Add Change-Id: I35db85891d6c54f6af09449668e09ea01c09ff7a Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-sound-player.c | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index c465e25..e71fd06 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.11 +Version: 13.0.12 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-sound-player.c b/src/module-sound-player.c index f23eed1..a1fe61b 100644 --- a/src/module-sound-player.c +++ b/src/module-sound-player.c @@ -491,22 +491,26 @@ static void handle_sound_play(DBusConnection *conn, DBusMessage *msg, void *user ret = pa_play_file_repeat(pa_namereg_get(u->module->core, NULL, PA_NAMEREG_SINK), filename, NULL, p, repeat, &stream_idx); if (ret != 0) { - pa_log_error("pa_play_file_repeat failed.\n"); + pa_dbus_send_error(conn, msg, DBUS_ERROR_NOT_SUPPORTED, "%s", + "org.tizen.multimedia.audio.UnsupportedMediaType"); goto exit; } pa_idxset_put(u->stream_idxs, pa_xmemdup(&stream_idx, sizeof(stream_idx)), NULL); result = (dbus_int32_t)stream_idx; + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_INT32, &result); + exit: pa_proplist_free(p); - pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_INT32, &result); } static void handle_sound_stop(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_sink_input *si; uint32_t stream_idx; struct userdata *u = (struct userdata *)userdata; + int32_t *i; + uint32_t idx; pa_assert(conn); pa_assert(msg); @@ -517,18 +521,19 @@ static void handle_sound_stop(DBusConnection *conn, DBusMessage *msg, void *user DBUS_TYPE_INVALID)); si = pa_idxset_get_by_index(u->module->core->sink_inputs, stream_idx); - if (si != NULL) { - int32_t *i = NULL; - uint32_t idx = 0; - - PA_IDXSET_FOREACH(i, u->stream_idxs, idx) { - if (*i == stream_idx) { - pa_idxset_remove_by_data(u->stream_idxs, i, NULL); - pa_xfree(i); - } + if (!si) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "%s", + "org.tizen.multimedia.audio.InvalidState"); + return; + } + + PA_IDXSET_FOREACH(i, u->stream_idxs, idx) { + if (*i == stream_idx) { + pa_idxset_remove_by_data(u->stream_idxs, i, NULL); + pa_xfree(i); } - pa_sink_input_unlink(si); } + pa_sink_input_unlink(si); pa_dbus_send_empty_reply(conn, msg); } -- 2.7.4 From e9ee2b3cc02b4fc461b51936bfa1a0a79d9102f6 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Wed, 22 Apr 2020 10:32:41 +0900 Subject: [PATCH 03/16] sound-player: Fix memory leak in stop APIs Every time stop APIs called, memory leak found. The reason why sink_input's userdata that has wrapper structure such as memblockq_stream, file_stream should be freed. [Version] 13.0.13 [Issue Type] Bug Change-Id: I221a6ace56b4c57f288c263c6e3b5fe5abf5e38a Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-sound-player.c | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index e71fd06..ba1d2cc 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.12 +Version: 13.0.13 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-sound-player.c b/src/module-sound-player.c index a1fe61b..7071e9c 100644 --- a/src/module-sound-player.c +++ b/src/module-sound-player.c @@ -290,8 +290,8 @@ static void _simple_stop(struct userdata *u, const char *file_path) { PA_IDXSET_FOREACH(si, u->module->core->sink_inputs, idx) { if (pa_safe_streq(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME), name_to_search)) { - pa_log_info("unlink sink-input : %u", si->index); - pa_sink_input_unlink(si); + pa_log_info("kill sink-input : %u", si->index); + pa_sink_input_kill(si); } } @@ -309,8 +309,8 @@ static void _simple_stop_all(struct userdata *u) { media_name = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME); if (media_name && pa_strneq(media_name, KEYTONE_PREFIX, strlen(KEYTONE_PREFIX))) { - pa_log_info("unlink sink-input : %u", si->index); - pa_sink_input_unlink(si); + pa_log_info("kill sink-input : %u", si->index); + pa_sink_input_kill(si); } } } @@ -533,7 +533,8 @@ static void handle_sound_stop(DBusConnection *conn, DBusMessage *msg, void *user pa_xfree(i); } } - pa_sink_input_unlink(si); + + pa_sink_input_kill(si); pa_dbus_send_empty_reply(conn, msg); } -- 2.7.4 From 7e66915a5148758be246b799eca48c09665ec207 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Thu, 23 Apr 2020 17:12:13 +0900 Subject: [PATCH 04/16] tizenaudio-policy: Removes unused pcm dump define [Version] 13.0.14 [Issue Type] None Change-Id: I83e01e8eeb120ad76f68d7f9070e1e40f0543be9 Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tizenaudio-policy.c | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index ba1d2cc..0c13813 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.13 +Version: 13.0.14 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-policy.c b/src/module-tizenaudio-policy.c index 9764fb9..56ecdbc 100644 --- a/src/module-tizenaudio-policy.c +++ b/src/module-tizenaudio-policy.c @@ -183,7 +183,6 @@ struct userdata { static void __load_dump_config(struct userdata *u) { dictionary * dict = NULL; - int vconf_dump = 0; dict = iniparser_load(PA_DUMP_INI_DEFAULT_PATH); if (!dict) { @@ -195,24 +194,12 @@ static void __load_dump_config(struct userdata *u) } } - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:decoder_out", 0) ? PA_PCM_DUMP_GST_DECODER_OUT : 0; - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_in", 0) ? PA_PCM_DUMP_GST_RESAMPLER_IN : 0; - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_out", 0) ? PA_PCM_DUMP_GST_RESAMPLER_OUT : 0; - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:gst_audio_sink", 0) ? PA_PCM_DUMP_GST_AUDIO_SINK_IN : 0; - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:pa_stream_write", 0) ? PA_PCM_DUMP_PA_STREAM_WRITE : 0; u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink_input", 0) ? PA_PCM_DUMP_PA_SINK_INPUT : 0; u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink", 0) ? PA_PCM_DUMP_PA_SINK : 0; u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source", 0) ? PA_PCM_DUMP_PA_SOURCE : 0; u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source_output", 0) ? PA_PCM_DUMP_PA_SOURCE_OUTPUT : 0; - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:pa_stream_read", 0) ? PA_PCM_DUMP_PA_STREAM_READ : 0; - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:gst_audio_src", 0) ? PA_PCM_DUMP_GST_AUDIO_SRC_OUT : 0; - vconf_dump |= iniparser_getboolean(dict, "pcm_dump:encoder_in", 0) ? PA_PCM_DUMP_GST_ENCODER_IN : 0; iniparser_freedict(dict); - - if (vconf_set_int(PA_PCM_DUMP_VCONF_KEY, vconf_dump)) { - pa_log_warn("vconf_set_int %s=%x failed", PA_PCM_DUMP_VCONF_KEY, vconf_dump); - } } /* threre is only one sco connected device */ -- 2.7.4 From 830dcfafdee38aad7e78564c82a2a7f56882ea89 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Tue, 28 Apr 2020 10:27:38 +0900 Subject: [PATCH 05/16] Changes PA_PCM_DUMP_PA_* to PA_PCM_DUMP_* Change-Id: Ie02cdb0a276b0f440057bb6b1124e1d5076f3124 Signed-off-by: Jaechul Lee --- src/module-tizenaudio-policy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/module-tizenaudio-policy.c b/src/module-tizenaudio-policy.c index 56ecdbc..2ba8198 100644 --- a/src/module-tizenaudio-policy.c +++ b/src/module-tizenaudio-policy.c @@ -194,10 +194,10 @@ static void __load_dump_config(struct userdata *u) } } - u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink_input", 0) ? PA_PCM_DUMP_PA_SINK_INPUT : 0; - u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink", 0) ? PA_PCM_DUMP_PA_SINK : 0; - u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source", 0) ? PA_PCM_DUMP_PA_SOURCE : 0; - u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source_output", 0) ? PA_PCM_DUMP_PA_SOURCE_OUTPUT : 0; + u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink_input", 0) ? PA_PCM_DUMP_SINK_INPUT : 0; + u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink", 0) ? PA_PCM_DUMP_SINK : 0; + u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source", 0) ? PA_PCM_DUMP_SOURCE : 0; + u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source_output", 0) ? PA_PCM_DUMP_SOURCE_OUTPUT : 0; iniparser_freedict(dict); } -- 2.7.4 From 25a91a14803c4885f1dd05273e16cfaeb4e54d79 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Mon, 4 May 2020 10:31:10 +0900 Subject: [PATCH 06/16] Fix resource leaks when pulseaudio is terminated This resource leak makes abort signal in core_free function and then spends times to make unnecessary crash dump. Change-Id: Iccc7cdce2e60985f73a819e83cf7f4887cfdaac9 Signed-off-by: Jaechul Lee --- src/module-sound-player.c | 2 ++ src/stream-manager-dbus.c | 2 ++ src/stream-manager.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/module-sound-player.c b/src/module-sound-player.c index 7071e9c..143b975 100644 --- a/src/module-sound-player.c +++ b/src/module-sound-player.c @@ -695,6 +695,8 @@ static void deinit_ipc(struct userdata *u) { if (u->dbus_conn) { if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(u->dbus_conn), SOUND_PLAYER_OBJECT_PATH)) pa_log_error("Failed to unregister object path"); + + pa_dbus_connection_unref(u->dbus_conn); u->dbus_conn = NULL; } #endif diff --git a/src/stream-manager-dbus.c b/src/stream-manager-dbus.c index 14ed732..64adff1 100644 --- a/src/stream-manager-dbus.c +++ b/src/stream-manager-dbus.c @@ -2480,6 +2480,8 @@ void deinit_sm_dbus(pa_stream_manager *m) { if (m->dbus_conn) { if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(m->dbus_conn), STREAM_MANAGER_OBJECT_PATH)) pa_log_error("failed to unregister object path"); + + pa_dbus_connection_unref(m->dbus_conn); m->dbus_conn = NULL; } #endif diff --git a/src/stream-manager.c b/src/stream-manager.c index 8ca0809..6411be7 100644 --- a/src/stream-manager.c +++ b/src/stream-manager.c @@ -3873,5 +3873,7 @@ void pa_stream_manager_unref(pa_stream_manager *m) { pa_hal_interface_unref(m->hal); } + pa_shared_remove(m->core, SHARED_STREAM_MANAGER); + pa_xfree(m); } -- 2.7.4 From 7be887bf0448e678a9546ca333ccd449b39c20ba Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Fri, 3 Apr 2020 09:32:53 +0900 Subject: [PATCH 07/16] tone-player: Adds new tone playback APIs These new APIs will be used by capi-media-tone-player. server : org.pulseaudio.Server object path : /org/pulseaudio/TonePlayer interface : org.pulseaudio.TonePlayer method name : TonePlay method argument : [in] unsigned int for tone index [in] signed int for duration [in] signed int for client pid [in] string for role [in] signed int for parent id [out] unsigned int for stream index return value : None method name : ToneStop method argument : [in] unsigned int for stream index return value : None [Version] 13.0.15 [Issue Type] Improvement Change-Id: I090e917e26697250bb187b0d2df8cd6042875354 Signed-off-by: Jaechul Lee --- Makefile.am | 6 + packaging/pulseaudio-modules-tizen.spec | 3 +- src/module-tone-player.c | 527 ++++++++++++++++++++++++++ src/tones.h | 649 ++++++++++++++++++++++++++++++++ 4 files changed, 1184 insertions(+), 1 deletion(-) create mode 100644 src/module-tone-player.c create mode 100644 src/tones.h diff --git a/Makefile.am b/Makefile.am index 9cb257c..6023379 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,6 +37,7 @@ pulsemodlibexec_LTLIBRARIES = \ module-tizenaudio-source.la \ module-tizenaudio-policy.la \ module-sound-player.la \ + module-tone-player.la \ module-poweroff.la if ENABLE_HALTC pulsemodlibexec_LTLIBRARIES += \ @@ -74,6 +75,11 @@ module_sound_player_la_LDFLAGS = $(MODULE_LDFLAGS) module_sound_player_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) module_sound_player_la_CFLAGS = $(MODULE_CFLAGS) $(DBUS_CFLAGS) +module_tone_player_la_SOURCES = src/module-tone-player.c +module_tone_player_la_LDFLAGS = $(MODULE_LDFLAGS) +module_tone_player_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) +module_tone_player_la_CFLAGS = $(MODULE_CFLAGS) $(DBUS_CFLAGS) + module_tizenaudio_policy_la_SOURCES = \ src/module-tizenaudio-policy.c \ src/stream-manager.c src/stream-manager.h src/stream-manager-priv.h \ diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 0c13813..9a56089 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.14 +Version: 13.0.15 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ @@ -70,6 +70,7 @@ install -m 0644 %SOURCE1 %{buildroot}%{_tmpfilesdir}/pulseaudio.conf %license LICENSE.LGPL-2.1+ %{_libdir}/pulse-13.0/modules/module-poweroff.so %{_libdir}/pulse-13.0/modules/module-sound-player.so +%{_libdir}/pulse-13.0/modules/module-tone-player.so %{_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 diff --git a/src/module-tone-player.c b/src/module-tone-player.c new file mode 100644 index 0000000..f4a64c9 --- /dev/null +++ b/src/module-tone-player.c @@ -0,0 +1,527 @@ +/*** + This file is part of PulseAudio. + + Copyright 2020 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 "tones.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_DBUS +#include +#include +#include +#endif + +PA_MODULE_AUTHOR("Jaechul Lee"); +PA_MODULE_DESCRIPTION("Tone Generator module"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); + +#define TONE_PLAYER_OBJECT_PATH "/org/pulseaudio/TonePlayer" +#define TONE_PLAYER_INTERFACE "org.pulseaudio.TonePlayer" + +#define TONE_PLAYER_METHOD_NAME_TONE_PLAY "TonePlay" +#define TONE_PLAYER_METHOD_NAME_TONE_STOP "ToneStop" + +#define TONE_SAMPLERATE 44100 +#define TONE_CHANNELS 1 +#define TONE_FORMAT 2 +#define TONE_FRAMESIZE (TONE_CHANNELS * TONE_FORMAT) + +/* + * Todo: reducing memory(lazy loading), set default rate,map for avoiding reample + */ + +#if HAVE_DBUS +struct userdata { + pa_module *module; + pa_dbus_connection *dbus_conn; +}; + +enum method_handler_index { + METHOD_HANDLER_TONE_PLAY, + METHOD_HANDLER_TONE_STOP, + METHOD_HANDLER_MAX +}; + +enum { + TONE_SOUND_MESSAGE_UNLINK = PA_SINK_INPUT_MESSAGE_MAX +}; + +struct tone_freq { + int16_t l_freq; + int16_t m_freq; + int16_t h_freq; + int16_t msec; + int16_t loop; + int16_t index; +}; + +struct tone { + struct tone_freq *info; + uint32_t num; + int32_t rbytes; + + uint16_t index; + uint32_t bytes; + uint16_t loop; + uint32_t sample; + bool silence; +}; + +inline uint32_t tone_msec_to_bytes(uint16_t msec) { + uint32_t size; + + size = TONE_SAMPLERATE * TONE_FRAMESIZE * msec / 1000; + if (size % TONE_FRAMESIZE) + size = PA_ROUND_DOWN(size, TONE_FRAMESIZE); + + return size; +} + +struct tone *tone_new(uint32_t index, int rbytes) { + struct tone *t = pa_xnew0(struct tone, 1); + + t->info = (struct tone_freq *)pa_xmemdup(tones[index], sizeof(int16_t)*TONE_ELEMENTS); + t->rbytes = rbytes; + t->num = index; + + t->silence = false; + t->loop = t->info[0].loop; + t->bytes = tone_msec_to_bytes(t->info[0].msec); + + pa_log_info("tone created. index(%u), request_bytes(%d)", t->num, t->rbytes); + + return t; +} + +void tone_free(struct tone *t) { + if (!t) + return; + + pa_xfree(t->info); + pa_xfree(t); + + return 0; +} + +int tone_peek_fixed_size(struct tone *t, pa_sink_input *si, pa_memchunk *chunk, uint32_t length) { + short *buf; + uint32_t i; + uint32_t size; + uint32_t quota = 0; + double amplitude, f1, f2, f3; + struct tone_freq* info = &(t->info[t->index]); + + /* EOS */ + if (t->rbytes != -1 && t->rbytes <= t->sample * TONE_FRAMESIZE) + return -1; + + /* silence */ + if (t->silence) + info->l_freq = info->m_freq = info->h_freq = 0; + + t->bytes -= size = PA_MIN(t->bytes, length); + + if (info->l_freq > 0) quota++; + if (info->m_freq > 0) quota++; + if (info->h_freq > 0) quota++; + + chunk->index = 0; + chunk->length = size; + chunk->memblock = pa_memblock_new(si->core->mempool, chunk->length); + buf = (short *)pa_memblock_acquire(chunk->memblock); + + for (i = 0; i < size / TONE_FRAMESIZE; i++) { + f1 = sin(2. * M_PI * info->l_freq * t->sample / TONE_SAMPLERATE); + f2 = sin(2. * M_PI * info->m_freq * t->sample / TONE_SAMPLERATE); + f3 = sin(2. * M_PI * info->h_freq * t->sample / TONE_SAMPLERATE); + + if (quota > 0) { + amplitude = (f1 + f2 + f3) / quota; + amplitude *= 1.0; + amplitude *= 32767; + } else { + amplitude = 0.; + } + *(buf++) = (short)amplitude; + t->sample++; + } + pa_memblock_release(chunk->memblock); + + /* calc index */ + if (t->bytes == 0) { + if (info->loop > 0) { + info->loop -= 1; + t->index = info->index; + t->bytes = tone_msec_to_bytes(t->info[t->index].msec); + return 0; + } + + /* check next item */ + info += 1; + t->index += 1; + + if (info->l_freq == -1 && info->m_freq == -1 && info->h_freq == -1) { + if (info->loop == 0) { /* infinite loop */ + memcpy(t->info, tones[t->num], sizeof(uint16_t)*TONE_ELEMENTS); + t->index = info->index; + } else if (info->loop == 1) { /* last playback */ + t->silence = true; + pa_log_debug("silence stream start"); + } else { /* counting loops */ + uint16_t tmp = info->loop - 1; + memcpy(t->info, tones[t->num], sizeof(uint16_t)*TONE_ELEMENTS); + t->info[t->index].loop = tmp; + t->index = info->index; + } + } + t->bytes = tone_msec_to_bytes(t->info[t->index].msec); + } + + pa_log_debug("%hd,%hd,%hd %hd remain(%d), size(%u), index(%d)", + info->l_freq, info->m_freq, info->h_freq, info->msec, + t->bytes, size, t->index); + + return 0; +} + +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { + struct tone *t = (struct tone *)i->userdata; + + if (!t) + return -1; + + if (tone_peek_fixed_size(t, i, chunk, length) < 0) { + if (pa_sink_input_safe_to_remove(i)) { + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(i), + TONE_SOUND_MESSAGE_UNLINK, NULL, 0, NULL, NULL); + } + return -1; + } + + chunk->length = PA_MIN(chunk->length, length); + + return 0; +} + +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct tone *t = i->userdata; + + if (!t) + return; +} + +static void sink_input_kill_cb(pa_sink_input *i) { + struct tone *t = i->userdata; + + tone_free(t); + pa_sink_input_unlink(i); + pa_sink_input_unref(i); +} + +static int sink_input_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { + pa_sink_input *i = PA_SINK_INPUT(o); + + switch (code) { + case TONE_SOUND_MESSAGE_UNLINK: + pa_sink_input_kill(i); + break; + } + + return pa_sink_input_process_msg(o, code, userdata, offset, chunk); +} + +static void handle_tone_play(DBusConnection *conn, DBusMessage *msg, void *userdata) { + dbus_uint32_t result = -1; + struct userdata *u = (struct userdata *)userdata; + + uint32_t index; + int32_t duration; + uint32_t client_pid; + const char *role; + uint32_t parent_id; + + struct tone *t; + pa_sink_input *i; + pa_sample_spec ss; + pa_sink_input_new_data data; + int32_t request_bytes; + pa_proplist *p = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(userdata); + + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_UINT32, &index, + DBUS_TYPE_INT32, &duration, + DBUS_TYPE_INT32, &client_pid, + DBUS_TYPE_STRING, &role, + DBUS_TYPE_INT32, &parent_id, + DBUS_TYPE_INVALID)); + + pa_log_info("index(%u), duration(%d), pid(%d), role(%s), parent_id(%d)", + index, duration, client_pid, role, parent_id); + + if (index >= sizeof(tones)/sizeof(uint16_t)/TONE_ELEMENTS) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", + "org.tizen.multimedia.InvalidArgument"); + goto exit; + } + + p = pa_proplist_new(); + pa_proplist_sets(p, PA_PROP_MEDIA_ROLE, role); + pa_proplist_setf(p, PA_PROP_APPLICATION_PROCESS_ID_ORIGIN, "%d", client_pid); + pa_proplist_setf(p, PA_PROP_MEDIA_PARENT_ID, "%d", parent_id); + + ss.format = PA_SAMPLE_S16LE; + ss.rate = TONE_SAMPLERATE; + ss.channels = TONE_CHANNELS; + + pa_sink_input_new_data_init(&data); + pa_sink_input_new_data_set_sink(&data, pa_namereg_get(u->module->core, NULL, PA_NAMEREG_SINK), false, true); + data.driver = __FILE__; + pa_sink_input_new_data_set_sample_spec(&data, &ss); + pa_sink_input_new_data_set_volume(&data, NULL); + pa_sink_input_new(&i, u->module->core, &data); + pa_sink_input_new_data_done(&data); + + request_bytes = duration != -1 ? pa_usec_to_bytes(duration*PA_MSEC_PER_SEC, &ss) : -1; + + t = tone_new(index, request_bytes); + if (!t) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", + "org.tizen.multimedia.OutOfMemory"); + goto exit; + } + + i->userdata = t; + i->pop = sink_input_pop_cb; + i->kill = sink_input_kill_cb; + i->process_rewind = sink_input_process_rewind_cb; + i->parent.process_msg = sink_input_process_msg; + + pa_sink_input_put(i); + + result = (dbus_uint32_t)i->index; + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &result); + +exit: + if (p) + pa_proplist_free(p); +} + +static void handle_tone_stop(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = (struct userdata *)userdata; + uint32_t stream_idx; + pa_sink_input *si; + + pa_assert(conn); + pa_assert(msg); + pa_assert(userdata); + + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_UINT32, &stream_idx, + DBUS_TYPE_INVALID)); + + pa_log_info("stop tone %u", stream_idx); + + si = pa_idxset_get_by_index(u->module->core->sink_inputs, stream_idx); + if (!si) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "%s", + "org.tizen.multimedia.audio.InvalidState"); + return; + } + + pa_sink_input_kill(si); + + pa_dbus_send_empty_reply(conn, msg); +} + +#define TONE_PLAYER_INTROSPECT_XML \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "" \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + "" + +static pa_dbus_arg_info tone_play_args[] = { { "index", "u", "in" }, + { "duration", "i", "in" }, + { "client_pid", "i", "in" }, + { "role", "s", "in" }, + { "parent_id", "i", "in" } }; +static pa_dbus_arg_info tone_stop_args[] = { { "stream_idx", "u", "in" } }; +static const char* signature_args_for_in[] = { "uiisi", "u" }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_TONE_PLAY] = { + .method_name = TONE_PLAYER_METHOD_NAME_TONE_PLAY, + .arguments = tone_play_args, + .n_arguments = sizeof(tone_play_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_tone_play }, + [METHOD_HANDLER_TONE_STOP] = { + .method_name = TONE_PLAYER_METHOD_NAME_TONE_STOP, + .arguments = tone_stop_args, + .n_arguments = sizeof(tone_stop_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_tone_stop } +}; + +static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, void *userdata) { + const char *xml = TONE_PLAYER_INTROSPECT_XML; + DBusMessage *r = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(userdata); + + pa_assert_se(r = dbus_message_new_method_return(msg)); + pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); + + if (r) { + pa_assert_se(dbus_connection_send((conn), r, NULL)); + dbus_message_unref(r); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata) { + int idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(userdata); + + for (idx = 0; idx < METHOD_HANDLER_MAX; idx++) { + if (dbus_message_is_method_call(msg, TONE_PLAYER_INTERFACE, method_handlers[idx].method_name)) { + if (pa_safe_streq(dbus_message_get_signature(msg), signature_args_for_in[idx])) { + method_handlers[idx].receive_cb(conn, msg, userdata); + return DBUS_HANDLER_RESULT_HANDLED; + } else { + pa_log_warn("Wrong Argument Signature"); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_SIGNATURE, "Wrong Signature, Expected %s", signature_args_for_in[idx]); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult method_handler_for_vt(DBusConnection *c, DBusMessage *m, void *userdata) { + struct userdata *u = userdata; + const char *path, *interface, *member; + + pa_assert(c); + 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, TONE_PLAYER_OBJECT_PATH)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) + return handle_introspect(c, m, u); + else + return handle_methods(c, m, u); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +int pa__init(pa_module *m) { + struct userdata *u; + DBusError err; + + pa_dbus_connection *conn = NULL; + static const DBusObjectPathVTable vtable = { + .message_function = method_handler_for_vt, + }; + pa_assert(m); + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->module = m; + + if (!(conn = pa_dbus_bus_get(u->module->core, DBUS_BUS_SYSTEM, &err)) || dbus_error_is_set(&err)) { + if (conn) + pa_dbus_connection_unref(conn); + + pa_log_error("Unable to contact D-Bus system bus: %s: %s", err.name, err.message); + } else + pa_log_debug("Got dbus connection"); + + u->dbus_conn = conn; + pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(conn), + TONE_PLAYER_OBJECT_PATH, &vtable, u)); + + return 0; +} + +void pa__done(pa_module *m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->dbus_conn) + pa_dbus_connection_unref(u->dbus_conn); + + pa_xfree(u); +} +#endif + diff --git a/src/tones.h b/src/tones.h new file mode 100644 index 0000000..9616d14 --- /dev/null +++ b/src/tones.h @@ -0,0 +1,649 @@ +/*** + This file is part of PulseAudio. + + Copyright 2020 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 footoneplayerfoo +#define footoneplayerfoo +#include + +#define TONE_ELEMENTS_ROW 14 +#define TONE_ELEMENTS_COLUMN 6 +#define TONE_ELEMENTS TONE_ELEMENTS_ROW * TONE_ELEMENTS_COLUMN + +/* need to improve: 18KiB approximately */ +static const uint16_t tones[][TONE_ELEMENTS] = { + {941, 1336, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 0 key: 1336Hz, 941Hz + + {697, 1209, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 1 key: 1209Hz, 697Hz + + {697, 1336, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 2 key: 1336Hz, 697Hz + + {697, 1477, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 3 key: 1477Hz, 697Hz + + {770, 1209, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 4 key: 1209Hz, 770Hz + + {770, 1336, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 5 key: 1336Hz, 770Hz + + {770, 1477, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 6 key: 1477Hz, 770Hz + + {852, 1209, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 7 key: 1209Hz, 852Hz + + {852, 1336, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 8 key: 1336Hz, 852Hz + + {852, 1477, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // 9 key: 1477Hz, 852Hz + + // 10 + {941, 1209, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // * key: 1209Hz, 941Hz + + {941, 1477, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // # key: 1477Hz, 941Hz + + {697, 1633, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // A key: 1633Hz, 697Hz + + {770, 1633, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // B key: 1633Hz, 770Hz + + {852, 1633, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // C key: 1633Hz, 852Hz + + {941, 1633, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, // D key: 1633Hz, 941Hz + + {425, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Dial tone: CEPT: 425Hz, continuous + + {350, 440, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Dial tone: ANSI (IS-95): 350Hz+440Hz, continuous + + {400, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Dial tone: JAPAN: 400Hz, continuous + + {425, 0, 0, 500, 0, 0, + 0, 0, 0, 500, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Busy: CEPT: 425Hz, 500ms ON, 500ms OFF... + + // 20 + {480, 620, 0, 500, 0, 0, + 0, 0, 0, 500, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Busy: ANSI (IS-95): 480Hz+620Hz, 500ms ON, 500ms OFF... + + {400, 0, 0, 500, 0, 0, + 0, 0, 0, 500, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Busy: JAPAN: 400Hz, 500ms ON, 500ms OFF... + + {425, 0, 0, 200, 0, 0, + 0, 0, 0, 200, 0, 0, + -1, -1, -1, -1, 1, 0}, //Call supervisory tone, Congestion: CEPT, JAPAN: 425Hz, 200ms ON, 200ms OFF + + {480, 620, 0, 250, 0, 0, + 0, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Congestion: ANSI (IS-95): 480Hz+620Hz, 250ms ON, 250ms OFF... + + {425, 0, 0, 200, 0, 0, + -1, -1, -1, -1, 1, 0}, //Call supervisory tone, Radio path acknowledgment : CEPT, ANSI: 425Hz, 200ms ON + + {400, 0, 0, 1000, 0, 0, + 0, 0, 0, 2000, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Radio path acknowledgment : JAPAN: 400Hz, 1s ON, 2s OFF... + + {425, 0, 0, 200, 0, 0, + 0, 0, 0, 200, 0, 0, + -1, -1, -1, -1, 3, 0}, //Call supervisory tone, Radio path not available: 425Hz, 200ms ON, 200 OFF 3 bursts + + {950, 1400, 1800, 330, 0, 0, + 0, 0, 0, 1000, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Error/Special info: 950Hz+1400Hz+1800Hz, 330ms ON, 1s OFF... + + {425, 0, 0, 200, 0, 0, + 0, 0, 0, 600, 0, 0, + 425, 0, 0, 200, 0, 0, + 0, 0, 0, 3000, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Call Waiting: CEPT, JAPAN: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... + + {440, 0, 0, 300, 0, 0, + 0, 0, 0, 9700, 0, 0, + 440, 0, 0, 100, 0, 0, + 0, 0, 0, 100, 0, 0, + 440, 0, 0, 100, 0, 0, + 0, 0, 0, 9700, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Call Waiting: ANSI (IS-95): 440 Hz, 300 ms ON, 9.7 s OFF, (100 ms ON, 100 ms OFF, 100 ms ON, 9.7s OFF ...) + + // 30 + {425, 0, 0, 1000, 0, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Ring Tone: CEPT, JAPAN: 425Hz, 1s ON, 4s OFF... + + {440, 480, 0, 2000, 0, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //Call supervisory tone, Ring Tone: ANSI (IS-95): 440Hz + 480Hz, 2s ON, 4s OFF... + + {400, 1200, 0, 35, 0, 0, + -1, -1, -1, -1, 1, 0}, // General beep: 400Hz+1200Hz, 35ms ON + + {1200, 0, 0, 100, 0, 0, + 0, 0, 0, 100, 0, 0, + -1, -1, -1, -1, 2, 0}, //Proprietary tone, positive acknowlegement: 1200Hz, 100ms ON, 100ms OFF 2 bursts + + {300, 400, 500, 400, 0, 0, + -1, -1, -1, -1, 1, 0}, //Proprietary tone, negative acknowlegement: 300Hz+400Hz+500Hz, 400ms ON + + {400, 1200, 0, 200, 0, 0, + -1, -1, -1, -1, 1, 0}, //Proprietary tone, prompt tone: 400Hz+1200Hz, 200ms ON + + {400, 1200, 0, 35, 0, 0, + 0, 0, 0, 200, 0, 0, + 400, 1200, 0, 35, 0, 0, + -1, -1, -1, -1, 1, 0}, //Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + + {440, 0, 0, 250, 0, 0, + 620, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 1, 0}, //Call supervisory tone (IS-95), intercept tone: alternating 440 Hz and 620 Hz tones, each on for 250 ms + + {440, 0, 0, 250, 0, 0, + 620, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 8, 0}, //Call supervisory tone (IS-95), abbreviated intercept: intercept tone limited to 4 seconds + + {480, 620, 0, 250, 0, 0, + 0, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 8, 0}, //Call supervisory tone (IS-95), abbreviated congestion: congestion tone limited to 4 seconds + + // 40 + {350, 440, 0, 100, 0, 0, + 0, 0, 0, 100, 0, 0, + -1, -1, -1, -1, 3, 0}, //Call supervisory tone (IS-95), confirm tone: a 350 Hz tone added to a 440 Hz tone repeated 3 times in a 100 ms on, 100 ms off cycle + + {480, 0, 0, 100, 0, 0, + 0, 0, 0, 100, 0, 0, + -1, -1, -1, -1, 4, 0}, //Call supervisory tone (IS-95), pip tone: four bursts of 480 Hz tone (0.1 s on, 0.1 s off). + + { 425, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, //425Hz continuous + + {440, 480, 0, 2000, 0, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA USA Ringback: 440Hz+480Hz 2s ON, 4000 OFF ... + + {440, 0, 0, 250, 0, 0, + 620, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA Intercept tone: 440Hz 250ms ON, 620Hz 250ms ON ... + + {440, 0, 0, 250, 0, 0, + 620, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 1, 0 }, //CDMA Abbr Intercept tone: 440Hz 250ms ON, 620Hz 250ms ON + + {480, 620, 0, 250, 0, 0, + 0, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA Reorder tone: 480Hz+620Hz 250ms ON, 250ms OFF... + + {480, 620, 0, 250, 0, 0, + 0, 0, 0, 250, 0, 0, + -1, -1, -1, -1, 8, 0}, //CDMA Abbr Reorder tone: 480Hz+620Hz 250ms ON, 250ms OFF repeated for 8 times + + {480, 620, 0, 500, 0, 0, + 0, 0, 0, 500, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA Network Busy tone: 480Hz+620Hz 500ms ON, 500ms OFF continuous + + {350, 440, 0, 100, 0, 0, + 0, 0, 0, 100, 0, 0, + -1, -1, -1, -1, 3, 0}, //CDMA Confirm tone: 350Hz+440Hz 100ms ON, 100ms OFF repeated for 3 times + + // 50 + {660, 1000, 0, 500, 0, 0, + 0, 0, 0, 100, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA answer tone: silent tone - definition Frequency 0, 0ms ON, 0ms OFF + + {440, 0, 0, 300, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA Network Callwaiting tone: 440Hz 300ms ON + + {480, 0, 0, 100, 0, 0, + 0, 0, 0, 100, 0, 0, + -1, -1, -1, -1, 4, 0}, //CDMA PIP tone: 480Hz 100ms ON, 100ms OFF repeated for 4 times + + {2090, 0, 0, 32, 0, 0, + 2556, 0, 0, 64, 19, 0, + 2090, 0, 0, 32, 0, 0, + 2556, 0, 0, 48, 0, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 1, 0}, //ISDN Call Signal Normal tone: {2091Hz 32ms ON, 2556 64ms ON} 20 times, 2091 32ms ON, 2556 48ms ON, 4s OFF + + {2091, 0, 0, 32, 0, 0, + 2556, 0, 0, 64, 7, 0, + 2091, 0, 0, 32, 0, 0, + 0, 0, 0, 400, 0, 0, + 2091, 0, 0, 32, 0, 0, + 2556, 0, 0, 64, 7, 4, + 2091, 0, 0, 32, 0, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 1, 0}, //ISDN Call Signal Intergroup tone: {2091Hz 32ms ON, 2556 64ms ON} 8 times, 2091Hz 32ms ON, 400ms OFF, {2091Hz 32ms ON, 2556Hz 64ms ON} 8times, 2091Hz 32ms ON, 4s OFF + + {2091, 0, 0, 32, 0, 0, + 2556, 0, 0, 64, 3, 0, + 2091, 0, 0, 32, 0, 0, + 0, 0, 0, 200, 0, 0, + 2091, 0, 0, 32, 0, 0, + 2556, 0, 0, 64, 3, 4, + 2091, 0, 0, 32, 0, 0, + 0, 0, 0, 200, 0, 0, + -1, -1, -1, -1, 1, 0},//ISDN Call Signal SP PRI tone:{2091Hz 32ms ON, 2556 64ms ON} 4 times 2091Hz 16ms ON, 200ms OFF, {2091Hz 32ms ON, 2556Hz 64ms ON} 4 times, 2091Hz 16ms ON, 200ms OFF + + {0, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 1, 0}, //ISDN Call sign PAT3 tone: silent tone + + {2091, 0, 0, 32, 0, 0, + 2556, 0, 0, 64, 4, 0, + 2091, 0, 0, 20, 0, 0, + -1, -1, -1, -1, 1, 0}, //ISDN Ping Ring tone: {2091Hz 32ms ON, 2556Hz 64ms ON} 5 times 2091Hz 20ms ON + + {0, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 1, 0}, //ISDN Pat5 tone: silent tone + + {0, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 1, 0}, //ISDN Pat6 tone: silent tone + + // 60 + {0, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 1, 0}, //ISDN Pat7 tone: silent tone + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 39, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //TONE_CDMA_HIGH_L tone: {3700Hz 25ms, 4000Hz 25ms} 40 times 4000ms OFF, Repeat .... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 39, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0},//TONE_CDMA_MED_L tone: {2600Hz 25ms, 2900Hz 25ms} 40 times 4000ms OFF, Repeat .... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 39, 0, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //TONE_CDMA_LOW_L tone: {1300Hz 25ms, 1450Hz 25ms} 40 times, 4000ms OFF, Repeat .... + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 15, 0, + 0, 0, 0, 400, 0, 0, + -1, -1, -1, -1, 0, 0},//CDMA HIGH SS tone: {3700Hz 25ms, 4000Hz 25ms} repeat 16 times, 400ms OFF, repeat .... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 15, 0, + 0, 0, 0, 400, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA MED SS tone: {2600Hz 25ms, 2900Hz 25ms} repeat 16 times, 400ms OFF, repeat .... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 15, 0, + 0, 0, 0, 400, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA LOW SS tone: {1300z 25ms, 1450Hz 25ms} repeat 16 times, 400ms OFF, repeat .... + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 15, 6, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA HIGH SSL tone: {3700Hz 25ms, 4000Hz 25ms} 8 times, 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} repeat 8 times, 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} repeat 16 times, 4000ms OFF, repeat ... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 15, 6, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA MED SSL tone: {2600Hz 25ms, 2900Hz 25ms} 8 times, 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} repeat 8 times, 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} repeat 16 times, 4000ms OFF, repeat ... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 15, 6, + 0, 0, 0, 4000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA LOW SSL tone: {1300Hz 25ms, 1450Hz 25ms} 8 times, 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} repeat 8 times, 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} repeat 16 times, 4000ms OFF, repeat ... + + // 70 + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 19, 0, + 0, 0, 0, 1000, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 19, 3, + 0, 0, 0, 3000, 0, 0, + -1, -1, -1, -1, 0, 0 },//CDMA HIGH SS2 tone: {3700Hz 25ms, 4000Hz 25ms} 20 times, 1000ms OFF, {3700Hz 25ms, 4000Hz 25ms} 20 times, 3000ms OFF, repeat .... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 19, 0, + 0, 0, 0, 1000, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 19, 3, + 0, 0, 0, 3000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA MED SS2 tone: {2600Hz 25ms, 2900Hz 25ms} 20 times, 1000ms OFF, {2600Hz 25ms, 2900Hz 25ms} 20 times, 3000ms OFF, repeat .... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 19, 0, + 0, 0, 0, 1000, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 19, 3, + 0, 0, 0, 3000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA LOW SS2 tone: {1300Hz 25ms, 1450Hz 25ms} 20 times, 1000ms OFF, {1300Hz 25ms, 1450Hz 25ms} 20 times, 3000ms OFF, repeat .... + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 9, 0, + 0, 0, 0, 500, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 19, 3, + 0, 0, 0, 500, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 9, 6, + 0, 0, 0, 3000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA HIGH SLS tone: {3700Hz 25ms, 4000Hz 25ms} 10 times, 500ms OFF, {3700Hz 25ms, 4000Hz 25ms} 20 times, 500ms OFF, {3700Hz 25ms, 4000Hz 25ms} 10 times, 3000ms OFF, REPEAT.... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 9, 0, + 0, 0, 0, 500, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 19, 3, + 0, 0, 0, 500, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 9, 6, + 0, 0, 0, 3000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA MED SLS tone: {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 20 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 3000ms OFF, REPEAT.... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 9, 0, + 0, 0, 0, 500, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 19, 3, + 0, 0, 0, 500, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 9, 6, + 0, 0, 0, 3000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA LOW SLS tone: {1300Hz 25ms, 1450Hz 25ms} 10 times, 500ms OFF, {1300Hz 25ms, 1450Hz 25ms} 20 times, 500ms OFF, {1300Hz 25ms, 1450Hz 25ms} 10 times, 3000ms OFF, REPEAT.... + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 9, 0, + 0, 0, 0, 500, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 9, 3, + 0, 0, 0, 500, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 9, 6, + 0, 0, 0, 2500, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA HIGH S X4 tone: {3700Hz 25ms, 4000Hz 25ms} 10 times, 500ms OFF, {3700Hz 25ms, 4000Hz 25ms} 10 times, 500ms OFF, {3700Hz 25ms, 4000Hz 25ms} 10 times, 500ms OFF, {3700Hz 25ms, 4000Hz 25ms} 10 times, 2500ms OFF, REPEAT.... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 9, 0, + 0, 0, 0, 500, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 9, 4, + 0, 0, 0, 500, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 9, 6, + 0, 0, 0, 2500, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA MED S X4 tone: {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 2500ms OFF, REPEAT.... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 9, 0, + 0, 0, 0, 500, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 9, 3, + 0, 0, 0, 500, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 9, 6, + 0, 0, 0, 2500, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA LOW S X4 tone: {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 2500ms OFF, REPEAT.... + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 19, 0, + 0, 0, 0, 2000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA HIGH PBX L: {3700Hz 25ms, 4000Hz 25ms}20 times, 2000ms OFF, REPEAT.... + + // 80 + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 19, 0, + 0, 0, 0, 2000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA MED PBX L: {2600Hz 25ms, 2900Hz 25ms}20 times, 2000ms OFF, REPEAT.... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 19, 0, + 0, 0, 0, 2000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA LOW PBX L: {1300Hz 25ms,1450Hz 25ms}20 times, 2000ms OFF, REPEAT.... + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 3, + 0, 0, 0, 2000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA HIGH PBX SS tone: {3700Hz 25ms, 4000Hz 25ms} 8 times 200 ms OFF, {3700Hz 25ms 4000Hz 25ms}8 times, 2000ms OFF, REPEAT.... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 3, + 0, 0, 0, 2000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA MED PBX SS tone: {2600Hz 25ms, 2900Hz 25ms} 8 times 200 ms OFF, {2600Hz 25ms 2900Hz 25ms}8 times, 2000ms OFF, REPEAT.... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 3, + 0, 0, 0, 2000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA LOW PBX SS tone: {1300Hz 25ms, 1450Hz 25ms} 8 times 200 ms OFF, {1300Hz 25ms 1450Hz 25ms}8 times, 2000ms OFF, REPEAT.... + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 15, 6, + 0, 0, 0, 1000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA HIGH PBX SSL tone:{3700Hz 25ms, 4000Hz 25ms} 8 times 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} 8 times, 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} 16 times, 1000ms OFF, REPEAT....// + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 15, 6, + 0, 0, 0, 1000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA MED PBX SSL tone:{2600Hz 25ms, 2900Hz 25ms} 8 times 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} 8 times, 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} 16 times, 1000ms OFF, REPEAT....// + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 15, 6, + 0, 0, 0, 1000, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA LOW PBX SSL tone:{1300Hz 25ms, 1450Hz 25ms} 8 times 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} 8 times, 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} 16 times, 1000ms OFF, REPEAT....// + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 15, 0, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 3, + 0, 0, 0, 1000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA HIGH PBX SLS tone:{3700Hz 25ms, 4000Hz 25ms} 8 times 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} 16 times, 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} 8 times, 1000ms OFF, REPEAT.... // + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 15, 0, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 3, + 0, 0, 0, 1000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA HIGH PBX SLS tone:{2600Hz 25ms, 2900Hz 25ms} 8 times 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} 16 times, 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} 8 times, 1000ms OFF, REPEAT....// + + // 90 + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 15, 0, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 3, + 0, 0, 0, 1000, 0, 0, + -1, -1, -1, -1, 0, 0 }, //CDMA HIGH PBX SLS tone:{1300Hz 25ms, 1450Hz 25ms} 8 times 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} 16 times, 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} 8 times, 1000ms OFF, REPEAT....// + + {3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 6, + 0, 0, 0, 200, 0, 0, + 3700, 0, 0, 25, 0, 0, + 4000, 0, 0, 25, 7, 9, + 0, 0, 0, 800, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA HIGH PBX X S4 tone: {3700Hz 25ms 4000Hz 25ms} 8 times, 200ms OFF, {3700Hz 25ms 4000Hz 25ms} 8 times, 200ms OFF, {3700Hz 25ms 4000Hz 25ms} 8 times, 200ms OFF, {3700Hz 25ms 4000Hz 25ms} 8 times, 800ms OFF, REPEAT... + + {2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 6, + 0, 0, 0, 200, 0, 0, + 2600, 0, 0, 25, 0, 0, + 2900, 0, 0, 25, 7, 9, + 0, 0, 0, 800, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA MED PBX X S4 tone: {2600Hz 25ms 2900Hz 25ms} 8 times, 200ms OFF, {2600Hz 25ms 2900Hz 25ms} 8 times, 200ms OFF, {2600Hz 25ms 2900Hz 25ms} 8 times, 200ms OFF, {2600Hz 25ms 2900Hz 25ms} 8 times, 800ms OFF, REPEAT... + + {1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 0, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 3, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 6, + 0, 0, 0, 200, 0, 0, + 1300, 0, 0, 25, 0, 0, + 1450, 0, 0, 25, 7, 9, + 0, 0, 0, 800, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA LOW PBX X S4 tone: {1300Hz 25ms 1450Hz 25ms} 8 times, 200ms OFF, {1300Hz 25ms 1450Hz 25ms} 8 times, 200ms OFF, {1300Hz 25ms 1450Hz 25ms} 8 times, 200ms OFF, {1300Hz 25ms 1450Hz 25ms} 8 times, 800ms OFF, REPEAT... + + {1109, 0, 0, 62, 0, 0, + 784, 0, 0, 62, 0, 0, + 740, 0, 0, 62, 0, 0, + 622, 0, 0, 62, 0, 0, + 1109, 0, 0, 62, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA Alert Network Lite tone: 1109Hz 62ms ON, 784Hz 62ms ON, 740Hz 62ms ON 622Hz 62ms ON, 1109Hz 62ms ON + + {1245, 0, 0, 62, 0, 0, + 659, 0, 0, 62, 0, 0, + 1245, 0, 0, 62, 0, 0, + 659, 0, 0, 62, 0, 0, + 1245, 0, 0, 62, 0, 0, + 659, 0, 0, 62, 0, 0, + 1245, 0, 0, 62, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA Alert Auto Redial tone: {1245Hz 62ms ON, 659Hz 62ms ON} 3 times, 1245 62ms ON// + + {1150, 770, 0, 400, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA One Min Beep tone: 1150Hz+770Hz 400ms ON// + + {941, 1477, 0, 120, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA KEYPAD Volume key lite tone: 941Hz+1477Hz 120ms ON + + {587, 0, 0, 375, 0, 0, + 1175, 0, 0, 125, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA PRESSHOLDKEY LITE tone: 587Hz 375ms ON, 1175Hz 125ms ON + + {587, 0, 0, 62, 0, 0, + 784, 0, 0, 62, 0, 0, + 831, 0, 0, 62, 0, 0, + 784, 0, 0, 62, 0, 0, + 1109, 0, 0, 62, 0, 0, + 784, 0, 0, 62, 0, 0, + 831, 0, 0, 62, 0, 0, + 784, 0, 0, 62, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA ALERT INCALL LITE tone: 587Hz 62ms, 784 62ms, 831Hz 62ms, 784Hz 62ms, 1109 62ms, 784Hz 62ms, 831Hz 62ms, 784Hz 62ms + + // 100 + {941, 0, 0, 125, 0, 0, + 0, 0, 0, 10, 0, 0, + 941, 0, 0, 125, 0, 0, + 0, 0, 0, 10, 0, 0, + 1245, 0, 0, 62, 0, 0, + 0, 0, 0, 10, 0, 0, + 0, 0, 0, 4990, 0, 0, + -1, -1, -1, -1, 0, 0}, //CDMA EMERGENCY RINGBACK tone: {941Hz 125ms ON, 10ms OFF} 3times 4990ms OFF, REPEAT... + + {1319, 0, 0, 125, 0, 0, + 0, 0, 0, 125, 0, 0, + -1, -1, -1, -1, 3, 0 }, //CDMA ALERT CALL GUARD tone: {1319Hz 125ms ON, 125ms OFF} 3 times + + {1047, 0, 0, 125, 0, 0, + 370, 0, 0, 125, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA SOFT ERROR LITE tone: 1047Hz 125ms ON, 370Hz 125ms + + {1480, 0, 0, 125, 0, 0, + 1397, 0, 0, 125, 0, 0, + 784, 0, 0, 125, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA CALLDROP LITE tone: 1480Hz 125ms, 1397Hz 125ms, 784Hz 125ms// + + {425, 0, 0, 125, 0, 0, + 0, 0, 0, 125, 0, 0, + -1, -1, -1, -1, 1, 0},//CDMA_NETWORK_BUSY_ONE_SHOT tone: 425Hz 500ms ON, 500ms OFF + + {1150, 770, 0, 400, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA_ABBR_ALERT tone: 1150Hz+770Hz 400ms ON + + {0, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 1, 0}, //CDMA_SIGNAL_OFF - silent tone + + {100, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, //100Hz continuous + + {200, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, //200Hz continuous + + {300, 0, 0, -1, 0, 0, + -1, -1, -1, -1, 0, 0}, //300Hz continuous +}; +#endif + -- 2.7.4 From f8cde90998a50f92479d8a39df9ada741262e054 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Mon, 18 May 2020 15:44:24 +0900 Subject: [PATCH 08/16] tone-player: Adds unlinked variable when a stream is stopped Unlink messages is sent twice in a row in I/O thread. So, it make sink_input_kill function call twice without a guard on TM1. [Version] 13.0.16 [Issue Type] Bugs Change-Id: Ibbe3041a64107ce4fdcf842658a472aad3c23d02 Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tone-player.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 9a56089..0cab8a7 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.15 +Version: 13.0.16 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tone-player.c b/src/module-tone-player.c index f4a64c9..99866ec 100644 --- a/src/module-tone-player.c +++ b/src/module-tone-player.c @@ -100,6 +100,7 @@ struct tone { uint16_t loop; uint32_t sample; bool silence; + bool unlinked; }; inline uint32_t tone_msec_to_bytes(uint16_t msec) { @@ -226,9 +227,10 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk return -1; if (tone_peek_fixed_size(t, i, chunk, length) < 0) { - if (pa_sink_input_safe_to_remove(i)) { + if (pa_sink_input_safe_to_remove(i) && !t->unlinked) { pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(i), TONE_SOUND_MESSAGE_UNLINK, NULL, 0, NULL, NULL); + t->unlinked = true; } return -1; } -- 2.7.4 From 41b023359a6e124e6c2cbb5f9523934fb19532b6 Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Tue, 19 May 2020 10:32:38 +0900 Subject: [PATCH 09/16] stream-manager: Select new proper device even if sink or source of new data is set In tizen policy, selecting device is performed in tizenaudio-policy module. The previous codes skip selecting the device logic if sink or source of new data is defined(probably aka default sink/source) in somewhere before the codes. This patch set it regardless of this matter. We need to be aware of this modification for possible issues regarding it. If the skip logic is needed anyway later, we may put that in another place. [Version] 13.0.17 [Issue Type] Bug fix Change-Id: I92df553b259d8ae1ff9de6c29723ccbb78bfaee6 Signed-off-by: Sangchul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/stream-manager.c | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 0cab8a7..64e1ab1 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.16 +Version: 13.0.17 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/stream-manager.c b/src/stream-manager.c index 6411be7..cb89244 100644 --- a/src/stream-manager.c +++ b/src/stream-manager.c @@ -1680,7 +1680,6 @@ static ret_msg_t prepare_and_invoke_hook_to_select_device(pa_stream_manager *m, fill_device_info_to_hook_data(m, &hook_call_select_data, command, type, s, is_new_data); hook_call_select_data.sample_spec = GET_STREAM_NEW_SAMPLE_SPEC(s, type); if (type == STREAM_SINK_INPUT) { - const char *modifier_gain = NULL; hook_call_select_data.occupying_role = m->cur_highest_priority.role_si; if (IS_ROLE_COMMUNICATION(hook_call_select_data.occupying_role)) { /* Currently, if bt-sco is used by communication stream, other streams can not go with bt-a2dp. @@ -1689,21 +1688,9 @@ static ret_msg_t prepare_and_invoke_hook_to_select_device(pa_stream_manager *m, hook_call_select_data.idx_avail_devices = filtered_avail_devices; } hook_call_select_data.proper_sink = &(((pa_sink_input_new_data*)s)->sink); - /* need to check modifier_gain, because we do not skip a stream that is from module-sound-player */ - modifier_gain = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type), PA_PROP_MEDIA_TIZEN_VOLUME_GAIN_TYPE); - if (((pa_sink_input_new_data*)s)->sink && !modifier_gain) { - pa_log_info(" - sink(%s) has been already selected, skip selecting sink", - (((pa_sink_input_new_data*)s)->sink)->name); - goto BREAK_WITH_FREE; - } } else if (type == STREAM_SOURCE_OUTPUT) { hook_call_select_data.occupying_role = m->cur_highest_priority.role_so; hook_call_select_data.proper_source = &(((pa_source_output_new_data*)s)->source); - if (((pa_source_output_new_data*)s)->source) { - pa_log_info(" - source(%s) has been already selected, skip selecting source", - (((pa_source_output_new_data*)s)->source)->name); - return ret; - } } } else { hook_call_select_data.stream_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), @@ -1726,7 +1713,7 @@ static ret_msg_t prepare_and_invoke_hook_to_select_device(pa_stream_manager *m, pa_source_output_move_to(PA_SOURCE_OUTPUT(s), hook_call_select_data.new_source, false); } } -BREAK_WITH_FREE: + pa_xfree(filtered_avail_devices); return ret; -- 2.7.4 From 18b4796906c12d7e65b14372740b8dfc3c7c26ea Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Wed, 27 May 2020 10:41:01 +0900 Subject: [PATCH 10/16] tone-player: Add sink_input_detach callback sink_input_detach_cb function was added to synchronize I/O and main thread. This commit fixes the unlink issue(f8cde9099) completely. In additional, handle_sound_stop returns invalid param error when it is called with invalid handle. [Version] 13.0.18 [Issue Type] Bug fix Change-Id: I1f2ede6c0acc6728e1055317124815db3dbd859c Signed-off-by: Jaechul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-sound-player.c | 2 +- src/module-tone-player.c | 45 +++++++++++++++------------------ 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 64e1ab1..64bc990 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.17 +Version: 13.0.18 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-sound-player.c b/src/module-sound-player.c index 143b975..0322544 100644 --- a/src/module-sound-player.c +++ b/src/module-sound-player.c @@ -523,7 +523,7 @@ static void handle_sound_stop(DBusConnection *conn, DBusMessage *msg, void *user si = pa_idxset_get_by_index(u->module->core->sink_inputs, stream_idx); if (!si) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "%s", - "org.tizen.multimedia.audio.InvalidState"); + "org.tizen.multimedia.audio.InvalidArgument"); return; } diff --git a/src/module-tone-player.c b/src/module-tone-player.c index 99866ec..de3d2f6 100644 --- a/src/module-tone-player.c +++ b/src/module-tone-player.c @@ -129,16 +129,6 @@ struct tone *tone_new(uint32_t index, int rbytes) { return t; } -void tone_free(struct tone *t) { - if (!t) - return; - - pa_xfree(t->info); - pa_xfree(t); - - return 0; -} - int tone_peek_fixed_size(struct tone *t, pa_sink_input *si, pa_memchunk *chunk, uint32_t length) { short *buf; uint32_t i; @@ -223,7 +213,7 @@ int tone_peek_fixed_size(struct tone *t, pa_sink_input *si, pa_memchunk *chunk, static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { struct tone *t = (struct tone *)i->userdata; - if (!t) + if (!PA_SINK_INPUT_IS_LINKED(i->state)) return -1; if (tone_peek_fixed_size(t, i, chunk, length) < 0) { @@ -240,28 +230,33 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk return 0; } -static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_detach_cb(pa_sink_input *i) { struct tone *t = i->userdata; if (!t) return; + + pa_xfree(t->info); + pa_xfree(t); } -static void sink_input_kill_cb(pa_sink_input *i) { - struct tone *t = i->userdata; +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + return; +} - tone_free(t); - pa_sink_input_unlink(i); - pa_sink_input_unref(i); +static void sink_input_kill_cb(pa_sink_input *i) { + return; } + static int sink_input_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { pa_sink_input *i = PA_SINK_INPUT(o); switch (code) { case TONE_SOUND_MESSAGE_UNLINK: - pa_sink_input_kill(i); - break; + pa_sink_input_unlink(i); + pa_sink_input_unref(i); + return 0; } return pa_sink_input_process_msg(o, code, userdata, offset, chunk); @@ -334,6 +329,7 @@ static void handle_tone_play(DBusConnection *conn, DBusMessage *msg, void *userd i->userdata = t; i->pop = sink_input_pop_cb; i->kill = sink_input_kill_cb; + i->detach = sink_input_detach_cb; i->process_rewind = sink_input_process_rewind_cb; i->parent.process_msg = sink_input_process_msg; @@ -351,7 +347,7 @@ exit: static void handle_tone_stop(DBusConnection *conn, DBusMessage *msg, void *userdata) { struct userdata *u = (struct userdata *)userdata; uint32_t stream_idx; - pa_sink_input *si; + pa_sink_input *i; pa_assert(conn); pa_assert(msg); @@ -363,14 +359,15 @@ static void handle_tone_stop(DBusConnection *conn, DBusMessage *msg, void *userd pa_log_info("stop tone %u", stream_idx); - si = pa_idxset_get_by_index(u->module->core->sink_inputs, stream_idx); - if (!si) { + i = pa_idxset_get_by_index(u->module->core->sink_inputs, stream_idx); + if (!i) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "%s", - "org.tizen.multimedia.audio.InvalidState"); + "org.tizen.multimedia.audio.InvalidArgument"); return; } - pa_sink_input_kill(si); + pa_sink_input_unlink(i); + pa_sink_input_unref(i); pa_dbus_send_empty_reply(conn, msg); } -- 2.7.4 From e5d087a8a99db6ccdb7e49163a9a19e80993247a Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Thu, 28 May 2020 10:53:46 +0900 Subject: [PATCH 11/16] Add Tizen_HW_Touch.ogg at /usr/share/sound/mm-sound directory This file is moved from libmm-sound because sound_server doesn't exist. [Version] 13.0.19 [Issue Type] None Change-Id: I14801824c87c4388addfbb465092d04b965e2bb9 Signed-off-by: Jaechul Lee --- Makefile.am | 3 +++ packaging/pulseaudio-modules-tizen.spec | 3 ++- res/Tizen_HW_Touch.ogg | Bin 0 -> 6646 bytes 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 res/Tizen_HW_Touch.ogg diff --git a/Makefile.am b/Makefile.am index 6023379..9b59496 100644 --- a/Makefile.am +++ b/Makefile.am @@ -145,3 +145,6 @@ delay_la_SOURCES = src/ladspa/delay.c src/ladspa/ladspa.h delay_la_LDFLAGS = -module -avoid-version -Xcompiler -nostartfiles delay_la_CFLAGS = -I. -Wall -Werror -O3 -fPIC +installsounddir = $(prefix)/share/sounds/mm-sound +installsound_DATA = res/Tizen_HW_Touch.ogg + diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 64bc990..fa7798e 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.18 +Version: 13.0.19 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ @@ -82,6 +82,7 @@ install -m 0644 %SOURCE1 %{buildroot}%{_tmpfilesdir}/pulseaudio.conf %{_libexecdir}/pulse/vconf-helper %endif %{_libdir}/ladspa/*.so +%{_datadir}/sounds/mm-sound/* %files -n pulseaudio-module-acm %manifest %{name}.manifest diff --git a/res/Tizen_HW_Touch.ogg b/res/Tizen_HW_Touch.ogg new file mode 100644 index 0000000000000000000000000000000000000000..acab43418503b4d628aa5f41bec2a2ace7a32b6c GIT binary patch literal 6646 zcmeG=X;_oT){_7stPKz(vS@&y37{nq6c9ATL?G;tAfQ;bAd3rItkx~8Vps|R5djqt z5F)FJB34U9L;*o$aY0c;MZp~_RjYpUCTQt>`lHWx|K9UV=AD_d&73o5=1knzulELM zFasAAqz(1Gg}-^PLBb2j2 zMk+}U68J_$hc9-IT~FK)5$q$8Pjn)ho0*xLEu}6+CW41QjuXd)Wygv5iMQzo$JIY@ zHuFQmRpGe37stt)W9J>TF^<1!JxWe0mcm%+g92Cp1O_g|Ho>)QE&w>#t6GAOs%{+8 z4k;zC8VHW0ULu5ynXbwXPDff9-zp+4R1*MMK&MV<<{n~nTaVu%RAzeKrgR{!_Pu7l z`W0s?3cb2ZbBo!X*5kTZ6+by>59UAG z0L=iL8}Y7vy1lP;d)4&3)s4K(jl5$Q-7sI|ZDH4M!Q#eqy}7&_e4h6bq;MrQYz*$> z`Q`tuNS01Sff7S&ks&4PfmPO+qewP6wFV7B5t9Uxg-0B-NPZ&7wf;ci(*uVh3M*kz zkdQ!!%ObM@CDPHq*J1ym>z}*y@XN-44sF>Hqbn^Au_Sqkg)al>Q@R(aJ(lJWN9%~Q zb%+d7mNs?ab}mC_71O?vcD#PJf9)TAf(Q_MrnHh#4a9Da(DbBPKPrlHzEbw;+DA=*UCs zE43%@6mPmqDHD}Uqa{rolicMhNC z51-n3_h#ljxKU@75b4t4O2F5kNr90_9WP13F(?T$%M?H~V8!4&a&(@McGj0YzkB zodrW^R^*hBg)o{P`ihJqMC1eck!K2@Jw3E|qD=MgkrSAf8lILFzPFGpEcsTo7 znpA23HHIM9H5ZLCtY>lrn%198X5fPg|9OG}cxR#HfdFW9(!b5=f$tmutVAJx0Nl6A z;-P?m=U0dUAfFeZ{m)|l{pf!vL?!k|6Rq2Y&I<9Pi%gXQ{lHb3aBVssg%G2W z+3C=X=E#$2p+*2F2kv{|yROqbA6mhLsO)kKyh)fjyokc_0}E(Zr(87P8C|IWkKO*{ z2w|BR?aARnv&$i7UlPtXRrLtJ%mzdfXdSi6f3)q;LSbcip|~>!vcgl^)tk%RH|Co^ zzPF5ASSfB<3>Eciusmt5Tz)`!;kRL!;4u5}u3T<_7F1UTB~**+l#o*%QgXRBmiBAx=Az?H^||_`%M7zHgQYcGc*Tb;`6jh#1QICx&TxM@qU;R zaouFgaZ~7sRumw%Vr{Ne9mndpHopRR?c)X@4eo^uPd2qeIYgbIpdakQAnS8rrc<4# zh~zCE_TP3!C}jlCVQ}}h)Aj+2LyUn@aKz7Nv)9uZ(z0;k{1lKZxVw|eOw%; zu3tfj>#?rJhaf3Mo`^bL9OuTqQUJ#mR)j*j;#@BqIMxI6pm<19p6A7QQk(=OAA}?* zK$luuxWUa#og*=~CTP9?)!Y$IqRMR&TSSV=BM=8vixGQDtPgKksp(?_W~}+BrA$In zr`U&FqTCxP(5(?-@gyVPwVwm66f_J4h$mX;e8kLio9m!OO_cy}j|RZFA&8%xTh`bL zFFG`GUnWzr6qs{i^viJ!M@mZe-?u+Xz<~oeX?CX;B25_$9>ev`N*@27`~ANW`pF)E zxFtoV)h$`&m|-zKQ*Ms-adm)|haR;T!f7@Q1*c>O_c%FGE)h(U32|9$_M%e++GE}1 zJpU-_jS}iOvHZG!h9zc@L9S>bMAce0<86$q+hNNdyU$BVy`&wd!l)84HZ(pijKW=^=IJQ=av0XVVtTBd;ooSe5f#~ zM-CL&S^>JzO{+Hcb-&T@Xn0DX1b5&QMY>7kCC?=aBLmqul82L*S27_4ZW1UJriA5R z5v9knWoYB~m#vT|@x8`Y%Nl`CgJCO2x?m z|G(tTyae`1#lvGaK?cwTYWwB{ADz2;UVnO z+h0h|b@NxPTX~K6xO|_`IyobiR{u4$s-(qglq*_ z#gbM!2Ivw=jwp~Km#V5}K+izSj6JxH0Yq%hTm!X60%-w9hSeSq8k$?PzXNl%bt1I1 zuJHs)EzOg3tUZ>2OP3)NQSuA#yt=4!C^|_aUdYyyyyseF0L=18I&>zJ7N@H>Z$44q zz>s7l`O9G@YXSiI%K^ECstXs=P__#eO0Oh=JeUttM@5{P+ErbLJst9nX_v81d#@<4 z0U*Mo90HaIP$-EQNgpNsD?|1X53mdDzOkPw22-4gR9OX+rBE4`Z{iG zz6<)7u%eega(^`Y;l$PHv*Kk1%LXSo%L_EfkxeHW30azlV`$o$sg<^0zG&&KFCnf9 zEOG>*&ck&#FW~VDFXs{1(Yk+|XuG^&5qakgX9v08s_iO0k=Y{?r z3WTofb$3>PlRxcWTK$=)|Fm}Gk*j#OpW}xPtq{zJT{D*=U&ZIx#pHm1x5nR-xS9Ti3@ic zXRKQE`hb>dTlj#GnVcCN&O(14eQ!O{wm9VUSx~;|`}?EY*XC3YD{cK_-s*=znwF7_ zfDP7|263?IhjwpV0@nVbrl+%xaoLJxq4IsLf%Un~g!QYQuGzE|Rk-x-*>f)sAD7h< zzp31Qdis-h{(Z*a!pJZo$(-$%=jp@o;TSW*Di37iRQH51tS%9(%qK=xR0yRX`j zOJcPqf_+VsPbP+%h|M9UCKU)*T}abx5A-019#yvi*~uPsocdt>lO=JPi<|_ytok2w zRGCI2S*HDo=?xBzqfd5@rUw;6TRg{~YXoY1sLGAh zi;+isbT&M%sf=i}c<#C;{aj{k`^fE+`a_YtHnZA|{%iaTzkJy({Vz~i=KI|))EwUX zr(AQ^ee~QMR^RYjt>Ay; zeEE$7N~QQ)>4PD^M7EkH+^kGCV0<)CS90{)loU6*+_@>b<8hh#ZS18|-vr0R9Lycd zsp%JY_g9_``#f^)P#d5_23Ds>~ z|K)5^du(N4+`2D6E=_;r()4o8?I06O>q(*ivyE6yrjeh}IN11}0u`GP$9F>l$+8&z7Thw*r(1PaKl2y!ptrh}2rWCG?Jbi`OLo!M1w^NevUu&*EbHR_M@Q zmu=m^2$(z&eFp#G<&MoeUAF$R(_mHqW)g>Bg-!CvXOViYJY=-H@ZV*WEa#X` zkFd5^z9-%AkBd<$X9X5n8y3dO+y;Y+sG8#%8pOk~55W-6rP+S@Vt=1&zH^(0XLmkQ zjLX6(o!L3&c`Uw~AL(*Iyp?X&D4~tYc7T~VDy>OONUu^0Q8A9pw7ewdZs1TE20{!~ zlZ{m~JgJamrk<(BrG{@Uj*q)}j5%>DIL2l1nsw2^I{s-BzghD)GW}P$TX5gzKv~rO%{pUKX$xx z`0@w0_3YS@wU!|MeS^HPvt`Fk85VC(RC1m|yRTfj_&3M6P*cAklG!PfSOfEmT%D9! zOnPTMI`4g`>9o_wD)Yk`kCsQw=SSsvg3UzSO5S3 literal 0 HcmV?d00001 -- 2.7.4 From 964bf1d5f07a0c68672b642a66cd4eee4855b60f Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Thu, 25 Jun 2020 16:05:26 +0900 Subject: [PATCH 12/16] Exclude ringbacktone stream from changing call active routing logic stream_is_call_family() is also renamed to is_stream_related_call_active_routing() [Version] 13.0.20 [Issue Type] Bug fix Change-Id: Iedee8a5c2e655d4bf4c9f0275c0ed8c0c5450621 Signed-off-by: Sangchul Lee --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/stream-manager-dbus.c | 2 +- src/stream-manager-priv.h | 2 +- src/stream-manager.c | 25 +++++++++++++------------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index fa7798e..3944878 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.19 +Version: 13.0.20 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/stream-manager-dbus.c b/src/stream-manager-dbus.c index 64adff1..a6e7506 100644 --- a/src/stream-manager-dbus.c +++ b/src/stream-manager-dbus.c @@ -512,7 +512,7 @@ static ret_msg_t update_devices_and_trigger_routing(pa_stream_manager *m, stream pa_log_debug(" -- cur_highest_priority_stream->index[%u] belongs to this stream_parent[%p], do notify for the route change", GET_STREAM_INDEX(cur_highest_priority_stream, type), sp); ret = do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, type, false, cur_highest_priority_stream); - if (!ret && stream_is_call_family(PA_OBJECT(cur_highest_priority_stream)) && m->on_call) { + if (!ret && is_stream_related_call_active_routing(PA_OBJECT(cur_highest_priority_stream)) && m->on_call) { pa_log_info("set active device for new call route device"); change_active_route_for_call(m, PA_OBJECT(cur_highest_priority_stream), false); } diff --git a/src/stream-manager-priv.h b/src/stream-manager-priv.h index 6425e99..f78a063 100644 --- a/src/stream-manager-priv.h +++ b/src/stream-manager-priv.h @@ -284,7 +284,7 @@ struct _stream_manager { } comm; }; -bool stream_is_call_family(pa_object *stream); +bool is_stream_related_call_active_routing(pa_object *stream); bool is_active_device_of_stream(const void *stream, stream_type_t stream_type, const char *device_type); bool is_stream_ducked(stream_ducking *sd); int32_t get_route_type(void *stream, stream_type_t stream_type, bool is_new_data, stream_route_type_t *stream_route_type); diff --git a/src/stream-manager.c b/src/stream-manager.c index cb89244..981d4c6 100644 --- a/src/stream-manager.c +++ b/src/stream-manager.c @@ -137,7 +137,7 @@ static bool is_valid_process_command(process_command_type_t command) { return (command < sizeof(process_command_type_str) / sizeof(char *)); } -bool stream_is_call_family(pa_object *stream) { +bool is_stream_related_call_active_routing(pa_object *stream) { const char *stream_role; pa_proplist *prop; @@ -148,9 +148,15 @@ bool stream_is_call_family(pa_object *stream) { else prop = PA_SOURCE_OUTPUT(stream)->proplist; - stream_role = pa_proplist_gets(prop, PA_PROP_MEDIA_ROLE); - if (stream_role && IS_ROLE_COMMUNICATION(stream_role)) + if (!(stream_role = pa_proplist_gets(prop, PA_PROP_MEDIA_ROLE))) + return false; + + if (pa_safe_streq(stream_role, STREAM_ROLE_CALL_VOICE) || + pa_safe_streq(stream_role, STREAM_ROLE_CALL_VIDEO) || + pa_safe_streq(stream_role, STREAM_ROLE_VOIP)) { + pa_log_info("%s relates call active routing", stream_role); return true; + } return false; } @@ -522,11 +528,6 @@ int change_active_route_for_call(pa_stream_manager *m, pa_object *stream, bool s stream_role = pa_proplist_gets(prop, PA_PROP_MEDIA_ROLE); - if (stream_is_call_family(stream) == false) { - pa_log_error("Not call family stream"); - return -1; - } - avail_device_types = get_avail_device_types(m, stream_role, GET_DIRECTION(stream)); if (!avail_device_types) { pa_log_error("No avail device typs for [%s]", stream_role); @@ -2495,7 +2496,7 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, pa_st process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_ADD_PARENT_ID, false); process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_APPLY_FILTER, false); process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_UPDATE_VOLUME, false); - if (stream_is_call_family(PA_OBJECT(i))) { + if (is_stream_related_call_active_routing(PA_OBJECT(i))) { change_active_route_for_call(m, PA_OBJECT(i), true); m->on_call = true; } @@ -2511,7 +2512,7 @@ static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, pa pa_log_info("sink-input(%p, index:%u)", i, i->index); - if (stream_is_call_family(PA_OBJECT(i))) { + if (is_stream_related_call_active_routing(PA_OBJECT(i))) { m->on_call = false; set_media_active_device(m); } @@ -2652,7 +2653,7 @@ static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, update_mirroring_streams(m, o, true); process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_ADD_PARENT_ID, false); process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_UPDATE_VOLUME, false); - if (stream_is_call_family(PA_OBJECT(o))) { + if (is_stream_related_call_active_routing(PA_OBJECT(o))) { change_active_route_for_call(m, PA_OBJECT(o), true); m->on_call = true; } @@ -2666,7 +2667,7 @@ static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output pa_log_info("source-output(%p, index:%u)", o, o->index); - if (stream_is_call_family(PA_OBJECT(o))) { + if (is_stream_related_call_active_routing(PA_OBJECT(o))) { m->on_call = false; set_media_active_device(m); } -- 2.7.4 From 2c1bdbb11d7fb3568f53c3f5996edde35a1ac5ed Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Mon, 23 Mar 2020 20:22:54 +0900 Subject: [PATCH 13/16] Add module-tizenaudio-discover / module-tizenaudio-publish for remote audio feature module-tizenaudio-discover : based on module-zeroconf-discover with mDNSResponder porting module-tizenaudio-publish : based on module-bonjour-publish with minor changes [Version] 13.0.21 [Issue Type] New feature Change-Id: Id47dad038bfae487bbe57a09aaece674b30f9008 --- Makefile.am | 12 + configure.ac | 4 + packaging/pulseaudio-modules-tizen.spec | 5 +- src/module-tizenaudio-discover.c | 733 ++++++++++++++++++++++++++++++++ src/module-tizenaudio-publish.c | 513 ++++++++++++++++++++++ 5 files changed, 1266 insertions(+), 1 deletion(-) create mode 100644 src/module-tizenaudio-discover.c create mode 100644 src/module-tizenaudio-publish.c diff --git a/Makefile.am b/Makefile.am index 9b59496..07985dc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,6 +36,8 @@ pulsemodlibexec_LTLIBRARIES = \ module-tizenaudio-sink.la \ module-tizenaudio-source.la \ module-tizenaudio-policy.la \ + module-tizenaudio-discover.la \ + module-tizenaudio-publish.la \ module-sound-player.la \ module-tone-player.la \ module-poweroff.la @@ -115,6 +117,16 @@ module_acm_sink_la_LIBADD = $(MODULE_LIBADD) module_acm_sink_la_CFLAGS = $(MODULE_CFLAGS) endif +module_tizenaudio_discover_la_SOURCES = src/module-tizenaudio-discover.c +module_tizenaudio_discover_la_LDFLAGS = $(MODULE_LDFLAGS) +module_tizenaudio_discover_la_LIBADD = $(MODULE_LIBADD) $(DNSSD_LIBS) +module_tizenaudio_discover_la_CFLAGS = $(MODULE_CFLAGS) $(DNSSD_CFLAGS) + +module_tizenaudio_publish_la_SOURCES = src/module-tizenaudio-publish.c +module_tizenaudio_publish_la_LDFLAGS = $(MODULE_LDFLAGS) +module_tizenaudio_publish_la_LIBADD = $(MODULE_LIBADD) $(DNSSD_LIBS) +module_tizenaudio_publish_la_CFLAGS = $(AM_CFLAGS) $(DNSSD_CFLAGS) + if ENABLE_VCONF_HELPER pulsemodlibexec_LTLIBRARIES += module-vconf.la diff --git a/configure.ac b/configure.ac index f38d675..8557992 100644 --- a/configure.ac +++ b/configure.ac @@ -361,6 +361,10 @@ PKG_CHECK_MODULES(VCONF, vconf) AC_SUBST(VCONF_CFLAGS) AC_SUBST(VCONF_LIBS) +PKG_CHECK_MODULES(DNSSD, dns_sd) +AC_SUBST(DNSSD_CFLAGS) +AC_SUBST(DNSSD_LIBS) + dnl use hal tc ------------------------------------------------------------ AC_ARG_ENABLE(haltc, AC_HELP_STRING([--enable-haltc], [using haltc]), [ diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 3944878..c3749d4 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.20 +Version: 13.0.21 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ @@ -20,6 +20,7 @@ BuildRequires: mm-hal-interface-devel BuildRequires: pkgconfig(libpulse) BuildRequires: pkgconfig(pulsecore) BuildRequires: pkgconfig(libsystemd) +BuildRequires: pkgconfig(dns_sd) BuildRequires: pulseaudio BuildRequires: m4 Requires(post): /sbin/ldconfig @@ -74,6 +75,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-discover.so +%{_libdir}/pulse-13.0/modules/module-tizenaudio-publish.so %{_libdir}/pulse-13.0/modules/libhal-interface.so %{_libdir}/pulse-13.0/modules/libcommunicator.so %{_tmpfilesdir}/pulseaudio.conf diff --git a/src/module-tizenaudio-discover.c b/src/module-tizenaudio-discover.c new file mode 100644 index 0000000..ac88209 --- /dev/null +++ b/src/module-tizenaudio-discover.c @@ -0,0 +1,733 @@ +/*** + This file is part of PulseAudio. + + Copyright 2020 Seungbae Shin + based on module-zeroconf-discover.c + + 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, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +PA_MODULE_AUTHOR("Seungbae Shin"); +PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); + +#define SERVICE_TYPE_SINK "_pulse-sink._tcp" +#define SERVICE_TYPE_SOURCE "_pulse-source._tcp" +#define MAX_IP_ADDR 32 +#define MAX_RECORD_LEN 256 + +static const char * const valid_modargs[] = { + NULL +}; + +typedef struct dns_request_data { + DNSServiceRef sd_ref; + pa_io_event *io; +} dns_request_data_t; + +typedef struct service_data { + struct userdata *u; + + int protocol; + dns_request_data_t resolve; + dns_request_data_t addr; + + /* browse */ + int flags; + unsigned int interface; + char *service_name; + char *service_type; + char *domain; + + /* resolve */ + char *host_name; + unsigned short port; + + char *device; + pa_sample_spec ss; + pa_channel_map cm; + bool channel_map_set; + + /* address */ + char *ip_address; +} service_data_t; + +struct tunnel { + unsigned int interface; + int protocol; + char *name, *type, *domain; + uint32_t module_index; +}; + +struct userdata { + pa_core *core; + pa_module *module; + + dns_request_data_t browse_sink; + dns_request_data_t browse_source; + + pa_hashmap *tunnels; +}; + +static unsigned tunnel_hash(const void *p) { + const struct tunnel *t = p; + + return + (unsigned) t->interface + + (unsigned) t->protocol + + pa_idxset_string_hash_func(t->name) + + pa_idxset_string_hash_func(t->type) + + pa_idxset_string_hash_func(t->domain); +} + +static int tunnel_compare(const void *a, const void *b) { + const struct tunnel *ta = a, *tb = b; + int r; + + if (ta->interface != tb->interface) + return 1; + if (ta->protocol != tb->protocol) + return 1; + if ((r = strcmp(ta->name, tb->name))) + return r; + if ((r = strcmp(ta->type, tb->type))) + return r; + if ((r = strcmp(ta->domain, tb->domain))) + return r; + + return 0; +} + +static struct tunnel *tunnel_new( + unsigned int interface, int protocol, + const char *name, const char *type, const char *domain) { + struct tunnel *t; + t = pa_xnew0(struct tunnel, 1); + t->interface = interface; + t->protocol = protocol; + t->name = pa_xstrdup(name); + t->type = pa_xstrdup(type); + t->domain = pa_xstrdup(domain); + t->module_index = PA_IDXSET_INVALID; + return t; +} + +static void tunnel_free(struct tunnel *t) { + pa_assert(t); + pa_xfree(t->name); + pa_xfree(t->type); + pa_xfree(t->domain); + pa_xfree(t); +} + +static service_data_t *service_data_new() { + return pa_xnew0(service_data_t, 1); +} + +static void service_data_free(service_data_t *data) { + pa_assert(data); + + pa_xfree(data->service_name); + pa_xfree(data->service_type); + pa_xfree(data->domain); + pa_xfree(data->host_name); + pa_xfree(data->ip_address); + pa_xfree(data->device); + + pa_xfree(data); +} + + +static int request_set_io_callback(pa_mainloop_api *m_api, dns_request_data_t *request, pa_io_event_cb_t cb, void *user_data) { + int fd = -1; + + pa_assert(request); + pa_assert(request->sd_ref); + + fd = DNSServiceRefSockFD(request->sd_ref); + + if (fd < 0) { + pa_log_error("DNSServiceRefSockFD(%p) error", request->sd_ref); + return -1; + } + + request->io = m_api->io_new(m_api, fd, + PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, + cb, user_data); + + pa_log_info("io(%p) with cb(%p) added for fd(%d)", request->io, cb, fd); + + return 0; +} + +static void request_clear(pa_mainloop_api *m_api, dns_request_data_t *request) { + if (request->sd_ref) { + DNSServiceRefDeallocate(request->sd_ref); + request->sd_ref = NULL; + } + + if (request->io) { + m_api->io_free(request->io); + request->io = NULL; + } +} + +static char *get_ip_str(const struct sockaddr *sa) { + char addr_str[MAX_IP_ADDR] = { 0, }; + + switch(sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), addr_str, MAX_IP_ADDR); + break; + + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), addr_str, MAX_IP_ADDR); + break; + + default: + pa_strlcpy(addr_str, "Unknown AF", MAX_IP_ADDR); + break; + } + + return pa_xstrdup(addr_str); +} + +static void parse_txt_record(service_data_t *data, + const unsigned char *txt_record, + unsigned short txt_len) { + char record_str[MAX_RECORD_LEN] = { 0, }; + int record_len; + const char *key_str = NULL; + const char *val_str = NULL; + const char *state = NULL; + size_t val_len; + const char *delimiter = "="; + const unsigned char *cur = txt_record; + + pa_assert(data); + pa_assert(txt_record); + pa_assert(txt_len > 0); + + while (cur < txt_record + txt_len) { + record_len = *cur; + state = NULL; + + pa_snprintf(record_str, record_len + 1, "%s", ++cur); + + key_str = pa_split_in_place(record_str, delimiter, &val_len, &state); + if (pa_strneq(key_str, "device", val_len)) { + data->device = pa_split(record_str, delimiter, &state); + } else if (pa_strneq(key_str, "rate", val_len)) { + data->ss.rate = (uint32_t)atoi(pa_split_in_place(record_str, delimiter, &val_len, &state)); + } else if (pa_strneq(key_str, "channels", val_len)) { + data->ss.channels = (uint8_t)atoi(pa_split_in_place(record_str, delimiter, &val_len, &state)); + } else if (pa_strneq(key_str, "format", val_len)) { + val_str = pa_split_in_place(record_str, delimiter, &val_len, &state); + data->ss.format = pa_parse_sample_format(val_str); + } else if (pa_strneq(key_str, "channel_map", val_len)) { + val_str = pa_split_in_place(record_str, delimiter, &val_len, &state); + pa_channel_map_parse(&data->cm, val_str); + } + + cur += record_len; + } + pa_log_info("-----------------------------------"); + pa_log_info("device : [%s]", data->device); + pa_log_info("rate : [%d]", data->ss.rate); + pa_log_info("channels : [%d]", data->ss.channels); + pa_log_info("format : [%s]", pa_sample_format_to_string(data->ss.format)); + pa_log_info("channel_map : [%s]", pa_channel_map_to_pretty_name(&data->cm)); + pa_log_info("-----------------------------------"); +} + +static void tunnel_add(service_data_t *data) { + struct tunnel *tnl; + char *dname, *module_name, *args; + const char *t; + char cmt[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_module *m; + + pa_assert(data); + pa_assert(data->u); + + tnl = tunnel_new(data->interface, data->protocol, data->service_name, data->service_type, data->domain); + + data->ss = data->u->core->default_sample_spec; + data->cm = data->u->core->default_channel_map; + + if (!data->channel_map_set && data->cm.channels != data->ss.channels) + pa_channel_map_init_extend(&data->cm, data->ss.channels, PA_CHANNEL_MAP_DEFAULT); + + if (!pa_sample_spec_valid(&data->ss)) { + pa_log("Service '%s' contains an invalid sample specification.", data->service_name); + goto finish; + } + + if (!pa_channel_map_valid(&data->cm) || data->cm.channels != data->ss.channels) { + pa_log("Service '%s' contains an invalid channel map.", data->service_name); + goto finish; + } + + if (data->device) + dname = pa_sprintf_malloc("tunnel.%s.%s", data->host_name, data->device); + else + dname = pa_sprintf_malloc("tunnel.%s", data->host_name); + + if (!pa_namereg_is_valid_name(dname)) { + pa_log("Cannot construct valid device name from credentials of service '%s'.", dname); + pa_xfree(dname); + goto finish; + } + + t = strstr(data->service_type, "sink") ? "sink" : "source"; + + module_name = pa_sprintf_malloc("module-tunnel-%s", t); + args = pa_sprintf_malloc("server=[%s]:%u " + "%s=%s " + "format=%s " + "channels=%u " + "rate=%u " + "%s_name=%s " + "channel_map=%s", + data->ip_address, + data->port, + t, data->device, + pa_sample_format_to_string(data->ss.format), + data->ss.channels, + data->ss.rate, + t, dname, + pa_channel_map_snprint(cmt, sizeof(cmt), &data->cm)); + + pa_log_debug("Loading %s with arguments '%s'", module_name, args); + + if (pa_module_load(&m, data->u->core, module_name, args) >= 0) { + tnl->module_index = m->index; + pa_hashmap_put(data->u->tunnels, tnl, tnl); + pa_log_info("Module(%u) loaded, Tunnel(%p) added success", m->index, tnl); + tnl = NULL; + } else { + pa_log_error("Failed to load Module(%s)", module_name); + } + + pa_xfree(module_name); + pa_xfree(dname); + pa_xfree(args); + +finish: + + if (tnl) + tunnel_free(tnl); +} + + +static void dnssd_getaddrinfo_reply_cb(DNSServiceRef sd_ref, + unsigned int flags, unsigned int if_index, + DNSServiceErrorType error_code, const char *host_name, + const struct sockaddr *address, unsigned int ttl, + void *user_data) { + service_data_t *data = (service_data_t *)user_data; + + pa_assert(data); + pa_assert(data->u); + + if (flags & kDNSServiceFlagsMoreComing) { + pa_log_warn("More results are queued, No need to send callback to " + "application at this stage"); + return; + } + + if (data->interface != if_index) + pa_log_warn("diff interface %d <=> %d", data->interface, if_index); + if (!pa_safe_streq(data->host_name, host_name)) + pa_log_warn("diff host_name %s <=> %s", data->host_name, host_name); + + data->ip_address = get_ip_str(address); + + pa_log_info("-----------------------------------"); + pa_log_info("Address Reply : ref(%p), userdata(%p), error(%d), flags(0x%x)", + sd_ref, user_data, error_code, flags); + pa_log_info(" Interface Index : %d", if_index); + pa_log_info(" Host Name : %s", data->host_name); + pa_log_info(" IP Address : %s", data->ip_address); + pa_log_info(" TTL : %d", ttl); + + pa_log_info("-----------------------------------"); + + tunnel_add(data); + + /* cleanup addr */ + request_clear(data->u->module->core->mainloop, &data->addr); + + /* cleanup data */ + service_data_free(data); +} + +static void io_addr_event_callback(pa_mainloop_api *io, pa_io_event *e, + int fd, pa_io_event_flags_t events, + void *user_data) { + service_data_t *data = (service_data_t *)user_data; + DNSServiceErrorType err; + + pa_assert(io); + pa_assert(data); + pa_assert(data->u); + + if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { + pa_log_warn("Lost connection fd : %d", fd); + goto fail; + } + + if (events & PA_IO_EVENT_INPUT) { + err = DNSServiceProcessResult(data->addr.sd_ref); + if (err == 0) + pa_log_info("DNSServiceProcessResult(%p, fd:%d) success", data->addr.sd_ref, fd); + else + pa_log_info("DNSServiceProcessResult(%p, fd:%d) error(%d)", data->addr.sd_ref, fd, err); + } + + return; + +fail: + request_clear(data->u->module->core->mainloop, &data->addr); +} + +static int dnssd_getaddrinfo(service_data_t *data) { + int ret; + + ret = DNSServiceGetAddrInfo(&(data->addr.sd_ref), data->flags, data->interface, + data->protocol, data->host_name, + dnssd_getaddrinfo_reply_cb, data); + if (ret != kDNSServiceErr_NoError) { + pa_log_error("DNSServiceGetAddrInfo() error(%d)", ret); + return -1; + } + + pa_log_info("DNSServiceGetAddrInfo(%p) success", data->addr.sd_ref); + + return request_set_io_callback(data->u->module->core->mainloop, &data->addr, io_addr_event_callback, data); +} + +static void dnssd_resolve_reply_cb(DNSServiceRef sd_ref, unsigned int flags, + unsigned int if_index, DNSServiceErrorType error_code, + const char *fullname, const char *host_name, + unsigned short port, unsigned short txt_len, + const unsigned char *txt_record, void *user_data) { + int ret; + service_data_t *data = (service_data_t *)user_data; + + pa_assert(data); + pa_assert(data->u); + + if (flags & kDNSServiceFlagsMoreComing) { + pa_log_warn("More results are queued"); + return; + } + + if (data->interface != if_index) + pa_log_warn("diff interface %d <=> %d", data->interface, if_index); + + data->flags = flags; + data->host_name = pa_xstrdup(host_name); + data->port = ntohs(port); + + pa_log_info("-----------------------------------"); + pa_log_info("Resolve Reply : ref(%p), userdata(%p), error(%d), flags(0x%x)", + sd_ref, user_data, error_code, flags); + pa_log_info(" Interface Index : %d", if_index); + pa_log_info(" Full Name : %s", fullname); + pa_log_info(" Host Name : %s", data->host_name); + pa_log_info(" Port : %d", data->port); + pa_log_info(" Text Record : [%p][%d]", txt_record, txt_len); + pa_log_info("-----------------------------------"); + + parse_txt_record(data, txt_record, txt_len); + + /* cleanup resolve */ + request_clear(data->u->module->core->mainloop, &data->resolve); + + /* Get Address Info details and send browse callback */ + ret = dnssd_getaddrinfo(data); + if (ret < 0) { + pa_log_error("failed to get address info!"); + service_data_free(data); + } +} + + +static void io_resolve_event_callback(pa_mainloop_api *io, pa_io_event *e, int fd, + pa_io_event_flags_t events, void *user_data) { + service_data_t *data = (service_data_t *)user_data; + DNSServiceErrorType err; + + pa_assert(io); + pa_assert(data); + pa_assert(data->u); + + if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { + pa_log_warn("Lost connection fd : %d", fd); + goto fail; + } + + if (events & PA_IO_EVENT_INPUT) { + err = DNSServiceProcessResult(data->resolve.sd_ref); + if (err == 0) + pa_log_info("DNSServiceProcessResult(%p, fd:%d) success", data->resolve.sd_ref, fd); + else + pa_log_info("DNSServiceProcessResult(%p, fd:%d) error(%d)", data->resolve.sd_ref, fd, err); + } + + return; + +fail: + request_clear(data->u->module->core->mainloop, &data->resolve); +} + + +static int dnssd_resolve_dns_service(struct userdata *u, + unsigned int flags, unsigned int if_index, int protocol, + const char *service_name, const char *service_type, + const char *domain) { + int ret; + + service_data_t *data = service_data_new(); + data->u = u; + data->flags = flags; + data->interface = if_index; + data->protocol = protocol; + data->service_name = pa_xstrdup(service_name); + data->service_type = pa_xstrdup(service_type); + data->domain = pa_xstrdup(domain); + + ret = DNSServiceResolve(&(data->resolve.sd_ref), + flags, if_index, + service_name, service_type, + domain, + dnssd_resolve_reply_cb, + data); + if (ret != kDNSServiceErr_NoError) { + pa_log_error("DNSServiceResolve() error(%d)", ret); + goto fail; + } + + pa_log_info("DNSServiceResolve(%p) success", data->resolve.sd_ref); + + ret = request_set_io_callback(u->module->core->mainloop, &data->resolve, io_resolve_event_callback, data); + if (ret < 0) + goto fail; + + return 0; + +fail: + service_data_free(data); + return -1; +} + +static void dnssd_browse_reply_cb(DNSServiceRef sd_ref, unsigned int flags, + unsigned int if_index, DNSServiceErrorType error_code, + const char *service_name, const char *service_type, + const char *domain, void *user_data) { + struct userdata *u = user_data; + struct tunnel *t; + int protocol = kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6; + + pa_log_info("-----------------------------------"); + pa_log_info("Browse Reply : ref(%p), userdata(%p), error(%d), flags(0x%x)", + sd_ref, user_data, error_code, flags); + pa_log_info(" Interface Index : %d", if_index); + pa_log_info(" Service Name : %s", service_name); + pa_log_info(" Service Type : %s", service_type); + pa_log_info(" Domain : %s", domain); + pa_log_info("-----------------------------------"); + + t = tunnel_new(if_index, protocol, service_name, service_type, domain); + + if (flags & kDNSServiceFlagsAdd) { + if (!pa_hashmap_get(u->tunnels, t)) + dnssd_resolve_dns_service(u, flags, if_index, protocol, + service_name, service_type, domain); + else + pa_log_warn("already exists %p", t); + } else { + struct tunnel *t2; + + if ((t2 = pa_hashmap_get(u->tunnels, t))) { + pa_module_unload_request_by_index(u->core, t2->module_index, true); + pa_hashmap_remove(u->tunnels, t2); + pa_log_info("Module(%u) Unloaded, Tunnel(%p) removed success", t2->module_index, t2); + tunnel_free(t2); + } else { + pa_log_warn("Not found %p", t); + } + } +} + +static void io_browse_handle_event(pa_mainloop_api *io, pa_io_event *e, int fd, + pa_io_event_flags_t events, struct userdata *u, dns_request_data_t *request) { + DNSServiceErrorType err; + + pa_assert(io); + pa_assert(u); + + if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { + pa_log_warn("Lost connection fd : %d", fd); + goto fail; + } + + if (events & PA_IO_EVENT_INPUT) { + err = DNSServiceProcessResult(request->sd_ref); + if (err == 0) + pa_log_info("DNSServiceProcessResult(%p, fd:%d) success", request->sd_ref, fd); + else + pa_log_info("DNSServiceProcessResult(%p, fd:%d) error(%d)", request->sd_ref, fd, err); + } + + return; + +fail: + request_clear(u->module->core->mainloop, request); + pa_module_unload_request(u->module, true); +} + +static void io_browse_sink_event_callback(pa_mainloop_api *io, pa_io_event *e, int fd, + pa_io_event_flags_t events, void *user_data) { + struct userdata *u = user_data; + + io_browse_handle_event(io, e, fd, events, u, &u->browse_sink); +} + +static void io_browse_source_event_callback(pa_mainloop_api *io, pa_io_event *e, int fd, + pa_io_event_flags_t events, void *user_data) { + struct userdata *u = user_data; + + io_browse_handle_event(io, e, fd, events, u, &u->browse_source); +} + +static int dnssd_browse_dns_service(struct userdata *u, const char *service_type) { + int ret; + dns_request_data_t *request; + pa_io_event_cb_t cb; + + if (pa_safe_streq(service_type, SERVICE_TYPE_SINK)) { + request = &u->browse_sink; + cb = io_browse_sink_event_callback; + } else if (pa_safe_streq(service_type, SERVICE_TYPE_SOURCE)) { + request = &u->browse_source; + cb = io_browse_source_event_callback; + } else { + pa_log_error("invalid service type %s", pa_strnull(service_type)); + return -1; + } + + ret = DNSServiceBrowse(&request->sd_ref, 0, + kDNSServiceInterfaceIndexAny, service_type, + NULL, dnssd_browse_reply_cb, + u); + if (ret != kDNSServiceErr_NoError) { + pa_log_error("DNSServiceBrowse() with %s error(%d)", service_type, ret); + return -1; + } + + pa_log_info("DNSServiceBrowse(%p) success", request->sd_ref); + + ret = request_set_io_callback(u->module->core->mainloop, request, cb, u); + if (ret < 0) { + request_clear(u->module->core->mainloop, request); + return -1; + } + + return 0; +} + +void pa__done(pa_module *m) { + struct userdata *u; + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->tunnels) { + struct tunnel *t; + + while ((t = pa_hashmap_steal_first(u->tunnels))) { + pa_module_unload_request_by_index(u->core, t->module_index, true); + tunnel_free(t); + } + + pa_hashmap_free(u->tunnels); + } + + request_clear(u->module->core->mainloop, &u->browse_sink); + request_clear(u->module->core->mainloop, &u->browse_source); + + pa_xfree(u); +} + +int pa__init(pa_module *m) { + + struct userdata *u; + pa_modargs *ma = NULL; + int ret; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->module = m; + u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare); + + ret = dnssd_browse_dns_service(u, SERVICE_TYPE_SINK); + if (ret < 0) + goto fail; + + ret = dnssd_browse_dns_service(u, SERVICE_TYPE_SOURCE); + if (ret < 0) + goto fail; + + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} diff --git a/src/module-tizenaudio-publish.c b/src/module-tizenaudio-publish.c new file mode 100644 index 0000000..480b98f --- /dev/null +++ b/src/module-tizenaudio-publish.c @@ -0,0 +1,513 @@ +/*** + This file is part of PulseAudio. + + Copyright 2020 Seungbae Shin + based on module-bonjour-publish.c + + 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, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PA_MODULE_AUTHOR("Seungbae Shin"); +PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); + +#define SERVICE_TYPE_SINK "_pulse-sink._tcp" +#define SERVICE_TYPE_SOURCE "_pulse-source._tcp" +#define SERVICE_TYPE_SERVER "_pulse-server._tcp" + +static const char* const valid_modargs[] = { + NULL +}; + +enum service_subtype { + SUBTYPE_HARDWARE, + SUBTYPE_VIRTUAL, + SUBTYPE_MONITOR +}; + +struct service { + struct userdata *userdata; + DNSServiceRef service; + DNSRecordRef rec, rec2; + char *service_name; + pa_object *device; + enum service_subtype subtype; +}; + +struct userdata { + pa_core *core; + pa_module *module; + + pa_hashmap *services; + char *service_name; + + pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot; + + pa_native_protocol *native; + DNSServiceRef main_service; +}; + +static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, pa_proplist **ret_proplist, enum service_subtype *ret_subtype) { + pa_assert(s); + pa_assert(ret_ss); + pa_assert(ret_proplist); + pa_assert(ret_subtype); + + if (pa_sink_isinstance(s->device)) { + pa_sink *sink = PA_SINK(s->device); + + *ret_ss = sink->sample_spec; + *ret_map = sink->channel_map; + *ret_name = sink->name; + *ret_proplist = sink->proplist; + *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL; + + } else if (pa_source_isinstance(s->device)) { + pa_source *source = PA_SOURCE(s->device); + + *ret_ss = source->sample_spec; + *ret_map = source->channel_map; + *ret_name = source->name; + *ret_proplist = source->proplist; + *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL); + + } else { + pa_assert_not_reached(); + } +} + +static void txt_record_server_data(pa_core *c, TXTRecordRef *txt) { + char s[128]; + char *t; + + pa_assert(c); + + TXTRecordSetValue(txt, "server-version", strlen(PACKAGE_NAME" "PACKAGE_VERSION), PACKAGE_NAME" "PACKAGE_VERSION); + + t = pa_get_user_name_malloc(); + TXTRecordSetValue(txt, "user-name", strlen(t), t); + pa_xfree(t); + + t = pa_machine_id(); + TXTRecordSetValue(txt, "machine-id", strlen(t), t); + pa_xfree(t); + + t = pa_uname_string(); + TXTRecordSetValue(txt, "uname", strlen(t), t); + pa_xfree(t); + + t = pa_get_fqdn(s, sizeof(s)); + TXTRecordSetValue(txt, "fqdn", strlen(t), t); + + snprintf(s, sizeof(s), "0x%08x", c->cookie); + TXTRecordSetValue(txt, "cookie", strlen(s), s); +} + +static void service_free(struct service *s); + +static void dns_service_register_reply(DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context) { + struct service *s = context; + + pa_assert(s); + + switch (errorCode) { + case kDNSServiceErr_NameConflict: + pa_log("DNS service reported kDNSServiceErr_NameConflict\n"); + service_free(s); + break; + + case kDNSServiceErr_NoError: + /* fall through */ + default: + break; + } +} + +static uint16_t compute_port(struct userdata *u) { + pa_strlist *i; + + pa_assert(u); + + for (i = pa_native_protocol_servers(u->native); i; i = pa_strlist_next(i)) { + pa_parsed_address a; + + if (pa_parse_address(pa_strlist_data(i), &a) >= 0 && + (a.type == PA_PARSED_ADDRESS_TCP4 || + a.type == PA_PARSED_ADDRESS_TCP6 || + a.type == PA_PARSED_ADDRESS_TCP_AUTO) && + a.port > 0) { + + pa_xfree(a.path_or_host); + return htons(a.port); + } + + pa_xfree(a.path_or_host); + } + + return htons(PA_NATIVE_DEFAULT_PORT); +} + +static int publish_service(struct service *s) { + int r = -1; + TXTRecordRef txt; + DNSServiceErrorType err; + const char *name = NULL, *t; + pa_proplist *proplist = NULL; + pa_sample_spec ss; + pa_channel_map map; + char cm[PA_CHANNEL_MAP_SNPRINT_MAX], tmp[64]; + enum service_subtype subtype; + + const char * const subtype_text[] = { + [SUBTYPE_HARDWARE] = "hardware", + [SUBTYPE_VIRTUAL] = "virtual", + [SUBTYPE_MONITOR] = "monitor" + }; + + pa_assert(s); + + if (s->service) { + DNSServiceRefDeallocate(s->service); + s->service = NULL; + } + + TXTRecordCreate(&txt, 0, NULL); + + txt_record_server_data(s->userdata->core, &txt); + + get_service_data(s, &ss, &map, &name, &proplist, &subtype); + TXTRecordSetValue(&txt, "device", strlen(name), name); + + snprintf(tmp, sizeof(tmp), "%u", ss.rate); + TXTRecordSetValue(&txt, "rate", strlen(tmp), tmp); + + snprintf(tmp, sizeof(tmp), "%u", ss.channels); + TXTRecordSetValue(&txt, "channels", strlen(tmp), tmp); + + t = pa_sample_format_to_string(ss.format); + TXTRecordSetValue(&txt, "format", strlen(t), t); + + t = pa_channel_map_snprint(cm, sizeof(cm), &map); + TXTRecordSetValue(&txt, "channel_map", strlen(t), t); + + t = subtype_text[subtype]; + TXTRecordSetValue(&txt, "subtype", strlen(t), t); + + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_DESCRIPTION))) + TXTRecordSetValue(&txt, "description", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_ICON_NAME))) + TXTRecordSetValue(&txt, "icon-name", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_VENDOR_NAME))) + TXTRecordSetValue(&txt, "vendor-name", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_PRODUCT_NAME))) + TXTRecordSetValue(&txt, "product-name", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_CLASS))) + TXTRecordSetValue(&txt, "class", strlen(t), t); + if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR))) + TXTRecordSetValue(&txt, "form-factor", strlen(t), t); + + err = DNSServiceRegister(&s->service, + 0, /* flags */ + kDNSServiceInterfaceIndexAny, + s->service_name, + pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE, + NULL, /* domain */ + NULL, /* host */ + compute_port(s->userdata), + TXTRecordGetLength(&txt), + TXTRecordGetBytesPtr(&txt), + dns_service_register_reply, s); + + if (err != kDNSServiceErr_NoError) { + pa_log("DNSServiceRegister() returned err %d", err); + goto finish; + } + + pa_log_debug("Successfully registered mDNS services for >%s<.", s->service_name); + return 0; + +finish: + + /* Remove this service */ + if (r < 0) + service_free(s); + + TXTRecordDeallocate(&txt); + + return r; +} + +static struct service *get_service(struct userdata *u, pa_object *device) { + struct service *s; + char *hn, *un; + const char *n; + + pa_assert(u); + pa_object_assert_ref(device); + + if ((s = pa_hashmap_get(u->services, device))) + return s; + + s = pa_xnew0(struct service, 1); + s->userdata = u; + s->device = device; + + if (pa_sink_isinstance(device)) { + if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) + n = PA_SINK(device)->name; + } else { + if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) + n = PA_SOURCE(device)->name; + } + + hn = pa_get_host_name_malloc(); + un = pa_get_user_name_malloc(); + + s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), kDNSServiceMaxDomainName-1); + + pa_xfree(un); + pa_xfree(hn); + + pa_hashmap_put(u->services, s->device, s); + + return s; +} + +static void service_free(struct service *s) { + pa_assert(s); + + pa_hashmap_remove(s->userdata->services, s->device); + + if (s->service) + DNSServiceRefDeallocate(s->service); + + pa_xfree(s->service_name); + pa_xfree(s); +} + +static bool shall_ignore(pa_object *o) { + pa_object_assert_ref(o); + + if (pa_sink_isinstance(o)) + return !!(PA_SINK(o)->flags & PA_SINK_NETWORK); + + if (pa_source_isinstance(o)) + return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK); + + pa_assert_not_reached(); +} + +static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) { + pa_assert(c); + pa_object_assert_ref(o); + + if (!shall_ignore(o)) + publish_service(get_service(u, o)); + + return PA_HOOK_OK; +} + +static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) { + struct service *s; + + pa_assert(c); + pa_object_assert_ref(o); + + if ((s = pa_hashmap_get(u->services, o))) + service_free(s); + + return PA_HOOK_OK; +} + +static int publish_main_service(struct userdata *u) { + DNSServiceErrorType err; + TXTRecordRef txt; + + pa_assert(u); + + if (u->main_service) { + DNSServiceRefDeallocate(u->main_service); + u->main_service = NULL; + } + + TXTRecordCreate(&txt, 0, NULL); + txt_record_server_data(u->core, &txt); + + err = DNSServiceRegister(&u->main_service, + 0, /* flags */ + kDNSServiceInterfaceIndexAny, + u->service_name, + SERVICE_TYPE_SERVER, + NULL, /* domain */ + NULL, /* host */ + compute_port(u), + TXTRecordGetLength(&txt), + TXTRecordGetBytesPtr(&txt), + NULL, NULL); + + if (err != kDNSServiceErr_NoError) { + pa_log("DNSServiceRegister() returned err %d", err); + return err; + } + + TXTRecordDeallocate(&txt); + + return 0; +} + +static int publish_all_services(struct userdata *u) { + pa_sink *sink; + pa_source *source; + uint32_t idx; + + pa_assert(u); + + pa_log_debug("Publishing services in mDNS"); + + for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx))) + if (!shall_ignore(PA_OBJECT(sink))) + publish_service(get_service(u, PA_OBJECT(sink))); + + for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx))) + if (!shall_ignore(PA_OBJECT(source))) + publish_service(get_service(u, PA_OBJECT(source))); + + return publish_main_service(u); +} + +static void unpublish_all_services(struct userdata *u) { + void *state = NULL; + struct service *s; + + pa_assert(u); + + pa_log_debug("Unpublishing services in mDNS"); + + while ((s = pa_hashmap_iterate(u->services, &state, NULL))) + service_free(s); + + if (u->main_service) + DNSServiceRefDeallocate(u->main_service); +} + +void pa__done(pa_module *m) { + struct userdata *u; + pa_assert(m); + + if (!(u = m->userdata)) + return; + + unpublish_all_services(u); + + if (u->services) + pa_hashmap_free(u->services); + + if (u->sink_new_slot) + pa_hook_slot_free(u->sink_new_slot); + if (u->source_new_slot) + pa_hook_slot_free(u->source_new_slot); + if (u->sink_changed_slot) + pa_hook_slot_free(u->sink_changed_slot); + if (u->source_changed_slot) + pa_hook_slot_free(u->source_changed_slot); + if (u->sink_unlink_slot) + pa_hook_slot_free(u->sink_unlink_slot); + if (u->source_unlink_slot) + pa_hook_slot_free(u->source_unlink_slot); + + if (u->native) + pa_native_protocol_unref(u->native); + + pa_xfree(u->service_name); + pa_xfree(u); +} + +int pa__init(pa_module *m) { + struct userdata *u; + pa_modargs *ma = NULL; + char *hn, *un; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->native = pa_native_protocol_get(u->core); + + u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); + u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); + u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); + u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); + u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); + u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); + + un = pa_get_user_name_malloc(); + hn = pa_get_host_name_malloc(); + u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), kDNSServiceMaxDomainName-1); + pa_xfree(un); + pa_xfree(hn); + + if (publish_all_services(u) != 0) + goto fail; + + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} -- 2.7.4 From 8e05a49407271c3b185298a7634b99c0be1bfdac Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Mon, 13 Jul 2020 11:12:18 +0900 Subject: [PATCH 14/16] tizenaudio-discover: ignore local services [Version] 13.0.22 [Issue Type] Update Change-Id: I6e8f334e6a16f5f4367c002ea5bd5809dc6b561b --- Makefile.am | 4 ++-- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tizenaudio-discover.c | 19 +++++++++++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 07985dc..90d1260 100644 --- a/Makefile.am +++ b/Makefile.am @@ -119,8 +119,8 @@ endif module_tizenaudio_discover_la_SOURCES = src/module-tizenaudio-discover.c module_tizenaudio_discover_la_LDFLAGS = $(MODULE_LDFLAGS) -module_tizenaudio_discover_la_LIBADD = $(MODULE_LIBADD) $(DNSSD_LIBS) -module_tizenaudio_discover_la_CFLAGS = $(MODULE_CFLAGS) $(DNSSD_CFLAGS) +module_tizenaudio_discover_la_LIBADD = $(MODULE_LIBADD) $(DNSSD_LIBS) $(VCONF_LIBS) +module_tizenaudio_discover_la_CFLAGS = $(MODULE_CFLAGS) $(DNSSD_CFLAGS) $(VCONF_CFLAGS) module_tizenaudio_publish_la_SOURCES = src/module-tizenaudio-publish.c module_tizenaudio_publish_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index c3749d4..541fad8 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.21 +Version: 13.0.22 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-discover.c b/src/module-tizenaudio-discover.c index ac88209..8b3ec80 100644 --- a/src/module-tizenaudio-discover.c +++ b/src/module-tizenaudio-discover.c @@ -26,10 +26,13 @@ #include #include #include +#include #include #include #include +#include +#include #include @@ -352,6 +355,16 @@ finish: tunnel_free(tnl); } +static bool is_local_service(const char *ip_addr) { + char *ipv4_addr = NULL; + bool is_local = false; + + ipv4_addr = vconf_get_str(VCONFKEY_NETWORK_IP); + is_local = pa_safe_streq(ipv4_addr, ip_addr); + pa_xfree(ipv4_addr); + + return is_local; +} static void dnssd_getaddrinfo_reply_cb(DNSServiceRef sd_ref, unsigned int flags, unsigned int if_index, @@ -383,10 +396,12 @@ static void dnssd_getaddrinfo_reply_cb(DNSServiceRef sd_ref, pa_log_info(" Host Name : %s", data->host_name); pa_log_info(" IP Address : %s", data->ip_address); pa_log_info(" TTL : %d", ttl); - pa_log_info("-----------------------------------"); - tunnel_add(data); + if (is_local_service(data->ip_address)) + pa_log_info("Skip local service!!!"); + else + tunnel_add(data); /* cleanup addr */ request_clear(data->u->module->core->mainloop, &data->addr); -- 2.7.4 From c50510164267eed6d013f9c665fce7eda12c8dfb Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Tue, 14 Jul 2020 20:24:16 +0900 Subject: [PATCH 15/16] tizenaudio-discover: use new tunnel module [Version] 13.0.23 [Issue Type] Update Change-Id: I42946234489dffb5e936d49ce7a6eb46aec5dcaa --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tizenaudio-discover.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 541fad8..78eccb1 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.22 +Version: 13.0.23 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-discover.c b/src/module-tizenaudio-discover.c index 8b3ec80..798f006 100644 --- a/src/module-tizenaudio-discover.c +++ b/src/module-tizenaudio-discover.c @@ -317,7 +317,7 @@ static void tunnel_add(service_data_t *data) { t = strstr(data->service_type, "sink") ? "sink" : "source"; - module_name = pa_sprintf_malloc("module-tunnel-%s", t); + module_name = pa_sprintf_malloc("module-tunnel-%s-new", t); args = pa_sprintf_malloc("server=[%s]:%u " "%s=%s " "format=%s " -- 2.7.4 From 2e2db377b4dfd8293a393ca8a8cb7a0e303f9807 Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Mon, 13 Jul 2020 11:13:47 +0900 Subject: [PATCH 16/16] tizenaudio-publish: publish only built-in devices [Version] 13.0.24 [Issue Type] Update Change-Id: I69753da561c813c80c35df7690d4efc1efc48a43 --- packaging/pulseaudio-modules-tizen.spec | 2 +- src/module-tizenaudio-publish.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 78eccb1..9324ca1 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.23 +Version: 13.0.24 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-publish.c b/src/module-tizenaudio-publish.c index 480b98f..cdead84 100644 --- a/src/module-tizenaudio-publish.c +++ b/src/module-tizenaudio-publish.c @@ -329,14 +329,30 @@ static void service_free(struct service *s) { pa_xfree(s); } +static bool is_builtin_device(pa_proplist *pl) +{ + /* FIXME: Determining the built-in device should be retrieved from device-manager. */ + return pa_safe_streq(pa_proplist_gets(pl, PA_PROP_DEVICE_FORM_FACTOR), "internal"); +} + static bool shall_ignore(pa_object *o) { pa_object_assert_ref(o); - if (pa_sink_isinstance(o)) + if (pa_sink_isinstance(o)) { + if (!is_builtin_device(PA_SINK(o)->proplist)) { + pa_log_error("sink [%s] is not an internal, skip this", PA_SINK(o)->name); + return true; + } return !!(PA_SINK(o)->flags & PA_SINK_NETWORK); + } - if (pa_source_isinstance(o)) + if (pa_source_isinstance(o)) { + if (!is_builtin_device(PA_SOURCE(o)->proplist)) { + pa_log_error("source [%s] is not an internal, skip this", PA_SOURCE(o)->name); + return true; + } return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK); + } pa_assert_not_reached(); } -- 2.7.4