2 This file is part of PulseAudio.
4 Copyright 2015-2016 Sangchul Lee <sc11.lee@samsung.com>
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
34 #include <pulse/xmalloc.h>
35 #include <pulse/proplist.h>
37 #include <pulsecore/module.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/sink.h>
40 #include <pulsecore/modargs.h>
41 #include <pulsecore/macro.h>
44 #include "stream-manager.h"
45 #include "stream-manager-priv.h"
46 #include "stream-manager-volume-priv.h"
47 #include "stream-manager-restriction-priv.h"
50 #define ARR_ARG_MAX 32
51 #define STREAM_MANAGER_OBJECT_PATH "/org/pulseaudio/StreamManager"
52 #define STREAM_MANAGER_INTERFACE "org.pulseaudio.StreamManager"
54 #define STREAM_MANAGER_METHOD_NAME_GET_STREAM_INFO "GetStreamInfo"
55 #define STREAM_MANAGER_METHOD_NAME_GET_STREAM_LIST "GetStreamList"
56 #define STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_DEVICES "SetStreamRouteDevices"
57 #define STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_OPTION "SetStreamRouteOption"
58 #define STREAM_MANAGER_METHOD_NAME_SET_VOLUME_LEVEL "SetVolumeLevel"
59 #define STREAM_MANAGER_METHOD_NAME_GET_VOLUME_LEVEL "GetVolumeLevel"
60 #define STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MAX_LEVEL "GetVolumeMaxLevel"
61 #define STREAM_MANAGER_METHOD_NAME_SET_VOLUME_MUTE "SetVolumeMute"
62 #define STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MUTE "GetVolumeMute"
63 #define STREAM_MANAGER_METHOD_NAME_GET_CURRENT_VOLUME_TYPE "GetCurrentVolumeType" /* the type that belongs to the stream of the current max priority */
64 #define STREAM_MANAGER_METHOD_NAME_UPDATE_FOCUS_STATUS "UpdateFocusStatus"
65 #define STREAM_MANAGER_METHOD_NAME_UPDATE_RESTRICTION "UpdateRestriction"
67 #define STREAM_MANAGER_SIGNAL_NAME_VOLUME_CHANGED "VolumeChanged"
68 #define STREAM_MANAGER_SIGNAL_NAME_COMMAND "Command"
70 static DBusHandlerResult method_handler_for_vt(DBusConnection *c, DBusMessage *m, void *userdata);
71 static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, void *userdata);
72 static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata);
73 static void handle_get_stream_info(DBusConnection *conn, DBusMessage *msg, void *userdata);
74 static void handle_get_stream_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
75 static void handle_set_stream_route_devices(DBusConnection *conn, DBusMessage *msg, void *userdata);
76 static void handle_set_stream_route_option(DBusConnection *conn, DBusMessage *msg, void *userdata);
77 static void handle_set_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata);
78 static void handle_get_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata);
79 static void handle_get_volume_max_level(DBusConnection *conn, DBusMessage *msg, void *userdata);
80 static void handle_set_volume_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
81 static void handle_get_volume_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
82 static void handle_get_current_volume_type(DBusConnection *conn, DBusMessage *msg, void *userdata);
83 static void handle_update_focus_status(DBusConnection *conn, DBusMessage *msg, void *userdata);
84 static void handle_update_restriction(DBusConnection *conn, DBusMessage *msg, void *userdata);
85 static void send_volume_changed_signal(DBusConnection *conn, const char *direction, const char *volume_type, const uint32_t volume_level);
86 static void send_command_signal(DBusConnection *conn, const char *name, int value);
88 enum method_handler_index {
89 METHOD_HANDLER_GET_STREAM_INFO,
90 METHOD_HANDLER_GET_STREAM_LIST,
91 METHOD_HANDLER_SET_STREAM_ROUTE_DEVICES,
92 METHOD_HANDLER_SET_STREAM_ROUTE_OPTION,
93 METHOD_HANDLER_SET_VOLUME_LEVEL,
94 METHOD_HANDLER_GET_VOLUME_LEVEL,
95 METHOD_HANDLER_GET_VOLUME_MAX_LEVEL,
96 METHOD_HANDLER_SET_VOLUME_MUTE,
97 METHOD_HANDLER_GET_VOLUME_MUTE,
98 METHOD_HANDLER_GET_CURRENT_VOLUME_TYPE,
99 METHOD_HANDLER_UPDATE_FOCUS_STATUS,
100 METHOD_HANDLER_UPDATE_RESTRICTION,
104 static pa_dbus_arg_info get_stream_info_args[] = { { "stream_type", "s", "in" },
105 { "priority", "i", "out" },
106 { "route_type", "i", "out" },
107 { "volume_types", "as", "out" },
108 { "avail_in_devices", "as", "out" },
109 { "avail_out_devices", "as", "out" },
110 { "avail_frameworks", "as", "out"} };
111 static pa_dbus_arg_info get_stream_list_args[] = { { "stream_type", "as", "out" },
112 { "priority", "ai", "out" } };
113 static pa_dbus_arg_info set_stream_route_devices_args[] = { { "parent_id", "u", "in" },
114 { "route_in_devices", "au", "in" },
115 { "route_out_devices", "au", "in" },
116 { "ret_msg", "s", "out" } };
117 static pa_dbus_arg_info set_stream_route_option_args[] = { { "parent_id", "u", "in" },
118 { "name", "s", "in" },
119 { "value", "i", "in" },
120 { "ret_msg", "s", "out" } };
121 static pa_dbus_arg_info set_volume_level_args[] = { { "io_direction", "s", "in" },
122 { "type", "s", "in" },
123 { "level", "u", "in" },
124 { "ret_msg", "s", "out" } };
125 static pa_dbus_arg_info get_volume_level_args[] = { { "io_direction", "s", "in" },
126 { "type", "s", "in" },
127 { "level", "u", "out" },
128 { "ret_msg", "s", "out" } };
129 static pa_dbus_arg_info get_volume_max_level_args[] = { { "io_direction", "s", "in" },
130 { "type", "s", "in" },
131 { "level", "u", "out" },
132 { "ret_msg", "s", "out" } };
133 static pa_dbus_arg_info set_volume_mute_args[] = { { "io_direction", "s", "in" },
134 { "type", "s", "in" },
136 { "ret_msg", "s", "out" } };
137 static pa_dbus_arg_info get_volume_mute_args[] = { { "io_direction", "s", "in" },
138 { "type", "s", "in" },
139 { "on", "u", "out" },
140 { "ret_msg", "s", "out" } };
141 static pa_dbus_arg_info get_current_volume_type_args[] = { { "io_direction", "s", "in" },
142 { "type", "s", "out" },
143 { "ret_msg", "s", "out" } };
144 static pa_dbus_arg_info update_focus_status_args[] = { { "parent_id", "u", "in" },
145 { "focus_status", "u", "in" },
146 { "ret_msg", "s", "out" } };
147 static pa_dbus_arg_info update_restriction_args[] = { { "name", "s", "in" },
148 { "value", "u", "in" },
149 { "ret_msg", "s", "out" } };
150 static const char* signature_args_for_in[] = { "s", "", "uauau", "usi", "ssu", "ss", "ss", "ssu", "ss", "s", "uu", "su"};
152 static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
153 [METHOD_HANDLER_GET_STREAM_INFO] = {
154 .method_name = STREAM_MANAGER_METHOD_NAME_GET_STREAM_INFO,
155 .arguments = get_stream_info_args,
156 .n_arguments = sizeof(get_stream_info_args) / sizeof(pa_dbus_arg_info),
157 .receive_cb = handle_get_stream_info },
158 [METHOD_HANDLER_GET_STREAM_LIST] = {
159 .method_name = STREAM_MANAGER_METHOD_NAME_GET_STREAM_LIST,
160 .arguments = get_stream_list_args,
161 .n_arguments = sizeof(get_stream_list_args) / sizeof(pa_dbus_arg_info),
162 .receive_cb = handle_get_stream_list },
163 [METHOD_HANDLER_SET_STREAM_ROUTE_DEVICES] = {
164 .method_name = STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_DEVICES,
165 .arguments = set_stream_route_devices_args,
166 .n_arguments = sizeof(set_stream_route_devices_args) / sizeof(pa_dbus_arg_info),
167 .receive_cb = handle_set_stream_route_devices },
168 [METHOD_HANDLER_SET_STREAM_ROUTE_OPTION] = {
169 .method_name = STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_OPTION,
170 .arguments = set_stream_route_option_args,
171 .n_arguments = sizeof(set_stream_route_option_args) / sizeof(pa_dbus_arg_info),
172 .receive_cb = handle_set_stream_route_option },
173 [METHOD_HANDLER_SET_VOLUME_LEVEL] = {
174 .method_name = STREAM_MANAGER_METHOD_NAME_SET_VOLUME_LEVEL,
175 .arguments = set_volume_level_args,
176 .n_arguments = sizeof(set_volume_level_args) / sizeof(pa_dbus_arg_info),
177 .receive_cb = handle_set_volume_level },
178 [METHOD_HANDLER_GET_VOLUME_LEVEL] = {
179 .method_name = STREAM_MANAGER_METHOD_NAME_GET_VOLUME_LEVEL,
180 .arguments = get_volume_level_args,
181 .n_arguments = sizeof(get_volume_level_args) / sizeof(pa_dbus_arg_info),
182 .receive_cb = handle_get_volume_level },
183 [METHOD_HANDLER_GET_VOLUME_MAX_LEVEL] = {
184 .method_name = STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MAX_LEVEL,
185 .arguments = get_volume_max_level_args,
186 .n_arguments = sizeof(get_volume_max_level_args) / sizeof(pa_dbus_arg_info),
187 .receive_cb = handle_get_volume_max_level },
188 [METHOD_HANDLER_SET_VOLUME_MUTE] = {
189 .method_name = STREAM_MANAGER_METHOD_NAME_SET_VOLUME_MUTE,
190 .arguments = set_volume_mute_args,
191 .n_arguments = sizeof(set_volume_mute_args) / sizeof(pa_dbus_arg_info),
192 .receive_cb = handle_set_volume_mute },
193 [METHOD_HANDLER_GET_VOLUME_MUTE] = {
194 .method_name = STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MUTE,
195 .arguments = get_volume_mute_args,
196 .n_arguments = sizeof(get_volume_mute_args) / sizeof(pa_dbus_arg_info),
197 .receive_cb = handle_get_volume_mute },
198 [METHOD_HANDLER_GET_CURRENT_VOLUME_TYPE] = {
199 .method_name = STREAM_MANAGER_METHOD_NAME_GET_CURRENT_VOLUME_TYPE,
200 .arguments = get_current_volume_type_args,
201 .n_arguments = sizeof(get_current_volume_type_args) / sizeof(pa_dbus_arg_info),
202 .receive_cb = handle_get_current_volume_type },
203 [METHOD_HANDLER_UPDATE_FOCUS_STATUS] = {
204 .method_name = STREAM_MANAGER_METHOD_NAME_UPDATE_FOCUS_STATUS,
205 .arguments = update_focus_status_args,
206 .n_arguments = sizeof(update_focus_status_args) / sizeof(pa_dbus_arg_info),
207 .receive_cb = handle_update_focus_status },
208 [METHOD_HANDLER_UPDATE_RESTRICTION] = {
209 .method_name = STREAM_MANAGER_METHOD_NAME_UPDATE_RESTRICTION,
210 .arguments = update_restriction_args,
211 .n_arguments = sizeof(update_restriction_args) / sizeof(pa_dbus_arg_info),
212 .receive_cb = handle_update_restriction },
215 const char *dbus_str_none = "none";
216 const char* stream_manager_dbus_ret_str[] = {"STREAM_MANAGER_RETURN_OK", "STREAM_MANAGER_RETURN_ERROR", "STREAM_MANAGER_RETURN_ERROR_NO_STREAM"};
220 RET_MSG_INDEX_ERROR_NO_STREAM,
223 #ifdef USE_DBUS_PROTOCOL
225 static pa_dbus_interface_info stream_manager_interface_info = {
226 .name = STREAM_MANAGER_INTERFACE,
227 .method_handlers = method_handlers,
228 .n_method_handlers = METHOD_HANDLER_MAX,
229 .property_handlers = ,
230 .n_property_handlers = ,
231 .get_all_properties_cb =,
238 #define STREAM_MGR_INTROSPECT_XML \
239 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
241 " <interface name=\"STREAM_MANAGER_INTERFACE\">" \
242 " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_STREAM_INFO\">" \
243 " <arg name=\"stream_type\" direction=\"in\" type=\"s\"/>" \
244 " <arg name=\"priority\" direction=\"out\" type=\"i\"/>" \
245 " <arg name=\"route_type\" direction=\"out\" type=\"i\"/>" \
246 " <arg name=\"volume_types\" direction=\"out\" type=\"as\"/>" \
247 " <arg name=\"avail_in_devices\" direction=\"out\" type=\"as\"/>" \
248 " <arg name=\"avail_out_devices\" direction=\"out\" type=\"as\"/>" \
249 " <arg name=\"avail_frameworks\" direction=\"out\" type=\"as\"/>" \
251 " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_STREAM_LIST\">" \
252 " <arg name=\"stream_type\" direction=\"in\" type=\"as\"/>" \
253 " <arg name=\"priority\" direction=\"in\" type=\"ai\"/>" \
255 " <method name=\"STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_DEVICES\">"\
256 " <arg name=\"parent_id\" direction=\"in\" type=\"u\"/>" \
257 " <arg name=\"route_in_devices\" direction=\"in\" type=\"au\"/>" \
258 " <arg name=\"route_out_devices\" direction=\"in\" type=\"au\"/>" \
259 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
261 " <method name=\"STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_OPTION\">" \
262 " <arg name=\"parent_id\" direction=\"in\" type=\"u\"/>" \
263 " <arg name=\"name\" direction=\"in\" type=\"s\"/>" \
264 " <arg name=\"value\" direction=\"in\" type=\"i\"/>" \
265 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
267 " <method name=\"STREAM_MANAGER_METHOD_NAME_SET_VOLUME_LEVEL\">" \
268 " <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
269 " <arg name=\"type\" direction=\"in\" type=\"s\"/>" \
270 " <arg name=\"level\" direction=\"in\" type=\"u\"/>" \
271 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
273 " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_VOLUME_LEVEL\">" \
274 " <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
275 " <arg name=\"type\" direction=\"in\" type=\"s\"/>" \
276 " <arg name=\"level\" direction=\"out\" type=\"u\"/>" \
277 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
279 " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MAX_LEVEL\">" \
280 " <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
281 " <arg name=\"type\" direction=\"in\" type=\"s\"/>" \
282 " <arg name=\"level\" direction=\"out\" type=\"u\"/>" \
283 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
285 " <method name=\"STREAM_MANAGER_METHOD_NAME_SET_VOLUME_MUTE\">" \
286 " <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
287 " <arg name=\"type\" direction=\"in\" type=\"s\"/>" \
288 " <arg name=\"on\" direction=\"in\" type=\"u\"/>" \
289 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
291 " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MUTE\">" \
292 " <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
293 " <arg name=\"type\" direction=\"in\" type=\"s\"/>" \
294 " <arg name=\"on\" direction=\"out\" type=\"u\"/>" \
295 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
297 " <method name=\"STREAM_MANAGER_METHOD_NAME_GET_CURRENT_VOLUME_TYPE\">" \
298 " <arg name=\"io_direction\" direction=\"in\" type=\"s\"/>" \
299 " <arg name=\"type\" direction=\"out\" type=\"s\"/>" \
300 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
302 " <method name=\"STREAM_MANAGER_METHOD_NAME_UPDATE_FOCUS_STATUS\">" \
303 " <arg name=\"parent_id\" direction=\"in\" type=\"u\"/>" \
304 " <arg name=\"focus_status\" direction=\"in\" type=\"u\"/>" \
305 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
307 " <method name=\"STREAM_MANAGER_METHOD_NAME_UPDATE_RESTRICTION\">" \
308 " <arg name=\"name\" direction=\"in\" type=\"s\"/>" \
309 " <arg name=\"value\" direction=\"in\" type=\"u\"/>" \
310 " <arg name=\"ret_msg\" direction=\"out\" type=\"s\"/>" \
312 " <signal name=\"STREAM_MANAGER_SIGNAL_NAME_VOLUME_CHANGED\">" \
313 " <arg name=\"direction\" type=\"s\"/>" \
314 " <arg name=\"volume_type\" type=\"s\"/>" \
315 " <arg name=\"volume_level\" type=\"u\"/>" \
317 " <signal name=\"STREAM_MANAGER_SIGNAL_NAME_COMMAND\">" \
318 " <arg name=\"name\" type=\"s\"/>" \
319 " <arg name=\"value\" type=\"i\"/>" \
322 " <interface name=\"org.freedesktop.DBus.Introspectable\">" \
323 " <method name=\"Introspect\">" \
324 " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
332 #define STREAM_MANAGER_CLIENT_NAME "SOUND_MANAGER_STREAM_INFO" /* The client via sound-manager */
333 #define VIRTUAL_STREAM_NAME "VIRTUAL_STREAM" /* The virtual stream created by sound-manager */
334 #define DEFAULT_ROLE "media"
335 #define SKIP_ROLE "skip"
336 #define ACTIVE_DEV_REMOVED "removed"
338 /* There are some streams that need to be skipped.
339 * In other words, we do not care about streams that have a name of listed as below */
340 #define NAME_FOR_SKIP_MAX 1
341 const char* stream_manager_media_names_for_skip[NAME_FOR_SKIP_MAX] = {"pulsesink probe"};
343 #define STREAM_FOCUS_NONE "0"
344 #define STREAM_FOCUS_PLAYBACK "1"
345 #define STREAM_FOCUS_CAPTURE "2"
347 typedef enum _process_stream_result {
348 PROCESS_STREAM_RESULT_OK,
349 PROCESS_STREAM_RESULT_STOP,
350 PROCESS_STREAM_RESULT_SKIP,
351 } process_stream_result_t;
353 typedef enum _process_command_type {
354 PROCESS_COMMAND_PREPARE,
355 PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED,
356 PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED,
357 PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED,
358 PROCESS_COMMAND_UPDATE_VOLUME,
359 PROCESS_COMMAND_ADD_PARENT_ID,
360 PROCESS_COMMAND_REMOVE_PARENT_ID,
361 PROCESS_COMMAND_UPDATE_BUFFER_ATTR,
362 } process_command_type_t;
364 typedef enum _notify_command_type {
365 NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT,
366 NOTIFY_COMMAND_CHANGE_ROUTE_START,
367 NOTIFY_COMMAND_CHANGE_ROUTE_END,
368 NOTIFY_COMMAND_UPDATE_ROUTE_OPTION,
369 NOTIFY_COMMAND_INFORM_STREAM_CONNECTED,
370 NOTIFY_COMMAND_INFORM_STREAM_DISCONNECTED,
371 } notify_command_type_t;
373 const char* process_command_type_str[] = {
375 "CHANGE_ROUTE_BY_STREAM_STARTED",
376 "CHANGE_ROUTE_BY_STREAM_ENDED",
377 "CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED",
381 "UPDATE_BUFFER_ATTR",
384 const char* notify_command_type_str[] = {
385 "SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT",
386 "CHANGE_ROUTE_START",
388 "UPDATE_ROUTE_OPTION",
389 "INFORM_STREAM_CONNECTED",
390 "INFORM_STREAM_DISCONNECTED",
393 #define STREAM_MAP_FILE "/etc/pulse/stream-map.json"
394 #define STREAM_MAP_VOLUMES "volumes"
395 #define STREAM_MAP_VOLUME_TYPE "type"
396 #define STREAM_MAP_VOLUME_IS_FOR_HAL "is-hal-volume"
397 #define STREAM_MAP_LATENCIES "latencies"
398 #define STREAM_MAP_LATENCY_TYPE "type"
399 #define STREAM_MAP_LATENCY_FRAGSIZE_MS "fragsize-ms"
400 #define STREAM_MAP_LATENCY_TLENGTH_MS "tlength-ms"
401 #define STREAM_MAP_LATENCY_MINREQ_MS "minreq-ms"
402 #define STREAM_MAP_LATENCY_PREBUF_MS "prebuf-ms"
403 #define STREAM_MAP_LATENCY_MAXLENGTH "maxlength"
404 #define STREAM_MAP_STREAMS "streams"
405 #define STREAM_MAP_STREAM_ROLE "role"
406 #define STREAM_MAP_STREAM_PRIORITY "priority"
407 #define STREAM_MAP_STREAM_ROUTE_TYPE "route-type"
408 #define STREAM_MAP_STREAM_DIRECTIONS "directions"
409 #define STREAM_MAP_STREAM_VOLUME_TYPES "volume-types"
410 #define STREAM_MAP_STREAM_VOLUME_TYPE_IN "in"
411 #define STREAM_MAP_STREAM_VOLUME_TYPE_OUT "out"
412 #define STREAM_MAP_STREAM_CAPTURE_VOLUME_TYPE "capture-volume-type"
413 #define STREAM_MAP_STREAM_PLAYBACK_VOLUME_TYPE "playback-volume-type"
414 #define STREAM_MAP_STREAM_AVAIL_IN_DEVICES "avail-in-devices"
415 #define STREAM_MAP_STREAM_AVAIL_OUT_DEVICES "avail-out-devices"
416 #define STREAM_MAP_STREAM_AVAIL_FRAMEWORKS "avail-frameworks"
418 typedef struct _stream_parent {
419 pa_idxset *idx_sink_inputs;
420 pa_idxset *idx_source_outputs;
421 pa_idxset *idx_route_in_devices;
422 pa_idxset *idx_route_out_devices;
423 focus_acquired_status_t focus_status;
426 #define AVAIL_DEVICES_MAX 16
427 #define AVAIL_FRAMEWORKS_MAX 16
428 #define AVAIL_STREAMS_MAX 32
429 typedef struct _stream_info_per_type {
432 int32_t num_of_in_devices;
433 int32_t num_of_out_devices;
434 int32_t num_of_frameworks;
435 const char *volume_types[STREAM_DIRECTION_MAX];
436 const char *avail_in_devices[AVAIL_DEVICES_MAX];
437 const char *avail_out_devices[AVAIL_DEVICES_MAX];
438 const char *avail_frameworks[AVAIL_FRAMEWORKS_MAX];
439 } stream_info_per_type;
440 typedef struct _stream_list {
441 int32_t num_of_streams;
442 char *types[AVAIL_STREAMS_MAX];
443 int32_t priorities[AVAIL_STREAMS_MAX];
445 typedef struct _stream_route_option {
448 } stream_route_option;
450 #define CONVERT_TO_DEVICE_DIRECTION(stream_type) \
451 ((stream_type == STREAM_SINK_INPUT) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
453 #define CONVERT_TO_DEVICE_ROLE(x_stream_role, x_device_role) { \
454 pa_assert(x_stream_role); \
455 if (pa_streq(x_stream_role, STREAM_ROLE_CALL_VOICE)) \
456 x_device_role = DEVICE_ROLE_CALL_VOICE; \
457 else if (pa_streq(x_stream_role, STREAM_ROLE_CALL_VIDEO)) \
458 x_device_role = DEVICE_ROLE_CALL_VIDEO; \
459 else if (pa_streq(x_stream_role, STREAM_ROLE_VOIP)) \
460 x_device_role = DEVICE_ROLE_VOIP; \
462 x_device_role = DEVICE_ROLE_NORMAL; \
465 #define SET_NEW_DATA_STREAM_TO_NULL_SINK_SOURCE(x_m, x_stream, x_stream_type) { \
466 pa_sink *null_sink; \
467 pa_source *null_source; \
468 if (x_stream_type == STREAM_SINK_INPUT && \
469 (!((pa_sink_input_new_data*)x_stream)->sink)) { \
470 if ((null_sink = (pa_sink*)pa_namereg_get(x_m->core, SINK_NAME_NULL, PA_NAMEREG_SINK))) \
471 ((pa_sink_input_new_data*)x_stream)->sink = null_sink; \
473 pa_log_warn("could not get null_sink"); \
474 } else if (x_stream_type == STREAM_SOURCE_OUTPUT && \
475 (!((pa_source_output_new_data*)x_stream)->source)) { \
476 if ((null_source = (pa_source*)pa_namereg_get(x_m->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE))) \
477 ((pa_source_output_new_data*)x_stream)->source = null_source; \
479 pa_log_warn("could not get null_source"); \
483 static void do_notify(pa_stream_manager *m, notify_command_type_t command, stream_type_t type, bool is_new_data, void *user_data);
484 static process_stream_result_t process_stream(pa_stream_manager *m, void *stream, stream_type_t type, process_command_type_t command, bool is_new_data);
486 static int32_t get_available_streams(pa_stream_manager *m, stream_list *list) {
488 stream_info *s = NULL;
492 pa_log_info("get_available_streams");
493 if (m->stream_infos) {
494 while ((s = pa_hashmap_iterate(m->stream_infos, &state, (const void**)&role))) {
495 if (i < AVAIL_STREAMS_MAX) {
496 list->priorities[i] = s->priority;
497 list->types[i++] = role;
498 pa_log_debug(" [%d] stream_type[%s], priority[%d]", i-1, role, s->priority);
500 pa_log_error(" out of range, [%d]", i);
504 list->num_of_streams = i;
505 pa_log_debug(" num_of_streams[%d]", i);
507 pa_log_error("stream_map is not initialized..");
513 static int32_t get_stream_info(pa_stream_manager *m, const char *stream_role, stream_info_per_type *info) {
517 stream_info *s = NULL;
518 pa_log_info("get_stream_info : role[%s]", stream_role);
519 if (m->stream_infos) {
520 s = pa_hashmap_get(m->stream_infos, stream_role);
522 info->priority = s->priority;
523 info->route_type = s->route_type;
524 for (i = 0; i < STREAM_DIRECTION_MAX; i++) {
525 pa_log_debug(" volume_types[%d] name : %s", i, s->volume_types[i]);
526 info->volume_types[i] = s->volume_types[i];
528 PA_IDXSET_FOREACH(name, s->idx_avail_in_devices, idx) {
529 pa_log_debug(" avail-in-device[%d] name : %s", idx, name);
530 if (idx < AVAIL_DEVICES_MAX)
531 info->avail_in_devices[idx] = name;
533 pa_log_error(" avail-in-devices, out of range, [%d]", idx);
535 info->num_of_in_devices = pa_idxset_size(s->idx_avail_in_devices);
536 PA_IDXSET_FOREACH(name, s->idx_avail_out_devices, idx) {
537 pa_log_debug(" avail-out-device[%d] name : %s", idx, name);
538 if (idx < AVAIL_DEVICES_MAX)
539 info->avail_out_devices[idx] = name;
541 pa_log_error(" avail-out-devices, out of range, [%d]", idx);
543 info->num_of_out_devices = pa_idxset_size(s->idx_avail_out_devices);
544 PA_IDXSET_FOREACH(name, s->idx_avail_frameworks, idx) {
545 pa_log_debug(" avail-frameworks[%d] name : %s", idx, name);
546 if (idx < AVAIL_FRAMEWORKS_MAX)
547 info->avail_frameworks[idx] = name;
549 pa_log_error(" avail-frameworks, out of range, [%d]", idx);
551 info->num_of_frameworks = pa_idxset_size(s->idx_avail_frameworks);
553 /* set variables for error */
555 info->num_of_in_devices = info->num_of_out_devices = info->num_of_frameworks = 1;
556 info->volume_types[0] = info->volume_types[1] = dbus_str_none;
557 info->avail_in_devices[0] = dbus_str_none;
558 info->avail_out_devices[0] = dbus_str_none;
559 info->avail_frameworks[0] = dbus_str_none;
560 pa_log_error("could not find the stream_role : %s", stream_role);
564 pa_log_error("stream_map is not initialized..");
571 static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, void *userdata) {
572 const char *xml = STREAM_MGR_INTROSPECT_XML;
573 DBusMessage *r = NULL;
579 pa_assert_se(r = dbus_message_new_method_return(msg));
580 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
583 pa_assert_se(dbus_connection_send((conn), r, NULL));
584 dbus_message_unref(r);
587 return DBUS_HANDLER_RESULT_HANDLED;
590 static void handle_get_stream_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
592 DBusMessage *reply = NULL;
593 DBusMessageIter msg_iter;
594 pa_stream_manager *m = (pa_stream_manager*)userdata;
600 pa_assert_se(dbus_message_get_args(msg, NULL,
602 pa_log_info("handle_get_stream_list() dbus method is called");
604 memset(&list, 0, sizeof(stream_list));
605 pa_assert_se((reply = dbus_message_new_method_return(msg)));
606 dbus_message_iter_init_append(reply, &msg_iter);
607 if (!get_available_streams(m, &list)) {
608 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &list.types, list.num_of_streams);
609 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_INT32, &list.priorities, list.num_of_streams);
611 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, NULL, 0);
612 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_INT32, NULL, 0);
614 pa_assert_se(dbus_connection_send(conn, reply, NULL));
615 dbus_message_unref(reply);
618 static void handle_get_stream_info(DBusConnection *conn, DBusMessage *msg, void *userdata) {
620 stream_info_per_type info;
621 DBusMessage *reply = NULL;
622 DBusMessageIter msg_iter;
623 pa_stream_manager *m = (pa_stream_manager*)userdata;
629 pa_assert_se(dbus_message_get_args(msg, NULL,
630 DBUS_TYPE_STRING, &type,
632 pa_log_info("handle_get_stream_info(), type[%s]", type);
634 memset(&info, 0, sizeof(stream_info_per_type));
635 pa_assert_se((reply = dbus_message_new_method_return(msg)));
636 dbus_message_iter_init_append(reply, &msg_iter);
637 get_stream_info(m, type, &info);
638 pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_INT32, &info.priority);
639 pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_INT32, &info.route_type);
640 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &info.volume_types, STREAM_DIRECTION_MAX);
641 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &info.avail_in_devices, info.num_of_in_devices);
642 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &info.avail_out_devices, info.num_of_out_devices);
643 pa_dbus_append_basic_array_variant(&msg_iter, DBUS_TYPE_STRING, &info.avail_frameworks, info.num_of_frameworks);
645 pa_assert_se(dbus_connection_send(conn, reply, NULL));
646 dbus_message_unref(reply);
649 static void handle_set_stream_route_devices(DBusConnection *conn, DBusMessage *msg, void *userdata) {
652 uint32_t *in_device_list = NULL;
653 uint32_t *out_device_list = NULL;
655 int list_len_out = 0;
657 uint32_t *device_id = NULL;
659 stream_parent *sp = NULL;
660 const char *route_type_str = NULL;
661 stream_route_type_t route_type;
662 DBusMessage *reply = NULL;
663 pa_stream_manager *m = (pa_stream_manager*)userdata;
669 pa_assert_se(dbus_message_get_args(msg, NULL,
670 DBUS_TYPE_UINT32, &id,
671 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &in_device_list, &list_len_in,
672 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &out_device_list, &list_len_out,
674 pa_log_info("handle_set_stream_route_devices(), id[%u], in_device_list[%p]:length[%d], out_device_list[%p]:length[%d]",
675 id, in_device_list, list_len_in, out_device_list, list_len_out);
677 pa_assert_se((reply = dbus_message_new_method_return(msg)));
679 sp = pa_hashmap_get(m->stream_parents, (const void*)id);
681 if (!in_device_list && !out_device_list) {
682 pa_log_error("invalid arguments");
686 if (sp->idx_route_in_devices) {
687 PA_IDXSET_FOREACH(device_id, sp->idx_route_in_devices, idx) {
688 pa_idxset_remove_by_data(sp->idx_route_in_devices, device_id, NULL);
691 if (in_device_list && list_len_in) {
692 for (i = 0; i < list_len_in; i++) {
693 pa_idxset_put(sp->idx_route_in_devices, pa_xmemdup(&in_device_list[i], sizeof(uint32_t)), NULL);
694 pa_log_debug(" -- [in] device id:%u", in_device_list[i]);
697 PA_IDXSET_FOREACH(stream, sp->idx_source_outputs, idx) {
698 /* find route type of stream */
699 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
700 if (route_type_str) {
701 pa_log_debug(" -- the route type of source_output that belongs to this parent id[%u] is [%s]", id, route_type_str);
705 /* if any stream that belongs to this id has been activated, do notify right away */
706 if (IS_ROUTE_TYPE_FOR_EXTERNAL_DEV(route_type_str, route_type)) {
707 PA_IDXSET_FOREACH(stream, sp->idx_source_outputs, idx) {
708 pa_log_debug(" -- source_output[%p] belongs to this parent id[%u], do notify for the select proper source", stream, id);
709 do_notify(m, NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT, STREAM_SOURCE_OUTPUT, false, stream);
711 } else if (m->cur_highest_priority.source_output) {
712 if (pa_idxset_get_by_data(sp->idx_source_outputs, m->cur_highest_priority.source_output, NULL)) {
713 pa_log_debug(" -- cur_highest_priority.source_output->index[%u] belongs to this parent id[%u], do notify for the route change",
714 (m->cur_highest_priority.source_output)->index, id);
715 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SOURCE_OUTPUT, false, m->cur_highest_priority.source_output);
719 pa_log_error("failed to update, idx_route_in_devices[%p]", sp->idx_route_in_devices);
723 if (sp->idx_route_out_devices) {
724 PA_IDXSET_FOREACH(device_id, sp->idx_route_out_devices, idx) {
725 pa_idxset_remove_by_data(sp->idx_route_out_devices, device_id, NULL);
728 if (out_device_list && list_len_out) {
729 for (i = 0; i < list_len_out; i++) {
730 pa_idxset_put(sp->idx_route_out_devices, pa_xmemdup(&out_device_list[i], sizeof(uint32_t)), NULL);
731 pa_log_debug(" -- [out] device id:%u", out_device_list[i]);
734 PA_IDXSET_FOREACH(stream, sp->idx_sink_inputs, idx) {
735 /* find route type of stream */
736 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
737 if (route_type_str) {
738 pa_log_debug(" -- the route type of sink_input that belongs to this parent id[%u] is [%s]", id, route_type_str);
742 /* if any stream that belongs to this id has been activated, do notify right away */
743 if (IS_ROUTE_TYPE_FOR_EXTERNAL_DEV(route_type_str, route_type)) {
744 PA_IDXSET_FOREACH(stream, sp->idx_sink_inputs, idx) {
745 pa_log_debug(" -- sink_input[%p] belongs to this parent id[%u], do notify for the select proper sink", stream, id);
746 do_notify(m, NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT, STREAM_SINK_INPUT, false, stream);
748 } else if (m->cur_highest_priority.sink_input) {
749 if (pa_idxset_get_by_data(sp->idx_sink_inputs, m->cur_highest_priority.sink_input, NULL)) {
750 pa_log_debug(" -- cur_highest_priority.sink_input->index[%u] belongs to this parent id[%u], do notify for the route change",
751 (m->cur_highest_priority.sink_input)->index, id);
752 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, m->cur_highest_priority.sink_input);
756 pa_log_error("failed to update, idx_route_out_devices[%p]", sp->idx_route_out_devices);
759 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
761 pa_log_error("could not find matching client for this parent_id[%u]", id);
765 pa_assert_se(dbus_connection_send(conn, reply, NULL));
766 dbus_message_unref(reply);
769 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
770 pa_assert_se(dbus_connection_send(conn, reply, NULL));
771 dbus_message_unref(reply);
774 static void handle_set_stream_route_option(DBusConnection *conn, DBusMessage *msg, void *userdata) {
776 const char *name = NULL;
778 bool updated = false;
779 stream_parent *sp = NULL;
780 stream_route_option route_option;
781 DBusMessage *reply = NULL;
782 pa_stream_manager *m = (pa_stream_manager*)userdata;
788 pa_assert_se(dbus_message_get_args(msg, NULL,
789 DBUS_TYPE_UINT32, &id,
790 DBUS_TYPE_STRING, &name,
791 DBUS_TYPE_INT32, &value,
793 pa_log_info("handle_set_stream_route_option(), name[%s], value[%d]", name, value);
795 pa_assert_se((reply = dbus_message_new_method_return(msg)));
797 sp = pa_hashmap_get(m->stream_parents, (const void*)id);
800 route_option.name = name;
801 route_option.value = value;
803 /* if any stream that belongs to this id has been activated, do notify right away */
804 if (m->cur_highest_priority.sink_input) {
805 if (pa_idxset_get_by_data(sp->idx_sink_inputs, m->cur_highest_priority.sink_input, NULL)) {
806 pa_log_debug(" -- cur_highest_priority.sink_input->index[%u] belongs to this parent id[%u], do notify for the options",
807 (m->cur_highest_priority.sink_input)->index, id);
808 do_notify(m, NOTIFY_COMMAND_UPDATE_ROUTE_OPTION, STREAM_SINK_INPUT, false, &route_option);
812 if (m->cur_highest_priority.source_output) {
813 if (pa_idxset_get_by_data(sp->idx_source_outputs, m->cur_highest_priority.source_output, NULL)) {
814 pa_log_debug(" -- cur_highest_priority.source_output->index[%u] belongs to this parent id[%u], do notify for the options",
815 (m->cur_highest_priority.source_output)->index, id);
816 do_notify(m, NOTIFY_COMMAND_UPDATE_ROUTE_OPTION, STREAM_SOURCE_OUTPUT, false, &route_option);
821 pa_log_error("invalid state");
822 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR_NO_STREAM], DBUS_TYPE_INVALID));
824 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
826 pa_log_error("invalid arguments");
827 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
831 pa_log_error("could not find matching client for this parent_id[%u]", id);
832 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
835 pa_assert_se(dbus_connection_send(conn, reply, NULL));
836 dbus_message_unref(reply);
839 static void handle_set_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata) {
840 const char *direction = NULL;
841 const char *type = NULL;
843 stream_type_t stream_type = STREAM_SINK_INPUT;
844 DBusMessage *reply = NULL;
845 pa_stream_manager *m = (pa_stream_manager*)userdata;
852 pa_assert_se(dbus_message_get_args(msg, NULL,
853 DBUS_TYPE_STRING, &direction,
854 DBUS_TYPE_STRING, &type,
855 DBUS_TYPE_UINT32, &level,
857 pa_log_info("handle_set_volume_level(), direction[%s], type[%s], level[%u]", direction, type, level);
859 pa_assert_se((reply = dbus_message_new_method_return(msg)));
861 if (pa_streq(direction, "in"))
862 stream_type = STREAM_SOURCE_OUTPUT;
863 else if (pa_streq(direction, "out"))
864 stream_type = STREAM_SINK_INPUT;
866 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
871 if ((ret = set_volume_level_by_type(m, stream_type, type, level)))
872 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
874 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
877 pa_assert_se(dbus_connection_send(conn, reply, NULL));
878 dbus_message_unref(reply);
881 send_volume_changed_signal(conn, direction, type, level);
884 static void handle_get_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata) {
885 const char *direction = NULL;
886 const char *type = NULL;
888 stream_type_t stream_type = STREAM_SINK_INPUT;
889 DBusMessage *reply = NULL;
890 pa_stream_manager *m = (pa_stream_manager*)userdata;
896 pa_assert_se(dbus_message_get_args(msg, NULL,
897 DBUS_TYPE_STRING, &direction,
898 DBUS_TYPE_STRING, &type,
900 pa_log_info("handle_get_volume_level(), direction(%s), type(%s)", direction, type);
902 pa_assert_se((reply = dbus_message_new_method_return(msg)));
904 if (pa_streq(direction, "in"))
905 stream_type = STREAM_SOURCE_OUTPUT;
906 else if (pa_streq(direction, "out"))
907 stream_type = STREAM_SINK_INPUT;
909 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, 0, DBUS_TYPE_INVALID));
910 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
914 if (get_volume_level_by_type(m, GET_VOLUME_CURRENT_LEVEL, stream_type, type, &level)) {
915 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, 0, DBUS_TYPE_INVALID));
916 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
918 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, &level, DBUS_TYPE_INVALID));
919 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
923 pa_assert_se(dbus_connection_send(conn, reply, NULL));
924 dbus_message_unref(reply);
927 static void handle_get_volume_max_level(DBusConnection *conn, DBusMessage *msg, void *userdata) {
928 const char *direction = NULL;
929 const char *type = NULL;
931 stream_type_t stream_type = STREAM_SINK_INPUT;
932 DBusMessage *reply = NULL;
933 pa_stream_manager *m = (pa_stream_manager*)userdata;
939 pa_assert_se(dbus_message_get_args(msg, NULL,
940 DBUS_TYPE_STRING, &direction,
941 DBUS_TYPE_STRING, &type,
943 pa_log_info("handle_get_volume_max_level(), direction[%s], type[%s]", direction, type);
945 pa_assert_se((reply = dbus_message_new_method_return(msg)));
947 if (pa_streq(direction, "in"))
948 stream_type = STREAM_SOURCE_OUTPUT;
949 else if (pa_streq(direction, "out"))
950 stream_type = STREAM_SINK_INPUT;
952 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, 0, DBUS_TYPE_INVALID));
953 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
957 if (get_volume_level_by_type(m, GET_VOLUME_MAX_LEVEL, stream_type, type, &level)) {
958 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, 0, DBUS_TYPE_INVALID));
959 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
961 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, &level, DBUS_TYPE_INVALID));
962 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
965 pa_assert_se(dbus_connection_send(conn, reply, NULL));
966 dbus_message_unref(reply);
969 static void handle_set_volume_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
970 const char *direction = NULL;
971 const char *type = NULL;
972 uint32_t do_mute = 0;
973 stream_type_t stream_type = STREAM_SINK_INPUT;
974 DBusMessage *reply = NULL;
975 pa_stream_manager *m = (pa_stream_manager*)userdata;
981 pa_assert_se(dbus_message_get_args(msg, NULL,
982 DBUS_TYPE_STRING, &direction,
983 DBUS_TYPE_STRING, &type,
984 DBUS_TYPE_UINT32, &do_mute,
986 pa_log_info("handle_set_volume_mute(), direction[%s], type[%s], do_mute[%u]", direction, type, do_mute);
988 pa_assert_se((reply = dbus_message_new_method_return(msg)));
990 if (pa_streq(direction, "in"))
991 stream_type = STREAM_SOURCE_OUTPUT;
992 else if (pa_streq(direction, "out"))
993 stream_type = STREAM_SINK_INPUT;
995 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
999 if (set_volume_mute_by_type(m, stream_type, type, (bool)do_mute))
1000 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
1002 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
1005 pa_assert_se(dbus_connection_send(conn, reply, NULL));
1006 dbus_message_unref(reply);
1009 static void handle_get_volume_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
1010 const char *direction = NULL;
1011 const char *type = NULL;
1012 uint32_t is_muted = 0;
1013 stream_type_t stream_type = STREAM_SINK_INPUT;
1014 DBusMessage *reply = NULL;
1015 pa_stream_manager *m = (pa_stream_manager*)userdata;
1021 pa_assert_se(dbus_message_get_args(msg, NULL,
1022 DBUS_TYPE_STRING, &direction,
1023 DBUS_TYPE_STRING, &type,
1024 DBUS_TYPE_INVALID));
1025 pa_log_info("handle_get_volume_mute(), direction[%s], type[%s]", direction, type);
1027 pa_assert_se((reply = dbus_message_new_method_return(msg)));
1029 if (pa_streq(direction, "in"))
1030 stream_type = STREAM_SOURCE_OUTPUT;
1031 else if (pa_streq(direction, "out"))
1032 stream_type = STREAM_SINK_INPUT;
1034 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, 0, DBUS_TYPE_INVALID));
1035 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
1039 if (get_volume_mute_by_type(m, stream_type, type, (bool*)&is_muted)) {
1040 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, 0, DBUS_TYPE_INVALID));
1041 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
1043 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, &is_muted, DBUS_TYPE_INVALID));
1044 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
1048 pa_assert_se(dbus_connection_send(conn, reply, NULL));
1049 dbus_message_unref(reply);
1052 static void handle_get_current_volume_type(DBusConnection *conn, DBusMessage *msg, void *userdata) {
1053 const char *direction = NULL;
1054 const char *type = NULL;
1056 stream_type_t stream_type = STREAM_SINK_INPUT;
1057 DBusMessage *reply = NULL;
1058 pa_stream_manager *m = (pa_stream_manager*)userdata;
1065 pa_assert_se(dbus_message_get_args(msg, NULL,
1066 DBUS_TYPE_STRING, &direction,
1067 DBUS_TYPE_INVALID));
1068 pa_log_info("handle_get_current_volume_type(), direction[%s]", direction);
1070 pa_assert_se((reply = dbus_message_new_method_return(msg)));
1072 if (pa_streq(direction, "in"))
1073 stream_type = STREAM_SOURCE_OUTPUT;
1074 else if (pa_streq(direction, "out"))
1075 stream_type = STREAM_SINK_INPUT;
1077 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &dbus_str_none, DBUS_TYPE_INVALID));
1078 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
1082 if ((s = (stream_type == STREAM_SINK_INPUT) ? (void*)(m->cur_highest_priority.sink_input) : (void*)(m->cur_highest_priority.source_output)))
1083 type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_TIZEN_VOLUME_TYPE);
1085 if (pa_idxset_size(m->core->sink_inputs)) {
1086 PA_IDXSET_FOREACH(s, m->core->sink_inputs, idx) {
1087 if ((type = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_TIZEN_VOLUME_TYPE)))
1094 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID));
1095 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
1097 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &dbus_str_none, DBUS_TYPE_INVALID));
1098 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR_NO_STREAM], DBUS_TYPE_INVALID));
1102 pa_assert_se(dbus_connection_send(conn, reply, NULL));
1103 dbus_message_unref(reply);
1106 static void handle_update_focus_status(DBusConnection *conn, DBusMessage *msg, void *userdata) {
1110 uint32_t acquired_focus_status = 0;
1111 stream_parent *sp = NULL;
1112 void *stream = NULL;
1113 DBusMessage *reply = NULL;
1114 pa_stream_manager *m = (pa_stream_manager*)userdata;
1115 int prev_status = STREAM_FOCUS_ACQUIRED_NONE;
1121 pa_assert_se(dbus_message_get_args(msg, NULL,
1122 DBUS_TYPE_UINT32, &id,
1123 DBUS_TYPE_UINT32, &acquired_focus_status,
1124 DBUS_TYPE_INVALID));
1125 pa_log_info("handle_update_focus_status(), id[%u], acquired_focus_status[0x%x]", id, acquired_focus_status);
1127 pa_assert_se((reply = dbus_message_new_method_return(msg)));
1129 if ((sp = pa_hashmap_get(m->stream_parents, (const void*)id))) {
1130 if (sp->focus_status != acquired_focus_status) {
1131 /* need to update */
1132 prev_status = sp->focus_status;
1133 sp->focus_status = acquired_focus_status;
1134 if (((prev_status ^ sp->focus_status) & STREAM_FOCUS_ACQUIRED_PLAYBACK) && sp->idx_sink_inputs) {
1135 count = pa_idxset_size(sp->idx_sink_inputs);
1136 PA_IDXSET_FOREACH(stream, sp->idx_sink_inputs, idx) {
1137 pa_proplist_sets(GET_STREAM_PROPLIST(stream, STREAM_SINK_INPUT), PA_PROP_MEDIA_FOCUS_STATUS,
1138 GET_FOCUS_STATUS(sp->focus_status, STREAM_SINK_INPUT) ? STREAM_FOCUS_PLAYBACK : STREAM_FOCUS_NONE);
1140 process_stream(m, stream, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED, false);
1143 if (((prev_status ^ sp->focus_status) & STREAM_FOCUS_ACQUIRED_CAPTURE) && sp->idx_source_outputs) {
1144 count = pa_idxset_size(sp->idx_source_outputs);
1145 PA_IDXSET_FOREACH(stream, sp->idx_source_outputs, idx) {
1146 pa_proplist_sets(GET_STREAM_PROPLIST(stream, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_FOCUS_STATUS,
1147 GET_FOCUS_STATUS(sp->focus_status, STREAM_SOURCE_OUTPUT) ? STREAM_FOCUS_CAPTURE : STREAM_FOCUS_NONE);
1149 process_stream(m, stream, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED, false);
1153 pa_log_debug("same as before, skip updating focus status[0x%x]", acquired_focus_status);
1156 pa_log_error("could not find matching client for this parent_id[%u]", id);
1157 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
1160 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
1162 pa_assert_se(dbus_connection_send(conn, reply, NULL));
1163 dbus_message_unref(reply);
1166 static void handle_update_restriction(DBusConnection *conn, DBusMessage *msg, void *userdata) {
1169 DBusMessage *reply = NULL;
1170 pa_stream_manager *m = (pa_stream_manager*)userdata;
1176 pa_assert_se(dbus_message_get_args(msg, NULL,
1177 DBUS_TYPE_STRING, &name,
1178 DBUS_TYPE_UINT32, &value,
1179 DBUS_TYPE_INVALID));
1180 pa_log_info("handle_update_restriction(), name[%s], value[%u]", name, value);
1182 pa_assert_se((reply = dbus_message_new_method_return(msg)));
1184 if (handle_restrictions(m, name, value) < 0) {
1185 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
1189 pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_OK], DBUS_TYPE_INVALID));
1191 pa_assert_se(dbus_connection_send(conn, reply, NULL));
1192 dbus_message_unref(reply);
1195 static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata) {
1197 pa_stream_manager *m = (pa_stream_manager*)userdata;
1203 for (idx = 0; idx < METHOD_HANDLER_MAX; idx++) {
1204 if (dbus_message_is_method_call(msg, STREAM_MANAGER_INTERFACE, method_handlers[idx].method_name)) {
1205 pa_log_debug("Message signature [%s] (Expected [%s])", dbus_message_get_signature(msg), signature_args_for_in[idx]);
1206 if (pa_streq(dbus_message_get_signature(msg), signature_args_for_in[idx])) {
1207 method_handlers[idx].receive_cb(conn, msg, userdata);
1208 return DBUS_HANDLER_RESULT_HANDLED;
1210 pa_log_warn("Wrong Argument Signature");
1211 pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_SIGNATURE, "Wrong Signature, Expected %s", signature_args_for_in[idx]);
1212 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1217 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1220 static DBusHandlerResult method_handler_for_vt(DBusConnection *c, DBusMessage *m, void *userdata) {
1221 pa_stream_manager *u = (pa_stream_manager*)userdata;
1222 const char *path, *interface, *member;
1228 path = dbus_message_get_path(m);
1229 interface = dbus_message_get_interface(m);
1230 member = dbus_message_get_member(m);
1232 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
1234 if (!pa_streq(path, STREAM_MANAGER_OBJECT_PATH))
1235 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1237 if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1238 return handle_introspect(c, m, u);
1240 return handle_methods(c, m, u);
1243 return DBUS_HANDLER_RESULT_HANDLED;
1246 static void send_volume_changed_signal(DBusConnection *conn, const char *direction, const char *volume_type, const uint32_t volume_level) {
1247 DBusMessage *signal_msg;
1248 DBusMessageIter msg_iter;
1251 pa_assert(direction);
1252 pa_assert(volume_type);
1254 pa_log_debug("Send volume changed signal: direction(%s), type(%s), level(%d)", direction, volume_type, volume_level);
1256 pa_assert_se(signal_msg = dbus_message_new_signal(STREAM_MANAGER_OBJECT_PATH, STREAM_MANAGER_INTERFACE, STREAM_MANAGER_SIGNAL_NAME_VOLUME_CHANGED));
1257 dbus_message_iter_init_append(signal_msg, &msg_iter);
1259 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &direction);
1260 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &volume_type);
1261 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_UINT32, &volume_level);
1263 pa_assert_se(dbus_connection_send(conn, signal_msg, NULL));
1264 dbus_message_unref(signal_msg);
1268 static void send_command_signal(DBusConnection *conn, const char *name, int value) {
1269 DBusMessage *signal_msg;
1270 DBusMessageIter msg_iter;
1275 pa_log_debug("Send command signal: name(%s), value(%d)", name, value);
1277 pa_assert_se(signal_msg = dbus_message_new_signal(STREAM_MANAGER_OBJECT_PATH, STREAM_MANAGER_INTERFACE, STREAM_MANAGER_SIGNAL_NAME_COMMAND));
1278 dbus_message_iter_init_append(signal_msg, &msg_iter);
1280 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name);
1281 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &value);
1283 pa_assert_se(dbus_connection_send(conn, signal_msg, NULL));
1284 dbus_message_unref(signal_msg);
1288 static int32_t convert_route_type(stream_route_type_t *route_type, const char *route_type_string) {
1291 pa_assert(route_type);
1292 pa_assert(route_type_string);
1294 if (pa_streq("auto", route_type_string))
1295 *route_type = STREAM_ROUTE_TYPE_AUTO;
1296 else if (pa_streq("auto-last-connected", route_type_string))
1297 *route_type = STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED;
1298 else if (pa_streq("auto-all", route_type_string))
1299 *route_type = STREAM_ROUTE_TYPE_AUTO_ALL;
1300 else if (pa_streq("manual", route_type_string))
1301 *route_type = STREAM_ROUTE_TYPE_MANUAL;
1302 else if (pa_streq("manual-ext", route_type_string))
1303 *route_type = STREAM_ROUTE_TYPE_MANUAL_EXT;
1306 pa_log_error("Not supported route_type(%s)", route_type_string);
1312 static int32_t get_route_type(void *stream, stream_type_t stream_type, bool is_new_data, stream_route_type_t *stream_route_type) {
1313 const char *route_type_str = NULL;
1316 pa_assert(stream_route_type);
1319 route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, stream_type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
1321 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, stream_type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
1322 if (!route_type_str) {
1323 pa_log_warn("could not get route type from the stream(%p)", stream);
1327 if (pa_atoi(route_type_str, (int32_t*)stream_route_type)) {
1328 pa_log_error("could not convert route_type_str(%s) to int", route_type_str);
1335 static void dump_stream_map(pa_stream_manager *m) {
1336 stream_info *s = NULL;
1337 const char *role = NULL;
1342 pa_log_debug("==========[START stream-map dump]==========");
1343 while (m->stream_infos && (s = pa_hashmap_iterate(m->stream_infos, &state, (const void **)&role))) {
1344 pa_log_debug("[role : %s]", role);
1345 pa_log_debug(" - priority : %d", s->priority);
1346 pa_log_debug(" - route-type : %d (0:auto,1:auto-last-conn,2:auto-all,3:manual,4:manual-ext)", s->route_type);
1347 pa_log_debug(" - volume-types : in[%s], out[%s]", s->volume_types[STREAM_DIRECTION_IN], s->volume_types[STREAM_DIRECTION_OUT]);
1348 pa_log_debug(" - avail-in-devices");
1349 PA_IDXSET_FOREACH(name, s->idx_avail_in_devices, idx)
1350 pa_log_debug(" name[%d] : %s", idx, name);
1351 pa_log_debug(" - avail-out-devices");
1352 PA_IDXSET_FOREACH(name, s->idx_avail_out_devices, idx)
1353 pa_log_debug(" name[%d] : %s", idx, name);
1354 pa_log_debug(" - avail-frameworks");
1355 PA_IDXSET_FOREACH(name, s->idx_avail_frameworks, idx)
1356 pa_log_debug(" name[%d] : %s", idx, name);
1358 pa_log_debug("===========[END stream-map dump]===========");
1361 static int init_stream_map(pa_stream_manager *m) {
1366 json_object *array_o;
1367 json_object *array_item_o;
1368 json_object *item_o;
1369 json_object *sub_array_o;
1370 int array_length = 0;
1371 int sub_array_length = 0;
1372 const char *type = NULL;
1373 const char *role = NULL;
1375 json_object *out_device_o;
1376 json_object *in_device_o;
1377 json_object *framework_o;
1382 o = json_object_from_file(STREAM_MAP_FILE);
1384 pa_log_error("Read stream-map file(%s) failed", STREAM_MAP_FILE);
1389 m->latency_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1390 if (json_object_object_get_ex(o, STREAM_MAP_LATENCIES, &array_o) && json_object_is_type(array_o, json_type_array)) {
1391 array_length = json_object_array_length(array_o);
1392 for (i = 0; i < array_length; i++) {
1393 if ((array_item_o = json_object_array_get_idx(array_o, i)) && json_object_is_type(array_item_o, json_type_object)) {
1394 l = pa_xmalloc0(sizeof(latency_info));
1395 pa_log_debug("latency found [%d]", i);
1396 if (json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_TYPE, &item_o) && json_object_is_type(item_o, json_type_string)) {
1397 type = json_object_get_string(item_o);
1398 pa_log_debug(" - type : %s", type);
1400 pa_log_error("Get type failed");
1403 if (json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_FRAGSIZE_MS, &item_o) && json_object_is_type(item_o, json_type_int)) {
1404 l->fragsize_ms = json_object_get_int(item_o);
1405 pa_log_debug(" - fragsize-ms : %d", l->fragsize_ms);
1407 pa_log_error("Get fragsize-ms failed");
1410 if (json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_TLENGTH_MS, &item_o) && json_object_is_type(item_o, json_type_int)) {
1411 l->tlength_ms = json_object_get_int(item_o);
1412 pa_log_debug(" - tlength-ms : %d", l->tlength_ms);
1414 pa_log_error("Get tlength-ms failed");
1417 if (json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_MINREQ_MS, &item_o) && json_object_is_type(item_o, json_type_int)) {
1418 l->minreq_ms = json_object_get_int(item_o);
1419 pa_log_debug(" - minreq-ms : %d", l->minreq_ms);
1421 pa_log_error("Get minreq-ms failed");
1424 if (json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_PREBUF_MS, &item_o) && json_object_is_type(item_o, json_type_int)) {
1425 l->prebuf_ms = json_object_get_int(item_o);
1426 pa_log_debug(" - prebuf-ms : %d", l->prebuf_ms);
1428 pa_log_error("Get prebuf-ms failed");
1431 if (json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_MAXLENGTH, &item_o) && json_object_is_type(item_o, json_type_int)) {
1432 l->maxlength = json_object_get_int(item_o);
1433 pa_log_debug(" - maxlength : %d", l->maxlength);
1435 pa_log_error("Get maxlength failed");
1438 pa_hashmap_put(m->latency_infos, (void*)type, l);
1444 m->volume_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1445 if (json_object_object_get_ex(o, STREAM_MAP_VOLUMES, &array_o) && json_object_is_type(array_o, json_type_array)) {
1446 array_length = json_object_array_length(array_o);
1447 for (i = 0; i < array_length; i++) {
1448 if ((array_item_o = json_object_array_get_idx(array_o, i)) && json_object_is_type(array_item_o, json_type_object)) {
1449 v = pa_xmalloc0(sizeof(volume_info));
1450 pa_log_debug("volume found [%d]", i);
1451 if (json_object_object_get_ex(array_item_o, STREAM_MAP_VOLUME_TYPE, &item_o) && json_object_is_type(item_o, json_type_string)) {
1452 type = json_object_get_string(item_o);
1453 pa_log_debug(" - type : %s", type);
1455 pa_log_error("Get volume type failed");
1458 if (json_object_object_get_ex(array_item_o, STREAM_MAP_VOLUME_IS_FOR_HAL, &item_o) && json_object_is_type(item_o, json_type_int)) {
1459 v->is_hal_volume_type = (bool)json_object_get_int(item_o);
1460 pa_log_debug(" - is-hal-volume : %d", v->is_hal_volume_type);
1462 pa_log_error("Get is-hal-volume failed");
1465 pa_hashmap_put(m->volume_infos, (void*)type, v);
1471 m->stream_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1472 if (json_object_object_get_ex(o, STREAM_MAP_STREAMS, &array_o) && json_object_is_type(array_o, json_type_array)) {
1473 array_length = json_object_array_length(array_o);
1474 for (i = 0; i < array_length; i++) {
1476 if ((array_item_o = json_object_array_get_idx(array_o, i)) && json_object_is_type(array_item_o, json_type_object)) {
1477 s = pa_xmalloc0(sizeof(stream_info));
1478 pa_log_debug("stream found [%d]", i);
1479 if (json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_ROLE, &item_o) && json_object_is_type(item_o, json_type_string)) {
1480 role = json_object_get_string(item_o);
1481 pa_log_debug(" - role : %s", role);
1483 pa_log_error("Get stream role failed");
1486 if (json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_PRIORITY, &item_o) && json_object_is_type(item_o, json_type_int)) {
1487 s->priority = json_object_get_int(item_o);
1488 pa_log_debug(" - priority : %d", s->priority);
1490 pa_log_error("Get stream priority failed");
1493 if (json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_ROUTE_TYPE, &item_o) && json_object_is_type(item_o, json_type_string)) {
1494 if (convert_route_type(&(s->route_type), json_object_get_string(item_o))) {
1495 pa_log_error("convert stream route-type failed");
1498 pa_log_debug(" - route-type : %d", s->route_type);
1500 pa_log_error("Get stream route-type failed");
1503 if (json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_VOLUME_TYPES, &sub_array_o) && json_object_is_type(sub_array_o, json_type_object)) {
1504 if (json_object_object_get_ex(sub_array_o, STREAM_MAP_STREAM_VOLUME_TYPE_IN, &item_o) && json_object_is_type(item_o, json_type_string))
1505 s->volume_types[STREAM_DIRECTION_IN] = json_object_get_string(item_o);
1507 pa_log_error("Get stream volume-type-in failed");
1510 if (json_object_object_get_ex(sub_array_o, STREAM_MAP_STREAM_VOLUME_TYPE_OUT, &item_o) && json_object_is_type(item_o, json_type_string))
1511 s->volume_types[STREAM_DIRECTION_OUT] = json_object_get_string(item_o);
1513 pa_log_error("Get stream volume-type-out failed");
1516 pa_log_debug(" - volume-types : in[%s], out[%s]", s->volume_types[STREAM_DIRECTION_IN], s->volume_types[STREAM_DIRECTION_OUT]);
1518 pa_log_error("Get stream volume-types failed");
1521 if (json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_AVAIL_IN_DEVICES, &sub_array_o) && json_object_is_type(sub_array_o, json_type_array)) {
1523 s->idx_avail_in_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1524 sub_array_length = json_object_array_length(sub_array_o);
1525 pa_log_debug(" - avail-in-devices");
1526 for (j = 0; j < sub_array_length; j++) {
1527 if ((in_device_o = json_object_array_get_idx(sub_array_o, j)) && json_object_is_type(in_device_o, json_type_string)) {
1528 pa_idxset_put(s->idx_avail_in_devices, (void*)json_object_get_string(in_device_o), NULL);
1529 pa_log_debug(" device[%d] : %s", j, json_object_get_string(in_device_o));
1533 pa_log_error("Get stream avail-in-devices failed");
1536 if (json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_AVAIL_OUT_DEVICES, &sub_array_o) && json_object_is_type(sub_array_o, json_type_array)) {
1538 s->idx_avail_out_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1539 sub_array_length = json_object_array_length(sub_array_o);
1540 pa_log_debug(" - avail-out-devices");
1541 for (j = 0; j < sub_array_length; j++) {
1542 if ((out_device_o = json_object_array_get_idx(sub_array_o, j)) && json_object_is_type(out_device_o, json_type_string)) {
1543 pa_idxset_put(s->idx_avail_out_devices, (void*)json_object_get_string(out_device_o), NULL);
1544 pa_log_debug(" device[%d] : %s", j, json_object_get_string(out_device_o));
1548 pa_log_error("Get stream avail-out-devices failed");
1551 if (json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_AVAIL_FRAMEWORKS, &sub_array_o) && json_object_is_type(sub_array_o, json_type_array)) {
1553 s->idx_avail_frameworks = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1554 sub_array_length = json_object_array_length(sub_array_o);
1555 pa_log_debug(" - avail-frameworks");
1556 for (j = 0; j < sub_array_length; j++) {
1557 if ((framework_o = json_object_array_get_idx(sub_array_o, j)) && json_object_is_type(framework_o, json_type_string)) {
1558 pa_idxset_put(s->idx_avail_frameworks, (void*)json_object_get_string(framework_o), NULL);
1559 pa_log_debug(" framework[%d] : %s", j, json_object_get_string(framework_o));
1563 pa_log_error("Get stream avail-frameworks failed");
1566 pa_hashmap_put(m->stream_infos, (void*)role, s);
1570 pa_log_error("Get streams object failed");
1578 pa_log_error("failed to initialize stream-map");
1579 if (m->stream_infos) {
1580 PA_HASHMAP_FOREACH(s, m->stream_infos, state) {
1581 if (s->idx_avail_in_devices)
1582 pa_idxset_free(s->idx_avail_in_devices, NULL);
1583 if (s->idx_avail_out_devices)
1584 pa_idxset_free(s->idx_avail_out_devices, NULL);
1585 if (s->idx_avail_frameworks)
1586 pa_idxset_free(s->idx_avail_frameworks, NULL);
1589 pa_hashmap_free(m->stream_infos);
1591 if (m->volume_infos) {
1592 PA_HASHMAP_FOREACH(v, m->volume_infos, state) {
1595 pa_hashmap_free(m->volume_infos);
1597 if (m->latency_infos) {
1598 PA_HASHMAP_FOREACH(l, m->latency_infos, state) {
1601 pa_hashmap_free(m->latency_infos);
1606 static void deinit_stream_map(pa_stream_manager *m) {
1607 stream_info *s = NULL;
1608 volume_info *v = NULL;
1609 latency_info *l = NULL;
1614 if (m->stream_infos) {
1615 PA_HASHMAP_FOREACH(s, m->stream_infos, state) {
1616 if (s->idx_avail_in_devices)
1617 pa_idxset_free(s->idx_avail_in_devices, NULL);
1618 if (s->idx_avail_out_devices)
1619 pa_idxset_free(s->idx_avail_out_devices, NULL);
1620 if (s->idx_avail_frameworks)
1621 pa_idxset_free(s->idx_avail_frameworks, NULL);
1624 pa_hashmap_free(m->stream_infos);
1626 if (m->volume_infos) {
1627 PA_HASHMAP_FOREACH(v, m->volume_infos, state) {
1630 pa_hashmap_free(m->volume_infos);
1632 if (m->latency_infos) {
1633 PA_HASHMAP_FOREACH(l, m->latency_infos, state) {
1636 pa_hashmap_free(m->latency_infos);
1640 static bool check_name_to_skip(pa_stream_manager *m, process_command_type_t command, void *stream, stream_type_t type, bool is_new_data) {
1642 const char *name = NULL;
1643 const char *role = NULL;
1649 if (command == PROCESS_COMMAND_PREPARE && is_new_data) {
1650 if ((name = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_NAME))) {
1651 for (i = 0; i < NAME_FOR_SKIP_MAX; i++)
1652 if (pa_streq(name, stream_manager_media_names_for_skip[i])) {
1654 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE, SKIP_ROLE);
1657 pa_log_info("name is [%s], skip(%d) command(%s)", name, ret, process_command_type_str[command]);
1661 role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
1663 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
1665 if (role && pa_streq(role, SKIP_ROLE))
1672 static bool check_role_to_skip(pa_stream_manager *m, process_command_type_t command, const char *role) {
1674 stream_info *s = NULL;
1677 pa_assert(m->stream_infos);
1680 if ((s = pa_hashmap_get(m->stream_infos, role)))
1682 if (s && pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING) &&
1683 (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED ||
1684 command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED))
1687 pa_log_debug("role is [%s], skip(%d)", role, ret);
1692 static bool check_route_type_to_skip(process_command_type_t command, const char *route_type_str) {
1694 stream_route_type_t route_type;
1696 pa_assert(route_type_str);
1698 if (!pa_atoi(route_type_str, (int32_t*)&route_type)) {
1699 if ((route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) &&
1700 (command > PROCESS_COMMAND_PREPARE && command < PROCESS_COMMAND_UPDATE_VOLUME))
1701 ret = true; /* this route route is for external device, need to skip */
1703 pa_log_error("could not convert route_type_str(%s) to int", route_type_str);
1706 pa_log_debug("command is [%s], skip(%d) for route_type(%d)", process_command_type_str[command], ret, route_type);
1711 static bool check_name_is_vstream(void *stream, stream_type_t type, bool is_new_data) {
1713 const char *name = NULL;
1718 name = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_NAME);
1720 name = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_NAME);
1722 if (pa_streq(name, VIRTUAL_STREAM_NAME)) {
1724 pa_log_info("name is [%s]", name);
1731 static bool update_priority_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, const char *role, bool is_new_data) {
1733 stream_info *s = NULL;
1737 pa_assert(m->stream_infos);
1740 if ((s = pa_hashmap_get(m->stream_infos, role))) {
1742 pa_proplist_set(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void*)&(s->priority), sizeof(s->priority));
1744 pa_proplist_set(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void*)&(s->priority), sizeof(s->priority));
1751 static bool update_route_type_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, const char *role) {
1753 stream_route_type_t route_type = STREAM_ROUTE_TYPE_AUTO;
1754 stream_info *s = NULL;
1758 pa_assert(m->stream_infos);
1761 if ((s = pa_hashmap_get(m->stream_infos, role))) {
1762 route_type = s->route_type;
1763 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE, "%d", route_type);
1770 static bool update_volume_type_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, const char *role) {
1772 const char *volume_type = NULL;
1773 stream_info *s = NULL;
1777 pa_assert(m->stream_infos);
1780 if ((s = pa_hashmap_get(m->stream_infos, role)))
1781 volume_type = s->volume_types[type];
1783 if (volume_type && (!pa_streq(volume_type, "none"))) {
1784 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_TIZEN_VOLUME_TYPE, volume_type);
1787 pa_log_warn("this stream[%p] does not have any volume type, skip updating volume type. stream_type[%d], role[%s]", stream, type, role);
1792 static bool update_focus_status_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, bool is_new_data) {
1794 uint32_t parent_idx = 0;
1795 stream_parent *sp = NULL;
1801 p_idx = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1803 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1804 if (p_idx && !pa_atou(p_idx, &parent_idx)) {
1805 sp = pa_hashmap_get(m->stream_parents, (const void*)parent_idx);
1808 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_FOCUS_STATUS, "%u", sp->focus_status);
1810 pa_proplist_setf(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_FOCUS_STATUS, "%u", sp->focus_status);
1811 pa_log_debug("p_idx(%s), idx(%u), focus_status(0x%x, 0x1:playback 0x2:capture 0x3:both)", p_idx, parent_idx, sp->focus_status);
1813 pa_log_error("could not find matching client for this parent_id(%u)", parent_idx);
1821 static bool update_stream_parent_info(pa_stream_manager *m, process_command_type_t command, stream_type_t type, void *stream) {
1823 uint32_t parent_idx;
1824 stream_parent *sp = NULL;
1829 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1830 if (p_idx && !pa_atou(p_idx, &parent_idx)) {
1831 pa_log_debug("p_idx(%s), idx(%u)", p_idx, parent_idx);
1832 sp = pa_hashmap_get(m->stream_parents, (const void*)parent_idx);
1834 uint32_t idx = (type == STREAM_SINK_INPUT) ? ((pa_sink_input*)stream)->index : ((pa_source_output*)stream)->index;
1835 if (command == PROCESS_COMMAND_ADD_PARENT_ID) {
1836 /* append this stream to the parent stream info. */
1837 pa_log_debug(" - append this stream(%p, %u) to the list. sp(%p), stream_type(%d)", stream, idx, sp, type);
1838 pa_idxset_put(type == STREAM_SINK_INPUT ? (sp->idx_sink_inputs) : (sp->idx_source_outputs), stream, NULL);
1840 } else if (command == PROCESS_COMMAND_REMOVE_PARENT_ID) {
1841 /* remove this stream from the parent stream info. */
1842 pa_log_debug(" - remove this stream(%p, %u) from the list. sp(%p), stream_type(%d)", stream, idx, sp, type);
1843 pa_idxset_remove_by_data(type == STREAM_SINK_INPUT ? (sp->idx_sink_inputs) : (sp->idx_source_outputs), stream, NULL);
1846 pa_log_error("invalid command(%d)", command);
1850 pa_log_error("could not find matching client for this parent_id(%u)", parent_idx);
1859 static bool update_the_highest_priority_stream(pa_stream_manager *m, process_command_type_t command, void *mine,
1860 stream_type_t type, const char *role, bool is_new_data, bool *need_to_update) {
1863 const int32_t *priority = NULL;
1864 const char *route_type_str = NULL;
1865 stream_route_type_t route_type;
1866 const char *focus_status_str = NULL;
1867 const char *active_dev = NULL;
1868 void *cur_max_stream = NULL;
1869 void *cur_max_stream_tmp = NULL;
1870 const int32_t *cur_max_priority = NULL;
1871 const char *cur_max_role = NULL;
1872 int32_t cur_max_focus_status = 0;
1873 int32_t focus_status = 0;
1875 const char *_role = NULL;
1876 pa_idxset *streams = NULL;
1877 pa_sink *sink = NULL;
1878 pa_source *source = NULL;
1879 bool is_vstream = false;
1884 pa_log_error("invalid input, role(%s)", role);
1888 *need_to_update = false;
1890 if (type == STREAM_SINK_INPUT) {
1891 cur_max_stream = m->cur_highest_priority.sink_input;
1892 } else if (type == STREAM_SOURCE_OUTPUT) {
1893 cur_max_stream = m->cur_highest_priority.source_output;
1896 pa_log_debug("update_the_highest_priority_stream(), stream_type(%d), role(%s), command(%d), is_new_data(%d)",
1897 type, role, command, is_new_data);
1898 if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED) {
1899 /* get focus status, route type */
1901 focus_status_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_FOCUS_STATUS);
1902 route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
1904 focus_status_str = pa_proplist_gets(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_FOCUS_STATUS);
1905 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
1907 if (focus_status_str && !pa_atoi(focus_status_str, &focus_status)) {
1908 pa_log_debug("focus status(0x%x) of mine", focus_status);
1910 /* check if this stream is for external device with ROUTE_TYPE_AUTO */
1911 if (IS_ROUTE_TYPE_FOR_AUTO(route_type_str, route_type) && is_new_data) {
1912 if (type == STREAM_SINK_INPUT)
1913 sink = ((pa_sink_input_new_data*)mine)->sink;
1915 source = ((pa_source_output_new_data*)mine)->source;
1916 is_vstream = check_name_is_vstream(mine, type, is_new_data);
1917 if (!is_vstream && ((sink && !(sink->use_internal_codec)) || (source && !(source->use_internal_codec)))) {
1918 pa_log_warn("stream(%p) uses external device, skip it", mine);
1919 *need_to_update = false;
1923 if (cur_max_stream == NULL) {
1924 *need_to_update = true;
1925 pa_log_debug("set cur_highest to mine");
1928 if (pa_proplist_get(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&priority, &size))
1929 pa_log_error("failed to pa_proplist_get() for priority");
1931 if (pa_proplist_get(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&priority, &size))
1932 pa_log_error("failed to pa_proplist_get() for priority");
1934 if (pa_proplist_get(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&cur_max_priority, &size))
1935 pa_log_error("failed to pa_proplist_get() for priority");
1936 focus_status_str = pa_proplist_gets(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_FOCUS_STATUS);
1937 if (focus_status_str && !pa_atoi(focus_status_str, &cur_max_focus_status)) {
1938 pa_log_debug("cur_max_focus status(0x%x)", cur_max_focus_status);
1940 cur_max_role = pa_proplist_gets(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_ROLE);
1941 if (!cur_max_priority || !cur_max_role) {
1942 pa_log_error("failed to pa_proplist_gets() for getting current max priority(%p) and it's role(%s)", cur_max_priority, cur_max_role);
1945 if (priority && cur_max_priority) {
1946 if (GET_FOCUS_STATUS(focus_status, type) ||
1947 (!GET_FOCUS_STATUS(cur_max_focus_status, type) && *priority >= *cur_max_priority)) {
1948 *need_to_update = true;
1949 pa_log_debug("update cur_highest to mine(%s)", role);
1951 /* no need to trigger,
1952 * update active device info if possible */
1953 if ((active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
1955 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, active_dev);
1957 pa_proplist_sets(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, active_dev);
1964 } else if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED ||
1965 command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED) {
1966 if (cur_max_stream == mine || command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED) {
1967 if (type == STREAM_SINK_INPUT) {
1968 streams = m->core->sink_inputs;
1969 } else if (type == STREAM_SOURCE_OUTPUT) {
1970 streams = m->core->source_outputs;
1972 /* find the next highest priority input */
1973 PA_IDXSET_FOREACH(i, streams, idx) {
1976 if (!(_role = pa_proplist_gets(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_ROLE))) {
1977 pa_log_error("failed to pa_proplist_gets() for role");
1980 if (pa_proplist_get(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&priority, &size)) {
1981 pa_log_warn("failed to pa_proplist_get() for priority, skip it");
1984 if (!(route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
1985 pa_log_warn("failed to pa_proplist_get() for route_type, skip it");
1987 } else if (IS_ROUTE_TYPE_FOR_EXTERNAL_DEV(route_type_str, route_type)) {
1988 pa_log_warn("stream(%p) has the route type for external device, skip it", i);
1991 is_vstream = check_name_is_vstream(i, type, false);
1993 (type == STREAM_SINK_INPUT ? !(((pa_sink_input*)i)->sink->use_internal_codec) :
1994 !(((pa_source_output*)i)->source->use_internal_codec))) {
1995 pa_log_warn("stream(%p) uses external audio codec, skip it", i);
1999 if (!(focus_status_str = pa_proplist_gets(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_FOCUS_STATUS))) {
2000 pa_log_warn("failed to pa_proplist_gets() for focus status");
2002 if (!pa_atoi(focus_status_str, &focus_status))
2003 pa_log_debug("focus status(0x%x)", focus_status);
2005 pa_log_debug("role(%s)/priority(%p)/route_type(%d)/focus_status(0x%x)/stream(%p)", _role, priority, route_type, focus_status, i);
2006 if (cur_max_priority == NULL) {
2007 cur_max_priority = priority;
2008 cur_max_focus_status = focus_status;
2009 cur_max_stream_tmp = i;
2010 cur_max_role = _role;
2012 if (cur_max_priority && priority) {
2013 if (GET_FOCUS_STATUS(cur_max_focus_status, type) ||
2014 (!GET_FOCUS_STATUS(focus_status, type) && (*cur_max_priority > *priority))) {
2017 cur_max_priority = priority;
2018 cur_max_focus_status = focus_status;
2019 cur_max_stream_tmp = i;
2020 cur_max_role = _role;
2024 pa_log_debug("updated max priority(%p)/stream(%p)", cur_max_priority, cur_max_stream_tmp);
2025 if (cur_max_stream_tmp) {
2026 if (type == STREAM_SINK_INPUT) {
2027 m->cur_highest_priority.sink_input = cur_max_stream_tmp;
2028 m->cur_highest_priority.role_si = cur_max_role;
2029 } else if (type == STREAM_SOURCE_OUTPUT) {
2030 m->cur_highest_priority.source_output = cur_max_stream_tmp;
2031 m->cur_highest_priority.role_so = cur_max_role;
2034 if (type == STREAM_SINK_INPUT) {
2035 m->cur_highest_priority.sink_input = NULL;
2036 m->cur_highest_priority.role_si = NULL;
2037 } else if (type == STREAM_SOURCE_OUTPUT) {
2038 m->cur_highest_priority.source_output = NULL;
2039 m->cur_highest_priority.role_so = NULL;
2042 *need_to_update = true;
2043 pa_log_info("need to update: type(%d), cur_highest_priority(sink_input=%p[%s]/source_output=%p[%s])",
2044 type, (void*)m->cur_highest_priority.sink_input, m->cur_highest_priority.role_si,
2045 (void*)m->cur_highest_priority.source_output, m->cur_highest_priority.role_so);
2047 /* no need to trigger */
2048 pa_log_info("no need to update: type(%d), cur_highest_priority(sink_input=%p[%s]/source_output=%p[%s])",
2049 type, (void*)m->cur_highest_priority.sink_input, m->cur_highest_priority.role_si,
2050 (void*)m->cur_highest_priority.source_output, m->cur_highest_priority.role_so);
2057 /* Update buffer attributes to new stream */
2058 static void update_buffer_attribute(pa_stream_manager *m, void *new_data, stream_type_t stream_type, bool is_new_data) {
2059 pa_sample_spec *sample_spec;
2060 const char *latency;
2062 int32_t maxlength = -1;
2064 int32_t tlength = -1;
2065 int32_t minreq = -1;
2066 /* Note: pulseaudio recommends to set it to -1, but there's an issue related
2067 * to a stream from gstreamer pulsesink which set this value to 0, so we set
2068 * default value to 0 temporarily. */
2070 int32_t tlength_ms = -1;
2071 int32_t minreq_ms = -1;
2072 int32_t prebuf_ms = -1;
2073 /* recording only */
2074 int32_t fragsize = -1;
2075 int32_t fragsize_ms = -1;
2078 pa_assert(new_data);
2081 pa_log_warn("updating buffer attribute is only for new data, skip it");
2085 if ((latency = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY)))
2086 pa_log_info("audio_latency : %s", latency);
2088 pa_log_warn("failed to get audio_latency");
2092 if (!(li = pa_hashmap_get(m->latency_infos, latency))) {
2093 pa_log_warn("not support this latency type[%s]", latency);
2097 maxlength = li->maxlength;
2098 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_MAXLENGTH, "%d", maxlength);
2100 sample_spec = GET_STREAM_NEW_SAMPLE_SPEC_PTR(new_data, stream_type);
2101 pa_log_info("*** sample_spec: format(%d), rate(%u), channels(%u)",
2102 sample_spec->format, sample_spec->rate, sample_spec->channels);
2104 if (stream_type == STREAM_SINK_INPUT) {
2105 tlength_ms = li->tlength_ms;
2106 minreq_ms = li->minreq_ms;
2107 prebuf_ms = li->prebuf_ms;
2110 minreq = pa_usec_to_bytes(minreq_ms * 1000, sample_spec);
2112 tlength = pa_usec_to_bytes(tlength_ms * 1000, sample_spec);
2114 prebuf = pa_usec_to_bytes(prebuf_ms * 1000, sample_spec);
2116 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_TLENGTH, "%d", tlength);
2117 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_MINREQ, "%d", minreq);
2118 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_PREBUF, "%d", prebuf);
2120 pa_log_info("*** maxlength:%d, tlength:%d, prebuf:%d, minreq:%d", maxlength, tlength, prebuf, minreq);
2122 } else if (stream_type == STREAM_SOURCE_OUTPUT) {
2123 fragsize_ms = li->fragsize_ms;
2125 if (fragsize_ms > 0)
2126 fragsize = pa_usec_to_bytes(fragsize_ms * 1000, sample_spec);
2128 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_FRAGSIZE, "%d", fragsize);
2130 pa_log_info("*** maxlength:%d, fragsize:%d", maxlength, fragsize);
2134 static void fill_device_info_to_hook_data(pa_stream_manager *m, void *hook_data, notify_command_type_t command, stream_type_t type, void *stream, bool is_new_data) {
2135 char *device_none = NULL;
2136 const char *p_idx = NULL;
2137 uint32_t parent_idx = 0;
2138 stream_parent *sp = NULL;
2139 pa_stream_manager_hook_data_for_select *select_data = NULL;
2140 pa_stream_manager_hook_data_for_route *route_data = NULL;
2142 pa_idxset *avail_devices;
2143 uint32_t list_len = 0;
2146 pa_assert(hook_data);
2147 pa_log_debug("fill_device_info_to_hook_data() for %s", notify_command_type_str[command]);
2150 case NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT: {
2151 select_data = (pa_stream_manager_hook_data_for_select*)hook_data;
2152 if ((si = pa_hashmap_get(m->stream_infos, select_data->stream_role))) {
2153 select_data->route_type = si->route_type;
2154 avail_devices = (type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices;
2155 list_len = pa_idxset_size(avail_devices);
2156 device_none = pa_idxset_get_by_data(avail_devices, "none", NULL);
2158 if (list_len == 0 || device_none) {
2159 pa_log_warn(" -- there is no available device, stream_type(%d)", type);
2162 select_data->idx_avail_devices = avail_devices;
2163 select_data->origins_from_new_data = is_new_data;
2164 if (si->route_type >= STREAM_ROUTE_TYPE_MANUAL) {
2166 p_idx = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
2168 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
2169 if (p_idx && !pa_atou(p_idx, &parent_idx)) {
2170 /* find parent idx, it's device info. and it's stream idxs */
2171 sp = pa_hashmap_get(m->stream_parents, (const void*)parent_idx);
2173 select_data->idx_manual_devices = (type == STREAM_SINK_INPUT) ? (sp->idx_route_out_devices) : (sp->idx_route_in_devices);
2175 pa_log_warn(" -- failed to get the stream parent of idx(%u)", parent_idx);
2177 pa_log_debug(" -- could not get the parent id of this stream, but keep going...");
2180 pa_log_error(" -- could not find (%s)", route_data->stream_role);
2184 case NOTIFY_COMMAND_CHANGE_ROUTE_START:
2185 case NOTIFY_COMMAND_CHANGE_ROUTE_END: {
2186 route_data = (pa_stream_manager_hook_data_for_route*)hook_data;
2187 if ((si = pa_hashmap_get(m->stream_infos, route_data->stream_role))) {
2188 avail_devices = (type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices;
2189 route_data->route_type = si->route_type;
2190 list_len = pa_idxset_size(avail_devices);
2191 device_none = pa_idxset_get_by_data(avail_devices, "none", NULL);
2193 if (list_len == 0 || device_none) {
2194 pa_log_warn(" -- there is no available device, stream_type(%d)", type);
2198 p_idx = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
2200 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
2201 if (p_idx && !pa_atou(p_idx, &parent_idx)) {
2202 sp = pa_hashmap_get(m->stream_parents, (const void*)parent_idx);
2204 pa_log_warn(" -- failed to get the stream parent of idx(%u)", parent_idx);
2206 pa_log_warn(" -- could not get the parent id of this stream, but keep going...");
2208 route_data->idx_avail_devices = avail_devices;
2209 if (si->route_type >= STREAM_ROUTE_TYPE_MANUAL) {
2211 route_data->idx_manual_devices = (type == STREAM_SINK_INPUT) ? (sp->idx_route_out_devices) : (sp->idx_route_in_devices);
2212 route_data->idx_streams = (type == STREAM_SINK_INPUT) ? (sp->idx_sink_inputs) : (sp->idx_source_outputs);
2214 pa_log_warn(" -- failed to get the stream parent of idx(%u)", parent_idx);
2217 pa_log_error(" -- could not find (%s)", route_data->stream_role);
2226 static void do_notify(pa_stream_manager *m, notify_command_type_t command, stream_type_t type, bool is_new_data, void *user_data) {
2227 pa_stream_manager_hook_data_for_select hook_call_select_data;
2228 pa_stream_manager_hook_data_for_route hook_call_route_data;
2229 hal_stream_connection_info stream_conn_info;
2230 hal_route_option route_option;
2231 const char *role = NULL;
2233 const char *modifier_gain = NULL;
2236 pa_log_debug("do_notify(%s): type(%d), is_new_data(%d), user_data(%p)", notify_command_type_str[command], type, is_new_data, user_data);
2239 case NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT: {
2240 pa_assert(user_data);
2241 memset(&hook_call_select_data, 0, sizeof(pa_stream_manager_hook_data_for_select));
2242 hook_call_select_data.stream = s = user_data;
2244 hook_call_select_data.stream_type = type;
2245 hook_call_select_data.origins_from_new_data = is_new_data;
2247 hook_call_select_data.stream_role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
2248 fill_device_info_to_hook_data(m, &hook_call_select_data, command, type, s, is_new_data);
2249 hook_call_select_data.sample_spec = GET_STREAM_NEW_SAMPLE_SPEC(s, type);
2250 if (type == STREAM_SINK_INPUT) {
2251 hook_call_select_data.occupying_role = m->cur_highest_priority.role_si;
2252 hook_call_select_data.proper_sink = &(((pa_sink_input_new_data*)s)->sink);
2253 /* need to check modifier_gain, because we do not skip a stream that is from module-sound-player */
2254 modifier_gain = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type), PA_PROP_MEDIA_TIZEN_VOLUME_GAIN_TYPE);
2255 if (((pa_sink_input_new_data*)s)->sink && !modifier_gain) {
2256 pa_log_info(" - sink(%s) has been already selected, skip selecting sink",
2257 (((pa_sink_input_new_data*)s)->sink)->name);
2260 } else if (type == STREAM_SOURCE_OUTPUT) {
2261 hook_call_select_data.occupying_role = m->cur_highest_priority.role_si;
2262 hook_call_select_data.proper_source = &(((pa_source_output_new_data*)s)->source);
2263 if (((pa_source_output_new_data*)s)->source) {
2264 pa_log_info(" - source(%s) has been already selected, skip selecting source",
2265 (((pa_source_output_new_data*)s)->source)->name);
2270 hook_call_select_data.stream_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
2271 fill_device_info_to_hook_data(m, &hook_call_select_data, command, type, s, is_new_data);
2272 hook_call_select_data.sample_spec = GET_STREAM_SAMPLE_SPEC(s, type);
2273 if (type == STREAM_SINK_INPUT)
2274 hook_call_select_data.proper_sink = &(((pa_sink_input*)s)->sink);
2275 else if (type == STREAM_SOURCE_OUTPUT)
2276 hook_call_select_data.proper_source = &(((pa_source_output*)s)->source);
2278 CONVERT_TO_DEVICE_ROLE(hook_call_select_data.stream_role, hook_call_select_data.device_role);
2279 pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE), &hook_call_select_data);
2283 case NOTIFY_COMMAND_CHANGE_ROUTE_START: {
2284 pa_assert(user_data);
2285 memset(&hook_call_route_data, 0, sizeof(pa_stream_manager_hook_data_for_route));
2286 hook_call_route_data.stream = s = user_data;
2289 hook_call_route_data.origins_from_new_data = true;
2290 role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
2291 hook_call_route_data.sample_spec = GET_STREAM_NEW_SAMPLE_SPEC(s, type);
2292 if (type == STREAM_SINK_INPUT) {
2293 hook_call_route_data.proper_sink = &(((pa_sink_input_new_data*)s)->sink);
2294 } else if (type == STREAM_SOURCE_OUTPUT) {
2295 hook_call_route_data.proper_source = &(((pa_source_output_new_data*)s)->source);
2298 role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
2299 hook_call_route_data.sample_spec = GET_STREAM_SAMPLE_SPEC(s, type);
2300 hook_call_route_data.idx_streams = (type == STREAM_SINK_INPUT) ? ((pa_sink_input*)s)->sink->inputs :
2301 ((pa_source_output*)s)->source->outputs;
2303 hook_call_route_data.stream_type = type;
2304 hook_call_route_data.stream_role = role;
2305 CONVERT_TO_DEVICE_ROLE(hook_call_route_data.stream_role, hook_call_route_data.device_role);
2306 fill_device_info_to_hook_data(m, &hook_call_route_data, command, type, s, is_new_data);
2307 if (hook_call_route_data.route_type >= STREAM_ROUTE_TYPE_MANUAL) {
2308 if (hook_call_route_data.idx_manual_devices && !pa_idxset_size(hook_call_route_data.idx_manual_devices)) {
2309 pa_log_warn("no manual device for this type(%d), skip it..", type);
2313 pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE), &hook_call_route_data);
2317 case NOTIFY_COMMAND_CHANGE_ROUTE_END: {
2318 memset(&hook_call_route_data, 0, sizeof(pa_stream_manager_hook_data_for_route));
2319 s = (type == STREAM_SINK_INPUT) ? (void*)(m->cur_highest_priority.sink_input) :
2320 (void*)(m->cur_highest_priority.source_output);
2322 hook_call_route_data.stream = s;
2323 hook_call_route_data.stream_type = type;
2324 hook_call_route_data.stream_role = (type == STREAM_SINK_INPUT) ? (m->cur_highest_priority.role_si) :
2325 (m->cur_highest_priority.role_so);
2326 CONVERT_TO_DEVICE_ROLE(hook_call_route_data.stream_role, hook_call_route_data.device_role);
2327 hook_call_route_data.sample_spec = GET_STREAM_SAMPLE_SPEC(s, type);
2328 hook_call_route_data.idx_streams = (type == STREAM_SINK_INPUT) ? ((pa_sink_input*)s)->sink->inputs :
2329 ((pa_source_output*)s)->source->outputs;
2330 fill_device_info_to_hook_data(m, &hook_call_route_data, command, type, s, is_new_data);
2332 pa_log_debug("no stream for this type(%d), need to unset route", type);
2333 hook_call_route_data.stream = NULL;
2334 hook_call_route_data.stream_type = type;
2336 pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE), &hook_call_route_data);
2339 case NOTIFY_COMMAND_UPDATE_ROUTE_OPTION: {
2340 pa_assert(user_data);
2341 memset(&route_option, 0, sizeof(hal_route_option));
2342 s = (type == STREAM_SINK_INPUT) ? (void*)(m->cur_highest_priority.sink_input) :
2343 (void*)(m->cur_highest_priority.source_output);
2345 route_option.role = (type == STREAM_SINK_INPUT) ? (m->cur_highest_priority.role_si) :
2346 (m->cur_highest_priority.role_so);
2347 route_option.name = ((stream_route_option*)user_data)->name;
2348 route_option.value = ((stream_route_option*)user_data)->value;
2349 pa_hal_interface_update_route_option(m->hal, &route_option);
2353 case NOTIFY_COMMAND_INFORM_STREAM_CONNECTED:
2354 case NOTIFY_COMMAND_INFORM_STREAM_DISCONNECTED: {
2355 pa_assert(user_data);
2356 memset(&stream_conn_info, 0, sizeof(hal_stream_connection_info));
2359 stream_conn_info.role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
2360 stream_conn_info.direction = (type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN;
2361 stream_conn_info.idx = (type == STREAM_SINK_INPUT) ? ((pa_sink_input*)s)->index : ((pa_source_output*)s)->index;
2362 stream_conn_info.is_connected = (command == NOTIFY_COMMAND_INFORM_STREAM_CONNECTED) ? true : false;
2363 pa_hal_interface_notify_stream_connection_changed(m->hal, &stream_conn_info);
2370 static void update_mirroring_streams(pa_stream_manager *m, pa_source_output *o, bool put) {
2376 if ((role = pa_proplist_gets(GET_STREAM_PROPLIST(o, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROLE)) &&
2377 pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING)) {
2379 pa_idxset_put(m->mirroring_streams, o, NULL);
2381 pa_idxset_remove_by_data(m->mirroring_streams, o, NULL);
2385 /* Load/unload forwarding device */
2386 static int update_forwarding_device(pa_stream_manager *m, bool load) {
2390 if (pa_idxset_size(m->mirroring_streams) == 0) {
2391 if (!pa_device_manager_load_forwarding(m->dm)) {
2392 pa_log_error("could not load forwarding device");
2395 pa_log_info("forwarding device is now loaded");
2397 pa_log_debug("forwarding device has been already loaded");
2400 if (pa_idxset_size(m->mirroring_streams) > 0)
2401 pa_log_debug("no need to unload forwarding device");
2403 pa_device_manager_unload_forwarding(m->dm);
2404 pa_log_info("forwarding device is now unloaded");
2411 static process_stream_result_t process_stream(pa_stream_manager *m, void *stream, stream_type_t type, process_command_type_t command, bool is_new_data) {
2412 process_stream_result_t result = PROCESS_STREAM_RESULT_OK;
2413 const char *role = NULL;
2414 const char *route_type_str = NULL;
2415 stream_route_type_t route_type;
2417 bool need_update = false;
2418 int32_t volume_ret = 0;
2419 volume_info *v = NULL;
2420 const char *si_volume_type_str = NULL;
2421 const int32_t *prior_priority = NULL;
2423 pa_format_info *req_format = NULL;
2424 char *format_str = NULL;
2425 const char *rate_str = NULL;
2426 const char *ch_str = NULL;
2428 pa_log_info(">>> process_stream(%s): stream(%p), stream_type(%d), is_new_data(%d)",
2429 process_command_type_str[command], stream, type, is_new_data);
2434 if (check_name_to_skip(m, command, stream, type, is_new_data)) {
2435 result = PROCESS_STREAM_RESULT_SKIP;
2436 /* set it to null sink/source */
2438 SET_NEW_DATA_STREAM_TO_NULL_SINK_SOURCE(m, stream, type);
2442 if (command == PROCESS_COMMAND_PREPARE) {
2443 if (type == STREAM_SINK_INPUT) {
2444 /* Parse request formats for samplerate, channel, format infomation */
2445 if (((pa_sink_input_new_data*)stream)->req_formats) {
2446 req_format = pa_idxset_first(((pa_sink_input_new_data*)stream)->req_formats, NULL);
2447 if (req_format && req_format->plist) {
2448 /* set sample_spec */
2449 if (pa_format_info_get_prop_string(req_format, PA_PROP_FORMAT_SAMPLE_FORMAT, &format_str) == 0)
2450 ((pa_sink_input_new_data*)stream)->sample_spec.format = pa_parse_sample_format((const char*)format_str);
2451 if ((rate_str = pa_proplist_gets(req_format->plist, PA_PROP_FORMAT_RATE)))
2452 ((pa_sink_input_new_data*)stream)->sample_spec.rate = atoi(rate_str);
2453 if ((ch_str = pa_proplist_gets(req_format->plist, PA_PROP_FORMAT_CHANNELS)))
2454 ((pa_sink_input_new_data*)stream)->sample_spec.channels = atoi(ch_str);
2456 pa_log_debug("req rate(%s), req ch(%s), req format(%s)", rate_str, ch_str, format_str);
2458 /* set channel map if it is not set by client */
2459 if (!((pa_sink_input_new_data*)stream)->channel_map_is_set) {
2460 pa_channel_map_init_auto(&(((pa_sink_input_new_data*)stream)->channel_map),
2461 ((pa_sink_input_new_data*)stream)->sample_spec.channels, PA_CHANNEL_MAP_ALSA);
2462 pa_log_debug("set default channel_map: channels(%u)", ((pa_sink_input_new_data*)stream)->channel_map.channels);
2463 ((pa_sink_input_new_data*)stream)->channel_map_is_set = true;
2467 pa_log_debug("no request formats available");
2470 role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2472 /* set default value for role and priority */
2473 role = DEFAULT_ROLE;
2474 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE, role);
2475 pa_log_warn("role is null, set default to [%s]", role);
2478 if (check_role_to_skip(m, command, role)) {
2479 result = PROCESS_STREAM_RESULT_SKIP;
2482 /* load forwarding device */
2483 if (type == STREAM_SOURCE_OUTPUT && pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING))
2484 update_forwarding_device(m, true);
2486 /* update the priority of this stream */
2487 ret = update_priority_of_stream(m, stream, type, role, is_new_data);
2489 pa_log_error("could not update the priority of '%s' role.", role);
2490 result = PROCESS_STREAM_RESULT_STOP;
2493 /* update the route type of this stream */
2494 ret = update_route_type_of_stream(m, stream, type, role);
2496 pa_log_error("could not update the route type of '%s' role.", role);
2497 result = PROCESS_STREAM_RESULT_STOP;
2500 /* update the volume type of this stream */
2501 ret = update_volume_type_of_stream(m, stream, type, role);
2503 pa_log_warn("could not update the volume type of '%s' role.", role);
2505 /* skip route types */
2506 if ((route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2507 if (check_route_type_to_skip(command, route_type_str)) {
2508 result = PROCESS_STREAM_RESULT_SKIP;
2513 /* check if it is a virtual stream */
2514 if (check_name_is_vstream(stream, type, is_new_data)) {
2515 pa_log_debug("skip notifying for selecting sink/source, rather set it to null sink/source");
2516 /* set it to null sink/source */
2518 SET_NEW_DATA_STREAM_TO_NULL_SINK_SOURCE(m, stream, type);
2521 /* notify to select sink or source */
2522 do_notify(m, NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT, type, true, stream);
2525 } else if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED) {
2527 role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2528 route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
2530 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2531 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
2535 if (check_role_to_skip(m, command, role)) {
2536 result = PROCESS_STREAM_RESULT_SKIP;
2540 /* skip route types */
2541 if (check_route_type_to_skip(command, route_type_str)) {
2542 result = PROCESS_STREAM_RESULT_SKIP;
2547 /* update the priority of this stream */
2548 ret = update_priority_of_stream(m, stream, type, role, is_new_data);
2550 pa_log_error("could not update the priority of '%s' role.", role);
2551 result = PROCESS_STREAM_RESULT_STOP;
2556 /* update the focus status */
2557 ret = update_focus_status_of_stream(m, stream, type, is_new_data);
2559 pa_log_warn("could not update focus status");
2561 /* update the highest priority */
2562 ret = update_the_highest_priority_stream(m, command, stream, type, role, is_new_data, &need_update);
2564 pa_log_error("could not update the highest priority stream");
2565 result = PROCESS_STREAM_RESULT_SKIP;
2569 /* need to skip if this stream does not belong to internal device */
2570 /* if needed, notify to update */
2573 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, type, true, stream);
2574 if (type == STREAM_SINK_INPUT)
2575 m->cur_highest_priority.need_to_update_si = true;
2577 m->cur_highest_priority.need_to_update_so = true;
2579 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, type, false, stream);
2580 if (type == STREAM_SINK_INPUT) {
2581 m->cur_highest_priority.sink_input = stream;
2582 m->cur_highest_priority.role_si = role;
2584 m->cur_highest_priority.source_output = stream;
2585 m->cur_highest_priority.role_so = role;
2591 do_notify(m, NOTIFY_COMMAND_INFORM_STREAM_CONNECTED, type, false, stream);
2593 } else if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED) {
2594 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2596 /* unload forwarding device */
2597 if (type == STREAM_SOURCE_OUTPUT && pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING))
2598 update_forwarding_device(m, false);
2601 if (check_role_to_skip(m, command, role)) {
2602 result = PROCESS_STREAM_RESULT_SKIP;
2606 /* skip route types */
2607 if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2608 if (check_route_type_to_skip(command, route_type_str)) {
2609 result = PROCESS_STREAM_RESULT_SKIP;
2614 /* check if it has already been processed (unlink or state_changed_cb) */
2615 if (pa_proplist_get(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&prior_priority, &size)) {
2616 pa_log_debug("it has already been processed for PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, skip it..");
2617 result = PROCESS_STREAM_RESULT_SKIP;
2621 pa_proplist_unset(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_PRIORITY);
2622 ret = update_the_highest_priority_stream(m, command, stream, type, role, is_new_data, &need_update);
2624 pa_log_error("could not update the highest priority stream");
2625 result = PROCESS_STREAM_RESULT_STOP;
2629 do_notify(m, NOTIFY_COMMAND_INFORM_STREAM_DISCONNECTED, type, false, stream);
2631 /* need to skip if this stream does not belong to internal device */
2632 /* if needed, notify to update */
2634 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_END, type, false, NULL);
2637 pa_log_error("role is null, skip it");
2640 } else if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED) {
2641 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2644 if (check_role_to_skip(m, command, role)) {
2645 result = PROCESS_STREAM_RESULT_SKIP;
2649 /* skip route types */
2650 if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2651 if (check_route_type_to_skip(command, route_type_str)) {
2652 result = PROCESS_STREAM_RESULT_SKIP;
2657 ret = update_the_highest_priority_stream(m, command, stream, type, role, is_new_data, &need_update);
2659 pa_log_error("could not update the highest priority stream");
2660 result = PROCESS_STREAM_RESULT_STOP;
2664 /* need to skip if this stream does not belong to internal device */
2665 /* if needed, notify to update */
2667 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_END, type, false, NULL);
2670 pa_log_error("role is null, skip it");
2673 } else if (command == PROCESS_COMMAND_UPDATE_VOLUME && is_new_data) {
2674 if ((si_volume_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_TIZEN_VOLUME_TYPE))) {
2675 v = pa_hashmap_get(m->volume_infos, si_volume_type_str);
2676 if (v && v->values[type].idx_volume_values) {
2677 /* Update volume-level */
2678 if ((volume_ret = set_volume_level_with_new_data(m, stream, type, v->values[type].current_level)))
2679 pa_log_error("failed to set_volume_level_by_idx(), stream_type(%d), level(%u), ret(0x%x)",
2680 type, v->values[type].current_level, volume_ret);
2681 /* Update volume-mute */
2682 if ((volume_ret = set_volume_mute_with_new_data(m, stream, type, v->values[type].is_muted)))
2683 pa_log_error("failed to set_volume_mute_by_idx(), stream_type(%d), mute(%d), ret(0x%x)",
2684 type, v->values[type].is_muted, volume_ret);
2688 } else if (command == PROCESS_COMMAND_ADD_PARENT_ID || command == PROCESS_COMMAND_REMOVE_PARENT_ID) {
2689 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2692 if (check_role_to_skip(m, command, role)) {
2693 result = PROCESS_STREAM_RESULT_SKIP;
2697 /* skip route types */
2698 if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2699 if (check_route_type_to_skip(command, route_type_str)) {
2700 result = PROCESS_STREAM_RESULT_SKIP;
2705 if (!IS_ROUTE_TYPE_FOR_EXTERNAL_DEV(route_type_str, route_type)) {
2706 if (command == PROCESS_COMMAND_ADD_PARENT_ID) {
2707 if (type == STREAM_SINK_INPUT && m->cur_highest_priority.need_to_update_si) {
2708 m->cur_highest_priority.sink_input = stream;
2709 m->cur_highest_priority.role_si = role;
2710 m->cur_highest_priority.need_to_update_si = false;
2712 if (type == STREAM_SOURCE_OUTPUT && m->cur_highest_priority.need_to_update_so) {
2713 m->cur_highest_priority.source_output = stream;
2714 m->cur_highest_priority.role_so = role;
2715 m->cur_highest_priority.need_to_update_so = false;
2717 do_notify(m, NOTIFY_COMMAND_INFORM_STREAM_CONNECTED, type, false, stream);
2721 /* update parent stream info. */
2722 ret = update_stream_parent_info(m, command, type, stream);
2724 pa_log_debug("could not update the parent information of this stream");
2725 //return PROCESS_STREAM_RESULT_STOP;
2728 } else if (command == PROCESS_COMMAND_UPDATE_BUFFER_ATTR) {
2729 update_buffer_attribute(m, stream, type, is_new_data);
2733 pa_log_debug("<<< process_stream(%s): result(%d), stream(%p)", process_command_type_str[command], result, stream);
2737 /* Remove the sink-input from muted streams */
2738 static void remove_sink_input_from_muted_streams(pa_stream_manager *m, pa_sink_input *i) {
2746 PA_HASHMAP_FOREACH(streams, m->muted_streams, state)
2747 PA_IDXSET_FOREACH(si, streams, idx)
2749 pa_idxset_remove_by_data(streams, i, NULL);
2752 static pa_hook_result_t sink_input_new_cb(pa_core *core, pa_sink_input_new_data *new_data, pa_stream_manager *m) {
2753 pa_core_assert_ref(core);
2755 pa_log_debug("start sink_input_new_cb");
2757 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_PREPARE, true);
2758 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_UPDATE_BUFFER_ATTR, true);
2759 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_UPDATE_VOLUME, true);
2760 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, true);
2765 static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2766 pa_core_assert_ref(core);
2767 pa_sink_input_assert_ref(i);
2769 pa_log_debug("start sink_input_put_cb, i(%p, index:%u)", i, i->index);
2771 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_ADD_PARENT_ID, false);
2776 static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2777 pa_core_assert_ref(core);
2778 pa_sink_input_assert_ref(i);
2780 pa_log_debug("start sink_input_unlink_cb, i(%p, index:%u)", i, i->index);
2782 remove_sink_input_from_muted_streams(m, i);
2783 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_REMOVE_PARENT_ID, false);
2784 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
2789 static pa_hook_result_t sink_input_state_changed_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2790 pa_sink_input_state_t state;
2795 state = pa_sink_input_get_state(i);
2796 pa_log_debug("start sink_input_state_changed_cb(), sink-input(%p), state(%d)", i, state);
2799 case PA_SINK_INPUT_CORKED: {
2800 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
2803 case PA_SINK_INPUT_DRAINED:
2804 case PA_SINK_INPUT_RUNNING: {
2805 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
2815 static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2816 pa_core_assert_ref(core);
2817 pa_sink_input_assert_ref(i);
2819 /* There's no point in doing anything if the core is shut down anyway */
2820 if (core->state == PA_CORE_SHUTDOWN)
2823 pa_log_debug("sink_input_move_start_cb, i(%p, index:%u)", i, i->index);
2825 set_volume_mute_by_idx(m, i->index, STREAM_SINK_INPUT, true);
2830 static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2831 pa_core_assert_ref(core);
2832 pa_sink_input_assert_ref(i);
2834 /* There's no point in doing anything if the core is shut down anyway */
2835 if (core->state == PA_CORE_SHUTDOWN)
2838 pa_log_debug("sink_input_move_finish_cb, i(%p, index:%u)", i, i->index);
2840 set_volume_mute_by_idx(m, i->index, STREAM_SINK_INPUT, false);
2845 static pa_hook_result_t source_output_new_cb(pa_core *core, pa_source_output_new_data *new_data, pa_stream_manager *m) {
2846 pa_core_assert_ref(core);
2848 pa_log_info("start source_output_new_new_cb");
2850 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_PREPARE, true);
2851 if (check_restrictions(m, new_data, STREAM_SOURCE_OUTPUT))
2852 return PA_HOOK_CANCEL;
2853 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_UPDATE_BUFFER_ATTR, true);
2854 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_UPDATE_VOLUME, true);
2855 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, true);
2860 static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2861 pa_core_assert_ref(core);
2862 pa_source_output_assert_ref(o);
2864 pa_log_info("start source_output_put_cb, o(%p, index:%u)", o, o->index);
2866 update_mirroring_streams(m, o, true);
2867 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_ADD_PARENT_ID, false);
2872 static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2873 pa_core_assert_ref(core);
2874 pa_source_output_assert_ref(o);
2876 pa_log_info("start source_output_unlink_cb, o(%p, index:%u)", o, o->index);
2878 update_mirroring_streams(m, o, false);
2879 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
2880 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_REMOVE_PARENT_ID, false);
2885 static pa_hook_result_t source_output_state_changed_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2886 pa_source_output_state_t state;
2891 state = pa_source_output_get_state(o);
2892 pa_log_debug("start source_output_state_changed_cb(), source-output(%p), state(%d)", o, state);
2895 case PA_SOURCE_OUTPUT_CORKED: {
2896 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
2899 case PA_SOURCE_OUTPUT_RUNNING: {
2900 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
2910 static pa_hook_result_t source_output_move_start_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2911 pa_core_assert_ref(core);
2912 pa_source_output_assert_ref(o);
2914 /* There's no point in doing anything if the core is shut down anyway */
2915 if (core->state == PA_CORE_SHUTDOWN)
2918 pa_log_debug("source_output_move_start_cb, o(%p, index:%u)", o, o->index);
2920 set_volume_mute_by_idx(m, o->index, STREAM_SINK_INPUT, true);
2925 static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2926 pa_core_assert_ref(core);
2927 pa_source_output_assert_ref(o);
2929 /* There's no point in doing anything if the core is shut down anyway */
2930 if (core->state == PA_CORE_SHUTDOWN)
2933 pa_log_debug("source_output_move_finish_cb, o(%p, index:%u)", o, o->index);
2935 set_volume_mute_by_idx(m, o->index, STREAM_SINK_INPUT, false);
2940 static void find_next_device_for_auto_route(pa_stream_manager *m, stream_route_type_t route_type, const char *role, stream_type_t stream_type, const char *cur_device_type, pa_tz_device **next_device) {
2941 stream_info *si = NULL;
2942 pa_idxset *devices = NULL;
2944 char *device_type = NULL;
2945 bool ret_next = false;
2946 pa_usec_t creation_time = 0;
2947 pa_usec_t latest_creation_time = 0;
2948 pa_tz_device* latest_device = NULL;
2951 pa_assert(m->stream_infos);
2953 pa_assert(cur_device_type);
2954 pa_assert(next_device);
2955 pa_assert((route_type == STREAM_ROUTE_TYPE_AUTO || route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED));
2957 *next_device = NULL;
2959 if (!(si = pa_hashmap_get(m->stream_infos, role))) {
2960 pa_log_warn("not support this role[%s]", role);
2963 if (si->route_type != route_type) {
2964 pa_log_warn("skip this route_type[%d]", si->route_type);
2968 if (!(devices = (stream_type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices)) {
2969 pa_log_error("could not found a device list for this role[%s], stream type[%d]", role, stream_type);
2973 if (route_type == STREAM_ROUTE_TYPE_AUTO) {
2974 PA_IDXSET_FOREACH(device_type, devices, idx) {
2975 if (pa_streq(device_type, cur_device_type)) {
2980 if ((*next_device = pa_device_manager_get_device(m->dm, device_type))) {
2981 pa_log_debug("found next device[%s, %p]", device_type, *next_device);
2987 } else if (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
2988 PA_IDXSET_FOREACH(device_type, devices, idx) {
2989 if ((*next_device = pa_device_manager_get_device(m->dm, device_type))) {
2990 creation_time = pa_tz_device_get_creation_time(*next_device);
2991 if (!latest_device || (latest_creation_time <= creation_time)) {
2992 latest_device = *next_device;
2993 latest_creation_time = creation_time;
2997 *next_device = latest_device;
2998 pa_log_debug("found next device[%s, %p], creation_time[%llu]", device_type, *next_device, latest_creation_time);
3001 pa_log_debug("next_device is [%p] for role[%s]/route_type[%d]/stream_type[%d]", *next_device, role, route_type, stream_type);
3004 static void is_available_device_for_auto_route(pa_stream_manager *m, stream_route_type_t route_type, const char *cur_device_type, const char *new_device_type, const char *role, stream_type_t stream_type, bool *available) {
3005 stream_info *si = NULL;
3006 pa_idxset *devices = NULL;
3008 char *device_type = NULL;
3011 pa_assert(m->stream_infos);
3013 pa_assert(cur_device_type);
3014 pa_assert(new_device_type);
3015 pa_assert(available);
3016 pa_assert((route_type == STREAM_ROUTE_TYPE_AUTO || route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED));
3020 if (!(si = pa_hashmap_get(m->stream_infos, role))) {
3021 pa_log_warn("not support this role[%s]", role);
3024 if (si->route_type != route_type) {
3025 pa_log_warn("skip this route_type[%d]", si->route_type);
3029 if (!(devices = (stream_type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices)) {
3030 pa_log_error("could not found a device list for this role[%s], stream type[%d]", role, stream_type);
3034 PA_IDXSET_FOREACH(device_type, devices, idx) {
3035 if (route_type == STREAM_ROUTE_TYPE_AUTO) {
3036 if (pa_streq(device_type, cur_device_type)) {
3037 pa_log_debug("cur_device[%s]'s priority is more higher than new_device[%s]", cur_device_type, new_device_type);
3041 if (pa_streq(device_type, new_device_type)) {
3047 pa_log_debug("is new_device[%s] available for role[%s]/stream_type[%d]:%d", new_device_type, role, stream_type, *available);
3050 /* Re-trigger for routing update for streams using auto route type */
3051 static void process_stream_as_device_change_for_auto_route(pa_stream_manager *m, void *stream, stream_type_t stream_type,
3052 bool is_connected, bool use_internal_codec) {
3053 stream_route_type_t route_type;
3058 pa_log_info("[SM][PROCESS_STREAM_FOR_AUTO] stream(%p), stream_type(%d), is_connected(%d), use_internal_codec(%d)",
3059 stream, stream_type, is_connected, use_internal_codec);
3061 if (get_route_type(stream, stream_type, false, &route_type) ||
3062 (route_type != STREAM_ROUTE_TYPE_AUTO && route_type != STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))
3066 /* it is caused by the connection of supported device for the stream */
3067 if (use_internal_codec) {
3068 if (((stream_type == STREAM_SINK_INPUT) && (!m->cur_highest_priority.sink_input || (m->cur_highest_priority.sink_input != stream))) ||
3069 ((stream_type == STREAM_SOURCE_OUTPUT) && (!m->cur_highest_priority.source_output || (m->cur_highest_priority.source_output != stream))))
3070 process_stream(m, stream, stream_type, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
3072 process_stream(m, stream, stream_type, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
3074 /* it is caused by the disconnection of external device
3075 * and the supported next device of this stream using internal audio codec */
3076 if (use_internal_codec)
3077 process_stream(m, stream, stream_type, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
3081 /* The state of a device using internal audio codec is handled here.
3082 * Regarding the state of an external device, those is handled in device-manager.c */
3083 static void set_device_state_if_using_internal_codec(pa_tz_device *device, stream_type_t stream_type, dm_device_state_t device_state) {
3084 bool use_internal_codec = false;
3088 if ((use_internal_codec = pa_tz_device_is_use_internal_codec(device)))
3089 pa_tz_device_set_state(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), device_state);
3092 static void update_sink_or_source_as_device_change(stream_route_type_t stream_route_type, pa_idxset *streams,
3093 stream_type_t stream_type, pa_tz_device *device, bool is_connected, pa_stream_manager *m) {
3094 #define MAX_CACHED_LEN 128
3095 typedef struct _cached_device_list {
3096 const char *device_type;
3098 } cached_device_list;
3101 const char *role = NULL;
3102 const char *device_type = NULL;
3103 const char *cur_device_type = NULL;
3104 const char *new_device_type = NULL;
3105 pa_tz_device *next_device = NULL;
3106 pa_tz_device *_device = NULL;
3107 stream_route_type_t route_type;
3108 pa_sink *sink = NULL;
3109 pa_sink *next_sink = NULL;
3110 pa_sink *null_sink = NULL;
3111 pa_source *source = NULL;
3112 pa_source *next_source = NULL;
3113 pa_source *null_source = NULL;
3114 bool available = false;
3115 bool use_internal_codec = false;
3116 cached_device_list cached_prev_dev_list[MAX_CACHED_LEN] = {{NULL, 0}, };
3118 pa_sink *combine_sink = NULL;
3124 null_sink = (pa_sink*)pa_namereg_get(m->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
3125 null_source = (pa_source*)pa_namereg_get(m->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE);
3126 if (!null_sink || !null_source) {
3127 pa_log_error("[SM][UPDATE_SINK_SOURCE] could not get null_sink(%p) or null_source(%p)", null_sink, null_source);
3130 device_type = pa_tz_device_get_type(device);
3132 if (stream_route_type == STREAM_ROUTE_TYPE_AUTO || stream_route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
3133 pa_log_debug("[SM][UPDATE_SINK_SOURCE][AUTO] route_type(%d), deivce_type(%s), is_connected(%d))",
3134 stream_route_type, device_type, is_connected);
3135 if (stream_type == STREAM_SINK_INPUT)
3136 sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
3138 source = pa_tz_device_get_source(device, DEVICE_ROLE_NORMAL);
3140 PA_IDXSET_FOREACH(s, streams, s_idx) { /* streams: core->source_outputs/core->sink_inputs */
3141 if (!get_route_type(s, stream_type, false, &route_type) && (route_type == stream_route_type)) {
3142 role = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROLE);
3143 pa_log_debug(" -- idx(%u), route_type(%d), role(%s)", s_idx, route_type, role);
3145 /* CONNECTED: move a stream to the new device if possible */
3146 if (sink && (sink != ((pa_sink_input*)s)->sink)) {
3147 if ((cur_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
3148 is_available_device_for_auto_route(m, route_type, cur_device_type, device_type, role, stream_type, &available);
3150 pa_sink_input_move_to(s, sink, false);
3151 pa_log_debug(" -- *** sink-input(%p,%u) moves to sink(%p,%s), new device(%s)",
3152 s, ((pa_sink_input*)s)->index, sink, sink->name, device_type);
3153 use_internal_codec = sink->use_internal_codec;
3156 pa_log_error(" -- could not find current device type for s->sink(%p)", ((pa_sink_input*)s)->sink);
3157 } else if (source && (source != ((pa_source_output*)s)->source)) {
3158 if ((cur_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
3159 is_available_device_for_auto_route(m, route_type, cur_device_type, device_type, role, stream_type, &available);
3161 pa_source_output_move_to(s, source, false);
3162 pa_log_debug(" -- *** source-output(%p,%u) moves to source(%p,%s), new device(%s)",
3163 s, ((pa_source_output*)s)->index, source, source->name, device_type);
3164 use_internal_codec = source->use_internal_codec;
3167 pa_log_error(" -- could not find current device type for s->source(%p)", ((pa_source_output*)s)->source);
3169 pa_log_debug("no need to move for stream(%p, idx:%u)", s, (stream_type == STREAM_SINK_INPUT ?
3170 ((pa_sink_input*)s)->index : ((pa_source_output*)s)->index));
3172 /* update activated device */
3173 pa_proplist_sets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
3174 if ((_device = pa_device_manager_get_device(m->dm, device_type)))
3175 set_device_state_if_using_internal_codec(_device, stream_type, DM_DEVICE_STATE_ACTIVATED);
3176 cached_prev_dev_list[cnt++].device_type = cur_device_type;
3177 /* trigger to update routing path */
3178 process_stream_as_device_change_for_auto_route(m, s, stream_type, is_connected, use_internal_codec);
3180 } else if (!is_connected) {
3181 /* DISCONNECTED: find a connected device that has the next priority */
3182 if (sink && (sink == ((pa_sink_input*)s)->sink)) {
3183 find_next_device_for_auto_route(m, route_type, role, stream_type, device_type, &next_device);
3185 if ((next_sink = pa_tz_device_get_sink(next_device, DEVICE_ROLE_NORMAL))) {
3186 new_device_type = pa_tz_device_get_type(next_device);
3187 /* update activated device */
3188 pa_proplist_sets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, new_device_type);
3189 set_device_state_if_using_internal_codec(next_device, stream_type, DM_DEVICE_STATE_ACTIVATED);
3190 cached_prev_dev_list[cnt++].device_type = device_type;
3191 /* trigger to update routing path if the next device uses internal audio codec */
3192 if (next_sink->use_internal_codec)
3193 process_stream_as_device_change_for_auto_route(m, s, stream_type, is_connected, next_sink->use_internal_codec);
3195 pa_sink_input_move_to(s, next_sink, false);
3196 pa_log_debug(" -- *** sink-input(%p,%u) moves to sink(%p,%s), new device(%s)",
3197 s, ((pa_sink_input*)s)->index, next_sink, next_sink->name, new_device_type);
3201 if (!next_device || !next_sink) {
3202 pa_sink_input_move_to(s, null_sink, false);
3203 pa_log_debug(" -- *** sink-input(%p,%u) moves to sink(%p,%s)",
3204 s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
3207 } else if (source && (source == ((pa_source_output*)s)->source)) {
3208 find_next_device_for_auto_route(m, route_type, role, stream_type, device_type, &next_device);
3210 if ((next_source = pa_tz_device_get_source(next_device, DEVICE_ROLE_NORMAL))) {
3211 new_device_type = pa_tz_device_get_type(next_device);
3212 /* update activated device */
3213 pa_proplist_sets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, new_device_type);
3214 set_device_state_if_using_internal_codec(next_device, stream_type, DM_DEVICE_STATE_ACTIVATED);
3215 cached_prev_dev_list[cnt++].device_type = device_type;
3216 /* trigger to update routing path if the next device uses internal audio codec */
3217 if (next_source->use_internal_codec)
3218 process_stream_as_device_change_for_auto_route(m, s, stream_type, is_connected, next_source->use_internal_codec);
3220 pa_source_output_move_to(s, next_source, false);
3221 pa_log_debug(" -- *** source-output(%p,%u) moves to source(%p,%s), new device(%s)",
3222 s, ((pa_source_output*)s)->index, next_source, next_source->name, new_device_type);
3225 if (!next_device || !next_source) {
3226 pa_source_output_move_to(s, null_source, false);
3227 pa_log_debug(" -- *** source-output(%p,%u) moves to source(%p,%s)",
3228 s, ((pa_source_output*)s)->index, null_source, null_source->name);
3235 /* set device state to be deactivated */
3237 if (stream_type == STREAM_SINK_INPUT) {
3238 combine_sink = (pa_sink*)pa_namereg_get(m->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
3239 if (combine_sink && pa_idxset_size(combine_sink->inputs)) {
3240 pa_log_warn(" -- combine sink has streams, skip it..");
3244 /* retrieve all the streams for checking current activated device */
3245 PA_IDXSET_FOREACH(s, streams, s_idx) { /* streams: core->source_outputs/core->sink_inputs */
3246 if ((cur_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
3247 for (cnt = 0; cached_prev_dev_list[cnt].device_type; cnt++) {
3248 if (pa_streq(cur_device_type, cached_prev_dev_list[cnt].device_type))
3249 cached_prev_dev_list[cnt].count++;
3253 /* if there's no activated device marked in previous device list, set it to be deactivated */
3254 for (cnt = 0; cached_prev_dev_list[cnt].device_type; cnt++) {
3255 if (cached_prev_dev_list[cnt].count == 0) {
3256 if ((_device = pa_device_manager_get_device(m->dm, cached_prev_dev_list[cnt].device_type)))
3257 set_device_state_if_using_internal_codec(_device, stream_type, DM_DEVICE_STATE_DEACTIVATED);
3262 } else if (stream_route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) {
3263 pa_log_debug("[SM][UPDATE_SINK_SOURCE][EXT] deivce_type(%s), is_connected(%d))", device_type, is_connected);
3264 if (!is_connected) {
3265 PA_IDXSET_FOREACH(s, streams, s_idx) { /* streams: source->outputs/sink->inputs */
3266 if (!get_route_type(s, stream_type, false, &route_type) && route_type == stream_route_type) {
3267 if (stream_type == STREAM_SOURCE_OUTPUT) {
3268 /* move it to null source if this role is for external device */
3269 pa_source_output_move_to((pa_source_output*)s, null_source, false);
3270 pa_log_debug(" -- *** source-output(%p,%u) moves to source(%p,%s)", s, ((pa_source_output*)s)->index, null_source, null_source->name);
3272 /* move it to null sink if this role is for external device */
3273 pa_sink_input_move_to((pa_sink_input*)s, null_sink, false);
3274 pa_log_debug(" -- *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
3280 pa_log_error("[SM][UPDATE_SINK_SOURCE] could not handle it here, stream_route_type(%d)", stream_route_type);
3283 static void mute_sink_inputs_as_device_disconnection(pa_stream_manager *m, uint32_t event_id, bool need_to_mute, void *user_data) {
3284 pa_idxset *muted_streams;
3288 const char *mute_key = "mute_by_device_disconnection";
3294 pa_log_error("invalid argument, inputs is needed");
3298 pa_parse_volume("0%", &vol.values[0]);
3299 muted_streams = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3300 PA_IDXSET_FOREACH(i, (pa_idxset *)user_data, s_idx) {
3301 pa_log_debug("found a stream(%p, %u) that should be muted.", i, i->index);
3302 pa_sink_input_add_volume_factor(i, mute_key, &vol);
3303 pa_idxset_put(muted_streams, i, NULL);
3305 pa_hashmap_put(m->muted_streams, (void*)event_id, muted_streams);
3307 if (!(muted_streams = pa_hashmap_get(m->muted_streams, (void*)event_id))) {
3308 pa_log_debug("could not find muted_streams for event_id(%u)", event_id);
3311 PA_IDXSET_FOREACH(i, muted_streams, s_idx) {
3312 pa_idxset_remove_by_data(muted_streams, i, NULL);
3313 pa_sink_input_remove_volume_factor(i, mute_key);
3314 pa_log_debug("found a stream(%p, %u) that should be un-muted.", i, i->index);
3316 pa_hashmap_remove(m->muted_streams, (void*)event_id);
3317 pa_idxset_free(muted_streams, NULL);
3323 static pa_hook_result_t event_fully_handled_hook_cb(pa_core *c, pa_subscribe_observer_hook_data_for_event_handled *event_handled_hook_data, pa_stream_manager *m) {
3325 pa_assert(event_handled_hook_data);
3328 pa_log_info("[SM][HANDLED] event_fully_handled_hook_cb is called. event-id(%u), event-type(%d)",
3329 event_handled_hook_data->event_id, event_handled_hook_data->event_type);
3331 /* un-mute streams */
3332 mute_sink_inputs_as_device_disconnection(m, event_handled_hook_data->event_id, false, NULL);
3337 /* Reorganize routing when a device has been connected or disconnected */
3338 static pa_hook_result_t device_connection_changed_hook_cb(pa_core *c, pa_tz_device_hook_data_for_conn_changed *data, pa_stream_manager *m) {
3339 const char *active_dev = NULL;
3340 const char *device_type = NULL;
3341 stream_route_type_t route_type;
3342 dm_device_direction_t device_direction = DM_DEVICE_DIRECTION_OUT;
3343 bool use_internal_codec = false;
3346 uint32_t device_id = 0;
3347 pa_sink *sink = NULL;
3348 pa_source *source = NULL;
3354 device_direction = pa_tz_device_get_direction(data->device);
3355 device_type = pa_tz_device_get_type(data->device);
3356 device_id = pa_tz_device_get_id(data->device);
3357 use_internal_codec = pa_tz_device_is_use_internal_codec(data->device);
3359 pa_log_info("[SM][CONN] device_connection_changed_hook_cb is called. evend_id(%u), is_connected(%d), device(%p, %s, %u), direction(0x%x), use_internal_codec(%d)",
3360 data->event_id, data->is_connected, data->device, device_type, device_id, device_direction, use_internal_codec);
3362 /* mute all the streams belong to this device, those will be un-muted in event_fully_handled_hook_cb */
3363 if (!data->is_connected && (device_direction & DM_DEVICE_DIRECTION_OUT))
3364 if ((sink = pa_tz_device_get_sink(data->device, DEVICE_ROLE_NORMAL)))
3365 mute_sink_inputs_as_device_disconnection(m, data->event_id, true, sink->inputs);
3367 /* Update streams belong to this external device that have MAUAL_EXT route type */
3368 if (!use_internal_codec && (device_direction & DM_DEVICE_DIRECTION_IN)) {
3369 if ((source = pa_tz_device_get_source(data->device, DEVICE_ROLE_NORMAL)))
3370 update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_MANUAL_EXT, source->outputs,
3371 STREAM_SOURCE_OUTPUT, data->device, data->is_connected, m);
3373 pa_log_error("[SM][CONN] could not get source");
3375 if (!use_internal_codec && (device_direction & DM_DEVICE_DIRECTION_OUT)) {
3376 if ((sink = pa_tz_device_get_sink(data->device, DEVICE_ROLE_NORMAL)))
3377 update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_MANUAL_EXT, sink->inputs,
3378 STREAM_SINK_INPUT, data->device, data->is_connected, m);
3380 pa_log_error("[SM][CONN] could not get sink");
3383 /* Update all the streams that have AUTO route type */
3384 if (device_direction & DM_DEVICE_DIRECTION_IN) {
3385 update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO, m->core->source_outputs,
3386 STREAM_SOURCE_OUTPUT, data->device, data->is_connected, m);
3387 update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->source_outputs,
3388 STREAM_SOURCE_OUTPUT, data->device, data->is_connected, m);
3390 if (device_direction & DM_DEVICE_DIRECTION_OUT) {
3391 update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO, m->core->sink_inputs,
3392 STREAM_SINK_INPUT, data->device, data->is_connected, m);
3393 update_sink_or_source_as_device_change(STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->sink_inputs,
3394 STREAM_SINK_INPUT, data->device, data->is_connected, m);
3398 /* If the route type of the stream is not manual, notify again */
3399 if (m->cur_highest_priority.source_output && (device_direction & DM_DEVICE_DIRECTION_IN)) {
3400 if (!get_route_type(m->cur_highest_priority.source_output, STREAM_SOURCE_OUTPUT, false, &route_type)) {
3401 if (route_type < STREAM_ROUTE_TYPE_MANUAL) {
3402 if (use_internal_codec) {
3403 PA_IDXSET_FOREACH(s, m->cur_highest_priority.source_output->source->outputs, s_idx) {
3404 if (!data->is_connected && !get_route_type(s, STREAM_SOURCE_OUTPUT, false, &route_type) &&
3405 ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
3406 /* remove activated device info. if it has the AUTO route type */
3407 active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
3408 if (active_dev && pa_streq(active_dev, device_type))
3409 pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
3412 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SOURCE_OUTPUT, false, m->cur_highest_priority.source_output);
3413 if (!((pa_source_output*)(m->cur_highest_priority.source_output))->source->use_internal_codec) {
3414 /* If the source of the cur_highest_priority stream uses external codec, it should be updated.
3415 * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
3416 * can update the cur_highest_priority, call it here */
3417 process_stream(m, m->cur_highest_priority.source_output, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
3423 if (m->cur_highest_priority.sink_input && (device_direction & DM_DEVICE_DIRECTION_OUT)) {
3424 if (!get_route_type(m->cur_highest_priority.sink_input, STREAM_SINK_INPUT, false, &route_type)) {
3425 if (route_type < STREAM_ROUTE_TYPE_MANUAL) {
3426 if (use_internal_codec) {
3427 PA_IDXSET_FOREACH(s, m->cur_highest_priority.sink_input->sink->inputs, s_idx) {
3428 if (!data->is_connected && !get_route_type(s, STREAM_SINK_INPUT, false, &route_type) &&
3429 ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
3430 /* remove activated device info. if it has the AUTO route type */
3431 active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
3432 if (active_dev && pa_streq(active_dev, device_type))
3433 pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
3436 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, m->cur_highest_priority.sink_input);
3437 if (((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED)) &&
3438 !((pa_sink_input*)(m->cur_highest_priority.sink_input))->sink->use_internal_codec) {
3439 /* If the sink of the cur_highest_priority stream uses external codec, it should be updated.
3440 * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
3441 * can update the cur_highest_priority, call it here */
3442 process_stream(m, m->cur_highest_priority.sink_input, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
3444 } else if (route_type == STREAM_ROUTE_TYPE_AUTO_ALL)
3445 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, m->cur_highest_priority.sink_input);
3453 static void subscribe_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, pa_stream_manager *m) {
3454 pa_client *client = NULL;
3455 stream_parent *sp = NULL;
3456 const char *name = NULL;
3457 uint32_t *device_id = NULL;
3460 pa_core_assert_ref(core);
3463 pa_log_info("subscribe_cb() is called, t(%x), idx(%u)", t, idx);
3465 if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE)) {
3466 if ((client = pa_idxset_get_by_index(core->clients, idx)) == NULL) {
3467 pa_log_error(" - could not find any client that has idx(%u)", idx);
3470 name = pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME);
3471 if (!name || (name && !pa_streq(name, STREAM_MANAGER_CLIENT_NAME))) {
3472 pa_log_warn(" - this is not a client(%s) that we should take care of, skip it", name);
3475 /* add a stream parent */
3476 sp = pa_xmalloc0(sizeof(stream_parent));
3477 sp->idx_sink_inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3478 sp->idx_source_outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3479 sp->idx_route_in_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3480 sp->idx_route_out_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3481 pa_hashmap_put(m->stream_parents, (void*)idx, sp);
3482 pa_log_debug(" - add sp(%p), idx(%u)", sp, idx);
3484 } else if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE)) {
3485 /* remove the stream parent */
3486 sp = pa_hashmap_get(m->stream_parents, (const void*)idx);
3488 pa_log_debug(" - remove sp(%p), idx(%u)", sp, idx);
3489 pa_hashmap_remove(m->stream_parents, (const void*)idx);
3490 if (sp->idx_route_in_devices)
3491 PA_IDXSET_FOREACH(device_id, sp->idx_route_in_devices, _idx)
3492 pa_xfree(device_id);
3493 if (sp->idx_route_out_devices)
3494 PA_IDXSET_FOREACH(device_id, sp->idx_route_out_devices, _idx)
3495 pa_xfree(device_id);
3496 pa_idxset_free(sp->idx_sink_inputs, NULL);
3497 pa_idxset_free(sp->idx_source_outputs, NULL);
3498 pa_idxset_free(sp->idx_route_in_devices, NULL);
3499 pa_idxset_free(sp->idx_route_out_devices, NULL);
3502 pa_log_error(" - could not find any stream_parent that has idx(%u)", idx);
3507 /* Message callback from HAL interface */
3508 static void message_cb(const char *name, int value, void *user_data) {
3509 pa_stream_manager *m;
3510 pa_stream_manager_hook_data_for_update_info hook_call_data;
3512 pa_assert(user_data);
3515 m = (pa_stream_manager*)user_data;
3517 /* For module-loopback parameters */
3518 if (strstr(name, STREAM_ROLE_LOOPBACK)) {
3519 memset(&hook_call_data, 0, sizeof(pa_stream_manager_hook_data_for_update_info));
3520 hook_call_data.stream_role = STREAM_ROLE_LOOPBACK;
3521 hook_call_data.name = name;
3522 hook_call_data.value = value;
3523 pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_UPDATE_INFORMATION), &hook_call_data);
3528 send_command_signal(pa_dbus_connection_get(m->dbus_conn), name, value);
3533 static int32_t init_ipc(pa_stream_manager *m) {
3535 pa_dbus_connection *conn = NULL;
3536 static const DBusObjectPathVTable vtable = {
3537 .message_function = method_handler_for_vt,
3542 pa_log_info("Initialization for IPC");
3544 #ifdef USE_DBUS_PROTOCOL
3545 m->dbus_protocol = pa_dbus_protocol_get(m->core);
3546 pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, STREAM_MANAGER_OBJECT_PATH, &stream_manager_interface_info, m) >= 0);
3547 pa_assert_se(pa_dbus_protocol_register_extension(m->dbus_protocol, STREAM_MANAGER_INTERFACE) >= 0);
3550 dbus_error_init(&err);
3552 if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err)) || dbus_error_is_set(&err)) {
3554 pa_dbus_connection_unref(conn);
3556 pa_log_error("Unable to contact D-Bus system bus: %s: %s", err.name, err.message);
3559 pa_log_notice("Got dbus connection");
3561 m->dbus_conn = conn;
3562 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(conn), STREAM_MANAGER_OBJECT_PATH, &vtable, m));
3565 pa_log_error("DBUS is not supported\n");
3571 pa_log_error("failed to initialize stream manager ipc");
3575 static void deinit_ipc(pa_stream_manager *m) {
3579 #ifdef USE_DBUS_PROTOCOL
3580 if (m->dbus_protocol) {
3581 pa_assert_se(pa_dbus_protocol_unregister_extension(m->dbus_protocol, STREAM_MANAGER_INTERFACE) >= 0);
3582 pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, STREAM_MANAGER_OBJECT_PATH, stream_manager_interface_info.name) >= 0);
3583 pa_dbus_protocol_unref(m->dbus_protocol);
3584 m->dbus_protocol = NULL;
3588 if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(m->dbus_conn), STREAM_MANAGER_OBJECT_PATH))
3589 pa_log_error("failed to unregister object path");
3590 m->dbus_conn = NULL;
3596 bool pa_stream_manager_check_name_is_vstream(void *stream, stream_type_t type, bool is_new_data) {
3597 return check_name_is_vstream(stream, type, is_new_data);
3600 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) {
3601 return get_route_type(stream, stream_type, is_new_data, stream_route_type);
3604 pa_stream_manager* pa_stream_manager_init(pa_core *c) {
3605 pa_stream_manager *m;
3609 m = pa_xnew0(pa_stream_manager, 1);
3612 m->hal = pa_hal_interface_get(c);
3613 if (pa_hal_interface_add_message_callback(m->hal, message_cb, m))
3614 pa_log_warn("skip adding message callback");
3615 m->dm = pa_device_manager_get(c);
3616 m->subs_ob = pa_subscribe_observer_get(c);
3618 #ifdef USE_DBUS_PROTOCOL
3619 m->dbus_protocol = NULL;
3621 m->dbus_conn = NULL;
3626 if (init_stream_map(m))
3628 if (init_volumes(m))
3630 m->stream_parents = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3631 m->muted_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3632 m->mirroring_streams = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3634 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);
3635 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);
3636 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);
3637 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_cb, m);
3638 m->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_move_start_cb, m);
3639 m->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_move_finish_cb, m);
3640 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);
3641 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);
3642 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);
3643 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_cb, m);
3644 m->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_move_start_cb, m);
3645 m->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_move_finish_cb, m);
3647 m->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CLIENT | PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, (pa_subscription_cb_t)subscribe_cb, m);
3649 m->comm.comm = pa_communicator_get(c);
3650 m->comm.comm_hook_device_connection_changed_slot = pa_hook_connect(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_DEVICE_CONNECTION_CHANGED),
3651 PA_HOOK_EARLY + 10, (pa_hook_cb_t)device_connection_changed_hook_cb, m);
3652 m->comm.comm_hook_event_fully_handled_slot = pa_hook_connect(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_EVENT_FULLY_HANDLED),
3653 PA_HOOK_EARLY + 10, (pa_hook_cb_t)event_fully_handled_hook_cb, m);
3658 pa_log_error("failed to initialize stream-manager");
3660 deinit_stream_map(m);
3663 pa_hal_interface_remove_message_callback(m->hal, message_cb);
3664 pa_hal_interface_unref(m->hal);
3667 pa_device_manager_unref(m->dm);
3669 pa_subscribe_observer_unref(m->subs_ob);
3674 void pa_stream_manager_done(pa_stream_manager *m) {
3681 if (m->comm.comm_hook_device_connection_changed_slot)
3682 pa_hook_slot_free(m->comm.comm_hook_device_connection_changed_slot);
3683 if (m->comm.comm_hook_event_fully_handled_slot)
3684 pa_hook_slot_free(m->comm.comm_hook_event_fully_handled_slot);
3685 pa_communicator_unref(m->comm.comm);
3688 if (m->subscription)
3689 pa_subscription_free(m->subscription);
3691 if (m->sink_input_new_slot)
3692 pa_hook_slot_free(m->sink_input_new_slot);
3693 if (m->sink_input_put_slot)
3694 pa_hook_slot_free(m->sink_input_put_slot);
3695 if (m->sink_input_unlink_slot)
3696 pa_hook_slot_free(m->sink_input_unlink_slot);
3697 if (m->sink_input_state_changed_slot)
3698 pa_hook_slot_free(m->sink_input_state_changed_slot);
3699 if (m->sink_input_move_start_slot)
3700 pa_hook_slot_free(m->sink_input_move_start_slot);
3701 if (m->sink_input_move_finish_slot)
3702 pa_hook_slot_free(m->sink_input_move_finish_slot);
3703 if (m->source_output_new_slot)
3704 pa_hook_slot_free(m->source_output_new_slot);
3705 if (m->source_output_put_slot)
3706 pa_hook_slot_free(m->source_output_put_slot);
3707 if (m->source_output_unlink_slot)
3708 pa_hook_slot_free(m->source_output_unlink_slot);
3709 if (m->source_output_state_changed_slot)
3710 pa_hook_slot_free(m->source_output_state_changed_slot);
3712 if (m->muted_streams) {
3713 PA_HASHMAP_FOREACH(streams, m->muted_streams, state)
3714 pa_idxset_free(streams, NULL);
3715 pa_hashmap_free(m->muted_streams);
3718 if (m->mirroring_streams) {
3719 if (pa_idxset_size(m->mirroring_streams))
3720 update_forwarding_device(m, false);
3721 pa_idxset_free(m->mirroring_streams, NULL);
3724 if (m->stream_parents)
3725 pa_hashmap_free(m->stream_parents);
3728 deinit_stream_map(m);
3732 pa_subscribe_observer_unref(m->subs_ob);
3735 pa_device_manager_unref(m->dm);
3738 pa_hal_interface_remove_message_callback(m->hal, message_cb);
3739 pa_hal_interface_unref(m->hal);