#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
void *dl_handle;
void *data;
audio_interface_t intf;
- } audio_mgr;
+ } hal_manager;
struct { // for burst-shot
pa_bool_t is_running;
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 {
/* 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;
}
/* 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;
}
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;
}
}
} 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;
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 {
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) {
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 {
}
#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
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;
}
#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"))) {
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;
}
}
/* 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;
}
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;
}
/* 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;
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;
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;
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);
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;
}
/* 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);
}
}
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
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);
}
}
}
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 {
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)) {
}
/* 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;
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;
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);
}
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);
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.",
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)
// 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]",
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;
}
__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);
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.*/) {
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);
}
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);
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;
#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;
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");
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);
--- /dev/null
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2015 Sangchul Lee <sc11.lee@samsung.com>
+
+ 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/dbus-util.h>
+#endif
+
+#include <json.h>
+#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 \
+ "<node>" \
+ " <interface name=\"STREAM_MANAGER_INTERFACE\">" \
+ " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_STREAM_INFO\">" \
+ " <arg name=\"stream_type\" direction=\"in\" type=\"s\"/>" \
+ " <arg name=\"priority\" direction=\"out\" type=\"i\"/>" \
+ " <arg name=\"route_type\" direction=\"out\" type=\"i\"/>" \
+ " <arg name=\"avail_in_devices\" direction=\"out\" type=\"a(s)\"/>" \
+ " <arg name=\"avail_out_devices\" direction=\"out\" type=\"a(s)\"/>"\
+ " <arg name=\"avail_frameworks\" direction=\"out\" type=\"a(s)\"/>" \
+ " </method>" \
+ " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_STREAM_LIST\">" \
+ " <arg name=\"stream_type\" direction=\"in\" type=\"a(s)\"/>" \
+ " <arg name=\"priority\" direction=\"in\" type=\"a(i)\"/>" \
+ " </method>" \
+ " </interface>" \
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">" \
+ " <method name=\"Introspect\">" \
+ " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
+ " </method>" \
+ " </interface>" \
+ "</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);
+}