return 0;
}
+static pa_sink* get_master_sink_of_filter(pa_stream_manager *m, const char *filter_name, const char *filter_group) {
+ pa_sink_input *i;
+ pa_sink *master_sink;
+ uint32_t idx;
+ const char *role = NULL;
+ const char *filter_apply = NULL;
+
+ pa_assert(m);
+ pa_assert(filter_name);
+ pa_assert(filter_group);
+
+ /* We can not find sink_master directly only with module_name and stream_role,
+ * therefore, we use sink-input information instead of it */
+ PA_IDXSET_FOREACH(i, m->core->sink_inputs, idx) {
+ role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE);
+ filter_apply = pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY);
+ if (pa_safe_streq(filter_name, filter_apply) && pa_safe_streq(role, filter_group)) {
+ if ((master_sink = pa_sink_get_master(i->sink))) {
+ pa_log_info("Found master sink name[%s] of [module-%s, filter_group:%s]",
+ master_sink->name, filter_name, filter_group);
+ return master_sink;
+ } else
+ continue;
+ }
+ }
+ pa_log_error("Failed to get master sink of [module-%s, filter_group:%s]", filter_name, filter_group);
+
+ return NULL;
+}
+
+int32_t reload_filter(pa_stream_manager *m, const char *stream_role, pa_sink *new_master_sink) {
+ filter_info *f = NULL;
+ const char *filter_name = NULL;
+ const char *filter_params = NULL;
+ const char *filter_group = NULL;
+ const char *role = NULL;
+ pa_sink *prev_master_sink = NULL;
+ pa_sink_input *i;
+ uint32_t idx;
+ int ret = 0;
+
+ pa_assert(m);
+ pa_assert(stream_role);
+ pa_assert(new_master_sink);
+
+ if (!(f = pa_hashmap_get(m->filter_infos, stream_role))) {
+ pa_log_error("could not find any filter info for [%s]", stream_role);
+ return -1;
+ }
+
+ filter_name = pa_xstrdup(f->filter_apply);
+ filter_params = pa_xstrdup(f->parameters);
+ filter_group = pa_xstrdup(f->group);
+ pa_log_debug("found filter: name[%s], params[%s], group[%s] for [%s]", filter_name, filter_params, filter_group, stream_role);
+
+ if (!(prev_master_sink = get_master_sink_of_filter(m, filter_name, filter_group))) {
+ ret = -1;
+ goto leave;
+ }
+
+ if (prev_master_sink == new_master_sink) {
+ pa_log_info("master sink is same as before, [%s], skip it", new_master_sink->name);
+ ret = 0;
+ goto leave;
+ }
+
+ if (update_filter(m, NULL, NULL, NULL, stream_role)) {
+ pa_log_error("failed to update filter to unload,");
+ ret = -1;
+ goto leave;
+ }
+
+ /* move all streams belongs to stream_role */
+ PA_IDXSET_FOREACH(i, prev_master_sink->inputs, idx) {
+ role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE);
+ if (pa_safe_streq(role, stream_role))
+ pa_sink_input_move_to(i, new_master_sink, false);
+ }
+
+ pa_log_info("name:%s, params:%s, group:%s", filter_name, filter_params, filter_group);
+ if (update_filter(m, filter_name, filter_params, filter_group, stream_role)) {
+ pa_log_error("failed to update filter to load");
+ ret = -1;
+ goto leave;
+ }
+ pa_log_info("reload filter successfully for [stream_role:%s, sink:%s]", stream_role, new_master_sink->name);
+
+leave:
+ pa_xfree((void*)filter_name);
+ pa_xfree((void*)filter_params);
+ pa_xfree((void*)filter_group);
+
+ return ret;
+}
+
static void filter_info_key_free_cb(char *key) {
pa_xfree(key);
}
}
}
if (!module) {
- pa_module_load(m->core, MODULE_FILTER_APPLY, NULL);
+ char *args = pa_sprintf_malloc("autoclean_interval=%d", 0);
+ pa_module_load(m->core, MODULE_FILTER_APPLY, args);
pa_log("module-filter-apply loaded");
+ pa_xfree(args);
}
}
}
}
-static void update_sink_or_source_as_device_change(stream_route_type_t stream_route_type, pa_idxset *streams,
- stream_type_t stream_type, pa_tz_device *device, bool is_connected, pa_stream_manager *m) {
+static bool is_filter_apply_stream(void *s, stream_type_t stream_type) {
+ const char *filter_apply = NULL;
+
+ pa_assert(s);
+
+ if ((filter_apply = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_FILTER_APPLY))) {
+ pa_log_info("stream index(%u) is for filter.apply(%s)", GET_STREAM_INDEX(s, stream_type), filter_apply);
+ return true;
+ }
+ return false;
+}
+
+static bool manage_filter_apply_stream(pa_stream_manager *m, pa_sink_input *si, pa_tz_device *device, bool is_connected) {
+ stream_route_type_t route_type;
+
+ pa_assert(m);
+ pa_assert(si);
+ pa_assert(device);
+
+ if (get_route_type(si, STREAM_SINK_INPUT, false, &route_type))
+ return false;
+ if (route_type != STREAM_ROUTE_TYPE_AUTO && route_type != STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED)
+ return false;
+
+ if (is_filter_apply_stream(si, STREAM_SINK_INPUT)) {
+ const char *stream_role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
+ const char *cur_device_type = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
+ char *device_type = pa_tz_device_get_type(device);
+ pa_tz_device *cur_device = pa_device_manager_get_device(m->dm, cur_device_type);
+ pa_sink *sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
+
+ if (is_connected) {
+ bool available = false;
+ pa_sink *cur_sink = pa_tz_device_get_sink(cur_device, DEVICE_ROLE_NORMAL);
+ if (cur_sink == sink)
+ return true;
+
+ is_available_device_for_auto_route(m, route_type, cur_device_type, device_type,
+ stream_role, STREAM_SINK_INPUT, &available);
+ if (available) {
+ if (!reload_filter(m, stream_role, sink)) {
+ pa_proplist_sets(si->proplist, PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
+ pa_log_info("[%s] is CONNECTED and will be set for the next device", device_type);
+ process_stream_as_device_change_for_auto_route(m, si, STREAM_SINK_INPUT, is_connected, device);
+ } else
+ pa_log_error("failed to reload filter");
+ }
+ } else {
+ pa_tz_device *next_device = NULL;
+ char *new_device_type = NULL;
+ pa_sink *cur_sink = NULL;
+
+ if (cur_device)
+ cur_sink = pa_tz_device_get_sink(cur_device, DEVICE_ROLE_NORMAL);
+ if (cur_sink && (cur_sink != sink))
+ return true;
+
+ if (is_active_device_of_stream(si, STREAM_SINK_INPUT, device_type)) {
+ pa_sink *next_sink = NULL;
+ find_next_device_for_auto_route(m, route_type, stream_role, STREAM_SINK_INPUT, cur_device_type, &next_device);
+ if (!next_device) {
+ pa_log_error("failed to get next_device");
+ return true;
+ }
+ next_sink = pa_tz_device_get_sink(next_device, DEVICE_ROLE_NORMAL);
+ if (!reload_filter(m, stream_role, next_sink)) {
+ new_device_type = pa_tz_device_get_type(next_device);
+ pa_proplist_sets(si->proplist, PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, new_device_type);
+ pa_log_info("[%s] is DISCONNECTED and [%s] will be set for the next device", device_type, new_device_type);
+ process_stream_as_device_change_for_auto_route(m, si, STREAM_SINK_INPUT, is_connected, next_device);
+ } else
+ pa_log_error("failed to reload filter");
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static void update_sink_or_source_as_device_change(pa_stream_manager *m, stream_route_type_t stream_route_type, pa_idxset *streams,
+ stream_type_t stream_type, pa_tz_device *device, bool is_connected) {
#define MAX_CACHED_LEN 128
typedef struct _cached_device_list {
const char *device_type;
uint32_t cnt = 0;
pa_sink *combine_sink = NULL;
+ pa_assert(m);
pa_assert(streams);
pa_assert(device);
- pa_assert(m);
null_sink = (pa_sink*)pa_namereg_get(m->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
null_source = (pa_source*)pa_namereg_get(m->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE);
continue;
if (route_type != stream_route_type)
continue;
+ if (stream_type == STREAM_SINK_INPUT && manage_filter_apply_stream(m, s, device, is_connected))
+ continue;
+
role = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROLE);
pa_log_debug(" -- idx(%u), route_type(%d), role(%s)", s_idx, route_type, role);
if (is_connected) {
uint32_t device_id = 0;
pa_sink *sink = NULL;
pa_source *source = NULL;
-
pa_sink_input *si = NULL;
- uint32_t si_idx = 0;
- const char *media_role = NULL;
- hal_route_option route_option;
pa_assert(c);
pa_assert(data);
/* mute all the streams belong to this device, those will be un-muted in event_fully_handled_hook_cb */
if (!data->is_connected && (device_direction & DM_DEVICE_DIRECTION_OUT)) {
+ const char *media_role = NULL;
+ hal_route_option route_option;
+
if ((sink = pa_tz_device_get_sink(data->device, DEVICE_ROLE_NORMAL))) {
pa_idxset *filtered_streams = get_streams_for_matching_active_device(sink->inputs, device_type);
mute_sink_inputs_as_device_disconnection(m, data->event_id, true, filtered_streams);
/* If Earjack is disconnected, search for sink-input which has radio role,
if found, let radio mute to avoid intermediate noise */
if (pa_safe_streq(device_type, DEVICE_TYPE_AUDIO_JACK)) {
- PA_IDXSET_FOREACH(si, c->sink_inputs, si_idx) {
+ PA_IDXSET_FOREACH(si, c->sink_inputs, s_idx) {
media_role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
if (pa_safe_streq(media_role, STREAM_ROLE_RADIO)) {
pa_log_info(" sink-input(%d) is radio, let HAL mute for disconnect of earjack", si->index);
}
/* Update streams belong to this external device that have MAUAL_EXT route type */
- if (!use_internal_codec && (device_direction & DM_DEVICE_DIRECTION_IN)) {
- if ((source = pa_tz_device_get_source(data->device, DEVICE_ROLE_NORMAL)))
- update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_MANUAL_EXT, source->outputs,
- STREAM_SOURCE_OUTPUT, data->device, data->is_connected, m);
- else
- pa_log_error("[SM][CONN] could not get source");
- }
- if (!use_internal_codec && (device_direction & DM_DEVICE_DIRECTION_OUT)) {
- if ((sink = pa_tz_device_get_sink(data->device, DEVICE_ROLE_NORMAL)))
- update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_MANUAL_EXT, sink->inputs,
- STREAM_SINK_INPUT, data->device, data->is_connected, m);
- else
- pa_log_error("[SM][CONN] could not get sink");
+ if (!use_internal_codec) {
+ if (device_direction & DM_DEVICE_DIRECTION_IN) {
+ if ((source = pa_tz_device_get_source(data->device, DEVICE_ROLE_NORMAL)))
+ update_sink_or_source_as_device_change(m, STREAM_ROUTE_TYPE_MANUAL_EXT, source->outputs,
+ STREAM_SOURCE_OUTPUT, data->device, data->is_connected);
+ }
+ if (device_direction & DM_DEVICE_DIRECTION_OUT) {
+ if ((sink = pa_tz_device_get_sink(data->device, DEVICE_ROLE_NORMAL)))
+ update_sink_or_source_as_device_change(m, STREAM_ROUTE_TYPE_MANUAL_EXT, sink->inputs,
+ STREAM_SINK_INPUT, data->device, data->is_connected);
+ }
}
/* Update all the streams that have AUTO route type */
if (device_direction & DM_DEVICE_DIRECTION_IN) {
- update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO, m->core->source_outputs,
- STREAM_SOURCE_OUTPUT, data->device, data->is_connected, m);
- update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->source_outputs,
- STREAM_SOURCE_OUTPUT, data->device, data->is_connected, m);
+ update_sink_or_source_as_device_change(m, STREAM_ROUTE_TYPE_AUTO, m->core->source_outputs,
+ STREAM_SOURCE_OUTPUT, data->device, data->is_connected);
+ update_sink_or_source_as_device_change(m, STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->source_outputs,
+ STREAM_SOURCE_OUTPUT, data->device, data->is_connected);
}
if (device_direction & DM_DEVICE_DIRECTION_OUT) {
- update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO, m->core->sink_inputs,
- STREAM_SINK_INPUT, data->device, data->is_connected, m);
- update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->sink_inputs,
- STREAM_SINK_INPUT, data->device, data->is_connected, m);
- }
-
-
- /* If the route type of the stream is not manual, notify again */
- if (m->cur_highest_priority.source_output && (device_direction & DM_DEVICE_DIRECTION_IN)) {
- if (!get_route_type(m->cur_highest_priority.source_output, STREAM_SOURCE_OUTPUT, false, &route_type)) {
- if (route_type != STREAM_ROUTE_TYPE_MANUAL && route_type != STREAM_ROUTE_TYPE_MANUAL_EXT) {
- if (use_internal_codec) {
- PA_IDXSET_FOREACH(s, m->cur_highest_priority.source_output->source->outputs, s_idx) {
- if (!data->is_connected && !get_route_type(s, STREAM_SOURCE_OUTPUT, false, &route_type) &&
- ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
- /* remove activated device info. if it has the AUTO route type */
- active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
- if (pa_safe_streq(active_dev, device_type))
- pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
- }
- }
- do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SOURCE_OUTPUT, false, m->cur_highest_priority.source_output);
- if (!((pa_source_output*)(m->cur_highest_priority.source_output))->source->use_internal_codec) {
- /* If the source of the cur_highest_priority stream uses external codec, it should be updated.
- * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
- * can update the cur_highest_priority, call it here */
- process_stream(m, m->cur_highest_priority.source_output, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
- }
+ update_sink_or_source_as_device_change(m, STREAM_ROUTE_TYPE_AUTO, m->core->sink_inputs,
+ STREAM_SINK_INPUT, data->device, data->is_connected);
+ update_sink_or_source_as_device_change(m, STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->sink_inputs,
+ STREAM_SINK_INPUT, data->device, data->is_connected);
+ }
+
+ /* If the route type is AUTO SERIES, notify again */
+ if ((device_direction & DM_DEVICE_DIRECTION_IN) && m->cur_highest_priority.source_output &&
+ !get_route_type(m->cur_highest_priority.source_output, STREAM_SOURCE_OUTPUT, false, &route_type)) {
+ if (IS_AUTO_ROUTE_TYPE_SERIES(route_type) && use_internal_codec) {
+ PA_IDXSET_FOREACH(s, m->cur_highest_priority.source_output->source->outputs, s_idx) {
+ if (!data->is_connected && !get_route_type(s, STREAM_SOURCE_OUTPUT, false, &route_type) &&
+ ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
+ /* remove activated device info. if it has the AUTO route type */
+ active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
+ if (pa_safe_streq(active_dev, device_type))
+ pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
}
}
+ do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SOURCE_OUTPUT, false, m->cur_highest_priority.source_output);
+ if (!((pa_source_output*)(m->cur_highest_priority.source_output))->source->use_internal_codec) {
+ /* If the source of the cur_highest_priority stream uses external codec, it should be updated.
+ * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
+ * can update the cur_highest_priority, call it here */
+ process_stream(m, m->cur_highest_priority.source_output, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
+ }
}
}
- if (m->cur_highest_priority.sink_input && (device_direction & DM_DEVICE_DIRECTION_OUT)) {
- if (!get_route_type(m->cur_highest_priority.sink_input, STREAM_SINK_INPUT, false, &route_type)) {
- if (route_type != STREAM_ROUTE_TYPE_MANUAL && route_type != STREAM_ROUTE_TYPE_MANUAL_EXT) {
- if (use_internal_codec) {
- PA_IDXSET_FOREACH(s, m->cur_highest_priority.sink_input->sink->inputs, s_idx) {
- if (!data->is_connected && !get_route_type(s, STREAM_SINK_INPUT, false, &route_type) &&
- ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
- /* remove activated device info. if it has the AUTO route type */
- active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
- if (pa_safe_streq(active_dev, device_type))
- pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
- }
- }
- do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, m->cur_highest_priority.sink_input);
- if (((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED)) &&
- !((pa_sink_input*)(m->cur_highest_priority.sink_input))->sink->use_internal_codec) {
- /* If the sink of the cur_highest_priority stream uses external codec, it should be updated.
- * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
- * can update the cur_highest_priority, call it here */
- process_stream(m, m->cur_highest_priority.sink_input, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
- }
- } else if (route_type == STREAM_ROUTE_TYPE_AUTO_ALL)
- do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, m->cur_highest_priority.sink_input);
+
+ if ((device_direction & DM_DEVICE_DIRECTION_OUT) && m->cur_highest_priority.sink_input &&
+ !get_route_type(m->cur_highest_priority.sink_input, STREAM_SINK_INPUT, false, &route_type)) {
+ if (IS_AUTO_ROUTE_TYPE_SERIES(route_type) && use_internal_codec) {
+ PA_IDXSET_FOREACH(s, m->cur_highest_priority.sink_input->sink->inputs, s_idx) {
+ if (!data->is_connected && !get_route_type(s, STREAM_SINK_INPUT, false, &route_type) &&
+ ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
+ /* remove activated device info. if it has the AUTO route type */
+ active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
+ if (pa_safe_streq(active_dev, device_type))
+ pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
+ }
}
- }
+ do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, m->cur_highest_priority.sink_input);
+ if (((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED)) &&
+ (!((pa_sink_input*)(m->cur_highest_priority.sink_input))->sink->use_internal_codec) && !is_filter_apply_stream(m->cur_highest_priority.sink_input, STREAM_SINK_INPUT)) {
+ /* If the sink of the cur_highest_priority stream uses external codec, it should be updated.
+ * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
+ * can update the cur_highest_priority, call it here */
+ process_stream(m, m->cur_highest_priority.sink_input, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
+ }
+ } else if (route_type == STREAM_ROUTE_TYPE_AUTO_ALL)
+ do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, m->cur_highest_priority.sink_input);
}
if (m->on_call) {
return get_route_type(stream, stream_type, is_new_data, stream_route_type);
}
+bool pa_stream_manager_check_filter_apply_stream(void *stream, stream_type_t stream_type) {
+ return is_filter_apply_stream(stream, stream_type);
+}
+
pa_stream_manager* pa_stream_manager_init(pa_core *c) {
pa_stream_manager *m;