This new feature is only able to be applid to two routing types below.
- STREAM_ROUTE_TYPE_AUTO, STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED
If user sets the preemptive device to a specific stream role, this device
has priority over all other devices that can be selected by the policy of
the stream role. Once it is set to a particular stream role, audio routing
of all other streams with the same stream role will be affected.
server : org.pulseaudio.Server
object path : /org/pulseaudio/StreamManager
interface : org.pulseaudio.StreamManager
method name : SetStreamPreemptiveDevice
method argument : [in] string for stream type (e.g. 'media')
[in] string for io direction ('in' or 'out')
[in] unsigned int for device id
return value : string for return message
- success : STREAM_MANAGER_RETURN_OK
- invalid argument error : STREAM_MANAGER_RETURN_ERROR_INVALID_ARGUMENT
- policy error : STREAM_MANAGER_RETURN_ERROR_POLICY
- internal error : STREAM_MANAGER_RETURN_ERROR_INTERNAL
method name : GetStreamPreemptiveDevice
method argument : [in] string for stream type (e.g. 'media')
[out] unsigned int for input device id
[out] unsigned int for output device id
return value : string for return message
- success : STREAM_MANAGER_RETURN_OK
- internal error : STREAM_MANAGER_RETURN_ERROR_INTERNAL
[Version] 11.1.92
[Issue Type] New feature
Change-Id: I12dd1d8d5463426331955a4f7a3799b551685a4d
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
Name: pulseaudio-modules-tizen
Summary: Pulseaudio modules for Tizen
-Version: 11.1.91
+Version: 11.1.92
Release: 0
Group: Multimedia/Audio
License: LGPL-2.1+
return false;
}
+static int32_t select_device_by_preemptive_device(struct userdata *u, pa_stream_manager_hook_data_for_select *data) {
+ uint32_t dm_device_id = 0;
+ pa_tz_device *device = NULL;
+ const char *device_type;
+
+ pa_assert(u);
+ pa_assert(data);
+
+ if (pa_stream_manager_get_preemptive_device_id(u->stream_manager, data->stream_type, data->stream_role, &dm_device_id))
+ return -1;
+
+ device = pa_device_manager_get_device_by_id(u->device_manager, dm_device_id);
+ device_type = pa_tz_device_get_type(device);
+ pa_log_debug("preemptive device is type:[%s] id:[%u]", device_type, dm_device_id);
+ if (data->stream_type == STREAM_SINK_INPUT)
+ *(data->proper_sink) = pa_tz_device_get_sink(device, data->device_role);
+ else
+ *(data->proper_source) = pa_tz_device_get_source(device, data->device_role);
+
+ if (data->origins_from_new_data)
+ pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type),
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
+ else
+ pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type),
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
+
+ return 0;
+}
+
static void select_device_by_auto_or_auto_all_routing(struct userdata *u, pa_stream_manager_hook_data_for_select *data, pa_idxset *conn_devices) {
const char *device_type = NULL;
uint32_t idx = 0;
pa_assert(data);
pa_assert(conn_devices);
+ if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
+ if (!select_device_by_preemptive_device(u, data))
+ return;
+ }
+
PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
pa_log_debug("[SELECT][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
if (!is_cached_device_connected(device_type, data->stream_type))
continue;
if (data->stream_type == STREAM_SINK_INPUT) {
- if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && u->module_combine_sink) {
- *(data->proper_sink) = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
- pa_log_info(" -- found the combine-sink, set it to the sink");
+ if (data->origins_from_new_data) {
+ if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && u->module_combine_sink) {
+ *(data->proper_sink) = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
+ pa_log_info(" -- found the combine-sink, set it to the sink");
+ } else {
+ *(data->proper_sink) = pa_tz_device_get_sink(device, data->device_role);
+ }
} else {
- *(data->proper_sink) = pa_tz_device_get_sink(device, data->device_role);
+ if (data->route_type == STREAM_ROUTE_TYPE_AUTO)
+ data->new_sink = pa_tz_device_get_sink(device, data->device_role);
}
- if (*(data->proper_sink) == NULL)
- continue;
} else {
- *(data->proper_source) = pa_tz_device_get_source(device, data->device_role);
- if (*(data->proper_source) == NULL)
- continue;
+ if (data->origins_from_new_data) {
+ *(data->proper_source) = pa_tz_device_get_source(device, data->device_role);
+ } else {
+ if (data->route_type == STREAM_ROUTE_TYPE_AUTO)
+ data->new_source = pa_tz_device_get_source(device, data->device_role);
+ }
}
-
if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
if (data->origins_from_new_data)
pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type),
- PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
else
pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type),
- PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
return;
}
}
pa_assert(data);
pa_assert(conn_devices);
+ if (!select_device_by_preemptive_device(u, data))
+ return;
+
PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
pa_log_debug("[SELECT][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
if (!is_cached_device_connected(device_type, data->stream_type))
}
/* update active device info. */
if (latest_device) {
- if (data->stream_type == STREAM_SINK_INPUT)
- *(data->proper_sink) = pa_tz_device_get_sink(latest_device, data->device_role);
- else
- *(data->proper_source) = pa_tz_device_get_source(latest_device, data->device_role);
-
+ dm_device_type = pa_tz_device_get_type(latest_device);
+ if (data->origins_from_new_data) {
+ if (data->stream_type == STREAM_SINK_INPUT)
+ *(data->proper_sink) = pa_tz_device_get_sink(latest_device, data->device_role);
+ else
+ *(data->proper_source) = pa_tz_device_get_source(latest_device, data->device_role);
+ } else {
+ if (data->stream_type == STREAM_SINK_INPUT)
+ data->new_sink = pa_tz_device_get_sink(latest_device, data->device_role);
+ else
+ data->new_source = pa_tz_device_get_source(latest_device, data->device_role);
+ }
if (data->origins_from_new_data)
pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type),
- PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, pa_tz_device_get_type(latest_device));
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
else
pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type),
- PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, pa_tz_device_get_type(latest_device));
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
}
}
}
not_found:
- if ((data->stream_type == STREAM_SINK_INPUT) ? !(*(data->proper_sink)) : !(*(data->proper_source))) {
- pa_log_warn("[SELECT] could not find a proper sink/source, set it to null sink/source");
- if (data->stream_type == STREAM_SINK_INPUT)
- *(data->proper_sink) = null_sink;
- else
- *(data->proper_source) = null_source;
+ if (data->origins_from_new_data) {
+ if ((data->stream_type == STREAM_SINK_INPUT) ? !(*(data->proper_sink)) : !(*(data->proper_source))) {
+ pa_log_warn("[SELECT] could not find a proper sink/source, set it to null sink/source");
+ if (data->stream_type == STREAM_SINK_INPUT)
+ *(data->proper_sink) = null_sink;
+ else
+ *(data->proper_source) = null_source;
+ }
}
return PA_HOOK_OK;
return PA_HOOK_OK;
}
+static int32_t update_route_by_preemptive_device(struct userdata *u, pa_stream_manager_hook_data_for_route *data, hal_route_info *route_info, pa_tz_device **device) {
+ uint32_t dm_device_id = 0;
+ const char *dm_device_type = NULL;
+ bool use_internal_codec = false;
+
+ pa_assert(u);
+ pa_assert(data);
+ pa_assert(route_info);
+ pa_assert(device);
+
+ if (pa_stream_manager_get_preemptive_device_id(u->stream_manager, data->stream_type, data->stream_role, &dm_device_id))
+ return -1;
+
+ /* if this device id is no longer valid - disconnected. */
+ if (!(*device = pa_device_manager_get_device_by_id(u->device_manager, dm_device_id)))
+ return -1;
+
+ dm_device_type = pa_tz_device_get_type(*device);
+ pa_log_debug("preemptive device is type:[%s] id:[%u]", dm_device_type, dm_device_id);
+
+ if (skip_usb_device(data->stream_role, *device))
+ return -1;
+
+ if ((use_internal_codec = pa_tz_device_is_use_internal_codec(*device))) {
+ /* if it needs to skip it, keep going to next device for proper UCM setting */
+ if (skip_device(data->stream_role, dm_device_type))
+ return -1;
+ if (skip_bt_sco_device(u, data->stream_role, dm_device_type))
+ return -1;
+ fill_device_info(route_info, dm_device_type, CONVERT_TO_HAL_DIRECTION(data->stream_type), dm_device_id);
+ return 0;
+ }
+
+ return -1;
+}
+
static pa_hook_result_t handle_auto_or_auto_all_routing(struct userdata *u, pa_stream_manager_hook_data_for_route *data,
hal_route_info *route_info, pa_idxset *conn_devices, pa_tz_device **device) {
const char *device_type = NULL;
pa_assert(conn_devices);
pa_assert(device);
+ if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
+ if (!update_route_by_preemptive_device(u, data, route_info, device)) {
+ dm_device_type = pa_tz_device_get_type(*device);
+ goto update_auto_active_dev;
+ }
+ }
+
PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
pa_log_debug("[ROUTE][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, route_info->role, device_type);
if (!is_cached_device_connected(device_type, data->stream_type))
continue;
if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
+update_auto_active_dev:
if (data->origins_from_new_data)
pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type),
PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
pa_assert(conn_devices);
pa_assert(device);
+ if (!update_route_by_preemptive_device(u, data, route_info, device)) {
+ latest_device = *device;
+ dm_device_type = pa_tz_device_get_type(*device);
+ goto update_auto_active_dev;
+ }
+
PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
pa_log_debug("[ROUTE][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
if (!is_cached_device_connected(device_type, data->stream_type))
else
pa_log_debug(" -- it does not use internal audio codec, skip it");
+update_auto_active_dev:
if (data->origins_from_new_data)
pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type),
PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
#define STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_OPTION "SetStreamRouteOption"
#define STREAM_MANAGER_METHOD_NAME_SET_STREAM_PREFERRED_DEVICE "SetStreamPreferredDevice"
#define STREAM_MANAGER_METHOD_NAME_GET_STREAM_PREFERRED_DEVICE "GetStreamPreferredDevice"
+#define STREAM_MANAGER_METHOD_NAME_SET_STREAM_PREEMPTIVE_DEVICE "SetStreamPreemptiveDevice"
+#define STREAM_MANAGER_METHOD_NAME_GET_STREAM_PREEMPTIVE_DEVICE "GetStreamPreemptiveDevice"
#define STREAM_MANAGER_METHOD_NAME_SET_VOLUME_LEVEL "SetVolumeLevel"
#define STREAM_MANAGER_METHOD_NAME_GET_VOLUME_LEVEL "GetVolumeLevel"
#define STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MAX_LEVEL "GetVolumeMaxLevel"
METHOD_HANDLER_SET_STREAM_ROUTE_OPTION,
METHOD_HANDLER_SET_STREAM_PREFERRED_DEVICE,
METHOD_HANDLER_GET_STREAM_PREFERRED_DEVICE,
+ METHOD_HANDLER_SET_STREAM_PREEMPTIVE_DEVICE,
+ METHOD_HANDLER_GET_STREAM_PREEMPTIVE_DEVICE,
METHOD_HANDLER_SET_VOLUME_LEVEL,
METHOD_HANDLER_GET_VOLUME_LEVEL,
METHOD_HANDLER_GET_VOLUME_MAX_LEVEL,
" <arg name=\"out_device_id\" direction=\"out\" type=\"u\"/>" \
" <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
" </method>" \
+ " <method name=\"STREAM_MANAGER_METHOD_NAME_SET_STREAM_PREEMPTIVE_DEVICE\">" \
+ " <arg name=\"stream_type\" direction=\"in\" type=\"s\"/>" \
+ " <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
+ " <arg name=\"device_id\" direction=\"in\" type=\"u\"/>" \
+ " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
+ " </method>" \
+ " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_STREAM_PREEMPTIVE_DEVICE\">" \
+ " <arg name=\"stream_type\" direction=\"in\" type=\"s\"/>" \
+ " <arg name=\"in_device_id\" direction=\"out\" type=\"u\"/>" \
+ " <arg name=\"out_device_id\" direction=\"out\" type=\"u\"/>" \
+ " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
+ " </method>" \
" <method name=\"STREAM_MANAGER_METHOD_NAME_SET_VOLUME_LEVEL\">" \
" <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
" <arg name=\"type\" direction=\"in\" type=\"s\"/>" \
static void handle_set_stream_route_option(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void handle_set_stream_preferred_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void handle_get_stream_preferred_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_stream_preemptive_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_stream_preemptive_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void handle_set_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void handle_get_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void handle_get_volume_max_level(DBusConnection *conn, DBusMessage *msg, void *userdata);
{ "in_device_id", "u", "out" },
{ "out_device_id", "u", "out" },
{ "ret_msg", "s", "out" } };
+static pa_dbus_arg_info set_stream_preemptive_device_args[] = { { "stream_type", "s", "in" },
+ { "io_direction", "s", "in" },
+ { "device_id", "u", "in" },
+ { "ret_msg", "s", "out" } };
+static pa_dbus_arg_info get_stream_preemptive_device_args[] = { { "stream_type", "s", "in" },
+ { "in_device_id", "u", "out" },
+ { "out_device_id", "u", "out" },
+ { "ret_msg", "s", "out" } };
static pa_dbus_arg_info set_volume_level_args[] = { { "io_direction", "s", "in" },
{ "type", "s", "in" },
{ "level", "u", "in" },
"usi", /* METHOD_HANDLER_SET_STREAM_ROUTE_OPTION */
"usu", /* METHOD_HANDLER_SET_STREAM_PREFERRED_DEVICE */
"u", /* METHOD_HANDLER_GET_STREAM_PREFERRED_DEVICE */
+ "ssu", /* METHOD_HANDLER_SET_STREAM_PREEMPTIVE_DEVICE */
+ "s", /* METHOD_HANDLER_GET_STREAM_PREEMPTIVE_DEVICE */
"ssu", /* METHOD_HANDLER_SET_VOLUME_LEVEL */
"ss", /* METHOD_HANDLER_GET_VOLUME_LEVEL */
"ss", /* METHOD_HANDLER_GET_VOLUME_MAX_LEVEL */
.arguments = get_stream_preferred_device_args,
.n_arguments = sizeof(get_stream_preferred_device_args) / sizeof(pa_dbus_arg_info),
.receive_cb = handle_get_stream_preferred_device },
+ [METHOD_HANDLER_SET_STREAM_PREEMPTIVE_DEVICE] = {
+ .method_name = STREAM_MANAGER_METHOD_NAME_SET_STREAM_PREEMPTIVE_DEVICE,
+ .arguments = set_stream_preemptive_device_args,
+ .n_arguments = sizeof(set_stream_preemptive_device_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_set_stream_preemptive_device },
+ [METHOD_HANDLER_GET_STREAM_PREEMPTIVE_DEVICE] = {
+ .method_name = STREAM_MANAGER_METHOD_NAME_GET_STREAM_PREEMPTIVE_DEVICE,
+ .arguments = get_stream_preemptive_device_args,
+ .n_arguments = sizeof(get_stream_preemptive_device_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_stream_preemptive_device },
[METHOD_HANDLER_SET_VOLUME_LEVEL] = {
.method_name = STREAM_MANAGER_METHOD_NAME_SET_VOLUME_LEVEL,
.arguments = set_volume_level_args,
dbus_message_unref(reply);
}
+static uint32_t get_num_of_target_streams(pa_stream_manager *m, pa_idxset *streams, stream_direction_t direction, const char *role) {
+ void *s;
+ const char *_role;
+ uint32_t idx;
+ uint32_t count = 0;
+
+ pa_assert(m);
+ pa_assert(streams);
+ pa_assert(role);
+
+ PA_IDXSET_FOREACH(s, streams, idx) {
+ _role = pa_proplist_gets((direction == STREAM_DIRECTION_OUT) ?
+ PA_SINK_INPUT(s)->proplist : PA_SOURCE_OUTPUT(s)->proplist, PA_PROP_MEDIA_ROLE);
+ if (pa_safe_streq(_role, role))
+ count++;
+ }
+ return count;
+}
+
+static void routing_process_to_preemptive_device(pa_stream_manager *m, stream_direction_t direction, const char *role, pa_tz_device *tz_device) {
+ void *stream;
+ void *device = NULL;
+ const char *_role;
+ uint32_t count = 0;
+ uint32_t idx;
+
+ pa_assert(m);
+ pa_assert(role);
+ pa_assert(tz_device);
+
+ count = get_num_of_target_streams(m, (direction == STREAM_DIRECTION_OUT) ?
+ m->core->sink_inputs : m->core->source_outputs,
+ direction, role);
+ PA_IDXSET_FOREACH(stream, (direction == STREAM_DIRECTION_OUT) ? m->core->sink_inputs : m->core->source_outputs, idx) {
+ _role = pa_proplist_gets((direction == STREAM_DIRECTION_OUT) ?
+ PA_SINK_INPUT(stream)->proplist : PA_SOURCE_OUTPUT(stream)->proplist, PA_PROP_MEDIA_ROLE);
+ if (!pa_safe_streq(_role, role))
+ continue;
+
+ /* move stream to the preemptive device */
+ device = pa_hashmap_first((direction == STREAM_DIRECTION_OUT) ?
+ tz_device->playback_devices : tz_device->capture_devices);
+ if (direction == STREAM_DIRECTION_OUT) {
+ pa_sink_input_move_to(stream, PA_SINK(device), false);
+ pa_proplist_sets(GET_STREAM_PROPLIST(stream, STREAM_SINK_INPUT),
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, tz_device->type);
+ } else {
+ pa_source_output_move_to(stream, PA_SOURCE(device), false);
+ pa_proplist_sets(GET_STREAM_PROPLIST(stream, STREAM_SOURCE_OUTPUT),
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, tz_device->type);
+ }
+
+ /* change routing */
+ if (--count == 0) {
+ process_stream(m, stream, (direction == STREAM_DIRECTION_OUT) ? STREAM_SINK_INPUT : STREAM_SOURCE_OUTPUT,
+ PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED, false);
+ }
+ }
+}
+
+static void rollback_process_from_preemptive_device(pa_stream_manager *m, stream_direction_t direction, const char *role, pa_tz_device *tz_device) {
+ void *device;
+ void *stream;
+ void *new_device;
+ uint32_t idx;
+ uint32_t count = 0;
+ const char *active_device = NULL;
+ const char *_role;
+
+ pa_assert(m);
+ pa_assert(role);
+ pa_assert(tz_device);
+
+ device = pa_hashmap_first((direction == STREAM_DIRECTION_OUT) ?
+ tz_device->playback_devices : tz_device->capture_devices);
+
+ PA_IDXSET_FOREACH(stream, (direction == STREAM_DIRECTION_OUT) ? PA_SINK(device)->inputs : PA_SOURCE(device)->outputs, idx) {
+ _role = pa_proplist_gets((direction == STREAM_DIRECTION_OUT) ?
+ PA_SINK_INPUT(stream)->proplist : PA_SOURCE_OUTPUT(stream)->proplist, PA_PROP_MEDIA_ROLE);
+ if (!pa_safe_streq(_role, role))
+ continue;
+
+ if (count++ == 0) {
+ /* find and set new device */
+ do_notify(m, NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT,
+ (direction == STREAM_DIRECTION_OUT) ? STREAM_SINK_INPUT : STREAM_SOURCE_OUTPUT,
+ false, stream);
+ new_device = (direction == STREAM_DIRECTION_OUT) ? (void*)(PA_SINK_INPUT(stream)->sink) :
+ (void*)(PA_SOURCE_OUTPUT(stream)->source);
+ active_device = pa_proplist_gets(GET_STREAM_PROPLIST(stream, (direction == STREAM_DIRECTION_OUT) ? STREAM_SINK_INPUT : STREAM_SOURCE_OUTPUT),
+ PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
+ /* change routing */
+ process_stream(m, stream, (direction == STREAM_DIRECTION_OUT) ? STREAM_SINK_INPUT : STREAM_SOURCE_OUTPUT,
+ PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED, false);
+
+ if (direction == STREAM_DIRECTION_OUT)
+ pa_sink_input_move_to(stream, PA_SINK(new_device), false);
+ else
+ pa_source_output_move_to(stream, PA_SOURCE(new_device), false);
+
+ continue;
+ }
+
+ /* move stream to the new device */
+ if (direction == STREAM_DIRECTION_OUT) {
+ pa_sink_input_move_to(stream, PA_SINK(new_device), false);
+ if (active_device)
+ pa_proplist_sets(GET_STREAM_PROPLIST(stream, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, active_device);
+ } else {
+ pa_source_output_move_to(stream, PA_SOURCE(new_device), false);
+ if (active_device)
+ pa_proplist_sets(GET_STREAM_PROPLIST(stream, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, active_device);
+ }
+ }
+}
+
+static void handle_set_stream_preemptive_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ const char *stream_type = NULL;
+ const char *device_direction = NULL;
+ stream_direction_t direction;
+ uint32_t device_id = 0;
+ stream_info *s = NULL;
+ pa_tz_device *device = NULL;
+ char *device_type = NULL;
+ const char *prev_device_type = NULL;
+ uint32_t prev_device_id = 0;
+ uint32_t idx;
+ bool found = false;
+ ret_msg_t ret = RET_MSG_OK;
+ DBusMessage *reply = NULL;
+
+ 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, &stream_type,
+ DBUS_TYPE_STRING, &device_direction,
+ DBUS_TYPE_UINT32, &device_id,
+ DBUS_TYPE_INVALID));
+ pa_log_info("stream type[%s], device direction[%s], device_id[%u]", stream_type, device_direction, device_id);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ if ((s = pa_hashmap_get(m->stream_infos, stream_type)) == NULL) {
+ pa_log_error("could not find this stream type(%s)", stream_type);
+ ret = RET_MSG_ERROR_INTERNAL;
+ goto finish;
+ }
+
+ if (pa_safe_streq(device_direction, "in"))
+ direction = STREAM_DIRECTION_IN;
+ else if (pa_safe_streq(device_direction, "out"))
+ direction = STREAM_DIRECTION_OUT;
+ else {
+ ret = RET_MSG_ERROR_INVALID_ARGUMENT;
+ goto finish;
+ }
+
+ /* allow only auto routing type */
+ if (s->route_type != STREAM_ROUTE_TYPE_AUTO &&
+ s->route_type != STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
+ pa_log_error("not allowed this route type[%d]", s->route_type);
+ ret = RET_MSG_ERROR_POLICY;
+ goto finish;
+ }
+
+ if (device_id > 0) {
+ if (!(device = pa_device_manager_get_device_by_id(m->dm, device_id))) {
+ pa_log_error("could not get device by id[%u]", device_id);
+ ret = RET_MSG_ERROR_INVALID_ARGUMENT;
+ goto finish;
+ }
+
+ PA_IDXSET_FOREACH(device_type, (direction == STREAM_DIRECTION_OUT) ? s->idx_avail_out_devices : s->idx_avail_in_devices, idx) {
+ if (pa_safe_streq(device_type, device->type)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ pa_log_error("not supported this device type[%s]", device->type);
+ ret = RET_MSG_ERROR_INVALID_ARGUMENT;
+ goto finish;
+ }
+ }
+
+ if (device_id == 0 && s->preemptive_device[direction].id == 0) {
+ pa_log_debug("there's no preemptive device, nothing to do");
+ goto finish;
+ }
+
+ prev_device_type = s->preemptive_device[direction].type;
+ prev_device_id = s->preemptive_device[direction].id;
+ s->preemptive_device[direction].type = device_id > 0 ? device->type : NULL;
+ s->preemptive_device[direction].id = device_id;
+
+ pa_log_info("preemptive [%s] device is set, [%s, id:%u]",
+ direction == STREAM_DIRECTION_OUT ? "out" : "in",
+ device_id > 0 ? device->type : NULL,
+ device_id);
+
+ /* move streams and change routing */
+ if (device_id == 0) {
+ if (!(device = pa_device_manager_get_device_by_id(m->dm, prev_device_id)) || !pa_safe_streq(device->type, prev_device_type)) {
+ pa_log_debug("could not find the previous device of id[%u], type[%s], nothing to do.", prev_device_id, prev_device_type);
+ goto finish;
+ }
+ rollback_process_from_preemptive_device(m, direction, stream_type, device);
+ } else {
+ routing_process_to_preemptive_device(m, direction, stream_type, device);
+ }
+
+finish:
+ pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[ret], DBUS_TYPE_INVALID));
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+static void handle_get_stream_preemptive_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ const char *stream_type = NULL;
+ stream_info *s = NULL;
+ uint32_t in_device_id = 0;
+ uint32_t out_device_id = 0;
+ const char *in_device_type;
+ const char *out_device_type;
+ ret_msg_t ret = RET_MSG_OK;
+ DBusMessage *reply = NULL;
+
+ 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, &stream_type,
+ DBUS_TYPE_INVALID));
+ pa_log_info("stream type[%s]", stream_type);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ if ((s = pa_hashmap_get(m->stream_infos, stream_type)) == NULL) {
+ pa_log_error("could not find this stream type(%s)", stream_type);
+ ret = RET_MSG_ERROR_INTERNAL;
+ goto finish;
+ }
+
+ in_device_id = s->preemptive_device[STREAM_DIRECTION_IN].id;
+ in_device_type = s->preemptive_device[STREAM_DIRECTION_IN].type;
+ out_device_id = s->preemptive_device[STREAM_DIRECTION_OUT].id;
+ out_device_type = s->preemptive_device[STREAM_DIRECTION_OUT].type;
+
+ pa_log_info("preemptive IN device: type[%s] id[%u]", in_device_type, in_device_id);
+ pa_log_info("preemptive OUT device: type[%s] id[%u]", out_device_type, out_device_id);
+
+finish:
+ pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, &in_device_id, DBUS_TYPE_INVALID));
+ pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, &out_device_id, DBUS_TYPE_INVALID));
+ pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[ret], DBUS_TYPE_INVALID));
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
static void handle_set_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata) {
const char *direction = NULL;
const char *type = NULL;
pa_idxset *idx_avail_in_devices;
pa_idxset *idx_avail_out_devices;
pa_idxset *idx_avail_frameworks;
+ struct _preemptive_device {
+ const char *type;
+ uint32_t id;
+ } preemptive_device[STREAM_DIRECTION_MAX];
} stream_info;
typedef struct _prior_max_priority_stream {
const char *stream_role;
pa_proplist *prop;
+ pa_assert(stream);
+
if (pa_sink_input_isinstance(stream))
prop = PA_SINK_INPUT(stream)->proplist;
else
PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
fill_device_info_to_hook_data(m, &hook_call_select_data, command, type, s, is_new_data);
hook_call_select_data.sample_spec = GET_STREAM_SAMPLE_SPEC(s, type);
- if (type == STREAM_SINK_INPUT)
- hook_call_select_data.proper_sink = &(((pa_sink_input*)s)->sink);
- else if (type == STREAM_SOURCE_OUTPUT)
- hook_call_select_data.proper_source = &(((pa_source_output*)s)->source);
}
if (hook_call_select_data.route_type == STREAM_ROUTE_TYPE_MANUAL)
CONVERT_TO_DEVICE_ROLE(hook_call_select_data.stream_role, hook_call_select_data.device_role);
- if (pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE), &hook_call_select_data))
+
+ if (pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE), &hook_call_select_data)) {
ret = RET_MSG_ERROR_INTERNAL;
+ } else {
+ if (!is_new_data) {
+ if (type == STREAM_SINK_INPUT)
+ pa_sink_input_move_to(PA_SINK_INPUT(s), hook_call_select_data.new_sink, false);
+ else
+ pa_source_output_move_to(PA_SOURCE_OUTPUT(s), hook_call_select_data.new_source, false);
+ }
+ }
BREAK_WITH_FREE:
pa_xfree(filtered_avail_devices);
stream_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROLE);
pa_log_debug(" -- idx(%u), route_type(%d), stream_role(%s)", s_idx, route_type, stream_role);
if (is_connected) {
+ uint32_t preemptive_device_id = 0;
+ if (!pa_stream_manager_get_preemptive_device_id(m, stream_type, stream_role, &preemptive_device_id)) {
+ pa_tz_device *device = pa_device_manager_get_device_by_id(m->dm, preemptive_device_id);
+ if (device) {
+ pa_log_debug("Skip moving this stream[%s, idx:%u] set to the preemptive device(%s, id:%u)", stream_role,
+ (stream_type == STREAM_SINK_INPUT) ? PA_SINK_INPUT(s)->index : PA_SOURCE_OUTPUT(s)->index,
+ device->type, preemptive_device_id);
+ continue;
+ }
+ }
/* CONNECTED: move a stream to the new device if possible */
if (sink && (sink != ((pa_sink_input*)s)->sink)) {
if ((cur_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
return get_route_type(stream, stream_type, is_new_data, stream_route_type);
}
+int32_t pa_stream_manager_get_preemptive_device_id(pa_stream_manager *m, stream_type_t stream_type, const char *role, uint32_t *id) {
+ stream_info *s = NULL;
+
+ pa_assert(m);
+ pa_assert(role);
+ pa_assert(id);
+
+ if (!(s = pa_hashmap_get(m->stream_infos, role))) {
+ pa_log_warn("%s is not valid role, return false", role);
+ return -1;
+ }
+
+ if (s->preemptive_device[stream_type == STREAM_SINK_INPUT ? STREAM_DIRECTION_OUT : STREAM_DIRECTION_IN].type) {
+ *id = s->preemptive_device[stream_type == STREAM_SINK_INPUT ? STREAM_DIRECTION_OUT : STREAM_DIRECTION_IN].id;
+ return 0;
+ }
+
+ return -1;
+}
+
bool pa_stream_manager_check_filter_apply_stream(void *stream, stream_type_t stream_type) {
return is_filter_apply_stream(stream, stream_type);
}
stream_route_type_t route_type;
pa_sink **proper_sink;
pa_source **proper_source;
+ pa_sink *new_sink;
+ pa_source *new_source;
pa_sample_spec sample_spec;
pa_idxset *idx_avail_devices;
pa_idxset *idx_manual_devices;
} pa_stream_manager_hook_data_for_update_info;
int32_t pa_stream_manager_get_route_type(void *stream, stream_type_t stream_type, bool is_new_data, stream_route_type_t *stream_route_type);
+int32_t pa_stream_manager_get_preemptive_device_id(pa_stream_manager *m, stream_type_t stream_type, const char *role, uint32_t *id);
bool pa_stream_manager_check_name_is_vstream(void *stream, stream_type_t type, bool is_new_data);
bool pa_stream_manager_check_filter_apply_stream(void *stream, stream_type_t stream_type);
bool pa_stream_manager_is_valid_stream_role(pa_core *c, const char *role);