From 1ff77b4e81e8dcc59431ce227071b4be4ab89820 Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Fri, 12 Jun 2015 15:54:56 +0900 Subject: [PATCH] policy: Add stream-manager.c and communicater.c Add stream-map parsing logic to stream-manager.c Add initial codes for matching up each sink-input/source-output and it's clients to stream-manager.c Support dbus methods for getting stream list and stream information per type to stream-manager.c Manage streams made by pa_scache, virtual-stream from sound-manager [Version] 0.5.1 [Profile] Common [Issue Type] Add feature Change-Id: Ia66f42960c3f260e046f211cef097d026cf67694 --- packaging/pulseaudio.spec | 2 +- src/Makefile.am | 6 +- src/modules/communicator.c | 89 +++ src/modules/communicator.h | 19 + src/modules/module-policy.c | 350 ++++++---- src/modules/stream-manager.c | 1447 ++++++++++++++++++++++++++++++++++++++++++ src/modules/stream-manager.h | 53 ++ src/modules/tizen-audio.h | 38 ++ src/pulse/proplist.h | 29 +- 9 files changed, 1901 insertions(+), 132 deletions(-) create mode 100644 src/modules/communicator.c create mode 100644 src/modules/communicator.h create mode 100644 src/modules/stream-manager.c create mode 100644 src/modules/stream-manager.h diff --git a/packaging/pulseaudio.spec b/packaging/pulseaudio.spec index 1f7c126..43e0997 100644 --- a/packaging/pulseaudio.spec +++ b/packaging/pulseaudio.spec @@ -11,7 +11,7 @@ Name: pulseaudio Summary: Improved Linux sound server Version: 5.0 -Release: 0 +Release: 1 Group: Multimedia/Audio License: GPL-2.0+ and LGPL-2.1+ URL: http://pulseaudio.org diff --git a/src/Makefile.am b/src/Makefile.am index a6808c5..bc80a45 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2296,10 +2296,10 @@ module_rygel_media_server_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libprotocol- module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) if USE_SAMSUNG_POLICY -module_policy_la_SOURCES = modules/module-policy.c +module_policy_la_SOURCES = modules/module-policy.c modules/communicator.c modules/communicator.h modules/stream-manager.c modules/stream-manager.h module_policy_la_LDFLAGS = $(MODULE_LDFLAGS) -module_policy_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) $(VCONF_LIBS) $(INIPARSER_LIBS) $(ASOUNDLIB_LIBS) libprotocol-native.la libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la -module_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS) $(INIPARSER_CFLAGS) $(ASOUNDLIB_CFLAGS) +module_policy_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) $(VCONF_LIBS) $(INIPARSER_LIBS) $(LIBJSON_LIBS) $(ASOUNDLIB_LIBS) libprotocol-native.la libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la +module_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS) $(INIPARSER_CFLAGS) $(LIBJSON_CFLAGS) $(ASOUNDLIB_CFLAGS) endif ################################### diff --git a/src/modules/communicator.c b/src/modules/communicator.c new file mode 100644 index 0000000..cd5d2f9 --- /dev/null +++ b/src/modules/communicator.c @@ -0,0 +1,89 @@ +/*** + This file is part of PulseAudio. + + Copyright 2015 Sangchul 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 "communicator.h" + +struct _pa_communicator { + PA_REFCNT_DECLARE; + + pa_core *core; + pa_hook hooks[PA_COMMUNICATOR_HOOK_MAX]; +}; + +pa_communicator* pa_communicator_get(pa_core *core) { + pa_communicator *c; + unsigned i; + + pa_assert(core); + + if ((c = pa_shared_get(core, "communicator"))) + return pa_communicator_ref(c); + + c = pa_xnew0(pa_communicator, 1); + PA_REFCNT_INIT(c); + c->core = core; + + for (i = 0; i < PA_COMMUNICATOR_HOOK_MAX; i++) + pa_hook_init(&c->hooks[i], c); + + pa_shared_set(core, "communicator", c); + + return c; +} + +pa_communicator* pa_communicator_ref(pa_communicator *c) { + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) > 0); + + PA_REFCNT_INC(c); + + return c; +} + +void pa_communicator_unref(pa_communicator *c) { + unsigned i; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) > 0); + + if (PA_REFCNT_DEC(c) > 0) + return; + + for (i = 0; i < PA_COMMUNICATOR_HOOK_MAX; i++) + pa_hook_done(&c->hooks[i]); + + if (c->core) + pa_shared_remove(c->core, "communicator"); + + pa_xfree(c); +} + +pa_hook* pa_communicator_hook(pa_communicator *c, pa_communicator_hook_t hook) { + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) > 0); + + return &c->hooks[hook]; +} + diff --git a/src/modules/communicator.h b/src/modules/communicator.h new file mode 100644 index 0000000..70cda4e --- /dev/null +++ b/src/modules/communicator.h @@ -0,0 +1,19 @@ +#ifndef foocommunicatorfoo +#define foocommunicatorfoo +#include + +typedef enum pa_communicator_hook { + PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE, + PA_COMMUNICATOR_HOOK_CHANGE_ROUTE, + PA_COMMUNICATOR_HOOK_UPDATE_ROUTE_OPTION, + PA_COMMUNICATOR_HOOK_MAX +} pa_communicator_hook_t; + +typedef struct _pa_communicator pa_communicator; + +pa_communicator* pa_communicator_get(pa_core *c); +pa_communicator* pa_communicator_ref(pa_communicator *c); +void pa_communicator_unref(pa_communicator *c); +pa_hook* pa_communicator_hook(pa_communicator *c, pa_communicator_hook_t hook); + +#endif diff --git a/src/modules/module-policy.c b/src/modules/module-policy.c index c6f13b6..6867b32 100644 --- a/src/modules/module-policy.c +++ b/src/modules/module-policy.c @@ -39,6 +39,8 @@ #include "module-policy-symdef.h" #include "tizen-audio.h" +#include "communicator.h" +#include "stream-manager.h" #define VCONFKEY_SOUND_HDMI_SUPPORT "memory/private/sound/hdmisupport" #ifdef PRIMARY_VOLUME @@ -256,7 +258,7 @@ struct userdata { void *dl_handle; void *data; audio_interface_t intf; - } audio_mgr; + } hal_manager; struct { // for burst-shot pa_bool_t is_running; @@ -269,6 +271,15 @@ struct userdata { pa_usec_t time_interval; pa_usec_t factor; /* timer boosting */ } audio_sample_userdata; + + struct { + pa_communicator *comm; + pa_hook_slot *comm_hook_select_proper_sink_or_source_slot; + pa_hook_slot *comm_hook_change_route_slot; + pa_hook_slot *comm_hook_update_route_option_slot; + } communicator; + + pa_stream_manager *stream_manager; }; enum { @@ -1814,7 +1825,7 @@ static audio_return_t policy_play_sample_continuously(struct userdata *u, pa_nat /* FIXME : Add gain_type parameter to API like volume_type */ audio_info.stream.gain_type = gain_type; - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.get_volume_value(u->audio_mgr.data, &audio_info, volume_type, volume_level, &volume_linear)))) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.get_volume_value(u->hal_manager.data, &audio_info, volume_type, volume_level, &volume_linear)))) { pa_log_warn("get_volume_value returns error:0x%x", audio_ret); goto exit; } @@ -1958,7 +1969,7 @@ static audio_return_t policy_play_sample(struct userdata *u, pa_native_connectio /* FIXME : Add gain_type parameter to API like volume_type */ audio_info.stream.gain_type = gain_type; - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.get_volume_value(u->audio_mgr.data, &audio_info, volume_type, volume_level, &volume_linear)))) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.get_volume_value(u->hal_manager.data, &audio_info, volume_type, volume_level, &volume_linear)))) { pa_log_warn("get_volume_value returns error:0x%x", audio_ret); goto exit; } @@ -2004,9 +2015,10 @@ static audio_return_t policy_reset(struct userdata *u) pa_log_debug("reset"); __load_dump_config(u); - if (u->audio_mgr.intf.reset) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.reset(&u->audio_mgr.data)))) { - pa_log_error("audio_mgr reset failed"); + + if (u->hal_manager.intf.reset) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.reset(&u->hal_manager.data)))) { + pa_log_error("hal_manager reset failed"); return audio_ret; } } @@ -2035,12 +2047,12 @@ static audio_return_t policy_set_session(struct userdata *u, uint32_t session, u } else { u->subsession = AUDIO_SUBSESSION_NONE; } - if (u->audio_mgr.intf.set_session) { - u->audio_mgr.intf.set_session(u->audio_mgr.data, session, u->subsession, AUDIO_SESSION_CMD_START); + if (u->hal_manager.intf.set_session) { + u->hal_manager.intf.set_session(u->hal_manager.data, session, u->subsession, AUDIO_SESSION_CMD_START); } } else { - if (u->audio_mgr.intf.set_session) { - u->audio_mgr.intf.set_session(u->audio_mgr.data, session, u->subsession, AUDIO_SESSION_CMD_END); + if (u->hal_manager.intf.set_session) { + u->hal_manager.intf.set_session(u->hal_manager.data, session, u->subsession, AUDIO_SESSION_CMD_END); } u->session = AUDIO_SESSION_MEDIA; u->subsession = AUDIO_SUBSESSION_NONE; @@ -2060,8 +2072,8 @@ static audio_return_t policy_set_session(struct userdata *u, uint32_t session, u if (need_route) { uint32_t route_flag = __get_route_flag(u); - if (u->audio_mgr.intf.set_route) { - u->audio_mgr.intf.set_route(u->audio_mgr.data, u->session, u->subsession, u->active_device_in, u->active_device_out, route_flag); + if (u->hal_manager.intf.set_route) { + u->hal_manager.intf.set_route(u->hal_manager.data, u->session, u->subsession, u->active_device_in, u->active_device_out, route_flag); } u->active_route_flag = route_flag; } else { @@ -2095,8 +2107,8 @@ static audio_return_t policy_set_subsession(struct userdata *u, uint32_t subsess u->subsession_opt = subsession_opt; #endif - if (u->audio_mgr.intf.set_session) { - u->audio_mgr.intf.set_session(u->audio_mgr.data, u->session, u->subsession, AUDIO_SESSION_CMD_SUBSESSION); + if (u->hal_manager.intf.set_session) { + u->hal_manager.intf.set_session(u->hal_manager.data, u->session, u->subsession, AUDIO_SESSION_CMD_SUBSESSION); } if (prev_subsession!= subsession) { @@ -2114,8 +2126,8 @@ static audio_return_t policy_set_subsession(struct userdata *u, uint32_t subsess if (need_route) { uint32_t route_flag = __get_route_flag(u); - if (u->audio_mgr.intf.set_route) { - u->audio_mgr.intf.set_route(u->audio_mgr.data, u->session, u->subsession, u->active_device_in, u->active_device_out, route_flag); + if (u->hal_manager.intf.set_route) { + u->hal_manager.intf.set_route(u->hal_manager.data, u->session, u->subsession, u->active_device_in, u->active_device_out, route_flag); } u->active_route_flag = route_flag; } else { @@ -2161,8 +2173,8 @@ static audio_return_t policy_set_active_device(struct userdata *u, uint32_t devi } #ifdef TIZEN_MICRO - if (u->audio_mgr.intf.set_route) { - u->audio_mgr.intf.set_route(u->audio_mgr.data, u->session, u->subsession, device_in, device_out, route_flag); + if (u->hal_manager.intf.set_route) { + u->hal_manager.intf.set_route(u->hal_manager.data, u->session, u->subsession, device_in, device_out, route_flag); } #else @@ -2170,7 +2182,7 @@ static audio_return_t policy_set_active_device(struct userdata *u, uint32_t devi if(u->active_device_out == device_out) { *need_update = false; } - if (u->audio_mgr.intf.set_route) { + if (u->hal_manager.intf.set_route) { audio_return_t audio_ret = AUDIO_RET_OK; const char *device_switching_str; uint32_t device_switching = 0; @@ -2200,7 +2212,7 @@ static audio_return_t policy_set_active_device(struct userdata *u, uint32_t devi } #endif - u->audio_mgr.intf.set_route(u->audio_mgr.data, u->session, u->subsession, device_in, device_out, route_flag); + u->hal_manager.intf.set_route(u->hal_manager.data, u->session, u->subsession, device_in, device_out, route_flag); /* Unmute sink inputs which are muted due to device switching */ PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) { if ((device_switching_str = pa_proplist_gets(si->proplist, "module-policy.device_switching"))) { @@ -2253,8 +2265,8 @@ static audio_return_t policy_get_volume_level_max(struct userdata *u, uint32_t v audio_return_t audio_ret = AUDIO_RET_OK; /* Call HAL function if exists */ - if (u->audio_mgr.intf.get_volume_level_max) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.get_volume_level_max(u->audio_mgr.data, volume_type, volume_level)))) { + if (u->hal_manager.intf.get_volume_level_max) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.get_volume_level_max(u->hal_manager.data, volume_type, volume_level)))) { pa_log_error("get_volume_level_max returns error:0x%x", audio_ret); return audio_ret; } @@ -2289,8 +2301,8 @@ static audio_return_t __update_volume(struct userdata *u, uint32_t stream_idx, u } /* Call HAL function if exists */ - if (u->audio_mgr.intf.set_volume_level && (stream_idx == PA_INVALID_INDEX)) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.set_volume_level(u->audio_mgr.data, NULL, volume_type, volume_level)))) { + if (u->hal_manager.intf.set_volume_level && (stream_idx == PA_INVALID_INDEX)) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.set_volume_level(u->hal_manager.data, NULL, volume_type, volume_level)))) { pa_log_error("set_volume_level returns error:0x%x", audio_ret); return audio_ret; } @@ -2309,8 +2321,8 @@ static audio_return_t __update_volume(struct userdata *u, uint32_t stream_idx, u pa_cvolume cv; /* Call HAL function if exists */ - if (u->audio_mgr.intf.set_volume_level) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.set_volume_level(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, volume_level)))) { + if (u->hal_manager.intf.set_volume_level) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.set_volume_level(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, volume_level)))) { pa_log_error("set_volume_level for sink-input[%d] returns error:0x%x", idx, audio_ret); __free_audio_info(&audio_info); return audio_ret; @@ -2318,8 +2330,8 @@ static audio_return_t __update_volume(struct userdata *u, uint32_t stream_idx, u } /* Get volume value by type & level */ - if (u->audio_mgr.intf.get_volume_value && (audio_ret != AUDIO_RET_USE_HW_CONTROL)) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.get_volume_value(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, volume_level, &volume_linear)))) { + if (u->hal_manager.intf.get_volume_value && (audio_ret != AUDIO_RET_USE_HW_CONTROL)) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.get_volume_value(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, volume_level, &volume_linear)))) { pa_log_warn("get_volume_value for sink-input[%d] returns error:0x%x", idx, audio_ret); __free_audio_info(&audio_info); return audio_ret; @@ -2362,8 +2374,8 @@ static audio_return_t __update_volume_by_value(struct userdata *u, uint32_t stre pa_cvolume cv; // 1. get gain first - if (u->audio_mgr.intf.get_gain_value) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.get_gain_value(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, &gain)))) { + if (u->hal_manager.intf.get_gain_value) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.get_gain_value(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, &gain)))) { pa_log_warn("get_gain_value for sink-input[%d] volume_type(%d), returns error:0x%x", idx, audio_info.stream.volume_type, audio_ret); __free_audio_info(&audio_info); return audio_ret; @@ -2374,8 +2386,8 @@ static audio_return_t __update_volume_by_value(struct userdata *u, uint32_t stre volume *= gain; /* 3. adjust hw volume(LPA), Call HAL function if exists */ - if (u->audio_mgr.intf.set_volume_value) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.set_volume_value(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, &volume)))) { + if (u->hal_manager.intf.set_volume_value) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.set_volume_value(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, &volume)))) { pa_log_error("set_volume_level for sink-input[%d] returns error:0x%x", idx, audio_ret); __free_audio_info(&audio_info); return audio_ret; @@ -2516,8 +2528,8 @@ static audio_return_t policy_get_volume_level(struct userdata *u, uint32_t strea pa_log_warn("volume_type (%d) invalid", *volume_type); return AUDIO_ERR_PARAMETER; } - if (u->audio_mgr.intf.get_volume_level) { - u->audio_mgr.intf.get_volume_level(u->audio_mgr.data, *volume_type, volume_level); + if (u->hal_manager.intf.get_volume_level) { + u->hal_manager.intf.get_volume_level(u->hal_manager.data, *volume_type, volume_level); } pa_log_info("get_volume_level stream_idx:%d type:%d level:%d", stream_idx, *volume_type, *volume_level); @@ -2538,8 +2550,8 @@ static audio_return_t policy_get_volume_value(struct userdata *u, uint32_t strea return AUDIO_ERR_UNDEFINED; } - if(u->audio_mgr.intf.get_volume_value) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.get_volume_value(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, *volume_level, volume_linear)))) { + if(u->hal_manager.intf.get_volume_value) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.get_volume_value(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, *volume_level, volume_linear)))) { pa_log_warn("get_volume_value for stream_idx[%d] returns error:0x%x", stream_idx, audio_ret); return audio_ret; } @@ -2555,8 +2567,8 @@ static audio_return_t policy_set_volume_level(struct userdata *u, uint32_t strea /* Store volume level of type */ if (volume_type != (uint32_t)-1) { - if (u->audio_mgr.intf.set_volume_level) { - u->audio_mgr.intf.set_volume_level(u->audio_mgr.data, NULL, volume_type, volume_level); + if (u->hal_manager.intf.set_volume_level) { + u->hal_manager.intf.set_volume_level(u->hal_manager.data, NULL, volume_type, volume_level); } } @@ -2577,8 +2589,8 @@ static audio_return_t policy_update_volume(struct userdata *u) { pa_log_info("update_volume"); for (volume_type = 0; volume_type < AUDIO_VOLUME_TYPE_MAX; volume_type++) { - if (u->audio_mgr.intf.get_volume_level) { - u->audio_mgr.intf.get_volume_level(u->audio_mgr.data, volume_type, &volume_level); + if (u->hal_manager.intf.get_volume_level) { + u->hal_manager.intf.get_volume_level(u->hal_manager.data, volume_type, &volume_level); } __update_volume(u, (uint32_t)-1, volume_type, volume_level); #ifdef WEARABLE_FIX // commit : update call mute status after changing audio pathupdate call mute status after changing audio path @@ -2586,10 +2598,10 @@ static audio_return_t policy_update_volume(struct userdata *u) { if (u->session == AUDIO_SESSION_VOICECALL) { uint32_t call_muted = 0; - if (u->audio_mgr.intf.get_mute) { - u->audio_mgr.intf.get_mute(u->audio_mgr.data, NULL, AUDIO_VOLUME_TYPE_CALL, AUDIO_DIRECTION_IN, &call_muted); - if (u->call_muted != (int)call_muted && u->audio_mgr.intf.set_mute) { - u->audio_mgr.intf.set_mute(u->audio_mgr.data, NULL, AUDIO_VOLUME_TYPE_CALL, AUDIO_DIRECTION_IN, u->call_muted); + if (u->hal_manager.intf.get_mute) { + u->hal_manager.intf.get_mute(u->hal_manager.data, NULL, AUDIO_VOLUME_TYPE_CALL, AUDIO_DIRECTION_IN, &call_muted); + if (u->call_muted != (int)call_muted && u->hal_manager.intf.set_mute) { + u->hal_manager.intf.set_mute(u->hal_manager.data, NULL, AUDIO_VOLUME_TYPE_CALL, AUDIO_DIRECTION_IN, u->call_muted); } } } @@ -2605,8 +2617,8 @@ static audio_return_t policy_get_mute(struct userdata *u, uint32_t stream_idx, u uint32_t idx; audio_info_t audio_info; - if (u->audio_mgr.intf.get_mute && (stream_idx == PA_INVALID_INDEX)) { - audio_ret = u->audio_mgr.intf.get_mute(u->audio_mgr.data, NULL, volume_type, direction, mute); + if (u->hal_manager.intf.get_mute && (stream_idx == PA_INVALID_INDEX)) { + audio_ret = u->hal_manager.intf.get_mute(u->hal_manager.data, NULL, volume_type, direction, mute); if (audio_ret == AUDIO_RET_USE_HW_CONTROL) { return audio_ret; } else { @@ -2626,8 +2638,8 @@ static audio_return_t policy_get_mute(struct userdata *u, uint32_t stream_idx, u if ((stream_idx == idx) || ((stream_idx == PA_INVALID_INDEX) && (audio_info.stream.volume_type == volume_type))) { /* Call HAL function if exists */ - if (u->audio_mgr.intf.get_mute) { - audio_ret = u->audio_mgr.intf.get_mute(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, direction, mute); + if (u->hal_manager.intf.get_mute) { + audio_ret = u->hal_manager.intf.get_mute(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, direction, mute); if (audio_ret == AUDIO_RET_USE_HW_CONTROL) { return audio_ret; } else if (AUDIO_IS_ERROR(audio_ret)) { @@ -2681,8 +2693,8 @@ static audio_return_t policy_set_mute(struct userdata *u, uint32_t stream_idx, u } /* Call HAL function if exists */ - if (u->audio_mgr.intf.set_mute && (stream_idx == PA_INVALID_INDEX)) { - audio_ret = u->audio_mgr.intf.set_mute(u->audio_mgr.data, NULL, volume_type, direction, mute); + if (u->hal_manager.intf.set_mute && (stream_idx == PA_INVALID_INDEX)) { + audio_ret = u->hal_manager.intf.set_mute(u->hal_manager.data, NULL, volume_type, direction, mute); if (audio_ret == AUDIO_RET_USE_HW_CONTROL) { pa_log_info("set_mute(call) returns error:0x%x mute:%d", audio_ret, mute); return audio_ret; @@ -2703,8 +2715,8 @@ static audio_return_t policy_set_mute(struct userdata *u, uint32_t stream_idx, u if ((stream_idx == idx) || ((stream_idx == PA_INVALID_INDEX) && (audio_info.stream.volume_type == volume_type))) { /* Call HAL function if exists */ - if (u->audio_mgr.intf.set_mute) { - audio_ret = u->audio_mgr.intf.set_mute(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, direction, mute); + if (u->hal_manager.intf.set_mute) { + audio_ret = u->hal_manager.intf.set_mute(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, direction, mute); if (AUDIO_IS_ERROR(audio_ret)) { pa_log_error("set_mute for sink-input[%d] returns error:0x%x", idx, audio_ret); return audio_ret; @@ -2758,9 +2770,9 @@ static void policy_get_buffer_attr(struct userdata *u, assert(minreq); assert(fragsize); - pa_log_debug("hal-latency : u->intf.audio_mgr.get_buffer_attr(%p)", u->audio_mgr.intf.get_buffer_attr); - if (u->audio_mgr.intf.get_buffer_attr != NULL) { - audio_return_t ret = u->audio_mgr.intf.get_buffer_attr(u->audio_mgr.data, latency, samplerate, format, channels, maxlength, tlength, prebuf, minreq, fragsize); + pa_log_debug("hal-latency : u->intf.hal_manager.get_buffer_attr(%p)", u->hal_manager.intf.get_buffer_attr); + if (u->hal_manager.intf.get_buffer_attr != NULL) { + audio_return_t ret = u->hal_manager.intf.get_buffer_attr(u->hal_manager.data, latency, samplerate, format, channels, maxlength, tlength, prebuf, minreq, fragsize); if (ret != AUDIO_RET_OK) { pa_log_error("Failed get_buffer_attr() - ret:%d", ret); } @@ -2978,7 +2990,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio u->muteall = enable; /* Use mute instead of volume for muteall */ -#if 1 +#if 1 /* volume feature will be moved to stream-manager */ #ifdef TIZEM_MICRO /* Special case. Set mute for call volume type in B3. */ policy_set_mute(u, (-1), AUDIO_VOLUME_TYPE_CALL, AUDIO_DIRECTION_OUT, u->muteall); @@ -3245,7 +3257,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n pa_log_debug(" New stream lacks property data."); return PA_HOOK_OK; } - +#if 1 /* If no policy exists, skip */ if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) { pa_log_debug("Not setting device for stream [%s], because it lacks policy.", @@ -3292,7 +3304,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n if(new_sink != new_data->sink) pa_sink_input_new_data_set_sink(new_data, new_sink, false); } - +#endif s = pa_strbuf_new(); master_name = pa_proplist_gets(new_data->sink->proplist, PA_PROP_DEVICE_MASTER_DEVICE); if (master_name) @@ -3308,8 +3320,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n // set role type __set_sink_input_role_type(new_data->proplist, audio_info.stream.gain_type); - if (u->audio_mgr.intf.get_volume_level) { - u->audio_mgr.intf.get_volume_level(u->audio_mgr.data, audio_info.stream.volume_type, &volume_level); + if (u->hal_manager.intf.get_volume_level) { + u->hal_manager.intf.get_volume_level(u->hal_manager.data, audio_info.stream.volume_type, &volume_level); } pa_strbuf_printf(s, "[%s] policy[%s] ch[%d] rate[%d] volume&gain[%d,%d] level[%d]", @@ -3324,16 +3336,16 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n pa_strbuf_printf(s, " sink[%s]", (new_data->sink)? new_data->sink->name : "null"); /* Call HAL function if exists */ - if (u->audio_mgr.intf.set_volume_level) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.set_volume_level(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, volume_level)))) { + if (u->hal_manager.intf.set_volume_level) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.set_volume_level(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, volume_level)))) { pa_log_warn("set_volume_level for new sink-input returns error:0x%x", audio_ret); goto exit; } } /* Get volume value by type & level */ - if (u->audio_mgr.intf.get_volume_value && (audio_ret != AUDIO_RET_USE_HW_CONTROL)) { - if (AUDIO_IS_ERROR((audio_ret = u->audio_mgr.intf.get_volume_value(u->audio_mgr.data, &audio_info, audio_info.stream.volume_type, volume_level, &volume_linear)))) { + if (u->hal_manager.intf.get_volume_value && (audio_ret != AUDIO_RET_USE_HW_CONTROL)) { + if (AUDIO_IS_ERROR((audio_ret = u->hal_manager.intf.get_volume_value(u->hal_manager.data, &audio_info, audio_info.stream.volume_type, volume_level, &volume_linear)))) { pa_log_warn("get_volume_value for new sink-input returns error:0x%x", audio_ret); goto exit; } @@ -3350,10 +3362,6 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n __free_audio_info(&audio_info); } - /* get buffer_attr by audio latency */ - pa_log_info("hal-latency - get buffer attr by audio latency"); - __add_hal_buffer_attr_by_latency(u, new_data->proplist, new_data->sample_spec); - exit: if (s) { s_info = pa_strbuf_tostring_free(s); @@ -3393,6 +3401,9 @@ static pa_hook_result_t sink_input_put_callback(pa_core *core, pa_sink_input *i, pa_sink_input_assert_ref(i); pa_assert(u); + /* get buffer_attr by audio latency */ + pa_log_info("hal-latency - get buffer attr by audio latency"); + __add_hal_buffer_attr_by_latency(u, i->proplist, i->sample_spec); #ifdef PRIMARY_VOLUME if ((si_volume_type_str = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_TIZEN_VOLUME_TYPE)) && pa_sink_input_get_state(i) != PA_SINK_INPUT_CORKED /* if sink-input is created by pulsesink, sink-input init state is cork.*/) { @@ -3411,8 +3422,10 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct uint32_t idx; char *args = NULL; - bool is_bt; - bool is_usb_alsa; + pa_bool_t is_bt; + pa_bool_t is_usb_alsa; + pa_bool_t is_need_to_move = true; + uint32_t device_out = AUDIO_DEVICE_OUT_BT_A2DP; pa_assert(c); pa_assert(sink); @@ -3433,10 +3446,23 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct } if (is_bt) { - /* Load mono_bt sink */ - args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_BT, sink->name); - u->module_mono_bt = pa_module_load(u->module->core, "module-remap-sink", args); - pa_xfree(args); +/* + pa_log_info("new bluetooth sink(card) is detected. volume level and route will be changed by sound_server a2dp_on function"); + is_need_to_move = false; + */ + } + + if (is_need_to_move) { + int ret = 0; + uint32_t route_flag = 0; + + /* Set active device out */ + if (u->active_device_out != device_out) { + route_flag = __get_route_flag(u); + if (u->hal_manager.intf.set_route) { + ret = u->hal_manager.intf.set_route(u->hal_manager.data, u->session, u->subsession, u->active_device_in, device_out, route_flag); + } + } /* load combine sink */ args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_COMBINED, sink->name, SINK_ALSA); @@ -4040,6 +4066,65 @@ static pa_hook_result_t source_output_unlink_post_hook_callback(pa_core *c, pa_s return PA_HOOK_OK; } +static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_select *data, struct userdata *u) { + pa_log("select_proper_sink_or_source_hook_cb is called. (%p), stream_type(%d), stream_role(%s), route_type(%d)", data, data->stream_type, data->stream_role, data->route_type); + int i = 0; + for (i = 0; i < data->device_list_len; i++) + pa_log(" - device : type[%s], direction[%d], id[%d]", data->device_list[i].type, data->device_list[i].direction, data->device_list[i].id); + return PA_HOOK_OK; +} + +static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_route *data, struct userdata *u) { + pa_log("route_change_hook_cb is called. (%p), stream_type(%d), stream_role(%s), route_type(%d)", data, data->stream_type, data->stream_role, data->route_type); + int i = 0; + for (i = 0; i < data->device_list_len; i++) + pa_log(" - device : type[%s], direction[%d], id[%d]", data->device_list[i].type, data->device_list[i].direction, data->device_list[i].id); +#if 0 + /* from stream manager */ + typedef struct _device { + int device_type; + int direction; + int id; + } device; + typedef struct _hook_call_data_for_route { + stream_type stream_type; + char *stream_role; + device *device_list; + int device_list_len; + pa_sample_spec sample_spec; + } pa_stream_manager_hook_data_for_route; + typedef struct device_info { + int32_t type; + int32_t direction; + int32_t id; + } device_info_t; + + /* to hal manager */ + typedef struct audio_route_info { + char *role; + device_info_t *device_infos; + int32_t num_of_devices; + } audio_route_info_t; +#endif + audio_route_info_t route_info; + route_info.role = data->stream_role; + if (u->hal_manager.intf.do_route) { + if (u->hal_manager.intf.do_route(&u->hal_manager.data, &route_info) != AUDIO_RET_OK) { + pa_log_error("do_route() failed"); + } + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t route_option_update_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_option *data, struct userdata *u) { + pa_log("route_option_update_hook_cb is called. (%p), stream_role(%d), option_list_len(%d)", data, data->stream_role, data->option_list_len); + int i = 0; + for (i = 0; i < data->option_list_len; i++) + pa_log(" - idx[%d] : option(%s)", data->option_list[i]); + return PA_HOOK_OK; +} + int pa__init(pa_module *m) { pa_modargs *ma = NULL; @@ -4127,44 +4212,46 @@ int pa__init(pa_module *m) #ifdef PRIMARY_VOLUME vconf_set_int (VCONFKEY_SOUND_PRIMARY_VOLUME_TYPE, -1); #endif - /* Get mono key value for init */ - vconf_get_bool(MONO_KEY, &u->is_mono); - /* Load library & init audio mgr */ - u->audio_mgr.dl_handle = dlopen(LIB_TIZEN_AUDIO, RTLD_NOW); - if (u->audio_mgr.dl_handle) { - u->audio_mgr.intf.init = dlsym(u->audio_mgr.dl_handle, "audio_init"); - u->audio_mgr.intf.deinit = dlsym(u->audio_mgr.dl_handle, "audio_deinit"); - u->audio_mgr.intf.reset = dlsym(u->audio_mgr.dl_handle, "audio_reset"); - u->audio_mgr.intf.set_callback = dlsym(u->audio_mgr.dl_handle, "audio_set_callback"); - u->audio_mgr.intf.get_volume_level_max = dlsym(u->audio_mgr.dl_handle, "audio_get_volume_level_max"); - u->audio_mgr.intf.get_volume_level = dlsym(u->audio_mgr.dl_handle, "audio_get_volume_level"); - u->audio_mgr.intf.get_volume_value = dlsym(u->audio_mgr.dl_handle, "audio_get_volume_value"); - u->audio_mgr.intf.set_volume_level = dlsym(u->audio_mgr.dl_handle, "audio_set_volume_level"); - u->audio_mgr.intf.set_volume_value = dlsym(u->audio_mgr.dl_handle, "audio_set_volume_value"); - u->audio_mgr.intf.get_gain_value = dlsym(u->audio_mgr.dl_handle, "audio_get_gain_value"); - u->audio_mgr.intf.get_mute = dlsym(u->audio_mgr.dl_handle, "audio_get_mute"); - u->audio_mgr.intf.set_mute = dlsym(u->audio_mgr.dl_handle, "audio_set_mute"); - u->audio_mgr.intf.alsa_pcm_open = dlsym(u->audio_mgr.dl_handle, "audio_alsa_pcm_open"); - u->audio_mgr.intf.alsa_pcm_close = dlsym(u->audio_mgr.dl_handle, "audio_alsa_pcm_close"); - u->audio_mgr.intf.pcm_open = dlsym(u->audio_mgr.dl_handle, "audio_pcm_open"); - u->audio_mgr.intf.pcm_close = dlsym(u->audio_mgr.dl_handle, "audio_pcm_close"); - u->audio_mgr.intf.pcm_avail = dlsym(u->audio_mgr.dl_handle, "audio_pcm_avail"); - u->audio_mgr.intf.pcm_write = dlsym(u->audio_mgr.dl_handle, "audio_pcm_write"); - u->audio_mgr.intf.set_session = dlsym(u->audio_mgr.dl_handle, "audio_set_session"); - u->audio_mgr.intf.set_route = dlsym(u->audio_mgr.dl_handle, "audio_set_route"); - u->audio_mgr.intf.set_mixer_value_string = dlsym(u->audio_mgr.dl_handle, "audio_set_mixer_value_string"); - - u->audio_mgr.intf.get_buffer_attr = dlsym(u->audio_mgr.dl_handle, "audio_get_buffer_attr"); - if (u->audio_mgr.intf.init) { - if (u->audio_mgr.intf.init(&u->audio_mgr.data, (void *)u) != AUDIO_RET_OK) { - pa_log_error("audio_mgr init failed"); + /* Load library & init HAL manager */ + u->hal_manager.dl_handle = dlopen(LIB_TIZEN_AUDIO, RTLD_NOW); + if (u->hal_manager.dl_handle) { + u->hal_manager.intf.init = dlsym(u->hal_manager.dl_handle, "audio_init"); + u->hal_manager.intf.deinit = dlsym(u->hal_manager.dl_handle, "audio_deinit"); + u->hal_manager.intf.reset = dlsym(u->hal_manager.dl_handle, "audio_reset"); + u->hal_manager.intf.set_callback = dlsym(u->hal_manager.dl_handle, "audio_set_callback"); + u->hal_manager.intf.get_volume_level_max = dlsym(u->hal_manager.dl_handle, "audio_get_volume_level_max"); + u->hal_manager.intf.get_volume_level = dlsym(u->hal_manager.dl_handle, "audio_get_volume_level"); + u->hal_manager.intf.get_volume_value = dlsym(u->hal_manager.dl_handle, "audio_get_volume_value"); + u->hal_manager.intf.set_volume_level = dlsym(u->hal_manager.dl_handle, "audio_set_volume_level"); + u->hal_manager.intf.set_volume_value = dlsym(u->hal_manager.dl_handle, "audio_set_volume_value"); + u->hal_manager.intf.get_gain_value = dlsym(u->hal_manager.dl_handle, "audio_get_gain_value"); + u->hal_manager.intf.get_mute = dlsym(u->hal_manager.dl_handle, "audio_get_mute"); + u->hal_manager.intf.set_mute = dlsym(u->hal_manager.dl_handle, "audio_set_mute"); + u->hal_manager.intf.alsa_pcm_open = dlsym(u->hal_manager.dl_handle, "audio_alsa_pcm_open"); + u->hal_manager.intf.alsa_pcm_close = dlsym(u->hal_manager.dl_handle, "audio_alsa_pcm_close"); + u->hal_manager.intf.pcm_open = dlsym(u->hal_manager.dl_handle, "audio_pcm_open"); + u->hal_manager.intf.pcm_close = dlsym(u->hal_manager.dl_handle, "audio_pcm_close"); + u->hal_manager.intf.pcm_avail = dlsym(u->hal_manager.dl_handle, "audio_pcm_avail"); + u->hal_manager.intf.pcm_write = dlsym(u->hal_manager.dl_handle, "audio_pcm_write"); + u->hal_manager.intf.set_session = dlsym(u->hal_manager.dl_handle, "audio_set_session"); + u->hal_manager.intf.set_route = dlsym(u->hal_manager.dl_handle, "audio_set_route"); + u->hal_manager.intf.set_mixer_value_string = dlsym(u->hal_manager.dl_handle, "audio_set_mixer_value_string"); +#ifdef ENABLE_NEW_ROUTE + u->hal_manager.intf.do_route = dlsym(u->hal_manager.dl_handle, "audio_do_route"); + u->hal_manager.intf.update_route_option = dlsym(u->hal_manager.dl_handle, "audio_update_route_option"); +#endif + + u->hal_manager.intf.get_buffer_attr = dlsym(u->hal_manager.dl_handle, "audio_get_buffer_attr"); + if (u->hal_manager.intf.init) { + if (u->hal_manager.intf.init(&u->hal_manager.data, (void *)u) != AUDIO_RET_OK) { + pa_log_error("hal_manager init failed"); } } - pa_shared_set(u->core, "tizen-audio-data", u->audio_mgr.data); - pa_shared_set(u->core, "tizen-audio-interface", &u->audio_mgr.intf); + pa_shared_set(u->core, "tizen-audio-data", u->hal_manager.data); + pa_shared_set(u->core, "tizen-audio-interface", &u->hal_manager.intf); - if (u->audio_mgr.intf.set_callback) { + if (u->hal_manager.intf.set_callback) { audio_cb_interface_t cb_interface; cb_interface.load_device = __load_device_callback; @@ -4172,12 +4259,27 @@ int pa__init(pa_module *m) cb_interface.close_all_devices = __close_all_devices_callback; cb_interface.close_device = __close_device_callback; cb_interface.unload_device = __unload_device_callback; - u->audio_mgr.intf.set_callback(u->audio_mgr.data, &cb_interface); + u->hal_manager.intf.set_callback(u->hal_manager.data, &cb_interface); } } else { - pa_log_error("open audio_mgr failed :%s", dlerror()); + pa_log_error("open hal_manager failed :%s", dlerror()); + } + + u->communicator.comm = pa_communicator_get(u->core); + if (u->communicator.comm) { + u->communicator.comm_hook_select_proper_sink_or_source_slot = pa_hook_connect(pa_communicator_hook(u->communicator.comm,PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE), + PA_HOOK_EARLY, (pa_hook_cb_t) select_proper_sink_or_source_hook_cb, u); + u->communicator.comm_hook_change_route_slot = pa_hook_connect(pa_communicator_hook(u->communicator.comm,PA_COMMUNICATOR_HOOK_CHANGE_ROUTE), + PA_HOOK_EARLY, (pa_hook_cb_t) route_change_hook_cb, u); + u->communicator.comm_hook_update_route_option_slot = pa_hook_connect(pa_communicator_hook(u->communicator.comm,PA_COMMUNICATOR_HOOK_UPDATE_ROUTE_OPTION), + PA_HOOK_EARLY, (pa_hook_cb_t) route_option_update_hook_cb, u); } + u->stream_manager = pa_stream_manager_init(u->core); +#if 0 + u->device_manager = pa_device_manager_init(u->core); +#endif + __load_dump_config(u); pa_log_info("policy module is loaded\n"); @@ -4227,14 +4329,30 @@ void pa__done(pa_module *m) if (u->source_output_new_hook_slot) pa_hook_slot_free(u->source_output_new_hook_slot); - /* Deinit audio mgr & unload library */ - if (u->audio_mgr.intf.deinit) { - if (u->audio_mgr.intf.deinit(&u->audio_mgr.data) != AUDIO_RET_OK) { - pa_log_error("audio_mgr deinit failed"); + /* Deinit HAL manager & unload library */ + if (u->hal_manager.intf.deinit) { + if (u->hal_manager.intf.deinit(&u->hal_manager.data) != AUDIO_RET_OK) { + pa_log_error("hal_manager deinit failed"); } } - if (u->audio_mgr.dl_handle) { - dlclose(u->audio_mgr.dl_handle); + if (u->hal_manager.dl_handle) { + dlclose(u->hal_manager.dl_handle); + } + + if (u->stream_manager) { + pa_stream_manager_done(u->stream_manager); + } +#if 0 + if (u->device_manager) { + pa_device_manager_done(u->device_manager); + } +#endif + if (u->communicator.comm) { + if (u->communicator.comm_hook_change_route_slot) + pa_hook_slot_free(u->communicator.comm_hook_change_route_slot); + if (u->communicator.comm_hook_change_route_slot) + pa_hook_slot_free(u->communicator.comm_hook_update_route_option_slot); + pa_communicator_unref(u->communicator.comm); } pa_xfree(u); diff --git a/src/modules/stream-manager.c b/src/modules/stream-manager.c new file mode 100644 index 0000000..197747b --- /dev/null +++ b/src/modules/stream-manager.c @@ -0,0 +1,1447 @@ +/*** + This file is part of PulseAudio. + + Copyright 2015 Sangchul Lee + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_DBUS +#include +#include +#include +#endif + +#include +#include "tizen-audio.h" +#include "stream-manager.h" +#include "communicator.h" + +#ifdef HAVE_DBUS +#define ARR_ARG_MAX 32 +#define STREAM_MANAGER_OBJECT_PATH "/org/pulseaudio/Ext/StreamManager" +#define STREAM_MANAGER_INTERFACE "org.pulseaudio.Ext.StreamManager" +#define STREAM_MANAGER_METHOD_NAME_GET_STREAM_INFO "GetStreamInfo" +#define STREAM_MANAGER_METHOD_NAME_GET_STREAM_LIST "GetStreamList" + +static DBusHandlerResult method_handler_for_vt(DBusConnection *c, DBusMessage *m, void *userdata); +static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, void *userdata); +static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_stream_info(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_stream_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +enum method_handler_index { + METHOD_HANDLER_GET_STREAM_INFO, + METHOD_HANDLER_GET_STREAM_LIST, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info sample_method_args[] = { { "uri", "s", "in" }, + { "conf", "i", "in" } }; +static pa_dbus_arg_info get_stream_info_args[] = { { "stream_type", "s", "in" }, + { "priority", "i", "out" }, + { "route_type", "i", "out" }, + { "avail_in_devices", "a(s)", "out" }, + { "avail_out_devices", "a(s)", "out" }, + { "avail_frameworks", "a(s)", "out"} }; +static pa_dbus_arg_info get_stream_list_args[] = { { "stream_type", "a(s)", "out" }, + { "priority", "a(i)", "out" } }; +static char* signature_args_for_in[] = { "s","" }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_GET_STREAM_INFO] = { + .method_name = STREAM_MANAGER_METHOD_NAME_GET_STREAM_INFO, + .arguments = get_stream_info_args, + .n_arguments = sizeof(get_stream_info_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_stream_info }, + [METHOD_HANDLER_GET_STREAM_LIST] = { + .method_name = STREAM_MANAGER_METHOD_NAME_GET_STREAM_LIST, + .arguments = get_stream_list_args, + .n_arguments = sizeof(get_stream_list_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_stream_list } +}; + +#ifdef USE_DBUS_PROTOCOL + +static pa_dbus_interface_info stream_manager_interface_info = { + .name = STREAM_MANAGER_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = , + .n_property_handlers = , + .get_all_properties_cb =, + .signals =, + .n_signals = +}; + +#else + +#define STREAM_MGR_INTROSPECT_XML \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "" \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " "\ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + "" +#endif + +#endif + +#define STREAM_MANAGER_CLIENT_NAME "SOUND_MANAGER_STREAM_INFO" +#define STREAM_PROCESSED_USING_PUT_UNLINK_1 "VIRTUAL_STREAM" +#define STREAM_PROCESSED_USING_PUT_UNLINK_2 "SIMPLE_PLAY" + +typedef enum pa_process_stream_result { + PA_PROCESS_STREAM_OK, + PA_PROCESS_STREAM_STOP, + PA_PROCESS_STREAM_SKIP, +} pa_process_stream_result_t; + +typedef enum _process_command_type { + PROCESS_COMMAND_PREPARE, + PROCESS_COMMAND_START, + PROCESS_COMMAND_END, +} process_command_type; + +typedef enum _notify_command_type { + NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT, + NOTIFY_COMMAND_CHANGE_ROUTE, + NOTIFY_COMMAND_UPDATE_ROUTE_OPTION, +} notify_command_type; + +typedef struct _prior_max_priority_stream { + pa_sink_input *sink_input; + pa_source_output *source_output; +} cur_max_priority_stream; + +struct _stream_manager { + pa_core *core; + pa_hashmap *stream_map; + cur_max_priority_stream cur_highest_priority; + void *new_starting_stream; + pa_hook_slot + *sink_input_new_slot, + *sink_input_put_slot, + *sink_input_unlink_slot, + *sink_input_state_changed_slot, + *source_output_new_slot, + *source_output_put_slot, + *source_output_unlink_slot, + *source_output_state_changed_slot; +#ifdef HAVE_DBUS +#ifdef USE_DBUS_PROTOCOL + pa_dbus_protocol *dbus_protocol; +#else + pa_dbus_connection *dbus_conn; +#endif +#endif + pa_subscription *subscription; + pa_communicator *comm; + pa_idxset *clients; +}; + +#define STREAM_MAP_FILE "/etc/pulse/stream-map.json" +#define STREAM_MAP_STREAMS "streams" +#define STREAM_MAP_STREAM_ROLE "role" +#define STREAM_MAP_STREAM_PRIORITY "priority" +#define STREAM_MAP_STREAM_ROUTE_TYPE "route-type" +#define STREAM_MAP_STREAM_DIRECTIONS "directions" +#define STREAM_MAP_STREAM_VOLUME_TYPES "volume-types" +#define STREAM_MAP_STREAM_VOLUME_TYPE_IN "in" +#define STREAM_MAP_STREAM_VOLUME_TYPE_OUT "out" +#define STREAM_MAP_STREAM_CAPTURE_VOLUME_TYPE "capture-volume-type" +#define STREAM_MAP_STREAM_PLAYBACK_VOLUME_TYPE "playback-volume-type" +#define STREAM_MAP_STREAM_AVAIL_IN_DEVICES "avail-in-devices" +#define STREAM_MAP_STREAM_AVAIL_OUT_DEVICES "avail-out-devices" +#define STREAM_MAP_STREAM_AVAIL_FRAMEWORKS "avail-frameworks" + +enum stream_direction { + STREAM_DIRECTION_IN, + STREAM_DIRECTION_OUT, + STREAM_DIRECTION_MAX, +}; + +typedef struct _stream_info { + char *role; + int32_t priority; + char *volume_types[STREAM_DIRECTION_MAX]; + stream_route_type route_type; + pa_hashmap *avail_in_devices; + pa_hashmap *avail_out_devices; + pa_hashmap *avail_frameworks; +} stream_info; + +typedef struct _stream_parent { + uint32_t idx; + pa_idxset *idx_sink_inputs; + pa_idxset *idx_source_outputs; +} stream_parent; + +#define AVAIL_DEVICES_MAX 16 +#define AVAIL_FRAMEWORKS_MAX 16 +#define AVAIL_STREAMS_MAX 32 +typedef struct _stream_info_per_type { + int32_t priority; + int32_t route_type; + int32_t num_of_in_devices; + int32_t num_of_out_devices; + int32_t num_of_frameworks; + char *avail_in_devices[AVAIL_DEVICES_MAX]; + char *avail_out_devices[AVAIL_DEVICES_MAX]; + char *avail_frameworks[AVAIL_FRAMEWORKS_MAX]; +} stream_info_per_type; +typedef struct _stream_list { + int32_t num_of_streams; + char* types[AVAIL_STREAMS_MAX]; + int32_t priorities[AVAIL_STREAMS_MAX]; +} stream_list; + +static int get_available_streams_from_map(pa_stream_manager *m, stream_list *list) { + void *state; + stream_info *s; + int i = 0; + pa_log_info("get_available_streams_from_map"); + if (m->stream_map) { + PA_HASHMAP_FOREACH(s, m->stream_map, state) { + if (i < AVAIL_STREAMS_MAX) { + list->priorities[i] = s->priority; + list->types[i++] = s->role; + pa_log_debug(" [%d] stream_type[%s], priority[%d]", i-1, s->role, s->priority); + } else { + pa_log_error(" out of range, [%d]", i); + break; + } + } + list->num_of_streams = i; + pa_log_debug(" num_of_streams[%d]",i); + } else { + pa_log_error("stream_map is not initialized.."); + return -1; + } + return 0; +} + +static int get_stream_info_from_map(pa_stream_manager *m, const char *stream_role, stream_info_per_type *info) { + void *state; + void *_state; + char *name; + stream_info *s; + int i = 0; + int j = 0; + int k = 0; + pa_log_info("get_stream_info_from_map : role[%s]", stream_role); + if (m->stream_map) { + PA_HASHMAP_FOREACH(s, m->stream_map, state) { + if (pa_streq(stream_role, s->role)) { + info->priority = s->priority; + info->route_type = s->route_type; + PA_HASHMAP_FOREACH(name, s->avail_in_devices, _state) { + pa_log_debug(" avail-in-device[%d] name : %s", i, name); + if (i < AVAIL_DEVICES_MAX) + info->avail_in_devices[i++] = name; + else + pa_log_error(" avail-in-devices, out of range, [%d]", i); + } + info->num_of_in_devices = i; + PA_HASHMAP_FOREACH(name, s->avail_out_devices, _state) { + pa_log_debug(" avail-out-device[%d] name : %s", j, name); + if (j < AVAIL_DEVICES_MAX) + info->avail_out_devices[j++] = name; + else + pa_log_error(" avail-out-devices, out of range, [%d]", j); + } + info->num_of_out_devices = j; + PA_HASHMAP_FOREACH(name, s->avail_frameworks, _state) { + pa_log_debug(" avail-framework-name[%d] name : %s", k, name); + if (j < AVAIL_FRAMEWORKS_MAX) + info->avail_frameworks[k++] = name; + else + pa_log_error(" avail-frameworks, out of range, [%d]", k); + } + info->num_of_frameworks = k; + break; + } + } + } else { + pa_log_error("stream_map is not initialized.."); + return -1; + } + return 0; +} + +#ifdef HAVE_DBUS +static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, void *userdata) { + const char *xml = STREAM_MGR_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 void handle_get_stream_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + stream_list list; + pa_stream_manager *m = (pa_stream_manager*)userdata; + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_INVALID)); + pa_log_info("handle_get_stream_list() dbus method is called"); + + memset(&list, 0, sizeof(stream_list)); + if(!get_available_streams_from_map(m, &list)) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &list.types, list.num_of_streams); + pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_INT32, &list.priorities, list.num_of_streams); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); + } +} + +static void handle_get_stream_info(DBusConnection *conn, DBusMessage *msg, void *userdata) { + char *type; + stream_info_per_type info; + pa_stream_manager *m = (pa_stream_manager*)userdata; + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &type, + DBUS_TYPE_INVALID)); + pa_log_info("handle_get_stream_info(), type[%s]", type); + memset(&info, 0, sizeof(stream_info_per_type)); + if(!get_stream_info_from_map(m, type, &info)) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_INT32, &info.priority); + pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_INT32, &info.route_type); + pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &info.avail_in_devices, info.num_of_in_devices); + pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &info.avail_out_devices, info.num_of_out_devices); + pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &info.avail_frameworks, info.num_of_frameworks); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); + } +} + +static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata) { + int idx = 0; + pa_stream_manager *m = (pa_stream_manager*)userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + for (idx = 0; idx < METHOD_HANDLER_MAX; idx++) { + if (dbus_message_is_method_call(msg, STREAM_MANAGER_INTERFACE, method_handlers[idx].method_name )) { + if (pa_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) { + pa_stream_manager *u = (pa_stream_manager*)userdata; + const char *path, *interface, *member; + + pa_assert(c); + pa_assert(m); + pa_assert(u); + + 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_streq(path, STREAM_MANAGER_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; +} +#endif + +static int convert_route_type(stream_route_type *route_type, const char *route_type_string) { + int ret = 0; + pa_assert(route_type); + pa_assert(route_type_string); + + if (pa_streq("auto", route_type_string)) + *route_type = STREAM_ROUTE_TYPE_AUTO; + else if (pa_streq("auto-all", route_type_string)) + *route_type = STREAM_ROUTE_TYPE_AUTO_ALL; + else if (pa_streq("manual", route_type_string)) + *route_type = STREAM_ROUTE_TYPE_MANUAL; + else { + ret = -1; + pa_log_error("Not supported route_type(%s)", route_type_string); + } + + return ret; +} +static int init_stream_map (pa_stream_manager *m) { + stream_info *s; + json_object *o; + json_object *stream_array_o; + json_object *role_o; + json_object *priority_o; + json_object *route_type_o; + json_object *volume_types_o; + json_object *avail_in_devices_o; + json_object *avail_out_devices_o; + json_object *avail_frameworks_o; + int num_of_stream_types = 0; + int i = 0; + pa_assert(m); + + o = json_object_from_file(STREAM_MAP_FILE); + if(is_error(o)) { + pa_log_error("Read stream-map file(%s) failed", STREAM_MAP_FILE); + return -1; + } + m->stream_map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + if((stream_array_o = json_object_object_get(o, STREAM_MAP_STREAMS)) && json_object_is_type(stream_array_o, json_type_array)){ + num_of_stream_types = json_object_array_length(stream_array_o); + for (i = 0; i < num_of_stream_types; i++) { + json_object *stream_o; + if((stream_o = json_object_array_get_idx(stream_array_o, i)) && json_object_is_type(stream_o, json_type_object)) { + char *string; + s = pa_xmalloc0(sizeof(stream_info)); + pa_log_debug("stream found [%d]", i); + if((role_o = json_object_object_get(stream_o, STREAM_MAP_STREAM_ROLE)) && json_object_is_type(role_o, json_type_string)) { + s->role = json_object_get_string(role_o); + pa_log_debug(" - role : %s", s->role); + } else { + pa_log_error("Get stream role failed"); + goto failed; + } + if((priority_o = json_object_object_get(stream_o, STREAM_MAP_STREAM_PRIORITY)) && json_object_is_type(priority_o, json_type_int)) { + s->priority = json_object_get_int(priority_o); + pa_log_debug(" - priority : %d", s->priority); + } else { + pa_log_error("Get stream priority failed"); + goto failed; + } + if((route_type_o = json_object_object_get(stream_o, STREAM_MAP_STREAM_ROUTE_TYPE)) && json_object_is_type(route_type_o, json_type_string)) { + if (convert_route_type(&(s->route_type), json_object_get_string(route_type_o))) { + pa_log_error("convert stream route-type failed"); + goto failed; + } + pa_log_debug(" - route-type : %d", s->route_type); + } else { + pa_log_error("Get stream route-type failed"); + goto failed; + } + if((volume_types_o = json_object_object_get(stream_o, STREAM_MAP_STREAM_VOLUME_TYPES)) && json_object_is_type(volume_types_o, json_type_object)) { + json_object *volume_type_in_o; + json_object *volume_type_out_o; + if((volume_type_in_o = json_object_object_get(volume_types_o, STREAM_MAP_STREAM_VOLUME_TYPE_IN)) && json_object_is_type(volume_type_in_o, json_type_string)) { + s->volume_types[STREAM_DIRECTION_IN] = json_object_get_string(volume_type_in_o); + } else { + pa_log_error("Get stream volume-type-in failed"); + goto failed; + } + if((volume_type_out_o = json_object_object_get(volume_types_o, STREAM_MAP_STREAM_VOLUME_TYPE_OUT)) && json_object_is_type(volume_type_out_o, json_type_string)) { + s->volume_types[STREAM_DIRECTION_OUT] = json_object_get_string(volume_type_out_o); + } else { + pa_log_error("Get stream volume-type-out failed"); + goto failed; + } + pa_log_debug(" - volume-types : in[%s], out[%s]", s->volume_types[STREAM_DIRECTION_IN], s->volume_types[STREAM_DIRECTION_OUT]); + } else { + pa_log_error("Get stream volume-types failed"); + goto failed; + } + if((avail_in_devices_o = json_object_object_get(stream_o, STREAM_MAP_STREAM_AVAIL_IN_DEVICES)) && json_object_is_type(avail_in_devices_o, json_type_array)) { + int j = 0; + json_object *in_device_o; + s->avail_in_devices = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + int num_of_avail_in_devices = json_object_array_length(avail_in_devices_o); + pa_log_debug(" - avail-in-devices"); + for (j = 0; j < num_of_avail_in_devices; j++) { + if((in_device_o = json_object_array_get_idx(avail_in_devices_o, j)) && json_object_is_type(in_device_o, json_type_string)) { + pa_hashmap_put(s->avail_in_devices, PA_INT_TO_PTR(j), json_object_get_string(in_device_o)); + pa_log_debug(" device[%d] : %s", j, json_object_get_string(in_device_o)); + } + } + } else { + pa_log_error("Get stream avail-in-devices failed"); + goto failed; + } + if((avail_out_devices_o = json_object_object_get(stream_o, STREAM_MAP_STREAM_AVAIL_OUT_DEVICES)) && json_object_is_type(avail_out_devices_o, json_type_array)) { + int j = 0; + json_object *out_device_o; + s->avail_out_devices = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + int num_of_avail_out_devices = json_object_array_length(avail_out_devices_o); + pa_log_debug(" - avail-out-devices"); + for (j = 0; j < num_of_avail_out_devices; j++) { + if((out_device_o = json_object_array_get_idx(avail_out_devices_o, j)) && json_object_is_type(out_device_o, json_type_string)) { + pa_hashmap_put(s->avail_out_devices, PA_INT_TO_PTR(j), json_object_get_string(out_device_o)); + pa_log_debug(" device[%d] : %s", j, json_object_get_string(out_device_o)); + } + } + } else { + pa_log_error("Get stream avail-out-devices failed"); + goto failed; + } + if((avail_frameworks_o = json_object_object_get(stream_o, STREAM_MAP_STREAM_AVAIL_FRAMEWORKS)) && json_object_is_type(avail_frameworks_o, json_type_array)) { + int j = 0; + json_object *framework_o; + s->avail_frameworks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + int num_of_avail_frameworks = json_object_array_length(avail_frameworks_o); + pa_log_debug(" - avail-frameworks"); + for (j = 0; j < num_of_avail_frameworks; j++) { + if((framework_o = json_object_array_get_idx(avail_frameworks_o, j)) && json_object_is_type(framework_o, json_type_string)) { + pa_hashmap_put(s->avail_frameworks, PA_INT_TO_PTR(j), json_object_get_string(framework_o)); + pa_log_debug(" framework[%d] : %s", j, json_object_get_string(framework_o)); + } + } + } else { + pa_log_error("Get stream avail-frameworks failed"); + goto failed; + } + pa_hashmap_put(m->stream_map, s->role, s); + } + } + } else { + pa_log_error("Get streams object failed"); + goto failed; + } + return 0; +failed: + if (m->stream_map) { + if (s->avail_in_devices) + pa_hashmap_free(s->avail_in_devices); + if (s->avail_out_devices) + pa_hashmap_free(s->avail_out_devices); + if (s->avail_frameworks) + pa_hashmap_free(s->avail_frameworks); + pa_hashmap_free(m->stream_map); + } + return -1; +} + +static void deinit_stream_map (pa_stream_manager *m) { + pa_assert(m); + if (m->stream_map) { + stream_info *s; + void *state; + PA_HASHMAP_FOREACH(s, m->stream_map, state) { + if (s->avail_in_devices) + pa_hashmap_free(s->avail_in_devices); + if (s->avail_out_devices) + pa_hashmap_free(s->avail_out_devices); + if (s->avail_frameworks) + pa_hashmap_free(s->avail_frameworks); + pa_xfree(s); + } + pa_hashmap_free(m->stream_map); + } + return; +} + +static void dump_stream_map (pa_stream_manager *m) { + pa_assert(m); + pa_log_debug("==========[START stream-map dump]=========="); + if (m->stream_map) { + stream_info *s; + char *name; + void *state; + void *_state; + PA_HASHMAP_FOREACH(s, m->stream_map, state) { + pa_log_debug("[role : %s]", s->role); + pa_log_debug(" - prirority : %d", s->priority); + pa_log_debug(" - route-type : %d (0:auto,1:auto-all,2:manual,3:manual-all)", s->route_type); + pa_log_debug(" - volume-types : in[%s], out[%s]", s->volume_types[STREAM_DIRECTION_IN], s->volume_types[STREAM_DIRECTION_OUT]); + pa_log_debug(" - avail-in-devices"); + PA_HASHMAP_FOREACH(name, s->avail_in_devices, _state) { + pa_log_debug(" name : %s", name); + } + pa_log_debug(" - avail-out-devices"); + PA_HASHMAP_FOREACH(name, s->avail_out_devices, _state) { + pa_log_debug(" name : %s", name); + } + pa_log_debug(" - avail-frameworks"); + PA_HASHMAP_FOREACH(name, s->avail_frameworks, _state) { + pa_log_debug(" name : %s", name); + } + } + } + pa_log_debug("===========[END stream-map dump]==========="); + return; +} + +static pa_bool_t check_role_to_skip(const char *role, pa_stream_manager *m) { + pa_bool_t ret = TRUE; + pa_assert(role); + pa_assert(m); + + if (m->stream_map) { + void *state; + stream_info *s; + PA_HASHMAP_FOREACH(s, m->stream_map, state) { + if (pa_streq(role, s->role)) { + ret = FALSE; + break; + } + } + } + + pa_log_info("role is %s, skip(%d)", role, ret); + + return ret; +} + +static pa_bool_t update_priority_of_stream(stream_type type, void *stream, const char *role, pa_stream_manager *m, int32_t *priority) { + pa_assert(role); + pa_assert(m); + + if (m->stream_map) { + stream_info *s; + s = pa_hashmap_get(m->stream_map, role); + *priority = s->priority; + } + + pa_proplist_setf(type==STREAM_SINK_INPUT?((pa_sink_input*)stream)->proplist:((pa_source_output*)stream)->proplist, PA_PROP_MEDIA_ROLE_PRIORITY, "%d", *priority); + + return TRUE; +} + +static pa_bool_t update_routing_type_of_stream(stream_type type, void *stream, const char *role, pa_stream_manager *m) { + stream_route_type route_type = STREAM_ROUTE_TYPE_AUTO; + pa_assert(role); + pa_assert(m); + + if (m->stream_map) { + stream_info *s = pa_hashmap_get(m->stream_map, role); + route_type = s->route_type; + } + + pa_proplist_setf(type==STREAM_SINK_INPUT?((pa_sink_input*)stream)->proplist:((pa_source_output*)stream)->proplist, PA_PROP_MEDIA_ROLE_ROUTE_TYPE, "%d", route_type); + + return TRUE; +} + +static pa_bool_t update_stream_parent_info(process_command_type command, stream_type type, void *stream, pa_stream_manager *m) { + char *p_idx; + uint32_t idx; + + pa_assert(stream); + pa_assert(m); + + p_idx = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)stream)->proplist:((pa_source_output*)stream)->proplist, PA_PROP_MEDIA_PARENT_ID); + if (p_idx && !pa_atou(p_idx, &idx)) { + pa_log_debug("***p_idx(%s), idx(%u)", p_idx, idx); + stream_parent *sp = NULL; + void *state = NULL; + while ((sp = pa_idxset_iterate(m->clients, &state, NULL))) { + if (sp->idx == idx) { + uint32_t *idx_addr = (type==STREAM_SINK_INPUT)?&((pa_sink_input*)stream)->index:&((pa_source_output*)stream)->index; + if (command == PROCESS_COMMAND_START) { + /* append this stream to the parent stream info. */ + pa_log_debug(" - append this idx_addr(%p),idx(%u) to the list, sp(%p), stream_type(%d)", idx_addr, *idx_addr, sp, type); + pa_idxset_put(type==STREAM_SINK_INPUT?(sp->idx_sink_inputs):(sp->idx_source_outputs), idx_addr, NULL); + return TRUE; + } else if (command == PROCESS_COMMAND_END) { + /* remove this stream from the parent stream info. */ + pa_log_debug(" - remove this idx_addr(%p),idx(%u) from the list, sp(%p), stream_type(%d)", idx_addr, *idx_addr, sp, type); + pa_idxset_remove_by_data(type==STREAM_SINK_INPUT?(sp->idx_sink_inputs):(sp->idx_source_outputs), idx_addr, NULL); + return TRUE; + } else { + pa_log_error("invalid command(%d)", command); + return FALSE; + } + } + } + if (sp == NULL) { + pa_log_error("could not find matching client for this parent_id(%u)", idx); + return FALSE; + } + } else { + pa_log_error("p_idx(%s) or idx(%u) is not valid", p_idx, idx); + return FALSE; + } + return TRUE; +} + +static pa_bool_t update_the_highest_priority_stream(stream_type type, void *mine, const char *role, int32_t priority, pa_stream_manager *m, pa_bool_t *need_to_update) { + uint32_t idx = 0; + int32_t p_max; + void *cur_max_stream = NULL; + char *cur_max_priority = NULL; + char *cur_max_role = NULL; + *need_to_update = FALSE; + + pa_assert(mine); + pa_assert(m); + if (!role) { + pa_log_error("invalid input, role(%s)", role); + return FALSE; + } + + if (type == STREAM_SINK_INPUT) { + cur_max_stream = m->cur_highest_priority.sink_input; + } else if (type == STREAM_SOURCE_OUTPUT) { + cur_max_stream = m->cur_highest_priority.source_output; + } + + pa_log_error("stream : type(%d), role(%s), priority(%d) ", type, role, priority); + if (priority != -1) { + if (cur_max_stream == NULL) { + *need_to_update = TRUE; + pa_log_debug("set cur_highest to mine"); + if (type == STREAM_SINK_INPUT) { + m->cur_highest_priority.sink_input = mine; + } else if (type == STREAM_SOURCE_OUTPUT) { + m->cur_highest_priority.source_output = mine; + } + } else { + /* TODO : need to check if this stream should be played to external devices */ + cur_max_priority = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)cur_max_stream)->proplist:((pa_source_output*)cur_max_stream)->proplist, PA_PROP_MEDIA_ROLE_PRIORITY); + cur_max_role = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)cur_max_stream)->proplist:((pa_source_output*)cur_max_stream)->proplist, PA_PROP_MEDIA_ROLE); + if (!cur_max_priority || !cur_max_role) { + pa_log_error("Failed to pa_proplist_gets() for getting current max priority(%s) and it's role(%s)", cur_max_priority, cur_max_role); + return FALSE; + } else { + if (pa_atoi(cur_max_priority, &p_max)) { + pa_log_error("Failed to pa_atoi(), cur_max_priority(%s)", cur_max_priority); + return FALSE; + } + if (priority < p_max) { + /* no need to trigger */ + return TRUE; + } else { + *need_to_update = TRUE; + pa_log_debug("update cur_highest to mine(%s)", role); + if (type == STREAM_SINK_INPUT) { + m->cur_highest_priority.sink_input = mine; + } else if (type == STREAM_SOURCE_OUTPUT) { + m->cur_highest_priority.source_output = mine; + } + } + } + } + } else { + void *cur_max_stream_tmp = NULL; + void *i = NULL; + char *role = NULL; + char *priority = NULL; + int32_t p; + pa_idxset *streams = NULL; + if (cur_max_stream == mine) { + if (type == STREAM_SINK_INPUT) { + streams = ((pa_sink_input*)mine)->sink->inputs; + } else if (type == STREAM_SOURCE_OUTPUT) { + streams = ((pa_source_output*)mine)->source->outputs; + } + /* find the next highest priority input */ + //PA_IDXSET_FOREACH(i, m->core->sinks, idx) { /* need to check a sink which this stream belongs to */ + PA_IDXSET_FOREACH(i, streams, idx) { + if (!(role = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)i)->proplist:((pa_source_output*)i)->proplist, PA_PROP_MEDIA_ROLE))){ + pa_log_error("Failed to pa_proplist_gets() for role"); + continue; + } + if (!(priority = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)i)->proplist:((pa_source_output*)i)->proplist, PA_PROP_MEDIA_ROLE_PRIORITY))){ + pa_log_error("Failed to pa_proplist_gets() for priority"); + continue; + } + pa_log_debug("role(%s)/priority(%s)/stream(%p)", role, priority, i); + if (cur_max_priority == NULL) { + cur_max_priority = priority; + cur_max_stream_tmp = i; + } + if (pa_atoi(cur_max_priority, &p_max)) { + pa_log_error("Failed to pa_atoi(), cur_max_priority(%s)", cur_max_priority); + continue; + } + if (pa_atoi(priority, &p)) { + pa_log_error("Failed to pa_atoi(), priority(%s)", priority); + continue; + } + if (p_max <= p) { + cur_max_priority = priority; + cur_max_stream_tmp = i; + p_max = p; + } + } + pa_log_debug("updated max priority(%s)/stream(%p)", cur_max_priority, cur_max_stream_tmp); + if ((p_max > -1) && cur_max_stream_tmp) { + if (type == STREAM_SINK_INPUT) { + m->cur_highest_priority.sink_input = cur_max_stream_tmp; + } else if (type == STREAM_SOURCE_OUTPUT) { + m->cur_highest_priority.source_output = cur_max_stream_tmp; + } + } else { + if (type == STREAM_SINK_INPUT) { + m->cur_highest_priority.sink_input = NULL; + } else if (type == STREAM_SOURCE_OUTPUT) { + m->cur_highest_priority.source_output = NULL; + } + } + *need_to_update = TRUE; + pa_log_info("need to update: type(%d), cur_highest_priority(sink_input=%p/source_output=%p)", + type, (void*)m->cur_highest_priority.sink_input, (void*)m->cur_highest_priority.sink_input); + } else { + /* no need to trigger */ + return TRUE; + } + } + return TRUE; +} + +static void fill_device_info_to_hook_data(void *hook_data, notify_command_type command, stream_type type, pa_stream_manager *m) { + int i = 0; + pa_assert(hook_data); + pa_assert(m); + switch (command) { + case NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT: { + pa_stream_manager_hook_data_for_select *data = (pa_stream_manager_hook_data_for_select*)hook_data; + stream_info *si = pa_hashmap_get(m->stream_map, data->stream_role); + data->route_type = si->route_type; + if (si->route_type == STREAM_ROUTE_TYPE_AUTO || si->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) { + pa_hashmap *avail_devices = (type==STREAM_SINK_INPUT)?si->avail_out_devices:si->avail_in_devices; + int list_len = pa_hashmap_size(avail_devices); + char *device_name = pa_hashmap_get(avail_devices, 0); + if (list_len == 1 && pa_streq(device_name, "none")) { + /* no available devices for this role */ + } else { + data->device_list = (device*)pa_xmalloc0(sizeof(device)*list_len); + for (i = 0; i < list_len; i++) { + data->device_list[i].type = pa_hashmap_get(avail_devices, i); + data->device_list[i].direction = !(int)type; + } + data->device_list_len = list_len; + } + } else if (si->route_type == STREAM_ROUTE_TYPE_MANUAL) { + /* TODO : need to check device for explicit routing */ + /* find parent idx and it's device info. */ + } + break; + } + case NOTIFY_COMMAND_CHANGE_ROUTE: { + pa_stream_manager_hook_data_for_route *data = (pa_stream_manager_hook_data_for_route*)hook_data; + stream_info *si = pa_hashmap_get(m->stream_map, data->stream_role); + data->route_type = si->route_type; + if (si->route_type == STREAM_ROUTE_TYPE_AUTO || si->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) { + pa_hashmap *avail_devices = (type==STREAM_SINK_INPUT)?si->avail_out_devices:si->avail_in_devices; + int list_len = pa_hashmap_size(avail_devices); + char *device_name = pa_hashmap_get(avail_devices, 0); + if (list_len == 1 && pa_streq(device_name, "none")) { + /* no available devices for this role */ + } else { + data->device_list = (device*)pa_xmalloc0(sizeof(device)*list_len); + for (i = 0; i < list_len; i++) { + data->device_list[i].type = pa_hashmap_get(avail_devices, i); + data->device_list[i].direction = !(int)type; + } + data->device_list_len = list_len; + } + } else if (si->route_type == STREAM_ROUTE_TYPE_MANUAL) { + /* TODO : need to check device for explicit routing */ +#if 0 + char *p_idx; + uint32_t idx; + p_idx = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)stream)->proplist:((pa_source_output*)stream)->proplist, PA_PROP_MEDIA_PARENT_ID); + if (p_idx && !pa_atou(p_idx, &idx)) { + /* find parent idx, it's device info. and it's children idxs */ + } +#endif + } + break; + } + default: + break; + } + return; +} + +static void do_notify(notify_command_type command, stream_type type, pa_stream_manager *m) { + char *priority = NULL; + char *role = NULL; + + pa_assert(m); + pa_log_debug("do_notify() : command(%d), type(%d)", command, type); + switch (command) { + case NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT: { + pa_stream_manager_hook_data_for_select hook_call_data; + memset(&hook_call_data, 0, sizeof(pa_stream_manager_hook_data_for_select)); + device* device_list = NULL; + void *s = m->new_starting_stream; + hook_call_data.stream_type = type; + hook_call_data.stream_role = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input_new_data*)s)->proplist:((pa_source_output_new_data*)s)->proplist, PA_PROP_MEDIA_ROLE); + fill_device_info_to_hook_data(&hook_call_data, command, type, m); + hook_call_data.sample_spec.format = (type==STREAM_SINK_INPUT?((pa_sink_input_new_data*)s)->sample_spec.format:((pa_source_output_new_data*)s)->sample_spec.format); + hook_call_data.sample_spec.rate = (type==STREAM_SINK_INPUT?((pa_sink_input_new_data*)s)->sample_spec.rate:((pa_source_output_new_data*)s)->sample_spec.rate); + if (type == STREAM_SINK_INPUT) + hook_call_data.proper_sink = &(((pa_sink_input_new_data*)s)->sink); + else if (type == STREAM_SOURCE_OUTPUT) + hook_call_data.proper_source = &(((pa_source_output_new_data*)s)->source); + + pa_hook_fire(pa_communicator_hook(m->comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE), &hook_call_data); + if (hook_call_data.device_list) + pa_xfree(hook_call_data.device_list); +#if 0 + { + /* TODO : need to notify to change route if needed */ + /* check if 1. this new role is needed to change route before opening the device */ + /* 2. if yes, do others exist? if no, do below */ + pa_stream_manager_hook_data_for_route hook_call_data; + pa_hook_fire(pa_communicator_hook(m->comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE), &hook_call_data); + } +#endif + break; + } + case NOTIFY_COMMAND_CHANGE_ROUTE: { + pa_stream_manager_hook_data_for_route hook_call_data; + memset(&hook_call_data, 0, sizeof(pa_stream_manager_hook_data_for_route)); + device* device_list = NULL; + void *s = (type==STREAM_SINK_INPUT)?m->cur_highest_priority.sink_input:m->cur_highest_priority.source_output; + if (s) { + priority = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)s)->proplist:((pa_source_output*)s)->proplist, PA_PROP_MEDIA_ROLE_PRIORITY); + role = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)s)->proplist:((pa_source_output*)s)->proplist, PA_PROP_MEDIA_ROLE); + hook_call_data.stream_type = type; + hook_call_data.stream_role = role; + hook_call_data.sample_spec.format = (type==STREAM_SINK_INPUT?((pa_sink_input*)s)->sample_spec.format:((pa_source_output*)s)->sample_spec.format); + hook_call_data.sample_spec.rate = (type==STREAM_SINK_INPUT?((pa_sink_input*)s)->sample_spec.rate:((pa_source_output*)s)->sample_spec.rate); + fill_device_info_to_hook_data(&hook_call_data, command, type, m); + } else { + pa_log_info("no stream for this type(%d), need to unset route", type); + hook_call_data.stream_type = type; + hook_call_data.stream_role = "reset"; + } + pa_hook_fire(pa_communicator_hook(m->comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE), &hook_call_data); + if (hook_call_data.device_list) + pa_xfree(hook_call_data.device_list); + break; + } + case NOTIFY_COMMAND_UPDATE_ROUTE_OPTION: { + pa_stream_manager_hook_data_for_option hook_call_data; + memset(&hook_call_data, 0, sizeof(pa_stream_manager_hook_data_for_option)); + int test_option_list_len = 2; + char* test_option_list[2] = {"test_option1","test_option2"}; + pa_hook_fire(pa_communicator_hook(m->comm, PA_COMMUNICATOR_HOOK_UPDATE_ROUTE_OPTION), &hook_call_data); + break; + } + } + + return; +} + +static pa_process_stream_result_t process_stream(stream_type type, void *stream, process_command_type command, pa_stream_manager *m) { + const char *role; + pa_bool_t ret = TRUE; + pa_bool_t need_update = FALSE; + + pa_log_info("START process_stream(): stream_type(%d), stream(%p), m(%p), command(%d)", type, stream, m, command); + pa_assert(stream); + pa_assert(m); + + if (command == PROCESS_COMMAND_PREPARE) { + role = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input_new_data*)stream)->proplist:((pa_source_output_new_data*)stream)->proplist, PA_PROP_MEDIA_ROLE); + if (!role) { + /* set default value for role and priority */ + #define DEFAULT_ROLE "media" + pa_proplist_sets(type==STREAM_SINK_INPUT?((pa_sink_input_new_data *)stream)->proplist:((pa_source_output_new_data *)stream)->proplist, PA_PROP_MEDIA_ROLE, DEFAULT_ROLE); + pa_log_error("role is null, set default to (%s)", DEFAULT_ROLE); + } else { + /* skip roles */ + if (check_role_to_skip(role, m)) + return PA_PROCESS_STREAM_SKIP; + } + m->new_starting_stream = stream; + + /* notify to update */ + do_notify(NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT, type, m); + + } else { + role = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)stream)->proplist:((pa_source_output*)stream)->proplist, PA_PROP_MEDIA_ROLE); + if (command == PROCESS_COMMAND_START) { + pa_log_debug("stream(%s) is about to be started", role); + int32_t priority = 0; + pa_log_error("role is (%s)", role); + + /* skip roles */ + if (check_role_to_skip(role, m)) + return PA_PROCESS_STREAM_SKIP; + + /* update the priority of this stream */ + ret = update_priority_of_stream(type, stream, role, m, &priority); + if (ret == FALSE) { + pa_log_error("could not update the priority of '%s' role.", role); + return PA_PROCESS_STREAM_STOP; + } + /* update the routing type of this stream */ + ret = update_routing_type_of_stream(type, stream, role, m); + if (ret == FALSE) { + pa_log_error("could not update the route type of '%s' role.", role); + return PA_PROCESS_STREAM_STOP; + } + /* update the highest priority */ + ret = update_the_highest_priority_stream(type, stream, role, priority, m, &need_update); + if (ret == FALSE) { + pa_log_error("could not update the highest priority stream"); + return PA_PROCESS_STREAM_STOP; + } + /* update parent stream info. */ + ret = update_stream_parent_info(command, type, stream, m); + if (ret == FALSE) { + pa_log_error("could not update the parent information of this stream"); + return PA_PROCESS_STREAM_STOP; + } + /* need to skip if this stream does not belong to internal device */ + /* if needed, notify to update */ + if (need_update) + do_notify(NOTIFY_COMMAND_CHANGE_ROUTE, type, m); + + } else if (command == PROCESS_COMMAND_END) { + pa_log_debug("stream(%s) is about to be ended", role); + if (role) { + /* skip roles */ + if (check_role_to_skip(role, m)) + return PA_PROCESS_STREAM_SKIP; + /* mark the priority of this stream to -1 */ + pa_proplist_setf(type==STREAM_SINK_INPUT?((pa_sink_input*)stream)->proplist:((pa_source_output*)stream)->proplist, PA_PROP_MEDIA_ROLE_PRIORITY, "%d", -1); + ret = update_the_highest_priority_stream(type, stream, role, -1, m, &need_update); + if (ret == FALSE) { + pa_log_error("could not update the highest priority stream"); + return PA_PROCESS_STREAM_STOP; + } + /* update parent stream info. */ + ret = update_stream_parent_info(command, type, stream, m); + if (ret == FALSE) { + pa_log_error("could not update the parent information of this stream"); + return PA_PROCESS_STREAM_STOP; + } + /* need to skip if this stream does not belong to internal device */ + /* if needed, notify to update */ + if (need_update) + do_notify(NOTIFY_COMMAND_CHANGE_ROUTE, type, m); + } else { + pa_log_error("role is null, skip it"); + } + } + } + pa_log_info("END process_stream()"); + return PA_PROCESS_STREAM_OK; +} + +static pa_bool_t is_good_to_process(stream_type type, void *stream) { + /* Normally, routing process is on input/output state changed cb. */ + /* but if a stream named as below, routing process is on put/unlink cb.*/ + /* Later on it could be changed if it is possible to get notified via */ + /* input/output state change cb. */ + const char *name = pa_proplist_gets(type==STREAM_SINK_INPUT?((pa_sink_input*)stream)->proplist:((pa_source_output*)stream)->proplist, PA_PROP_MEDIA_NAME); + if (strncmp (name, STREAM_PROCESSED_USING_PUT_UNLINK_1, strlen(STREAM_PROCESSED_USING_PUT_UNLINK_1)) || + strncmp (name, STREAM_PROCESSED_USING_PUT_UNLINK_2, strlen(STREAM_PROCESSED_USING_PUT_UNLINK_2)) ) { + return TRUE; + } + return FALSE; +} + +static pa_hook_result_t sink_input_new_cb(pa_core *core, pa_sink_input_new_data *new_data, pa_stream_manager *m) { + pa_log_info("start sink_input_new_cb"); + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + pa_core_assert_ref(core); + + process_result = process_stream(STREAM_SINK_INPUT, new_data, PROCESS_COMMAND_PREPARE, m); + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) { + pa_log_info("start sink_input_put_cb, i(%p, index:%u)", i, i->index); + pa_bool_t ret = FALSE; + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + + ret = is_good_to_process(STREAM_SINK_INPUT, i); + if (ret) { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SINK_INPUT, i, PROCESS_COMMAND_START, m); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) { + pa_log_info("start sink_input_unlink_cb, i(%p, index:%u)", i, i->index); + pa_bool_t ret = FALSE; + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + + ret = is_good_to_process(STREAM_SINK_INPUT, i); + if (ret) { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SINK_INPUT, i, PROCESS_COMMAND_END, m); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) { + pa_sink_input_state_t state; + + pa_assert(i); + pa_assert(m); + + state = pa_sink_input_get_state(i); + pa_log_debug("start sink_input_state_changed_hook_cb(), sink-input(%p), state(%d)", i, state); + + switch(state) { + case PA_SINK_INPUT_CORKED: { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SINK_INPUT, i, PROCESS_COMMAND_END, m); + break; + } + case PA_SINK_INPUT_DRAINED: + case PA_SINK_INPUT_RUNNING: { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SINK_INPUT, i, PROCESS_COMMAND_START, m); + break; + } + default: + break; + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_new_cb(pa_core *core, pa_source_output_new_data *new_data, pa_stream_manager *m) { + pa_log_info("start source_output_new_new_cb"); + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + pa_core_assert_ref(core); + + process_result = process_stream(STREAM_SOURCE_OUTPUT, new_data, PROCESS_COMMAND_PREPARE, m); + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) { + pa_log_info("start source_output_put_cb, o(%p, index:%u)", o, o->index); + pa_bool_t ret = FALSE; + pa_core_assert_ref(core); + pa_source_output_assert_ref(o); + + ret = is_good_to_process(STREAM_SOURCE_OUTPUT, o); + if (ret) { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SOURCE_OUTPUT, o, PROCESS_COMMAND_START, m); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) { + pa_log_info("start source_output_unlink_cb, o(%p, index:%u)", o, o->index); + pa_bool_t ret = FALSE; + pa_core_assert_ref(core); + pa_source_output_assert_ref(o); + + ret = is_good_to_process(STREAM_SOURCE_OUTPUT, o); + if (ret) { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SOURCE_OUTPUT, o, PROCESS_COMMAND_END, m); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) { + pa_source_output_state_t state; + + pa_assert(o); + pa_assert(m); + + state = pa_source_output_get_state(o); + pa_log_debug("start source_output_state_changed_hook_cb(), source-output(%p), state(%d)", o, state); + + switch(state) { + case PA_SINK_INPUT_CORKED: { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SOURCE_OUTPUT, o, PROCESS_COMMAND_END, m); + break; + } + case PA_SINK_INPUT_DRAINED: + case PA_SINK_INPUT_RUNNING: { + pa_process_stream_result_t process_result = PA_PROCESS_STREAM_OK; + process_result = process_stream(STREAM_SOURCE_OUTPUT, o, PROCESS_COMMAND_START, m); + break; + } + default: + break; + } + + return PA_HOOK_OK; +} + +static void subscribe_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, pa_stream_manager *m) { + pa_core_assert_ref(core); + pa_assert(m); + pa_client *client = NULL; + const char *name = NULL; + pa_log_info("subscribe_cb() is called, t(%x), idx(%u)", t, idx); + + if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE)) { + client = pa_idxset_get_by_index(core->clients, idx); + if (client == NULL) { + pa_log_error(" - could not find any client that has idx(%u)", idx); + return; + } + name = pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME); + if (strncmp (name, STREAM_MANAGER_CLIENT_NAME, strlen(STREAM_MANAGER_CLIENT_NAME))) { + pa_log_warn(" - this is not a client(%s) that we should take care of, skip it", name); + return; + } + /* add a stream parent */ + uint32_t p_idx = 0; + stream_parent *sp = pa_xmalloc0(sizeof(stream_parent)); + sp->idx = idx; + sp->idx_sink_inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + sp->idx_source_outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + pa_idxset_put(m->clients, sp, NULL); + pa_log_debug(" - add sp(%p), idx(%u)", sp, idx); + } else if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE)) { + /* remove the stream parent */ + stream_parent *sp = NULL; + void *state = NULL; + while ((sp = pa_idxset_iterate(m->clients, &state, NULL))) { + if (sp->idx == idx) { + pa_log_debug(" - remove sp(%p), idx(%u)", sp, idx); + pa_idxset_remove_by_data(m->clients, sp, NULL); + pa_idxset_free(sp->idx_sink_inputs, NULL); + pa_idxset_free(sp->idx_source_outputs, NULL); + pa_xfree(sp); + break; + } + } + } +} + +static int init_ipc (pa_stream_manager *m) { + + pa_assert(m); + + pa_log_info("Initialization for IPC"); + +#ifdef HAVE_DBUS +#ifdef USE_DBUS_PROTOCOL + m->dbus_protocol = pa_dbus_protocol_get(m->core); + pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, STREAM_MANAGER_OBJECT_PATH, &stream_manager_interface_info, m) >= 0); + pa_assert_se(pa_dbus_protocol_register_extension(m->dbus_protocol, STREAM_MANAGER_INTERFACE) >= 0); +#else + DBusError err; + pa_dbus_connection *conn = NULL; + static const DBusObjectPathVTable vtable = { + .message_function = method_handler_for_vt, + }; + dbus_error_init(&err); + + if (!(conn = pa_dbus_bus_get(m->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); + goto fail; + } else { + pa_log_notice("Got dbus connection"); + } + m->dbus_conn = conn; + pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(conn), STREAM_MANAGER_OBJECT_PATH, &vtable, m)); +#endif +#else + pa_log_error("DBUS is not supported\n"); + goto fail; +#endif + + return 0; +fail: + return -1; +} + +static void deinit_ipc (pa_stream_manager *m) { + + pa_assert(m); + +#ifdef HAVE_DBUS +#ifdef USE_DBUS_PROTOCOL + if (m->dbus_protocol) { + pa_assert_se(pa_dbus_protocol_unregister_extension(m->dbus_protocol, STREAM_MANAGER_INTERFACE) >= 0); + pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, STREAM_MANAGER_OBJECT_PATH, stream_manager_interface_info.name) >= 0); + pa_dbus_protocol_unref(m->dbus_protocol); + m->dbus_protocol = NULL; + } +#else + 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"); + m->dbus_conn = NULL; + } +#endif +#endif + return; +} + +pa_stream_manager* pa_stream_manager_init(pa_core *c) { + pa_stream_manager *m; + const char *ipc_type = NULL; + + pa_assert(c); + + m = pa_xnew0(pa_stream_manager, 1); + m->core = c; + +#ifdef HAVE_DBUS +#ifdef USE_DBUS_PROTOCOL + m->dbus_protocol = NULL; +#else + m->dbus_conn = NULL; +#endif +#endif + if (init_ipc(m)) + goto fail; +#if 1 + if (init_stream_map(m)) + goto fail; +#endif + + dump_stream_map(m); + + m->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_cb, m); + m->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_put_cb, m); + m->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_unlink_cb, m); + m->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_state_changed_hook_cb, m); + m->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_cb, m); + m->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_put_cb, m); + m->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_unlink_cb, m); + m->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_state_changed_hook_cb, m); + + m->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CLIENT | PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, subscribe_cb, m); + + m->comm = pa_communicator_get(c); + + m->clients = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + return m; + +fail: + deinit_stream_map(m); + deinit_ipc(m); + pa_xfree(m); + return 0; +} + +void pa_stream_manager_done(pa_stream_manager *m) { + pa_assert(m); + + if (m->clients) + pa_idxset_free(m->clients, NULL); + + if (m->comm) + pa_communicator_unref(m->comm); + + if (m->subscription) + pa_subscription_free(m->subscription); + + if (m->sink_input_new_slot) + pa_hook_slot_free(m->sink_input_new_slot); + if (m->sink_input_put_slot) + pa_hook_slot_free(m->sink_input_put_slot); + if (m->sink_input_unlink_slot) + pa_hook_slot_free(m->sink_input_unlink_slot); + if (m->source_output_new_slot) + pa_hook_slot_free(m->source_output_new_slot); + if (m->source_output_put_slot) + pa_hook_slot_free(m->source_output_put_slot); + if (m->source_output_unlink_slot) + pa_hook_slot_free(m->source_output_unlink_slot); + + deinit_stream_map(m); + + deinit_ipc(m); + + pa_xfree(m); +} diff --git a/src/modules/stream-manager.h b/src/modules/stream-manager.h new file mode 100644 index 0000000..affb439 --- /dev/null +++ b/src/modules/stream-manager.h @@ -0,0 +1,53 @@ +#ifndef foostreammanagerfoo +#define foostreammanagerfoo +#include + +typedef struct _stream_manager pa_stream_manager; + +typedef enum _stream_type { + STREAM_SINK_INPUT, + STREAM_SOURCE_OUTPUT, +} stream_type; + +typedef enum stream_route_type { + STREAM_ROUTE_TYPE_AUTO, /* the policy of decision device(s) is automatic and it's routing path is particular to one device */ + STREAM_ROUTE_TYPE_AUTO_ALL, /* the policy of decision device(s) is automatic and it's routing path can be several devices */ + STREAM_ROUTE_TYPE_MANUAL, /* the policy of decision device(s) is manual */ +} stream_route_type; + +typedef struct _device { + char *type; + int direction; + int id; +} device; + +typedef struct _hook_call_data_for_select { + stream_type stream_type; + char *stream_role; + stream_route_type route_type; + device *device_list; + int device_list_len; + pa_sink **proper_sink; + pa_source **proper_source; + pa_sample_spec sample_spec; +} pa_stream_manager_hook_data_for_select; + +typedef struct _hook_call_data_for_route { + stream_type stream_type; + char *stream_role; + stream_route_type route_type; + device *device_list; + int device_list_len; + pa_sample_spec sample_spec; +} pa_stream_manager_hook_data_for_route; + +typedef struct _hook_call_data_for_option { + char *stream_role; + char **option_list; + int option_list_len; +} pa_stream_manager_hook_data_for_option; + +pa_stream_manager* pa_stream_manager_init(pa_core *c); +void pa_stream_manager_done(pa_stream_manager* m); + +#endif diff --git a/src/modules/tizen-audio.h b/src/modules/tizen-audio.h index d045cad..d52ef17 100755 --- a/src/modules/tizen-audio.h +++ b/src/modules/tizen-audio.h @@ -148,6 +148,23 @@ typedef enum audio_device_param { AUDIO_DEVICE_PARAM_MAX, } audio_device_param_t; +enum audio_device_type { + AUDIO_DEVICE_NONE, + AUDIO_DEVICE_BUILTIN_SPEAKER, + AUDIO_DEVICE_BUILTIN_RECEIVER, + AUDIO_DEVICE_BUILTIN_MIC, + AUDIO_DEVICE_AUDIO_JACK, + AUDIO_DEVICE_BT, + AUDIO_DEVICE_HDMI, + AUDIO_DEVICE_AUX, + AUDIO_DEVICE_MAX +}; + +enum audio_device_direction_type{ + AUDIO_DEVICE_DIRECTION_IN, + AUDIO_DEVICE_DIRECTION_OUT +}; + typedef struct audio_device_param_info { audio_device_param_t param; union { @@ -180,6 +197,23 @@ typedef struct audio_device_info { }; } audio_device_info_t; +typedef struct device_info { + int32_t type; + int32_t direction; + int32_t id; +} device_info_t; + +typedef struct audio_route_info { + char *role; + device_info_t *device_infos; + int32_t num_of_devices; +} audio_route_info_t; + +typedef struct audio_route_option { + char *role; + char **options; + int32_t num_of_options; +} audio_route_option_t; /* Stream */ @@ -297,6 +331,8 @@ typedef struct audio_interface { audio_return_t (*set_mute)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute); audio_return_t (*set_session)(void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd); audio_return_t (*set_route)(void *userdata, uint32_t session, uint32_t subsession, uint32_t device_in, uint32_t device_out, uint32_t route_flag); + audio_return_t (*do_route)(void *userdata, audio_route_info_t *info); + audio_return_t (*update_route_option)(void *userdata, audio_route_option_t *option); audio_return_t (*alsa_pcm_open)(void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode); audio_return_t (*alsa_pcm_close)(void *userdata, void *pcm_handle); audio_return_t (*pcm_open)(void *userdata, void **pcm_handle, void *sample_spec, uint32_t direction); @@ -321,6 +357,8 @@ audio_return_t audio_get_gain_value (void *userdata, audio_info_t *info, uint32_ audio_return_t audio_get_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t *mute); audio_return_t audio_set_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute); audio_return_t audio_set_session (void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd); +audio_return_t audio_do_route (void *userdata, audio_route_info_t *info); +audio_return_t audio_update_route_option (void *userdata, audio_route_option_t *option); audio_return_t audio_alsa_pcm_open (void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode); audio_return_t audio_alsa_pcm_close (void *userdata, void *pcm_handle); audio_return_t audio_pcm_open(void *userdata, void **pcm_handle, void *sample_spec, uint32_t direction); diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 44e03f6..0425f3c 100755 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -33,18 +33,6 @@ PA_C_DECL_BEGIN -#ifdef __TIZEN__ -#define PA_PROP_MEDIA_TIZEN_VOLUME_TYPE "media.tizen_volume_type" -#define PA_PROP_MEDIA_TIZEN_GAIN_TYPE "media.tizen_gain_type" -#define PA_PROP_MEDIA_TIZEN_FADE_STATUS "media.tizen_fade_status" -#define PA_PROP_MEDIA_TIZEN_FADE_RETURN_VOLUME "media.tizen_fade_return_volume" -/** For streams: the policy to ignore the preset sink rather use a sink picked by module-policy. One of the strings "yes", "no" */ -#define PA_PROP_MEDIA_POLICY_IGNORE_PRESET_SINK "media.policy.ignore_preset_sink" -#define PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY "media.tizen_audio_latency" -/** For streams: logic role of this media. One of the strings "auto", "phone" */ -#define PA_PROP_MEDIA_POLICY "media.policy" -#endif - /** For streams: localized media name, formatted as UTF-8. E.g. "Guns'N'Roses: Civil War".*/ #define PA_PROP_MEDIA_NAME "media.name" @@ -86,6 +74,23 @@ PA_C_DECL_BEGIN /** For streams: the name of a filter that should specifically suppressed (i.e.\ overrides PA_PROP_FILTER_WANT). Useful for the times that PA_PROP_FILTER_WANT is automatically added (e.g. echo-cancellation for phone streams when $VOIP_APP does it's own, internal AEC) \since 1.0 */ #define PA_PROP_FILTER_SUPPRESS "filter.suppress" +#ifdef __TIZEN__ +/** For streams: logic role of this media. One of the strings "auto", "phone" */ +#define PA_PROP_MEDIA_POLICY "media.policy" + +/** For streams: the policy to ignore the preset sink rather use a sink picked by module-policy. One of the strings "yes", "no" */ +#define PA_PROP_MEDIA_POLICY_IGNORE_PRESET_SINK "media.policy.ignore_preset_sink" + +#define PA_PROP_MEDIA_ROLE_PRIORITY "media.role.priority" +#define PA_PROP_MEDIA_ROLE_ROUTE_TYPE "media.role.route_type" +#define PA_PROP_MEDIA_PARENT_ID "media.parent_id" +#define PA_PROP_MEDIA_TIZEN_VOLUME_TYPE "media.tizen_volume_type" +#define PA_PROP_MEDIA_TIZEN_GAIN_TYPE "media.tizen_gain_type" +#define PA_PROP_MEDIA_TIZEN_FADE_STATUS "media.tizen_fade_status" +#define PA_PROP_MEDIA_TIZEN_FADE_RETURN_VOLUME "media.tizen_fade_return_volume" +#define PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY "media.tizen_audio_latency" +#endif + /** For event sound streams: XDG event sound name. e.g.\ "message-new-email" (Event sound streams are those with media.role set to "event") */ #define PA_PROP_EVENT_ID "event.id" -- 2.7.4