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/timeval.h>
35 #include <pulse/rtclock.h>
37 #include <pulse/xmalloc.h>
38 #include <pulse/proplist.h>
40 #include <pulsecore/module.h>
41 #include <pulsecore/log.h>
42 #include <pulsecore/sink.h>
43 #include <pulsecore/modargs.h>
44 #include <pulsecore/macro.h>
45 #include <pulsecore/shared.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/proplist-util.h>
50 #include "stream-manager.h"
51 #include "stream-manager-priv.h"
52 #include "stream-manager-volume-priv.h"
53 #include "stream-manager-restriction-priv.h"
54 #include "stream-manager-filter-priv.h"
56 #include "stream-manager-dbus-priv.h"
59 #define SHARED_STREAM_MANAGER "tizen-stream-manager"
61 #define IS_AVAILABLE_DIRECTION(stream_type, device_direction) \
62 ((stream_type == STREAM_SINK_INPUT) ? (device_direction & DM_DEVICE_DIRECTION_OUT) : (device_direction & DM_DEVICE_DIRECTION_IN))
63 #define GET_DIRECTION(stream) \
64 ((pa_sink_input_isinstance(stream)) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
65 #define CONVERT_TO_DEVICE_DIRECTION(stream_type) \
66 ((stream_type == STREAM_SINK_INPUT) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
68 #define STREAM_MANAGER_CLIENT_INFO "SOUND_MANAGER_STREAM_INFO" /* The stream info client via sound-manager */
69 #define STREAM_MANAGER_CLIENT_DUCKING "SOUND_MANAGER_STREAM_DUCKING" /* The ducking client via sound-manager */
71 #define DEFAULT_ROLE "media"
72 #define SKIP_ROLE "skip"
73 #define ACTIVE_DEV_REMOVED "removed"
74 #define STRING_NONE "none"
76 /* There are some streams that need to be skipped.
77 * In other words, we do not care about streams that have a name of listed as below */
78 #define NAME_FOR_SKIP_MAX 1
79 static const char* stream_manager_media_names_for_skip[NAME_FOR_SKIP_MAX] = {"pulsesink probe"};
81 #define MUTE_KEY "mute_by_device_disconnection"
82 #define TIMED_UNMUTE_USEC 300000
84 static const char* process_command_type_str[] = {
85 [PROCESS_COMMAND_PREPARE] = "PREPARE",
86 [PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED] = "CHANGE_ROUTE_BY_STREAM_STARTED",
87 [PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED] = "CHANGE_ROUTE_BY_STREAM_ENDED",
88 [PROCESS_COMMAND_CHANGE_ROUTE_BY_STATE_CHANGED_RUNNING] = "CHANGE_ROUTE_BY_STATE_CHANGED_RUNNING",
89 [PROCESS_COMMAND_CHANGE_ROUTE_BY_STATE_CHANGED_CORKED] = "CHANGE_ROUTE_BY_STATE_CHANGED_CORKED",
90 [PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED] = "CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED",
91 [PROCESS_COMMAND_UPDATE_VOLUME] = "UPDATE_VOLUME",
92 [PROCESS_COMMAND_ADD_STREAM] = "ADD_STREAM",
93 [PROCESS_COMMAND_REMOVE_STREAM] = "REMOVE_STREAM",
94 [PROCESS_COMMAND_UPDATE_BUFFER_ATTR] = "UPDATE_BUFFER_ATTR",
95 [PROCESS_COMMAND_APPLY_FILTER] = "APPLY_FILTER",
96 [PROCESS_COMMAND_SET_AEC_REFERENCE_DEVICE] = "SET_AEC_REFERENCE",
99 static const char* notify_command_type_str[] = {
100 [NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT] = "SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT",
101 [NOTIFY_COMMAND_CHANGE_ROUTE_START] = "CHANGE_ROUTE_START",
102 [NOTIFY_COMMAND_CHANGE_ROUTE_END] = "CHANGE_ROUTE_END",
103 [NOTIFY_COMMAND_UPDATE_ROUTE_OPTION] = "UPDATE_ROUTE_OPTION",
104 [NOTIFY_COMMAND_INFORM_STREAM_CONNECTED] = "INFORM_STREAM_CONNECTED",
105 [NOTIFY_COMMAND_INFORM_STREAM_DISCONNECTED] = "INFORM_STREAM_DISCONNECTED",
108 #define STREAM_MAP_FILE PA_DEFAULT_CONFIG_DIR"/stream-map.json"
109 #define STREAM_MAP_VOLUMES "volumes"
110 #define STREAM_MAP_VOLUME_TYPE "type"
111 #define STREAM_MAP_VOLUME_IS_FOR_HAL "is-hal-volume"
112 #define STREAM_MAP_LATENCIES "latencies"
113 #define STREAM_MAP_LATENCY_TYPE "type"
114 #define STREAM_MAP_LATENCY_FRAGSIZE_MS "fragsize-ms"
115 #define STREAM_MAP_LATENCY_TLENGTH_MS "tlength-ms"
116 #define STREAM_MAP_LATENCY_MINREQ_MS "minreq-ms"
117 #define STREAM_MAP_LATENCY_PREBUF_MS "prebuf-ms"
118 #define STREAM_MAP_LATENCY_MAXLENGTH "maxlength"
119 #define STREAM_MAP_STREAMS "streams"
120 #define STREAM_MAP_STREAM_ROLE "role"
121 #define STREAM_MAP_STREAM_PRIORITY "priority"
122 #define STREAM_MAP_STREAM_ROUTE_TYPE "route-type"
123 #define STREAM_MAP_STREAM_DIRECTIONS "directions"
124 #define STREAM_MAP_STREAM_VOLUME_TYPES "volume-types"
125 #define STREAM_MAP_STREAM_VOLUME_TYPE_IN "in"
126 #define STREAM_MAP_STREAM_VOLUME_TYPE_OUT "out"
127 #define STREAM_MAP_STREAM_CAPTURE_VOLUME_TYPE "capture-volume-type"
128 #define STREAM_MAP_STREAM_PLAYBACK_VOLUME_TYPE "playback-volume-type"
129 #define STREAM_MAP_STREAM_AVAIL_IN_DEVICES "avail-in-devices"
130 #define STREAM_MAP_STREAM_AVAIL_OUT_DEVICES "avail-out-devices"
131 #define STREAM_MAP_STREAM_AVAIL_FRAMEWORKS "avail-frameworks"
133 PA_DEFINE_PRIVATE_CLASS(stream_manager_msg, pa_msgobject);
136 MESSAGE_RAMP_FINISHED,
139 struct stream_manager_param {
140 pa_stream_manager *m;
141 pa_sink_input *sink_input;
145 /* Called from main context */
146 static void process_ramp_finish(struct stream_manager_param *param) {
150 /* Find a context id from all the ducked stream list by this stream index.
151 * Check the number of managed streams of the context id, if it is the last one
152 * then broadcast a signal with context id.*/
153 PA_HASHMAP_FOREACH(sd, param->m->stream_duckings, state) {
154 if (!pa_idxset_get_by_data(sd->idx_ducking_streams, param->sink_input, NULL)) {
155 pa_log_debug("not found matched stream(%p, index:%u) in sd(%p)",
156 param->sink_input, param->index, sd);
160 pa_log_info("found matched stream(%p, index:%u) in sd(%p, ducking_stream_count:%d, state:%u)",
161 param->sink_input, param->index, sd, sd->ducking_stream_count, sd->state);
163 if (sd->ducking_stream_count <= 0)
166 /* Remove trigger when unducked */
167 if (sd->state == STREAM_DUCKING_STATE_UNDUCKING)
168 pa_idxset_remove_by_data(sd->idx_ducking_streams, (void *)param->sink_input, NULL);
170 /* Send signal when all streams are ducked.
171 * Note that the condition of increasing count value below is located in
172 * handle_activate_ducking() of DBus handler. */
173 if (--sd->ducking_stream_count == 0) {
174 if (sd->state == STREAM_DUCKING_STATE_DUCKING) {
175 sd->state = STREAM_DUCKING_STATE_DUCKED;
176 } else if (sd->state == STREAM_DUCKING_STATE_UNDUCKING) {
177 sd->state = STREAM_DUCKING_STATE_UNDUCKED;
179 pa_log_warn("sd->state(%d), already ducked or unducked, skip sending signal", sd->state);
183 pa_log_info("send signal for ramp finished - sd(%p, state:%u)", sd, sd->state);
184 send_ducking_state_changed_signal(pa_dbus_connection_get(param->m->dbus_conn), sd->trigger_index, is_stream_ducked(sd));
189 static bool is_valid_notify_command(notify_command_type_t command) {
190 return (command < sizeof(notify_command_type_str) / sizeof(char *));
193 static bool is_valid_process_command(process_command_type_t command) {
194 return (command < sizeof(process_command_type_str) / sizeof(char *));
197 bool is_stream_related_call_active_routing(pa_object *stream) {
198 const char *stream_role;
203 if (pa_sink_input_isinstance(stream))
204 prop = PA_SINK_INPUT(stream)->proplist;
206 prop = PA_SOURCE_OUTPUT(stream)->proplist;
208 if (!(stream_role = pa_proplist_gets(prop, PA_PROP_MEDIA_ROLE)))
211 if (pa_safe_streq(stream_role, STREAM_ROLE_CALL_VOICE) ||
212 pa_safe_streq(stream_role, STREAM_ROLE_CALL_VIDEO) ||
213 pa_safe_streq(stream_role, STREAM_ROLE_VOIP)) {
214 pa_log_info("%s relates call active routing", stream_role);
221 static int32_t convert_route_type(const char *route_type_str, stream_route_type_t *route_type) {
224 pa_assert(route_type);
225 pa_assert(route_type_str);
227 if (pa_safe_streq("auto", route_type_str))
228 *route_type = STREAM_ROUTE_TYPE_AUTO;
229 else if (pa_safe_streq("auto-last-connected", route_type_str))
230 *route_type = STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED;
231 else if (pa_safe_streq("auto-all", route_type_str))
232 *route_type = STREAM_ROUTE_TYPE_AUTO_ALL;
233 else if (pa_safe_streq("manual", route_type_str))
234 *route_type = STREAM_ROUTE_TYPE_MANUAL;
235 else if (pa_safe_streq("manual-ext", route_type_str))
236 *route_type = STREAM_ROUTE_TYPE_MANUAL_EXT;
239 pa_log_error("Not supported route_type(%s)", route_type_str);
245 int32_t get_route_type(void *stream, stream_type_t stream_type, bool is_new_data, stream_route_type_t *stream_route_type) {
246 const char *route_type_str = NULL;
249 pa_assert(stream_route_type);
252 route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, stream_type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
254 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, stream_type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
255 if (!route_type_str) {
256 pa_log_warn("could not get route type from the stream(%p)", stream);
260 if (pa_atoi(route_type_str, (int32_t*)stream_route_type)) {
261 pa_log_error("could not convert route_type_str(%s) to int", route_type_str);
268 int32_t get_stream_info(pa_stream_manager *m, const char *stream_role, stream_info_per_type *info) {
272 stream_info *s = NULL;
273 pa_log_info("role[%s]", stream_role);
274 if (m->stream_infos) {
275 s = pa_hashmap_get(m->stream_infos, stream_role);
277 info->priority = s->priority;
278 info->route_type = s->route_type;
279 for (i = 0; i < STREAM_DIRECTION_MAX; i++) {
280 pa_log_debug(" volume_types[%d] name : %s", i, s->volume_types[i]);
281 info->volume_types[i] = s->volume_types[i];
283 PA_IDXSET_FOREACH(name, s->idx_avail_in_devices, idx) {
284 pa_log_debug(" avail-in-device[%d] name : %s", idx, name);
285 if (idx < AVAIL_DEVICES_MAX) {
286 info->avail_in_devices[idx] = name;
288 pa_log_error(" avail-in-devices, out of range, [%d]", idx);
292 info->num_of_in_devices = pa_idxset_size(s->idx_avail_in_devices);
293 PA_IDXSET_FOREACH(name, s->idx_avail_out_devices, idx) {
294 pa_log_debug(" avail-out-device[%d] name : %s", idx, name);
295 if (idx < AVAIL_DEVICES_MAX) {
296 info->avail_out_devices[idx] = name;
298 pa_log_error(" avail-out-devices, out of range, [%d]", idx);
302 info->num_of_out_devices = pa_idxset_size(s->idx_avail_out_devices);
303 PA_IDXSET_FOREACH(name, s->idx_avail_frameworks, idx) {
304 pa_log_debug(" avail-frameworks[%d] name : %s", idx, name);
305 if (idx < AVAIL_FRAMEWORKS_MAX) {
306 info->avail_frameworks[idx] = name;
308 pa_log_error(" avail-frameworks, out of range, [%d]", idx);
312 info->num_of_frameworks = pa_idxset_size(s->idx_avail_frameworks);
314 /* set variables for error */
316 info->num_of_in_devices = info->num_of_out_devices = info->num_of_frameworks = 1;
317 info->volume_types[0] = info->volume_types[1] = STRING_NONE;
318 info->avail_in_devices[0] = STRING_NONE;
319 info->avail_out_devices[0] = STRING_NONE;
320 info->avail_frameworks[0] = STRING_NONE;
321 pa_log_error("could not find the stream_role : %s", stream_role);
325 pa_log_error("stream_map is not initialized..");
331 int32_t get_available_streams(pa_stream_manager *m, stream_list *list) {
333 stream_info *s = NULL;
337 if (m->stream_infos) {
338 while ((s = pa_hashmap_iterate(m->stream_infos, &state, (const void**)&role))) {
339 if (i < AVAIL_STREAMS_MAX) {
340 list->priorities[i] = s->priority;
341 list->types[i++] = role;
342 pa_log_debug(" [%d] stream_type[%s], priority[%d]", i-1, role, s->priority);
344 pa_log_error(" out of range, [%d]", i);
348 list->num_of_streams = i;
349 pa_log_debug(" num_of_streams[%d]", i);
351 pa_log_error("stream_map is not initialized..");
357 static stream_parent* get_stream_parent(pa_stream_manager *m, pa_object *stream) {
359 const char *parent_id;
360 uint32_t parent_id_u;
365 if (pa_sink_input_isinstance(stream))
366 prop = PA_SINK_INPUT(stream)->proplist;
368 prop = PA_SOURCE_OUTPUT(stream)->proplist;
370 if ((parent_id = pa_proplist_gets(prop, PA_PROP_MEDIA_PARENT_ID)) == NULL) {
371 pa_log_warn("No parent for this stream");
375 if (pa_atou(parent_id, &parent_id_u) < 0) {
376 pa_log_warn("Invalid parent id '%s'", parent_id);
380 return pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(parent_id_u));
383 static pa_idxset* get_route_devices(pa_stream_manager *m, pa_object *stream) {
389 if ((sp = get_stream_parent(m, stream)) == NULL) {
390 pa_log_error("No stream parent for this stream");
394 if (pa_sink_input_isinstance(stream))
395 return sp->idx_route_out_devices;
397 return sp->idx_route_in_devices;
400 static pa_idxset* get_avail_device_types(pa_stream_manager *m, const char *stream_role, dm_device_direction_t direction) {
404 pa_assert(stream_role);
406 if (!(si = pa_hashmap_get(m->stream_infos, stream_role))) {
407 pa_log_warn("not supported role[%s]", stream_role);
411 if (direction == DM_DEVICE_DIRECTION_IN)
412 return si->idx_avail_in_devices;
414 return si->idx_avail_out_devices;
417 static void activate_device_only(pa_stream_manager *m, pa_tz_device *device) {
418 pa_idxset *device_list;
419 pa_tz_device *device_iter;
420 uint32_t device_idx, id, others_id;
421 dm_device_direction_t direction, others_direction;
422 dm_device_state_t others_state;
428 direction = pa_tz_device_get_direction(device);
429 id = pa_tz_device_get_id(device);
430 device_type = pa_tz_device_get_type(device);
432 pa_log_info("activate device(%s/%u)", device_type, id);
434 pa_tz_device_set_state(device, DM_DEVICE_STATE_ACTIVATED);
436 /* deactivate others */
437 device_list = pa_device_manager_get_device_list(m->dm);
438 PA_IDXSET_FOREACH(device_iter, device_list, device_idx) {
439 if (device_iter != device) {
440 others_state = pa_tz_device_get_state(device_iter);
441 if (others_state == DM_DEVICE_STATE_DEACTIVATED)
443 others_direction = pa_tz_device_get_direction(device_iter);
444 others_id = pa_tz_device_get_id(device_iter);
445 if (direction & others_direction) {
446 pa_log_debug("device(%u) is deactivated by device(%u)", others_id, id);
447 pa_tz_device_set_state(device_iter, DM_DEVICE_STATE_DEACTIVATED);
455 pa_tz_device* get_media_auto_device(pa_stream_manager *m, dm_device_direction_t find_direction) {
456 pa_idxset *avail_device_types;
458 pa_tz_device *device;
459 dm_device_direction_t direction;
464 pa_log_info("get media auto device for %d", find_direction);
466 if (find_direction == DM_DEVICE_DIRECTION_BOTH) {
467 pa_log_error("Invalid direction");
471 avail_device_types = get_avail_device_types(m, STREAM_ROLE_MEDIA, find_direction);
472 if (!avail_device_types) {
473 pa_log_error("No avail device types for media");
477 PA_IDXSET_FOREACH(device_type, avail_device_types, idx) {
478 device = pa_device_manager_get_device(m->dm, device_type, NULL);
481 direction = pa_tz_device_get_direction(device);
482 if (find_direction & direction)
489 pa_tz_device* get_media_last_device(pa_stream_manager *m, dm_device_direction_t find_direction) {
491 pa_tz_device *next_device, *latest_device = NULL;
492 pa_usec_t creation_time = 0;
493 pa_usec_t latest_creation_time = 0;
494 dm_device_direction_t direction;
495 pa_idxset *avail_device_types;
500 pa_log_info("get media last device for %d", find_direction);
502 /* Only can get playback or capture device one by one */
503 if (find_direction == DM_DEVICE_DIRECTION_BOTH) {
504 pa_log_error("Invalid direction");
508 avail_device_types = get_avail_device_types(m, STREAM_ROLE_MEDIA, find_direction);
509 if (!avail_device_types) {
510 pa_log_error("No avail device types for media");
514 PA_IDXSET_FOREACH(device_type, avail_device_types, idx) {
515 if ((next_device = pa_device_manager_get_device(m->dm, device_type, NULL))) {
516 creation_time = pa_tz_device_get_creation_time(next_device);
517 direction = pa_tz_device_get_direction(next_device);
518 if ((find_direction & direction) == 0)
520 if (!latest_device || (latest_creation_time <= creation_time)) {
521 latest_device = next_device;
522 latest_creation_time = creation_time;
528 id = pa_tz_device_get_id(latest_device);
529 device_type = pa_tz_device_get_type(latest_device);
530 pa_log_info("last %s-device: (%s/%u)", find_direction == DM_DEVICE_DIRECTION_IN ? "in" : "out", device_type, id);
532 pa_log_info("no %s-device", find_direction == DM_DEVICE_DIRECTION_IN ? "in" : "out");
535 return latest_device;
538 /* select device which of type is in avail_device_types, and highest priority in
540 static pa_tz_device* select_device_from_avail_device_types(pa_stream_manager *m,
541 pa_idxset *avail_device_types, pa_idxset *route_devices) {
543 uint32_t route_device_idx, device_type_idx;
544 uint32_t *route_device_id;
545 pa_tz_device *device, *candidate = NULL;
549 if (avail_device_types == NULL)
551 if (route_devices == NULL)
554 PA_IDXSET_FOREACH(device_type, avail_device_types, device_type_idx) {
555 PA_IDXSET_FOREACH(route_device_id, route_devices, route_device_idx) {
556 pa_log_warn("device id for call : %u", *route_device_id);
557 device = pa_device_manager_get_device_by_id(m->dm, *route_device_id);
559 pa_log_info("Found higher priority device(%u)", *route_device_id);
568 /* stream should be one of 'call-voice' or 'call-video' or 'voip' */
569 int change_active_route_for_call(pa_stream_manager *m, pa_object *stream, bool stream_start) {
570 pa_tz_device *device;
571 pa_idxset *avail_device_types, *route_devices;
573 const char *stream_role;
576 pa_assert(m->stream_parents);
579 pa_log_info("Call virtual stream %s, change active device", stream_start ? "start" : "change");
581 if (pa_sink_input_isinstance(stream))
582 prop = PA_SINK_INPUT(stream)->proplist;
584 prop = PA_SOURCE_OUTPUT(stream)->proplist;
586 stream_role = pa_proplist_gets(prop, PA_PROP_MEDIA_ROLE);
588 avail_device_types = get_avail_device_types(m, stream_role, GET_DIRECTION(stream));
589 if (!avail_device_types) {
590 pa_log_error("No avail device typs for [%s]", stream_role);
593 route_devices = get_route_devices(m, stream);
594 if (!route_devices) {
595 pa_log_error("No route devices for this stream");
599 device = select_device_from_avail_device_types(m, avail_device_types, route_devices);
601 pa_log_error("Failed to select device to activate on call state");
604 activate_device_only(m, device);
609 bool is_active_device_of_stream(const void *stream, stream_type_t stream_type, const char *device_type) {
610 const char *active_dev;
613 pa_assert(device_type);
615 active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(stream, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
616 if (pa_safe_streq(active_dev, device_type)) {
617 pa_log_info("stream(%p)'s active_dev(%s) is same as device_type(%s)", stream, active_dev, device_type);
624 bool is_stream_ducked(stream_ducking *sd) {
627 pa_log_info("state : %u", sd->state);
630 case STREAM_DUCKING_STATE_DUCKED:
631 case STREAM_DUCKING_STATE_UNDUCKING:
633 case STREAM_DUCKING_STATE_UNDUCKED:
634 case STREAM_DUCKING_STATE_DUCKING:
640 static void set_media_active_device(pa_stream_manager *m) {
641 pa_tz_device *playback_device, *capture_device;
642 stream_info *media_info;
645 pa_assert(m->stream_infos);
647 pa_log_info("set media active device");
649 media_info = pa_hashmap_get(m->stream_infos, STREAM_ROLE_MEDIA);
651 pa_log_warn("No media roles");
655 if (media_info->route_type == STREAM_ROUTE_TYPE_AUTO) {
656 playback_device = get_media_auto_device(m, DM_DEVICE_DIRECTION_OUT);
657 capture_device = get_media_auto_device(m, DM_DEVICE_DIRECTION_IN);
658 } else if(media_info->route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
659 playback_device = get_media_last_device(m, DM_DEVICE_DIRECTION_OUT);
660 capture_device = get_media_last_device(m, DM_DEVICE_DIRECTION_IN);
662 pa_log_error("unexpected routing type for media[%d]", media_info->route_type);
667 activate_device_only(m, playback_device);
669 pa_log_info("There is no playback-device");
672 activate_device_only(m, capture_device);
674 pa_log_info("There is no capture-device");
677 static void set_initial_active_device(pa_stream_manager *m) {
680 set_media_active_device(m);
683 static void dump_stream_map(pa_stream_manager *m) {
684 stream_info *s = NULL;
685 const char *role = NULL;
690 pa_log_debug("==========[START stream-map dump]==========");
691 while (m->stream_infos && (s = pa_hashmap_iterate(m->stream_infos, &state, (const void **)&role))) {
692 pa_log_debug("[role : %s]", role);
693 pa_log_debug(" - priority : %d", s->priority);
694 pa_log_debug(" - route-type : %d (0:auto,1:auto-last-conn,2:auto-all,3:manual,4:manual-ext)", s->route_type);
695 pa_log_debug(" - volume-types : in[%s], out[%s]", s->volume_types[STREAM_DIRECTION_IN], s->volume_types[STREAM_DIRECTION_OUT]);
696 pa_log_debug(" - avail-in-devices");
697 PA_IDXSET_FOREACH(name, s->idx_avail_in_devices, idx)
698 pa_log_debug(" name[%d] : %s", idx, name);
699 pa_log_debug(" - avail-out-devices");
700 PA_IDXSET_FOREACH(name, s->idx_avail_out_devices, idx)
701 pa_log_debug(" name[%d] : %s", idx, name);
702 pa_log_debug(" - avail-frameworks");
703 PA_IDXSET_FOREACH(name, s->idx_avail_frameworks, idx)
704 pa_log_debug(" name[%d] : %s", idx, name);
706 pa_log_debug("===========[END stream-map dump]===========");
709 static void stream_info_free(stream_info *s) {
712 if (s->idx_avail_in_devices)
713 pa_idxset_free(s->idx_avail_in_devices, NULL);
714 if (s->idx_avail_out_devices)
715 pa_idxset_free(s->idx_avail_out_devices, NULL);
716 if (s->idx_avail_frameworks)
717 pa_idxset_free(s->idx_avail_frameworks, NULL);
722 static void deinit_stream_map(pa_stream_manager *m) {
725 if (m->stream_infos) {
726 pa_hashmap_free(m->stream_infos);
727 m->stream_infos = NULL;
729 if (m->volume_infos) {
730 pa_hashmap_free(m->volume_infos);
731 m->volume_infos = NULL;
733 if (m->latency_infos) {
734 pa_hashmap_free(m->latency_infos);
735 m->latency_infos = NULL;
739 static int get_latency_infos(json_object *o, pa_hashmap *latency_infos) {
740 json_object *array_o;
741 json_object *array_item_o;
743 int array_length = 0;
744 const char *type = NULL;
745 latency_info *l = NULL;
748 pa_assert(latency_infos);
751 if (json_object_object_get_ex(o, STREAM_MAP_LATENCIES, &array_o) && json_object_is_type(array_o, json_type_array)) {
752 array_length = json_object_array_length(array_o);
753 for (i = 0; i < array_length; i++) {
754 if ((array_item_o = json_object_array_get_idx(array_o, i)) && json_object_is_type(array_item_o, json_type_object)) {
755 l = pa_xmalloc0(sizeof(latency_info));
756 pa_log_debug("latency found [%d]", i);
757 if (!json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_TYPE, &item_o) || !json_object_is_type(item_o, json_type_string)) {
758 pa_log_error("Get type failed");
761 type = json_object_get_string(item_o);
762 pa_log_debug(" - type : %s", type);
764 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)) {
765 pa_log_error("Get fragsize-ms failed");
768 l->fragsize_ms = json_object_get_int(item_o);
769 pa_log_debug(" - fragsize-ms : %d", l->fragsize_ms);
771 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)) {
772 pa_log_error("Get tlength-ms failed");
775 l->tlength_ms = json_object_get_int(item_o);
776 pa_log_debug(" - tlength-ms : %d", l->tlength_ms);
778 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)) {
779 pa_log_error("Get minreq-ms failed");
782 l->minreq_ms = json_object_get_int(item_o);
783 pa_log_debug(" - minreq-ms : %d", l->minreq_ms);
785 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)) {
786 pa_log_error("Get prebuf-ms failed");
789 l->prebuf_ms = json_object_get_int(item_o);
790 pa_log_debug(" - prebuf-ms : %d", l->prebuf_ms);
792 if (!json_object_object_get_ex(array_item_o, STREAM_MAP_LATENCY_MAXLENGTH, &item_o) || !json_object_is_type(item_o, json_type_int)) {
793 pa_log_error("Get maxlength failed");
796 l->maxlength = json_object_get_int(item_o);
797 pa_log_debug(" - maxlength : %d", l->maxlength);
799 pa_hashmap_put(latency_infos, (void*)type, l);
811 static int get_volume_infos(json_object *o, pa_hashmap *volume_infos) {
812 json_object *array_o;
813 json_object *array_item_o;
815 int array_length = 0;
816 const char *type = NULL;
817 volume_info *v = NULL;
820 pa_assert(volume_infos);
823 if (json_object_object_get_ex(o, STREAM_MAP_VOLUMES, &array_o) && json_object_is_type(array_o, json_type_array)) {
824 array_length = json_object_array_length(array_o);
825 for (i = 0; i < array_length; i++) {
826 if ((array_item_o = json_object_array_get_idx(array_o, i)) && json_object_is_type(array_item_o, json_type_object)) {
827 v = pa_xmalloc0(sizeof(volume_info));
828 pa_log_debug("volume found [%d]", i);
829 if (!json_object_object_get_ex(array_item_o, STREAM_MAP_VOLUME_TYPE, &item_o) || !json_object_is_type(item_o, json_type_string)) {
830 pa_log_error("Get volume type failed");
833 type = json_object_get_string(item_o);
834 pa_log_debug(" - type : %s", type);
836 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)) {
837 pa_log_error("Get is-hal-volume failed");
840 v->is_hal_volume_type = (bool)json_object_get_int(item_o);
841 pa_log_debug(" - is-hal-volume : %d", v->is_hal_volume_type);
843 pa_hashmap_put(volume_infos, (void*)type, v);
855 static int get_stream_infos(json_object *o, pa_hashmap *stream_infos) {
856 json_object *array_o;
857 json_object *array_item_o;
859 json_object *sub_array_o;
860 int array_length = 0;
861 int sub_array_length = 0;
862 const char *role = NULL;
863 stream_info *s = NULL;
864 json_object *out_device_o;
865 json_object *in_device_o;
866 json_object *framework_o;
869 pa_assert(stream_infos);
872 if (json_object_object_get_ex(o, STREAM_MAP_STREAMS, &array_o) && json_object_is_type(array_o, json_type_array)) {
873 array_length = json_object_array_length(array_o);
874 for (i = 0; i < array_length; i++) {
875 if ((array_item_o = json_object_array_get_idx(array_o, i)) && json_object_is_type(array_item_o, json_type_object)) {
876 s = pa_xmalloc0(sizeof(stream_info));
877 pa_log_debug("stream found [%d]", i);
878 if (!json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_ROLE, &item_o) || !json_object_is_type(item_o, json_type_string)) {
879 pa_log_error("Get stream role failed");
882 role = json_object_get_string(item_o);
883 pa_log_debug(" - role : %s", role);
885 if (!json_object_object_get_ex(array_item_o, STREAM_MAP_STREAM_PRIORITY, &item_o) || !json_object_is_type(item_o, json_type_int)) {
886 pa_log_error("Get stream priority failed");
889 s->priority = json_object_get_int(item_o);
890 pa_log_debug(" - priority : %d", s->priority);
892 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)) {
893 pa_log_error("Get stream route-type failed");
896 if (convert_route_type(json_object_get_string(item_o), &(s->route_type))) {
897 pa_log_error("convert stream route-type failed");
900 pa_log_debug(" - route-type : %d", s->route_type);
902 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)) {
903 pa_log_error("Get stream volume-types failed");
906 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)) {
907 pa_log_error("Get stream volume-type-in failed");
910 s->volume_types[STREAM_DIRECTION_IN] = json_object_get_string(item_o);
912 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)) {
913 pa_log_error("Get stream volume-type-out failed");
916 s->volume_types[STREAM_DIRECTION_OUT] = json_object_get_string(item_o);
918 pa_log_debug(" - volume-types : in[%s], out[%s]", s->volume_types[STREAM_DIRECTION_IN], s->volume_types[STREAM_DIRECTION_OUT]);
920 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)) {
921 pa_log_error("Get stream avail-in-devices failed");
924 s->idx_avail_in_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
925 sub_array_length = json_object_array_length(sub_array_o);
926 pa_log_debug(" - avail-in-devices");
927 for (j = 0; j < sub_array_length; j++) {
928 if ((in_device_o = json_object_array_get_idx(sub_array_o, j)) && json_object_is_type(in_device_o, json_type_string)) {
929 pa_idxset_put(s->idx_avail_in_devices, (void*)json_object_get_string(in_device_o), NULL);
930 pa_log_debug(" device[%d] : %s", j, json_object_get_string(in_device_o));
934 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)) {
935 pa_log_error("Get stream avail-out-devices failed");
938 s->idx_avail_out_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
939 sub_array_length = json_object_array_length(sub_array_o);
940 pa_log_debug(" - avail-out-devices");
941 for (j = 0; j < sub_array_length; j++) {
942 if ((out_device_o = json_object_array_get_idx(sub_array_o, j)) && json_object_is_type(out_device_o, json_type_string)) {
943 pa_idxset_put(s->idx_avail_out_devices, (void*)json_object_get_string(out_device_o), NULL);
944 pa_log_debug(" device[%d] : %s", j, json_object_get_string(out_device_o));
948 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)) {
949 pa_log_error("Get stream avail-frameworks failed");
952 s->idx_avail_frameworks = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
953 sub_array_length = json_object_array_length(sub_array_o);
954 pa_log_debug(" - avail-frameworks");
955 for (j = 0; j < sub_array_length; j++) {
956 if ((framework_o = json_object_array_get_idx(sub_array_o, j)) && json_object_is_type(framework_o, json_type_string)) {
957 pa_idxset_put(s->idx_avail_frameworks, (void*)json_object_get_string(framework_o), NULL);
958 pa_log_debug(" framework[%d] : %s", j, json_object_get_string(framework_o));
962 pa_hashmap_put(stream_infos, (void*)role, s);
974 static int init_stream_map(pa_stream_manager *m) {
979 o = json_object_from_file(STREAM_MAP_FILE);
981 pa_log_error("Read stream-map file(%s) failed", STREAM_MAP_FILE);
986 m->latency_infos = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
987 if (get_latency_infos(o, m->latency_infos))
991 m->volume_infos = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
992 if (get_volume_infos(o, m->volume_infos))
996 m->stream_infos = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t)stream_info_free);
997 if (get_stream_infos(o, m->stream_infos))
1005 pa_log_error("failed to initialize stream-map");
1006 deinit_stream_map(m);
1011 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) {
1013 const char *name = NULL;
1014 const char *role = NULL;
1020 if (command == PROCESS_COMMAND_PREPARE && is_new_data) {
1021 if ((name = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_NAME))) {
1022 for (i = 0; i < NAME_FOR_SKIP_MAX; i++)
1023 if (pa_safe_streq(name, stream_manager_media_names_for_skip[i])) {
1025 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE, SKIP_ROLE);
1028 pa_log_info("name is [%s], skip(%d) command(%s)", name, ret, process_command_type_str[command]);
1032 role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
1034 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
1036 if (pa_safe_streq(role, SKIP_ROLE))
1043 static bool is_invalid_role(pa_stream_manager *m, const char *role) {
1044 stream_info *s = NULL;
1047 pa_assert(m->stream_infos);
1050 if (!(s = pa_hashmap_get(m->stream_infos, role))) {
1051 pa_log_warn("invalid role[%s], skip it", role);
1058 static bool is_invalid_route_type(const char *route_type_str, stream_route_type_t *route_type) {
1059 int32_t _route_type;
1061 pa_assert(route_type_str);
1063 if (pa_atoi(route_type_str, &_route_type)) {
1064 pa_log_error("could not convert route_type_str(%s) to int", route_type_str);
1069 *route_type = (stream_route_type_t)_route_type;
1074 static bool check_name_is_vstream(void *stream, stream_type_t type, bool is_new_data) {
1076 const char *name = NULL;
1081 name = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_NAME);
1083 name = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_NAME);
1085 if (pa_safe_streq(name, VIRTUAL_STREAM_NAME)) {
1087 pa_log_debug("name is [%s]", name);
1093 static bool update_priority_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, const char *role, bool is_new_data) {
1095 stream_info *s = NULL;
1099 pa_assert(m->stream_infos);
1102 if ((s = pa_hashmap_get(m->stream_infos, role))) {
1104 pa_proplist_set(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void*)&(s->priority), sizeof(s->priority));
1106 pa_proplist_set(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void*)&(s->priority), sizeof(s->priority));
1113 static bool update_route_type_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, const char *role) {
1115 stream_route_type_t route_type = STREAM_ROUTE_TYPE_AUTO;
1116 stream_info *s = NULL;
1120 pa_assert(m->stream_infos);
1123 if ((s = pa_hashmap_get(m->stream_infos, role))) {
1124 route_type = s->route_type;
1125 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE, "%d", route_type);
1132 static bool update_volume_type_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, const char *role) {
1134 const char *volume_type = NULL;
1135 stream_info *s = NULL;
1139 pa_assert(m->stream_infos);
1142 if ((s = pa_hashmap_get(m->stream_infos, role)))
1143 volume_type = s->volume_types[type];
1145 if (volume_type && (!pa_safe_streq(volume_type, "none"))) {
1146 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_TIZEN_VOLUME_TYPE, volume_type);
1149 pa_log_warn("this stream[%p] does not have any volume type, skip updating volume type. stream_type[%d], role[%s]", stream, type, role);
1154 static void _set_focus_status_forcedly_if_needed(pa_stream_manager *m, void *stream, stream_type_t type, bool is_new_data)
1156 const char *role = NULL;
1157 pa_proplist* p = NULL;
1162 p = (is_new_data ? GET_STREAM_NEW_PROPLIST(stream, type) : GET_STREAM_PROPLIST(stream, type));
1166 if ((role = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE))) {
1167 if (!pa_hashmap_get(m->stream_infos, role)) {
1168 pa_log_warn("undefined role[%s], skip it", role);
1171 if (pa_safe_streq(role, STREAM_ROLE_SOLO)) {
1172 /* skip updating focus status for solo stream during the most important stream type is occupied. */
1173 if (m->cur_highest_priority.sink_input) {
1174 const char *highest_role;
1175 pa_proplist *prop = GET_STREAM_PROPLIST(m->cur_highest_priority.sink_input, STREAM_SINK_INPUT);
1176 if ((highest_role = pa_proplist_gets(prop, PA_PROP_MEDIA_ROLE))) {
1177 if (IS_ROLE_COMMUNICATION(highest_role)) {
1178 pa_log_warn("current highest priority role is [%s], skip updating focus for [%s] stream", highest_role, role);
1183 pa_log_info("This stream is [%s], update focus status to STREAM_FOCUS_STATE_ACQUIRED", role);
1184 pa_proplist_setf(p, PA_PROP_MEDIA_FOCUS_STATUS, "%s", STREAM_FOCUS_STATE_ACQUIRED);
1186 /* FIXME : this code is workaround code and needed to be revised soon */
1187 else if (pa_safe_streq(role, STREAM_ROLE_RADIO)) {
1188 pa_log_info("This stream is [%s], update focus status to STREAM_FOCUS_STATE_ACQUIRED", role);
1189 pa_proplist_setf(p, PA_PROP_MEDIA_FOCUS_STATUS, "%s", STREAM_FOCUS_STATE_ACQUIRED);
1194 static bool update_focus_status_of_stream(pa_stream_manager *m, void *stream, stream_type_t type, bool is_new_data) {
1196 uint32_t parent_idx = 0;
1197 stream_parent *sp = NULL;
1203 p_idx = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1205 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1206 if (p_idx && !pa_atou(p_idx, &parent_idx)) {
1207 if (!(sp = pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(parent_idx)))) {
1208 pa_log_error("could not find matching client for this parent_id(%u)", parent_idx);
1212 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_FOCUS_STATUS, "%u", sp->focus_status);
1214 pa_proplist_setf(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_FOCUS_STATUS, "%u", sp->focus_status);
1216 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);
1219 /* set focus status regardless of parent id existence */
1220 _set_focus_status_forcedly_if_needed(m, stream, type, is_new_data);
1225 static void update_preferred_device_role(pa_stream_manager *m, void *stream, stream_type_t type) {
1226 const char *p_idx_str;
1227 uint32_t parent_idx = 0;
1228 stream_parent *sp = NULL;
1233 p_idx_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1234 if (!p_idx_str || pa_atou(p_idx_str, &parent_idx))
1237 if (!(sp = pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(parent_idx)))) {
1238 pa_log_error("could not find matching client for this parent_id(%u)", parent_idx);
1242 if (sp->preferred_device.roles[type])
1243 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(stream, type),
1244 PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE,
1245 sp->preferred_device.roles[type]);
1248 static void get_preferred_device_type_and_role(pa_stream_manager *m, void *stream, stream_type_t stream_type, const char **type, const char **role) {
1249 const char *p_idx_str;
1250 uint32_t parent_idx = 100;
1251 stream_parent *sp = NULL;
1258 p_idx_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, stream_type), PA_PROP_MEDIA_PARENT_ID);
1259 if (!p_idx_str || pa_atou(p_idx_str, &parent_idx)) {
1262 if (!(sp = pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(parent_idx)))) {
1263 pa_log_warn("could not find matching client for this parent_id(%u)", parent_idx);
1267 *type = sp->preferred_device.types[stream_type];
1268 *role = sp->preferred_device.roles[stream_type];
1270 pa_log_debug("%s(%u), parent_id(%u), preferred type(%s), role(%s)",
1271 stream_type == STREAM_SINK_INPUT ? "sink-input" : "source-output",
1272 stream_type == STREAM_SINK_INPUT ? PA_SINK_INPUT(stream)->index : PA_SOURCE_OUTPUT(stream)->index,
1273 parent_idx, *type, *role);
1276 static bool update_stream_parent_info(pa_stream_manager *m, process_command_type_t command, stream_type_t type, void *stream) {
1278 uint32_t parent_idx;
1279 stream_parent *sp = NULL;
1284 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1285 if (p_idx && !pa_atou(p_idx, &parent_idx)) {
1286 pa_log_debug("p_idx(%s), idx(%u)", p_idx, parent_idx);
1287 sp = pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(parent_idx));
1289 uint32_t idx = GET_STREAM_INDEX(stream, type);
1290 if (command == PROCESS_COMMAND_ADD_STREAM) {
1291 /* append this stream to the parent stream info. */
1292 pa_log_debug(" - append this stream(%p, %u) to the list. sp(%p), stream_type(%d)", stream, idx, sp, type);
1293 pa_idxset_put(type == STREAM_SINK_INPUT ? (sp->idx_sink_inputs) : (sp->idx_source_outputs), stream, NULL);
1295 } else if (command == PROCESS_COMMAND_REMOVE_STREAM) {
1296 /* remove this stream from the parent stream info. */
1297 pa_log_debug(" - remove this stream(%p, %u) from the list. sp(%p), stream_type(%d)", stream, idx, sp, type);
1298 pa_idxset_remove_by_data(type == STREAM_SINK_INPUT ? (sp->idx_sink_inputs) : (sp->idx_source_outputs), stream, NULL);
1301 pa_log_error("invalid process command(%d)", command);
1305 pa_log_error("could not find matching client for this parent_id(%u)", parent_idx);
1314 static bool update_the_highest_priority_stream(pa_stream_manager *m, process_command_type_t command, void *mine,
1315 stream_type_t type, const char *role, bool is_new_data, bool *need_to_update) {
1318 const int32_t *priority = NULL;
1319 const char *route_type_str = NULL;
1320 stream_route_type_t route_type;
1321 const char *focus_status_str = NULL;
1322 const char *active_dev = NULL;
1323 void *cur_max_stream = NULL;
1324 void *cur_max_stream_tmp = NULL;
1325 const int32_t *cur_max_priority = NULL;
1326 const char *cur_max_role = NULL;
1327 int32_t cur_max_focus_status = 0;
1328 int32_t focus_status = 0;
1330 const char *_role = NULL;
1331 pa_idxset *streams = NULL;
1332 pa_sink *sink = NULL;
1333 pa_source *source = NULL;
1334 bool is_vstream = false;
1339 pa_log_error("invalid input, role(%s)", pa_strnull(role));
1343 *need_to_update = false;
1345 if (type == STREAM_SINK_INPUT) {
1346 cur_max_stream = m->cur_highest_priority.sink_input;
1347 } else if (type == STREAM_SOURCE_OUTPUT) {
1348 cur_max_stream = m->cur_highest_priority.source_output;
1351 pa_log_debug("stream_type(%d), role(%s), command(%d), is_new_data(%d)", type, role, command, is_new_data);
1352 if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED) {
1353 /* get focus status, route type */
1355 focus_status_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_FOCUS_STATUS);
1356 route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
1358 focus_status_str = pa_proplist_gets(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_FOCUS_STATUS);
1359 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
1361 if (focus_status_str && !pa_atoi(focus_status_str, &focus_status)) {
1362 pa_log_debug("focus status(0x%x) of mine", focus_status);
1364 /* check if this stream is for external device with ROUTE_TYPE_AUTO */
1365 if (IS_ROUTE_TYPE_FOR_AUTO(route_type_str, route_type) && is_new_data) {
1366 if (type == STREAM_SINK_INPUT)
1367 sink = ((pa_sink_input_new_data*)mine)->sink;
1369 source = ((pa_source_output_new_data*)mine)->source;
1370 is_vstream = check_name_is_vstream(mine, type, is_new_data);
1371 if (!is_vstream && ((sink && !(sink->use_internal_codec)) || (source && !(source->use_internal_codec)))) {
1372 pa_log_warn("stream(%p) uses external device, skip it", mine);
1373 *need_to_update = false;
1377 if (cur_max_stream == NULL) {
1378 *need_to_update = true;
1379 pa_log_debug("set cur_highest to mine");
1382 if (pa_proplist_get(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&priority, &size))
1383 pa_log_error("failed to pa_proplist_get() for priority");
1385 if (pa_proplist_get(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&priority, &size))
1386 pa_log_error("failed to pa_proplist_get() for priority");
1388 if (pa_proplist_get(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&cur_max_priority, &size))
1389 pa_log_error("failed to pa_proplist_get() for priority");
1390 focus_status_str = pa_proplist_gets(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_FOCUS_STATUS);
1391 if (focus_status_str && !pa_atoi(focus_status_str, &cur_max_focus_status)) {
1392 pa_log_debug("cur_max_focus status(0x%x)", cur_max_focus_status);
1394 cur_max_role = pa_proplist_gets(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_ROLE);
1395 if (!cur_max_priority || !cur_max_role) {
1396 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);
1399 if (priority && cur_max_priority) {
1400 if (GET_FOCUS_STATUS(focus_status, type) ||
1401 (!GET_FOCUS_STATUS(cur_max_focus_status, type) && *priority >= *cur_max_priority)) {
1402 *need_to_update = true;
1403 pa_log_debug("update cur_highest to mine(%s)", role);
1405 /* no need to trigger,
1406 * update active device info if possible */
1407 if (IS_ROUTE_TYPE_FOR_AUTO(route_type_str, route_type) || IS_ROUTE_TYPE_FOR_AUTO_LAST_CONN(route_type_str, route_type)) {
1408 if ((active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(cur_max_stream, type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
1410 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(mine, type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, active_dev);
1412 pa_proplist_sets(GET_STREAM_PROPLIST(mine, type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, active_dev);
1420 } else if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED ||
1421 command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED) {
1422 if (cur_max_stream == mine || command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED) {
1423 if (type == STREAM_SINK_INPUT) {
1424 streams = m->core->sink_inputs;
1425 } else if (type == STREAM_SOURCE_OUTPUT) {
1426 streams = m->core->source_outputs;
1428 /* find the next highest priority input */
1429 PA_IDXSET_FOREACH(i, streams, idx) {
1430 if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED && i == mine)
1432 if (!CHECK_STREAM_RUNNING(i, type) || CHECK_STREAM_UNLINKED(i, type))
1434 if (!(_role = pa_proplist_gets(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_ROLE))) {
1435 pa_log_error("failed to pa_proplist_gets() for role");
1438 if (pa_proplist_get(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&priority, &size)) {
1439 pa_log_warn("failed to pa_proplist_get() for priority, skip it");
1442 if (!(route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
1443 pa_log_warn("failed to pa_proplist_get() for route_type, skip it");
1445 } else if (IS_ROUTE_TYPE_FOR_EXTERNAL_DEV(route_type_str, route_type)) {
1446 pa_log_warn("stream(%p) has the route type for external device, skip it", i);
1449 is_vstream = check_name_is_vstream(i, type, false);
1451 (type == STREAM_SINK_INPUT ? !(((pa_sink_input*)i)->sink->use_internal_codec) :
1452 !(((pa_source_output*)i)->source->use_internal_codec))) {
1453 pa_log_warn("stream(%p) uses external audio codec, skip it", i);
1457 if (!(focus_status_str = pa_proplist_gets(GET_STREAM_PROPLIST(i, type), PA_PROP_MEDIA_FOCUS_STATUS))) {
1458 pa_log_warn("failed to pa_proplist_gets() for focus status");
1460 if (!pa_atoi(focus_status_str, &focus_status))
1461 pa_log_debug("focus status(0x%x)", focus_status);
1463 pa_log_debug("role(%s)/priority(%p)/route_type(%d)/focus_status(0x%x)/stream(%p)", _role, priority, route_type, focus_status, i);
1464 if (cur_max_priority == NULL) {
1465 cur_max_priority = priority;
1466 cur_max_focus_status = focus_status;
1467 cur_max_stream_tmp = i;
1468 cur_max_role = _role;
1470 if (cur_max_priority && priority) {
1471 if (GET_FOCUS_STATUS(cur_max_focus_status, type) ||
1472 (!GET_FOCUS_STATUS(focus_status, type) && (*cur_max_priority > *priority))) {
1475 cur_max_priority = priority;
1476 cur_max_focus_status = focus_status;
1477 cur_max_stream_tmp = i;
1478 cur_max_role = _role;
1482 pa_log_debug("updated max priority(%p)/stream(%p)", cur_max_priority, cur_max_stream_tmp);
1483 if (cur_max_stream_tmp) {
1484 if (type == STREAM_SINK_INPUT) {
1485 m->cur_highest_priority.sink_input = cur_max_stream_tmp;
1486 m->cur_highest_priority.role_si = cur_max_role;
1487 } else if (type == STREAM_SOURCE_OUTPUT) {
1488 m->cur_highest_priority.source_output = cur_max_stream_tmp;
1489 m->cur_highest_priority.role_so = cur_max_role;
1492 if (type == STREAM_SINK_INPUT) {
1493 m->cur_highest_priority.sink_input = NULL;
1494 m->cur_highest_priority.role_si = NULL;
1495 } else if (type == STREAM_SOURCE_OUTPUT) {
1496 m->cur_highest_priority.source_output = NULL;
1497 m->cur_highest_priority.role_so = NULL;
1500 *need_to_update = true;
1501 pa_log_info("need to update: type(%d), cur_highest_priority(sink_input=%p[%s]/source_output=%p[%s])",
1502 type, (void*)m->cur_highest_priority.sink_input, m->cur_highest_priority.role_si,
1503 (void*)m->cur_highest_priority.source_output, m->cur_highest_priority.role_so);
1505 /* no need to trigger */
1506 pa_log_info("no need to update: type(%d), cur_highest_priority(sink_input=%p[%s]/source_output=%p[%s])",
1507 type, (void*)m->cur_highest_priority.sink_input, m->cur_highest_priority.role_si,
1508 (void*)m->cur_highest_priority.source_output, m->cur_highest_priority.role_so);
1515 /* Update buffer attributes to new stream */
1516 static void update_buffer_attribute(pa_stream_manager *m, void *new_data, stream_type_t stream_type, bool is_new_data) {
1517 pa_sample_spec *sample_spec;
1518 const char *latency;
1520 int32_t maxlength = -1;
1522 int32_t tlength = -1;
1523 int32_t minreq = -1;
1524 /* Note: pulseaudio recommends to set it to -1, but there's an issue related
1525 * to a stream from gstreamer pulsesink which set this value to 0, so we set
1526 * default value to 0 temporarily. */
1528 int32_t tlength_ms = -1;
1529 int32_t minreq_ms = -1;
1530 int32_t prebuf_ms = -1;
1531 /* recording only */
1532 int32_t fragsize = -1;
1533 int32_t fragsize_ms = -1;
1536 pa_assert(new_data);
1539 pa_log_warn("updating buffer attribute is only for new data, skip it");
1543 if ((latency = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY)))
1544 pa_log_info("audio_latency : %s", latency);
1546 pa_log_warn("failed to get audio_latency");
1550 if (!(li = pa_hashmap_get(m->latency_infos, latency))) {
1551 pa_log_warn("not support this latency type[%s]", latency);
1555 maxlength = li->maxlength;
1556 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_MAXLENGTH, "%d", maxlength);
1558 sample_spec = GET_STREAM_NEW_SAMPLE_SPEC_PTR(new_data, stream_type);
1559 pa_log_info("*** sample_spec: format(%d), rate(%u), channels(%u)",
1560 sample_spec->format, sample_spec->rate, sample_spec->channels);
1562 if (stream_type == STREAM_SINK_INPUT) {
1563 tlength_ms = li->tlength_ms;
1564 minreq_ms = li->minreq_ms;
1565 prebuf_ms = li->prebuf_ms;
1568 minreq = pa_usec_to_bytes(minreq_ms * 1000, sample_spec);
1570 tlength = pa_usec_to_bytes(tlength_ms * 1000, sample_spec);
1572 prebuf = pa_usec_to_bytes(prebuf_ms * 1000, sample_spec);
1576 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_TLENGTH, "%d", tlength);
1577 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_MINREQ, "%d", minreq);
1578 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_PREBUF, "%d", prebuf);
1580 pa_log_info("*** maxlength:%d, tlength:%d, prebuf:%d, minreq:%d", maxlength, tlength, prebuf, minreq);
1582 } else if (stream_type == STREAM_SOURCE_OUTPUT) {
1583 fragsize_ms = li->fragsize_ms;
1585 if (fragsize_ms > 0)
1586 fragsize = pa_usec_to_bytes(fragsize_ms * 1000, sample_spec);
1588 pa_proplist_setf(GET_STREAM_NEW_PROPLIST(new_data, stream_type), PA_PROP_BUFFER_ATTR_FRAGSIZE, "%d", fragsize);
1590 pa_log_info("*** maxlength:%d, fragsize:%d", maxlength, fragsize);
1594 static int exclude_device_filter_func(const void *device_type, const void *exclude_device_type) {
1595 pa_assert(device_type);
1596 pa_assert(exclude_device_type);
1598 if (pa_safe_streq((const char*)device_type, (const char*)exclude_device_type)) {
1599 pa_log_info("[%s] is excluded", (const char*)device_type);
1606 static pa_idxset* get_avail_devices_except_bt_a2dp(pa_idxset *avail_devices) {
1607 pa_idxset *filtered_devices;
1609 pa_assert(avail_devices);
1611 filtered_devices = pa_idxset_filtered_copy(avail_devices, NULL, exclude_device_filter_func, DEVICE_TYPE_BT_A2DP);
1613 return filtered_devices;
1616 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) {
1617 char *device_none = NULL;
1618 const char *p_idx = NULL;
1619 uint32_t parent_idx = 0;
1620 stream_parent *sp = NULL;
1621 pa_stream_manager_hook_data_for_select *select_data = NULL;
1622 pa_stream_manager_hook_data_for_route *route_data = NULL;
1624 pa_idxset *avail_devices;
1625 uint32_t list_len = 0;
1628 pa_assert(hook_data);
1630 if (!is_valid_notify_command(command)) {
1631 pa_log_error("invalid notify command: %d", command);
1634 pa_log_info("command: %s", notify_command_type_str[command]);
1637 case NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT: {
1638 select_data = (pa_stream_manager_hook_data_for_select*)hook_data;
1639 if (!(si = pa_hashmap_get(m->stream_infos, select_data->stream_role))) {
1640 pa_log_error(" -- could not find (%s)", select_data->stream_role);
1643 select_data->route_type = si->route_type;
1644 avail_devices = (type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices;
1645 list_len = pa_idxset_size(avail_devices);
1646 device_none = pa_idxset_get_by_data(avail_devices, "none", NULL);
1647 if (list_len == 0 || device_none) {
1648 pa_log_warn(" -- there is no available device, stream_type(%d)", type);
1651 select_data->idx_avail_devices = avail_devices;
1652 select_data->origins_from_new_data = is_new_data;
1654 if (si->route_type != STREAM_ROUTE_TYPE_MANUAL && si->route_type != STREAM_ROUTE_TYPE_MANUAL_EXT)
1658 p_idx = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1660 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1661 if (!p_idx || pa_atou(p_idx, &parent_idx)) {
1662 pa_log_debug(" -- could not get the parent id of this stream, but keep going...");
1665 /* find parent idx, it's device info. and it's stream idxs */
1666 if ((sp = pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(parent_idx)))) {
1667 select_data->idx_manual_devices = (type == STREAM_SINK_INPUT) ? (sp->idx_route_out_devices) : (sp->idx_route_in_devices);
1670 pa_log_warn(" -- failed to get the stream parent of idx(%u)", parent_idx);
1673 case NOTIFY_COMMAND_CHANGE_ROUTE_START:
1674 case NOTIFY_COMMAND_CHANGE_ROUTE_END: {
1675 route_data = (pa_stream_manager_hook_data_for_route*)hook_data;
1676 if (!(si = pa_hashmap_get(m->stream_infos, route_data->stream_role))) {
1677 pa_log_error(" -- could not find (%s)", route_data->stream_role);
1680 avail_devices = (type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices;
1681 route_data->route_type = si->route_type;
1682 list_len = pa_idxset_size(avail_devices);
1683 device_none = pa_idxset_get_by_data(avail_devices, "none", NULL);
1685 if (list_len == 0 || device_none) {
1686 pa_log_warn(" -- there is no available device, stream_type(%d)", type);
1690 route_data->idx_avail_devices = avail_devices;
1692 if (si->route_type != STREAM_ROUTE_TYPE_MANUAL && si->route_type != STREAM_ROUTE_TYPE_MANUAL_EXT)
1696 p_idx = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1698 p_idx = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_PARENT_ID);
1699 if (!p_idx || pa_atou(p_idx, &parent_idx)) {
1700 pa_log_warn(" -- could not get the parent id of this stream, but keep going...");
1703 if (!(sp = pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(parent_idx)))) {
1704 pa_log_warn(" -- failed to get the stream parent of idx(%u)", parent_idx);
1707 route_data->idx_manual_devices = (type == STREAM_SINK_INPUT) ? (sp->idx_route_out_devices) : (sp->idx_route_in_devices);
1708 route_data->idx_streams = (type == STREAM_SINK_INPUT) ? (sp->idx_sink_inputs) : (sp->idx_source_outputs);
1716 static ret_msg_t prepare_and_invoke_hook_to_select_device(pa_stream_manager *m, notify_command_type_t command, stream_type_t type,
1717 bool is_new_data, void *user_data) {
1718 ret_msg_t ret = RET_MSG_OK;
1719 pa_stream_manager_hook_data_for_select hook_call_select_data;
1720 pa_idxset *filtered_avail_devices = NULL;
1722 const char *parent_id;
1723 uint32_t parent_id_u;
1726 pa_assert(user_data);
1728 if (command != NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT)
1729 return RET_MSG_ERROR_INVALID_ARGUMENT;
1731 memset(&hook_call_select_data, 0, sizeof(pa_stream_manager_hook_data_for_select));
1732 hook_call_select_data.stream = s = user_data;
1733 hook_call_select_data.stream_type = type;
1734 hook_call_select_data.origins_from_new_data = is_new_data;
1736 hook_call_select_data.stream_role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type),
1737 PA_PROP_MEDIA_ROLE);
1738 hook_call_select_data.device_role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type),
1739 PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
1740 fill_device_info_to_hook_data(m, &hook_call_select_data, command, type, s, is_new_data);
1741 hook_call_select_data.sample_spec = GET_STREAM_NEW_SAMPLE_SPEC(s, type);
1742 if (type == STREAM_SINK_INPUT) {
1743 hook_call_select_data.occupying_role = m->cur_highest_priority.role_si;
1744 if (IS_ROLE_COMMUNICATION(hook_call_select_data.occupying_role)) {
1745 /* Currently, if bt-sco is used by communication stream, other streams can not go with bt-a2dp.
1746 Here we exclude bt-a2dp device type, therefore this stream can be mixed together by using another device */
1747 filtered_avail_devices = get_avail_devices_except_bt_a2dp(hook_call_select_data.idx_avail_devices);
1748 hook_call_select_data.idx_avail_devices = filtered_avail_devices;
1750 hook_call_select_data.proper_sink = &(((pa_sink_input_new_data*)s)->sink);
1751 } else if (type == STREAM_SOURCE_OUTPUT) {
1752 hook_call_select_data.occupying_role = m->cur_highest_priority.role_so;
1753 hook_call_select_data.proper_source = &(((pa_source_output_new_data*)s)->source);
1756 hook_call_select_data.stream_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type),
1757 PA_PROP_MEDIA_ROLE);
1758 hook_call_select_data.device_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type),
1759 PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
1760 fill_device_info_to_hook_data(m, &hook_call_select_data, command, type, s, is_new_data);
1761 hook_call_select_data.sample_spec = GET_STREAM_SAMPLE_SPEC(s, type);
1763 if (hook_call_select_data.route_type == STREAM_ROUTE_TYPE_MANUAL)
1764 CONVERT_TO_DEVICE_ROLE(hook_call_select_data.stream_role, hook_call_select_data.device_role);
1766 parent_id = is_new_data ? pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type), PA_PROP_MEDIA_PARENT_ID) :
1767 pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_PARENT_ID);
1768 if (parent_id && pa_atou(parent_id, &parent_id_u) == 0)
1769 hook_call_select_data.parent_id = parent_id_u;
1771 hook_call_select_data.parent_id = -1;
1773 if (pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE), &hook_call_select_data)) {
1774 ret = RET_MSG_ERROR_INTERNAL;
1777 if (type == STREAM_SINK_INPUT)
1778 pa_sink_input_move_to(PA_SINK_INPUT(s), hook_call_select_data.new_sink, false);
1780 pa_source_output_move_to(PA_SOURCE_OUTPUT(s), hook_call_select_data.new_source, false);
1784 pa_xfree(filtered_avail_devices);
1789 static ret_msg_t prepare_and_invoke_hook_to_change_route(pa_stream_manager *m, notify_command_type_t command, stream_type_t type,
1790 bool is_new_data, void *user_data) {
1791 ret_msg_t ret = RET_MSG_OK;
1792 pa_stream_manager_hook_data_for_route hook_call_route_data;
1797 if (command != NOTIFY_COMMAND_CHANGE_ROUTE_START &&
1798 command != NOTIFY_COMMAND_CHANGE_ROUTE_END)
1799 return RET_MSG_ERROR_INVALID_ARGUMENT;
1801 if (command == NOTIFY_COMMAND_CHANGE_ROUTE_START) {
1802 pa_assert(user_data);
1803 memset(&hook_call_route_data, 0, sizeof(pa_stream_manager_hook_data_for_route));
1804 hook_call_route_data.stream = s = user_data;
1807 hook_call_route_data.origins_from_new_data = true;
1808 hook_call_route_data.stream_role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
1809 hook_call_route_data.device_role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(s, type), PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
1810 hook_call_route_data.sample_spec = GET_STREAM_NEW_SAMPLE_SPEC(s, type);
1811 if (type == STREAM_SINK_INPUT) {
1812 hook_call_route_data.proper_sink = &(((pa_sink_input_new_data*)s)->sink);
1813 } else if (type == STREAM_SOURCE_OUTPUT) {
1814 hook_call_route_data.proper_source = &(((pa_source_output_new_data*)s)->source);
1817 hook_call_route_data.stream_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
1818 hook_call_route_data.device_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
1819 hook_call_route_data.sample_spec = GET_STREAM_SAMPLE_SPEC(s, type);
1820 hook_call_route_data.idx_streams = (type == STREAM_SINK_INPUT) ? ((pa_sink_input*)s)->sink->inputs :
1821 ((pa_source_output*)s)->source->outputs;
1823 hook_call_route_data.stream_type = type;
1824 if (hook_call_route_data.route_type == STREAM_ROUTE_TYPE_MANUAL)
1825 CONVERT_TO_DEVICE_ROLE(hook_call_route_data.stream_role, hook_call_route_data.device_role);
1826 fill_device_info_to_hook_data(m, &hook_call_route_data, command, type, s, is_new_data);
1827 if (hook_call_route_data.route_type == STREAM_ROUTE_TYPE_MANUAL || hook_call_route_data.route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) {
1828 if (hook_call_route_data.idx_manual_devices && !pa_idxset_size(hook_call_route_data.idx_manual_devices)) {
1829 pa_log_warn("no manual device for this type(%d)", type);
1830 hook_call_route_data.stream = NULL;
1835 memset(&hook_call_route_data, 0, sizeof(pa_stream_manager_hook_data_for_route));
1836 s = (type == STREAM_SINK_INPUT) ? (void*)(m->cur_highest_priority.sink_input) :
1837 (void*)(m->cur_highest_priority.source_output);
1839 hook_call_route_data.stream = s;
1840 hook_call_route_data.stream_type = type;
1841 hook_call_route_data.stream_role = (type == STREAM_SINK_INPUT) ? (m->cur_highest_priority.role_si) :
1842 (m->cur_highest_priority.role_so);
1843 CONVERT_TO_DEVICE_ROLE(hook_call_route_data.stream_role, hook_call_route_data.device_role);
1844 hook_call_route_data.sample_spec = GET_STREAM_SAMPLE_SPEC(s, type);
1845 hook_call_route_data.idx_streams = (type == STREAM_SINK_INPUT) ? ((pa_sink_input*)s)->sink->inputs :
1846 ((pa_source_output*)s)->source->outputs;
1847 fill_device_info_to_hook_data(m, &hook_call_route_data, command, type, s, is_new_data);
1849 pa_log_debug("no stream for this type(%d), need to unset route", type);
1850 hook_call_route_data.stream = NULL;
1851 hook_call_route_data.stream_type = type;
1855 if (pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE), &hook_call_route_data))
1856 ret = RET_MSG_ERROR_INTERNAL;
1861 static ret_msg_t prepare_and_invoke_hook_to_inform_stream_connection(pa_stream_manager *m, bool is_connected,
1862 stream_type_t type, void *s) {
1863 pa_stream_manager_hook_data_for_stream hook_call_stream_data;
1864 const char *parent_id;
1865 uint32_t parent_id_u;
1870 memset(&hook_call_stream_data, 0, sizeof(pa_stream_manager_hook_data_for_stream));
1871 hook_call_stream_data.stream = s;
1872 hook_call_stream_data.stream_type = type;
1873 hook_call_stream_data.stream_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
1874 parent_id = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_PARENT_ID);
1875 if (parent_id && pa_atou(parent_id, &parent_id_u) == 0)
1876 hook_call_stream_data.parent_id = parent_id_u;
1878 hook_call_stream_data.parent_id = -1;
1879 hook_call_stream_data.is_connected = is_connected;
1881 if (pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_STREAM_CONNECTION_CHANGED), &hook_call_stream_data))
1882 return RET_MSG_ERROR_INTERNAL;
1887 ret_msg_t do_notify(pa_stream_manager *m, notify_command_type_t command, stream_type_t type, bool is_new_data, void *user_data) {
1888 hal_stream_connection_info stream_conn_info;
1889 hal_route_option route_option;
1891 ret_msg_t ret = RET_MSG_OK;
1895 if (!is_valid_notify_command(command)) {
1896 pa_log_error("invalid notify command: %d", command);
1897 return RET_MSG_ERROR_INVALID_ARGUMENT;
1899 pa_log_info("command: %s", notify_command_type_str[command]);
1902 case NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT:
1903 ret = prepare_and_invoke_hook_to_select_device(m, command, type, is_new_data, user_data);
1906 case NOTIFY_COMMAND_CHANGE_ROUTE_START:
1907 case NOTIFY_COMMAND_CHANGE_ROUTE_END:
1908 ret = prepare_and_invoke_hook_to_change_route(m, command, type, is_new_data, user_data);
1911 case NOTIFY_COMMAND_UPDATE_ROUTE_OPTION: {
1912 pa_assert(user_data);
1913 memset(&route_option, 0, sizeof(hal_route_option));
1914 s = (type == STREAM_SINK_INPUT) ? (void*)(m->cur_highest_priority.sink_input) :
1915 (void*)(m->cur_highest_priority.source_output);
1916 /* route_option.role can be null if there's no occupied stream */
1918 route_option.role = (type == STREAM_SINK_INPUT) ? (m->cur_highest_priority.role_si) :
1919 (m->cur_highest_priority.role_so);
1920 route_option.name = ((stream_route_option*)user_data)->name;
1921 route_option.value = ((stream_route_option*)user_data)->value;
1922 if (pa_hal_interface_update_route_option(m->hal, &route_option))
1923 ret = RET_MSG_ERROR_INTERNAL;
1927 case NOTIFY_COMMAND_INFORM_STREAM_CONNECTED:
1928 case NOTIFY_COMMAND_INFORM_STREAM_DISCONNECTED: {
1929 pa_assert(user_data);
1930 memset(&stream_conn_info, 0, sizeof(hal_stream_connection_info));
1933 ret = prepare_and_invoke_hook_to_inform_stream_connection(m, command == NOTIFY_COMMAND_INFORM_STREAM_CONNECTED, type, s);
1934 if (ret != RET_MSG_OK)
1935 pa_log_error("failed to prepare_and_invoke_hook_to_inform_stream_connection()");
1936 stream_conn_info.role = pa_proplist_gets(GET_STREAM_PROPLIST(s, type), PA_PROP_MEDIA_ROLE);
1937 stream_conn_info.direction = (type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN;
1938 stream_conn_info.idx = GET_STREAM_INDEX(s, type);
1939 stream_conn_info.is_connected = (command == NOTIFY_COMMAND_INFORM_STREAM_CONNECTED);
1940 if (pa_hal_interface_notify_stream_connection_changed(m->hal, &stream_conn_info))
1941 ret = RET_MSG_ERROR_INTERNAL;
1950 #ifndef __TIZEN_TV_EXTERNAL_TV_SOURCE__
1951 static void update_mirroring_streams(pa_stream_manager *m, pa_source_output *o, bool put) {
1957 if ((role = pa_proplist_gets(GET_STREAM_PROPLIST(o, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROLE)) &&
1958 pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING)) {
1960 pa_idxset_put(m->mirroring_streams, o, NULL);
1962 pa_idxset_remove_by_data(m->mirroring_streams, o, NULL);
1966 /* Load/unload forwarding device */
1967 static int update_forwarding_device(pa_stream_manager *m, bool load) {
1971 if (pa_idxset_size(m->mirroring_streams) == 0) {
1972 if (!pa_device_manager_load_forwarding(m->dm)) {
1973 pa_log_error("could not load forwarding device");
1976 pa_log_info("forwarding device is now loaded");
1978 pa_log_debug("forwarding device has been already loaded");
1981 if (pa_idxset_size(m->mirroring_streams) > 0)
1982 pa_log_debug("no need to unload forwarding device");
1984 pa_device_manager_unload_forwarding(m->dm);
1985 pa_log_info("forwarding device is now unloaded");
1993 static void set_new_data_stream_to_null_sink_source(pa_stream_manager *m, void *stream, stream_type_t type) {
1995 pa_source *null_source;
2000 if (type == STREAM_SINK_INPUT && !((pa_sink_input_new_data*)stream)->sink) {
2001 if ((null_sink = (pa_sink*)pa_namereg_get(m->core, SINK_NAME_NULL, PA_NAMEREG_SINK)))
2002 ((pa_sink_input_new_data*)stream)->sink = null_sink;
2004 pa_log_warn("could not get null_sink");
2005 } else if (type == STREAM_SOURCE_OUTPUT && !((pa_source_output_new_data*)stream)->source) {
2006 if ((null_source = (pa_source*)pa_namereg_get(m->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE)))
2007 ((pa_source_output_new_data*)stream)->source = null_source;
2009 pa_log_warn("could not get null_source");
2013 static bool check_preferred_role_of_active_device_exist(pa_stream_manager *m, void *stream, stream_type_t type, const char *preferred_role) {
2014 const char *active_device_type;
2015 pa_tz_device *device;
2019 pa_assert(preferred_role);
2021 if ((active_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
2022 if ((device = pa_device_manager_get_device(m->dm, active_device_type, preferred_role))) {
2023 pa_log_debug("preferred device role[%s] of active device[%s] is found", preferred_role, pa_tz_device_get_type(device));
2031 static process_stream_result_t handle_command_prepare(pa_stream_manager *m, void *stream, stream_type_t type, bool is_new_data) {
2032 pa_format_info *req_format = NULL;
2033 char *format_str = NULL;
2034 const char *rate_str = NULL;
2035 const char *ch_str = NULL;
2036 const char *role = NULL;
2037 const char *route_type_str = NULL;
2042 if (type == STREAM_SINK_INPUT) {
2043 /* Parse request formats for samplerate, channel, format information */
2044 if (((pa_sink_input_new_data*)stream)->req_formats) {
2045 req_format = pa_idxset_first(((pa_sink_input_new_data*)stream)->req_formats, NULL);
2046 if (req_format && req_format->plist) {
2047 /* set sample_spec */
2048 if (pa_format_info_get_prop_string(req_format, PA_PROP_FORMAT_SAMPLE_FORMAT, &format_str) == 0)
2049 ((pa_sink_input_new_data*)stream)->sample_spec.format = pa_parse_sample_format((const char*)format_str);
2050 if ((rate_str = pa_proplist_gets(req_format->plist, PA_PROP_FORMAT_RATE)))
2051 ((pa_sink_input_new_data*)stream)->sample_spec.rate = atoi(rate_str);
2052 if ((ch_str = pa_proplist_gets(req_format->plist, PA_PROP_FORMAT_CHANNELS)))
2053 ((pa_sink_input_new_data*)stream)->sample_spec.channels = atoi(ch_str);
2055 pa_log_debug("req rate(%s), req ch(%s), req format(%s)", rate_str, ch_str, format_str);
2057 /* set channel map if it is not set by client */
2058 if (!((pa_sink_input_new_data*)stream)->channel_map_is_set) {
2059 pa_channel_map_init_auto(&(((pa_sink_input_new_data*)stream)->channel_map),
2060 ((pa_sink_input_new_data*)stream)->sample_spec.channels, PA_CHANNEL_MAP_ALSA);
2061 pa_log_debug("set default channel_map: channels(%u)", ((pa_sink_input_new_data*)stream)->channel_map.channels);
2062 ((pa_sink_input_new_data*)stream)->channel_map_is_set = true;
2064 pa_xfree(format_str);
2067 pa_log_debug("no request formats available");
2070 role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2072 /* set default value for role and priority */
2073 role = DEFAULT_ROLE;
2074 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE, role);
2075 pa_log_warn("role is null, set default to [%s]", role);
2078 if (is_invalid_role(m, role))
2079 return PROCESS_STREAM_RESULT_SKIP;
2081 #ifndef __TIZEN_TV_EXTERNAL_TV_SOURCE__
2082 /* load forwarding device */
2083 if (type == STREAM_SOURCE_OUTPUT && pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING))
2084 update_forwarding_device(m, true);
2087 /* update the priority of this stream */
2088 if (!update_priority_of_stream(m, stream, type, role, is_new_data)) {
2089 pa_log_error("could not update the priority of '%s' role.", role);
2090 return PROCESS_STREAM_RESULT_STOP;
2092 /* update the route type of this stream */
2093 if (!update_route_type_of_stream(m, stream, type, role)) {
2094 pa_log_error("could not update the route type of '%s' role.", role);
2095 return PROCESS_STREAM_RESULT_STOP;
2097 /* update the volume type of this stream */
2098 if (!update_volume_type_of_stream(m, stream, type, role))
2099 pa_log_warn("could not update the volume type of '%s' role.", role);
2101 /* skip route types */
2102 if ((route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2103 if (is_invalid_route_type(route_type_str, NULL))
2104 return PROCESS_STREAM_RESULT_SKIP;
2107 /* update the preferred device role, it will affect only built-in devices. */
2108 update_preferred_device_role(m, stream, type);
2110 /* check if it is a virtual stream */
2111 if (check_name_is_vstream(stream, type, is_new_data) && !pa_safe_streq(role, STREAM_ROLE_LOOPBACK)) {
2112 pa_log_debug("skip notifying for selecting sink/source, rather set it to null sink/source");
2113 /* set it to null sink/source */
2115 set_new_data_stream_to_null_sink_source(m, stream, type);
2118 /* notify to select sink or source */
2119 do_notify(m, NOTIFY_COMMAND_SELECT_PROPER_SINK_OR_SOURCE_FOR_INIT, type, true, stream);
2122 return PROCESS_STREAM_RESULT_OK;
2125 static process_stream_result_t handle_command_change_route_by_stream_started(pa_stream_manager *m, void *stream,
2126 stream_type_t type, bool is_new_data) {
2127 const char *role = NULL;
2128 const char *route_type_str = NULL;
2129 stream_route_type_t route_type;
2130 const char *preferred_device_role;
2131 bool need_update = false;
2137 role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2138 route_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
2139 preferred_device_role = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type),
2140 PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
2142 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2143 route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE);
2144 preferred_device_role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type),
2145 PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
2148 /* skip updating priority, focus status, the highest priority, ucm to HAL */
2149 if (preferred_device_role) {
2151 pa_log_debug("skip updating priority, focus status, the highest priority, ucm to HAL");
2152 goto skip_notifying_route_start;
2154 if (check_preferred_role_of_active_device_exist(m, stream, type, preferred_device_role)) {
2155 pa_log_debug("skip updating priority, focus status, the highest priority, ucm to HAL");
2156 goto skip_notifying_route_start;
2162 if (is_invalid_role(m, role))
2163 return PROCESS_STREAM_RESULT_SKIP;
2164 if (pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING)) {
2165 pa_log_debug("role is [%s], skip it", role);
2166 return PROCESS_STREAM_RESULT_SKIP;
2169 /* skip route types */
2170 if (is_invalid_route_type(route_type_str, &route_type))
2171 return PROCESS_STREAM_RESULT_SKIP;
2172 if (route_type == STREAM_ROUTE_TYPE_MANUAL_EXT)
2173 return PROCESS_STREAM_RESULT_SKIP;
2176 /* update the priority of this stream */
2177 if (!update_priority_of_stream(m, stream, type, role, is_new_data)) {
2178 pa_log_error("could not update the priority of '%s' role.", role);
2179 return PROCESS_STREAM_RESULT_STOP;
2183 /* update the focus status */
2184 if (!update_focus_status_of_stream(m, stream, type, is_new_data))
2185 pa_log_warn("could not update focus status");
2187 /* update the highest priority */
2188 if (!update_the_highest_priority_stream(m, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, stream, type, role, is_new_data, &need_update)) {
2189 pa_log_error("could not update the highest priority stream");
2190 return PROCESS_STREAM_RESULT_SKIP;
2193 /* need to skip if this stream does not belong to internal device */
2194 /* if needed, notify to update */
2197 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, type, true, stream);
2198 if (type == STREAM_SINK_INPUT)
2199 m->cur_highest_priority.need_to_update_si = true;
2201 m->cur_highest_priority.need_to_update_so = true;
2203 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, type, false, stream);
2204 if (type == STREAM_SINK_INPUT) {
2205 m->cur_highest_priority.sink_input = stream;
2206 m->cur_highest_priority.role_si = role;
2208 m->cur_highest_priority.source_output = stream;
2209 m->cur_highest_priority.role_so = role;
2214 skip_notifying_route_start:
2216 do_notify(m, NOTIFY_COMMAND_INFORM_STREAM_CONNECTED, type, false, stream);
2218 return PROCESS_STREAM_RESULT_OK;
2221 static process_stream_result_t handle_command_change_route_by_stream_ended(pa_stream_manager *m, void *stream,
2222 stream_type_t type, bool is_new_data, bool by_corked) {
2223 const char *role = NULL;
2224 const char *route_type_str = NULL;
2225 const char *preferred_device_role;
2226 bool need_update = false;
2227 const int32_t *prior_priority = NULL;
2233 if (!(role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE))) {
2234 pa_log_warn("role is null, skip it");
2235 return PROCESS_STREAM_RESULT_SKIP;
2238 preferred_device_role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type),
2239 PA_PROP_MEDIA_ROUTE_AUTO_PREFERRED_DEVICE_ROLE);
2240 /* skip updating the highest priority, ucm to HAL */
2241 if (preferred_device_role) {
2242 if (check_preferred_role_of_active_device_exist(m, stream, type, preferred_device_role)) {
2243 pa_log_debug("skip updating the highest priority, ucm to HAL");
2244 goto skip_notifying_route_end;
2248 #ifndef __TIZEN_TV_EXTERNAL_TV_SOURCE__
2249 /* unload forwarding device */
2250 if (type == STREAM_SOURCE_OUTPUT && pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING))
2251 update_forwarding_device(m, false);
2255 if (is_invalid_role(m, role))
2256 return PROCESS_STREAM_RESULT_SKIP;
2257 if (pa_safe_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING)) {
2258 pa_log_debug("role is [%s], skip it", role);
2259 return PROCESS_STREAM_RESULT_SKIP;
2262 /* skip route types */
2263 if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2264 stream_route_type_t route_type;
2265 if (is_invalid_route_type(route_type_str, &route_type))
2266 return PROCESS_STREAM_RESULT_SKIP;
2267 if (route_type == STREAM_ROUTE_TYPE_MANUAL_EXT)
2268 return PROCESS_STREAM_RESULT_SKIP;
2271 /* check if it has already been processed (unlink or state_changed_cb) */
2272 if (pa_proplist_get(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_PRIORITY, (const void**)&prior_priority, &size)) {
2273 pa_log_debug("it has already been processed for PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, skip it..");
2274 return PROCESS_STREAM_RESULT_SKIP;
2277 if (!update_the_highest_priority_stream(m, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, stream, type,
2278 role, is_new_data, &need_update)) {
2279 pa_log_error("could not update the highest priority stream");
2280 return PROCESS_STREAM_RESULT_STOP;
2283 skip_notifying_route_end:
2284 if (by_corked || CHECK_STREAM_RUNNING(stream, type))
2285 do_notify(m, NOTIFY_COMMAND_INFORM_STREAM_DISCONNECTED, type, false, stream);
2287 /* need to skip if this stream does not belong to internal device */
2288 /* if needed, notify to update */
2290 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_END, type, false, NULL);
2292 return PROCESS_STREAM_RESULT_OK;
2295 static process_stream_result_t handle_command_change_route_by_stream_focus_changed(pa_stream_manager *m, void *stream,
2296 stream_type_t type, bool is_new_data) {
2297 const char *role = NULL;
2298 const char *route_type_str = NULL;
2299 bool need_update = false;
2304 if (!(role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE))) {
2305 pa_log_warn("role is null, skip it");
2306 return PROCESS_STREAM_RESULT_SKIP;
2310 if (is_invalid_role(m, role))
2311 return PROCESS_STREAM_RESULT_SKIP;
2313 /* skip route types */
2314 if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2315 stream_route_type_t route_type;
2316 if (is_invalid_route_type(route_type_str, &route_type))
2317 return PROCESS_STREAM_RESULT_SKIP;
2318 if (route_type == STREAM_ROUTE_TYPE_MANUAL_EXT)
2319 return PROCESS_STREAM_RESULT_SKIP;
2322 if (!update_the_highest_priority_stream(m, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED, stream, type, role,
2323 is_new_data, &need_update)) {
2324 pa_log_error("could not update the highest priority stream");
2325 return PROCESS_STREAM_RESULT_STOP;
2328 /* need to skip if this stream does not belong to internal device */
2329 /* if needed, notify to update */
2331 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_END, type, false, NULL);
2333 return PROCESS_STREAM_RESULT_OK;
2336 static process_stream_result_t handle_command_update_volume(pa_stream_manager *m, void *stream, stream_type_t type, bool is_new_data) {
2337 int32_t volume_ret = 0;
2338 volume_info *v = NULL;
2339 const char *si_volume_type_str = NULL;
2345 si_volume_type_str = pa_proplist_gets(GET_STREAM_NEW_PROPLIST(stream, type), PA_PROP_MEDIA_TIZEN_VOLUME_TYPE);
2347 si_volume_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_TIZEN_VOLUME_TYPE);
2348 if (si_volume_type_str && is_active_device_of_stream(stream, STREAM_SINK_INPUT, DEVICE_TYPE_NETWORK)) {
2349 /* In case of network device, reset the volume of this sink-input. */
2351 pa_cvolume_reset(&cv, ((pa_sink_input*)stream)->sample_spec.channels) ;
2352 pa_sink_input_set_volume((pa_sink_input*)stream, &cv, true, true);
2353 pa_log_debug("This stream belongs to a network device, reset volume.");
2354 return PROCESS_STREAM_RESULT_OK;
2358 if (!si_volume_type_str)
2359 return PROCESS_STREAM_RESULT_SKIP;
2361 v = pa_hashmap_get(m->volume_infos, si_volume_type_str);
2362 if (v && v->values[type].idx_volume_values) {
2364 /* Update volume-level */
2365 if ((volume_ret = set_volume_level_with_new_data(m, stream, type, v->values[type].current_level)))
2366 pa_log_error("failed to set_volume_level_with_new_data(), stream_type(%d), level(%u), ret(0x%x)",
2367 type, v->values[type].current_level, volume_ret);
2368 /* Update volume-mute */
2369 if ((volume_ret = set_volume_mute_with_new_data(m, stream, type, v->values[type].is_muted)))
2370 pa_log_error("failed to set_volume_mute_with_new_data(), stream_type(%d), mute(%d), ret(0x%x)",
2371 type, v->values[type].is_muted, volume_ret);
2373 /* Update volume-level by stream index*/
2374 if ((volume_ret = set_volume_level_by_idx(m, type, GET_STREAM_INDEX(stream, type), v->values[type].current_level)))
2375 pa_log_error("failed to set_volume_level_by_idx(), stream_type(%d), index(%u), level(%u), ret(0x%x)",
2376 type, GET_STREAM_INDEX(stream, type), v->values[type].current_level, volume_ret);
2380 return PROCESS_STREAM_RESULT_OK;
2383 static process_stream_result_t handle_command_add_remove_stream(pa_stream_manager *m, process_command_type_t command,
2384 void *stream, stream_type_t type, bool is_new_data) {
2385 const char *role = NULL;
2386 const char *route_type_str = NULL;
2387 stream_route_type_t route_type;
2392 if (command != PROCESS_COMMAND_ADD_STREAM &&
2393 command != PROCESS_COMMAND_REMOVE_STREAM)
2394 return PROCESS_STREAM_RESULT_SKIP;
2396 if (!(role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE))) {
2397 pa_log_warn("role is null, skip it");
2398 return PROCESS_STREAM_RESULT_SKIP;
2402 if (is_invalid_role(m, role))
2403 return PROCESS_STREAM_RESULT_SKIP;
2405 /* skip route types */
2406 if ((route_type_str = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE_ROUTE_TYPE))) {
2407 if (is_invalid_route_type(route_type_str, NULL))
2408 return PROCESS_STREAM_RESULT_SKIP;
2411 if (!IS_ROUTE_TYPE_FOR_EXTERNAL_DEV(route_type_str, route_type)) {
2412 if (command == PROCESS_COMMAND_ADD_STREAM && CHECK_STREAM_RUNNING(stream, type)) {
2413 if (type == STREAM_SINK_INPUT && m->cur_highest_priority.need_to_update_si) {
2414 m->cur_highest_priority.sink_input = stream;
2415 m->cur_highest_priority.role_si = role;
2416 m->cur_highest_priority.need_to_update_si = false;
2418 if (type == STREAM_SOURCE_OUTPUT && m->cur_highest_priority.need_to_update_so) {
2419 m->cur_highest_priority.source_output = stream;
2420 m->cur_highest_priority.role_so = role;
2421 m->cur_highest_priority.need_to_update_so = false;
2423 do_notify(m, NOTIFY_COMMAND_INFORM_STREAM_CONNECTED, type, false, stream);
2427 /* update parent stream info. */
2428 if (!update_stream_parent_info(m, command, type, stream)) {
2429 pa_log_debug("could not update the parent information of this stream");
2430 //return PROCESS_STREAM_RESULT_STOP;
2433 return PROCESS_STREAM_RESULT_OK;
2436 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) {
2437 process_stream_result_t result = PROCESS_STREAM_RESULT_OK;
2442 if (!is_valid_process_command(command)) {
2443 pa_log_error("invalid process command: %d", command);
2444 return PROCESS_STREAM_RESULT_SKIP;
2446 pa_log_info(">>> [%s]: stream(%p), stream_type(%d), is_new_data(%d)",
2447 process_command_type_str[command], stream, type, is_new_data);
2449 if (check_name_to_skip(m, command, stream, type, is_new_data)) {
2450 result = PROCESS_STREAM_RESULT_SKIP;
2451 /* set it to null sink/source */
2453 set_new_data_stream_to_null_sink_source(m, stream, type);
2458 case PROCESS_COMMAND_PREPARE:
2459 result = handle_command_prepare(m, stream, type, is_new_data);
2461 case PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED:
2462 case PROCESS_COMMAND_CHANGE_ROUTE_BY_STATE_CHANGED_RUNNING:
2463 result = handle_command_change_route_by_stream_started(m, stream, type, is_new_data);
2465 case PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED:
2466 case PROCESS_COMMAND_CHANGE_ROUTE_BY_STATE_CHANGED_CORKED:
2467 result = handle_command_change_route_by_stream_ended(m, stream, type, is_new_data,
2468 (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STATE_CHANGED_CORKED));
2470 case PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_FOCUS_CHANGED:
2471 result = handle_command_change_route_by_stream_focus_changed(m, stream, type, is_new_data);
2473 case PROCESS_COMMAND_UPDATE_VOLUME:
2474 result = handle_command_update_volume(m, stream, type, is_new_data);
2476 case PROCESS_COMMAND_ADD_STREAM:
2477 case PROCESS_COMMAND_REMOVE_STREAM:
2478 result = handle_command_add_remove_stream(m, command, stream, type, is_new_data);
2480 case PROCESS_COMMAND_UPDATE_BUFFER_ATTR:
2481 update_buffer_attribute(m, stream, type, is_new_data);
2483 case PROCESS_COMMAND_APPLY_FILTER: {
2484 const char *role = NULL;
2485 filter_info *f = NULL;
2486 /* Apply filter using module-filter-apply when put sink-input. */
2487 if (type == STREAM_SINK_INPUT) {
2488 role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
2490 f = pa_hashmap_get(m->filter_infos, (const void*)role);
2492 apply_filter_to_sink_input((pa_sink_input *)stream, f, false);
2497 case PROCESS_COMMAND_SET_AEC_REFERENCE_DEVICE: {
2499 pa_tz_device *device;
2500 const char *ref_device;
2501 pa_proplist *p = ((pa_source_output_new_data*)stream)->proplist;
2504 result = PROCESS_STREAM_RESULT_SKIP;
2506 ref_device = pa_proplist_gets(p, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_DEVICE);
2510 result = PROCESS_STREAM_RESULT_STOP;
2512 if (pa_atoi(ref_device, &id) < 0) {
2513 pa_log_error("Can't convert ref_device(%s) to integer", ref_device);
2517 device = pa_device_manager_get_device_by_id(m->dm, id);
2519 pa_log_error("Can't find device by id(%d)", id);
2523 s = pa_tz_device_get_sink(device, "normal");
2525 pa_log_error("Can't find sink by device");
2529 pa_proplist_setf(p, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_SINK, "%d", s->index);
2531 result = PROCESS_STREAM_RESULT_OK;
2538 pa_log_debug("<<< [%s]: result(%d), stream(%p)", process_command_type_str[command], result, stream);
2542 /* Remove the sink-input from muted streams */
2543 static void remove_sink_input_from_muted_streams(pa_stream_manager *m, pa_sink_input *i) {
2551 PA_HASHMAP_FOREACH(streams, m->muted_streams, state)
2552 PA_IDXSET_FOREACH(si, streams, idx)
2554 pa_idxset_remove_by_data(streams, i, NULL);
2557 /* It can be utilized when it requires to invoke the ramp finish hook forcedly */
2558 static stream_ducking* fire_ramp_finish_hook_when_ducking_or_unducking_state(pa_stream_manager *m, pa_sink_input *i) {
2560 stream_ducking *sd = NULL;
2561 void *stream = NULL;
2566 PA_HASHMAP_FOREACH(sd, m->stream_duckings, state) {
2567 PA_IDXSET_FOREACH(stream, sd->idx_ducking_streams, idx) {
2570 if (sd->state == STREAM_DUCKING_STATE_DUCKING ||
2571 sd->state == STREAM_DUCKING_STATE_UNDUCKING) {
2572 pa_log_debug("stream(%p, index:%u) ducking state[%s]",
2573 i, i->index, sd->state == STREAM_DUCKING_STATE_DUCKING ? "DUCKING" : "UNDUCKING");
2574 pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_RAMP_FINISH], i);
2583 /* Remove the sink-input from ducking streams */
2584 static void remove_sink_input_from_ducking_streams(pa_stream_manager *m, pa_sink_input *i) {
2589 if ((sd = fire_ramp_finish_hook_when_ducking_or_unducking_state(m, i))) {
2590 pa_log_error("remove stream(%p, index:%u) from idx_ducking_streams, trigger_index(%u)",
2591 i, i->index, sd->trigger_index);
2592 pa_idxset_remove_by_data(sd->idx_ducking_streams, i, NULL);
2596 /* Add the sink-input to ducking streams */
2597 static void add_sink_input_to_ducking_streams(pa_stream_manager *m, pa_sink_input *i) {
2598 stream_ducking *sd = NULL;
2603 PA_HASHMAP_FOREACH(sd, m->stream_duckings, state) {
2604 pa_cvolume_ramp vol_ramp;
2607 if (!pa_safe_streq(sd->target_role, pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
2610 pa_log_error("add stream(idx:%u,%p) to idx_ducking_streams, trigger_index(%u), volume(%u)",
2611 i->index, i, sd->trigger_index, sd->set_vol);
2613 pa_idxset_put(sd->idx_ducking_streams, (void *)i, NULL);
2615 pa_cvolume_ramp_set(&vol_ramp, i->volume.channels,
2616 PA_VOLUME_RAMP_TYPE_LINEAR, (long)sd->duration, sd->set_vol);
2618 pa_cvolume_set(&vol, i->volume.channels, sd->set_vol);
2620 pa_sink_input_add_volume_factor(i, sd->vol_key, &vol);
2621 pa_sink_input_add_volume_ramp_factor(i, sd->vol_key, &vol_ramp, false);
2627 static pa_hook_result_t sink_input_new_cb(pa_core *core, pa_sink_input_new_data *new_data, pa_stream_manager *m) {
2628 pa_core_assert_ref(core);
2630 pa_log_debug("sink-input-new-data(%p)", new_data);
2632 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_PREPARE, true);
2633 if (is_remote_restricted(new_data, STREAM_SINK_INPUT))
2634 return PA_HOOK_CANCEL;
2635 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_UPDATE_BUFFER_ATTR, true);
2636 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_UPDATE_VOLUME, true);
2637 process_stream(m, new_data, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, true);
2642 static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2643 pa_core_assert_ref(core);
2644 pa_sink_input_assert_ref(i);
2646 pa_log_info("sink-input(%p, index:%u)", i, i->index);
2648 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_ADD_STREAM, false);
2649 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_APPLY_FILTER, false);
2650 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_UPDATE_VOLUME, false);
2651 if (is_stream_related_call_active_routing(PA_OBJECT(i))) {
2652 change_active_route_for_call(m, PA_OBJECT(i), true);
2656 add_sink_input_to_ducking_streams(m, i);
2661 static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2662 pa_core_assert_ref(core);
2663 pa_sink_input_assert_ref(i);
2665 pa_log_info("sink-input(%p, index:%u)", i, i->index);
2667 if (is_stream_related_call_active_routing(PA_OBJECT(i))) {
2669 set_media_active_device(m);
2672 remove_sink_input_from_muted_streams(m, i);
2673 remove_sink_input_from_ducking_streams(m, i);
2675 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_REMOVE_STREAM, false);
2676 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
2681 static pa_hook_result_t sink_input_state_changed_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2685 pa_log_debug("sink-input(%p, index:%u, state:%d)", i, i->index, i->state);
2688 case PA_SINK_INPUT_CORKED:
2689 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STATE_CHANGED_CORKED, false);
2690 fire_ramp_finish_hook_when_ducking_or_unducking_state(m, i);
2692 case PA_SINK_INPUT_RUNNING:
2693 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STATE_CHANGED_RUNNING, false);
2702 static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2703 pa_core_assert_ref(core);
2704 pa_sink_input_assert_ref(i);
2706 /* There's no point in doing anything if the core is shut down anyway */
2707 if (core->state == PA_CORE_SHUTDOWN)
2710 pa_log_debug("sink-input(%p, index:%u)", i, i->index);
2712 set_volume_mute_by_idx(m, i->index, STREAM_SINK_INPUT, true);
2713 process_stream(m, i, STREAM_SINK_INPUT, PROCESS_COMMAND_UPDATE_VOLUME, false);
2718 static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2719 pa_core_assert_ref(core);
2720 pa_sink_input_assert_ref(i);
2722 /* There's no point in doing anything if the core is shut down anyway */
2723 if (core->state == PA_CORE_SHUTDOWN)
2726 pa_log_debug("sink-input(%p, index:%u)", i, i->index);
2728 set_volume_mute_by_idx(m, i->index, STREAM_SINK_INPUT, false);
2733 static bool is_in_main_thread()
2735 return (getpid() == gettid());
2738 /* Called from either IO thread context or main context */
2739 /* FIXME : make this callback be invoked from the main context only */
2740 static pa_hook_result_t sink_input_ramp_finish_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
2741 struct stream_manager_param param;
2743 pa_core_assert_ref(core);
2744 pa_sink_input_assert_ref(i);
2746 /* There's no point in doing anything if the core is shut down anyway */
2747 if (core->state == PA_CORE_SHUTDOWN)
2751 param.sink_input = i;
2752 param.index = i->index;
2754 pa_log_info("sink-input(%p, index:%u)", i, i->index);
2756 if (is_in_main_thread()) {
2757 process_ramp_finish(¶m);
2758 pa_log_info("sink-input(%p, index:%u) : direct process_ramp_finish() done", i, i->index);
2760 /* Post message to make process_ramp_finish() run from main thread */
2761 pa_asyncmsgq_post(m->thread_mq.outq, PA_MSGOBJECT(m->msg), MESSAGE_RAMP_FINISHED,
2762 pa_xmemdup(¶m, sizeof(struct stream_manager_param)), 0, NULL, pa_xfree);
2763 pa_log_info("sink-input(%p, index:%u) : posting MESSAGE_RAMP_FINISHED done", i, i->index);
2769 static pa_hook_result_t source_output_new_cb(pa_core *core, pa_source_output_new_data *new_data, pa_stream_manager *m) {
2770 pa_core_assert_ref(core);
2772 pa_log_debug("source-output-new-data(%p)", new_data);
2774 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_PREPARE, true);
2775 if (is_restricted(m, new_data, STREAM_SOURCE_OUTPUT) ||
2776 is_remote_restricted(new_data, STREAM_SOURCE_OUTPUT))
2777 return PA_HOOK_CANCEL;
2778 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_UPDATE_BUFFER_ATTR, true);
2779 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_UPDATE_VOLUME, true);
2780 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, true);
2781 process_stream(m, new_data, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_SET_AEC_REFERENCE_DEVICE, true);
2786 static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2787 pa_core_assert_ref(core);
2788 pa_source_output_assert_ref(o);
2790 pa_log_info("source-output(%p, index:%u)", o, o->index);
2792 #ifndef __TIZEN_TV_EXTERNAL_TV_SOURCE__
2793 update_mirroring_streams(m, o, true);
2795 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_ADD_STREAM, false);
2796 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_UPDATE_VOLUME, false);
2797 if (is_stream_related_call_active_routing(PA_OBJECT(o))) {
2798 change_active_route_for_call(m, PA_OBJECT(o), true);
2805 static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2806 pa_core_assert_ref(core);
2807 pa_source_output_assert_ref(o);
2809 pa_log_info("source-output(%p, index:%u)", o, o->index);
2811 if (is_stream_related_call_active_routing(PA_OBJECT(o))) {
2813 set_media_active_device(m);
2815 #ifndef __TIZEN_TV_EXTERNAL_TV_SOURCE__
2816 update_mirroring_streams(m, o, false);
2818 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
2819 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_REMOVE_STREAM, false);
2824 static pa_hook_result_t source_output_state_changed_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2828 pa_log_debug("source-output(%p, index:%u), state(%d)", o, o->index, o->state);
2831 case PA_SOURCE_OUTPUT_CORKED:
2832 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
2834 case PA_SOURCE_OUTPUT_RUNNING:
2835 process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
2844 static pa_hook_result_t source_output_move_start_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2845 pa_core_assert_ref(core);
2846 pa_source_output_assert_ref(o);
2848 /* There's no point in doing anything if the core is shut down anyway */
2849 if (core->state == PA_CORE_SHUTDOWN)
2852 pa_log_debug("source-output(%p, index:%u)", o, o->index);
2854 set_volume_mute_by_idx(m, o->index, STREAM_SOURCE_OUTPUT, true);
2859 static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, pa_stream_manager *m) {
2860 pa_core_assert_ref(core);
2861 pa_source_output_assert_ref(o);
2863 /* There's no point in doing anything if the core is shut down anyway */
2864 if (core->state == PA_CORE_SHUTDOWN)
2867 pa_log_debug("source-output(%p, index:%u)", o, o->index);
2869 set_volume_mute_by_idx(m, o->index, STREAM_SOURCE_OUTPUT, false);
2874 static void notify_remote_connection(pa_core *core, pa_object *obj, pa_stream_manager *m, bool connected) {
2877 pa_source_output *o;
2880 if (pa_source_output_isinstance(obj)) {
2881 o = PA_SOURCE_OUTPUT(obj);
2886 i = PA_SINK_INPUT(obj);
2893 pa_log_error("unknown remote client");
2897 if (pa_proplist_has_remote_name(p)) {
2898 send_remote_found_signal(pa_dbus_connection_get(m->dbus_conn),
2899 pa_source_output_isinstance(obj) ? 1 : 0, connected, index,
2900 pa_proplist_gets(p, PA_PROP_MEDIA_REMOTE_NAME),
2901 pa_proplist_gets(p, "native-protocol.peer"));
2905 static pa_hook_result_t remote_client_put_cb(pa_core *core, pa_object *o, pa_stream_manager *m) {
2906 pa_core_assert_ref(core);
2907 pa_object_assert_ref(o);
2909 notify_remote_connection(core, o, m, true);
2914 static pa_hook_result_t remote_client_unlink_cb(pa_core *core, pa_object *o, pa_stream_manager *m) {
2915 pa_core_assert_ref(core);
2916 pa_object_assert_ref(o);
2918 notify_remote_connection(core, o, m, false);
2923 static void find_next_device_for_auto_route(pa_stream_manager *m, stream_route_type_t route_type, const char *stream_role,
2924 stream_type_t stream_type, const char *cur_device_type, const char *preferred_device_role, pa_tz_device **next_device) {
2925 stream_info *si = NULL;
2926 pa_idxset *devices = NULL;
2928 char *device_type = NULL;
2929 bool ret_next = false;
2930 pa_usec_t creation_time = 0;
2931 pa_usec_t latest_creation_time = 0;
2932 pa_tz_device* latest_device = NULL;
2935 pa_assert(m->stream_infos);
2936 pa_assert(stream_role);
2937 pa_assert(cur_device_type);
2938 pa_assert(next_device);
2939 pa_assert((route_type == STREAM_ROUTE_TYPE_AUTO || route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED));
2941 *next_device = NULL;
2943 if (!(si = pa_hashmap_get(m->stream_infos, stream_role))) {
2944 pa_log_warn("not support this stream_role[%s]", stream_role);
2947 if (si->route_type != route_type) {
2948 pa_log_warn("skip this route_type[%d]", si->route_type);
2952 if (!(devices = (stream_type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices)) {
2953 pa_log_error("could not found a device list for this stream_role[%s], stream type[%d]", stream_role, stream_type);
2957 if (route_type == STREAM_ROUTE_TYPE_AUTO) {
2958 PA_IDXSET_FOREACH(device_type, devices, idx) {
2959 if (pa_safe_streq(device_type, cur_device_type)) {
2964 if ((*next_device = pa_device_manager_get_device(m->dm, device_type, preferred_device_role))) {
2965 pa_log_debug("found next device[%s, %s, %p]", device_type, preferred_device_role, *next_device);
2970 } else if (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
2971 const char *device_role;
2972 PA_IDXSET_FOREACH(device_type, devices, idx) {
2973 device_role = device_type_is_builtin(device_type) ? preferred_device_role : NULL;
2974 if ((*next_device = pa_device_manager_get_device(m->dm, device_type, device_role))) {
2975 creation_time = pa_tz_device_get_creation_time(*next_device);
2976 if (!latest_device || (latest_creation_time <= creation_time)) {
2977 latest_device = *next_device;
2978 latest_creation_time = creation_time;
2982 *next_device = latest_device;
2983 pa_log_debug("found next device[%p], creation_time[%" PRIu64 "]", *next_device, latest_creation_time);
2986 pa_log_debug("next_device is [%p] for stream_role[%s]/route_type[%d]/stream_type[%d]", *next_device, stream_role, route_type, stream_type);
2989 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) {
2990 stream_info *si = NULL;
2991 pa_idxset *devices = NULL;
2993 char *device_type = NULL;
2996 pa_assert(m->stream_infos);
2998 pa_assert(cur_device_type);
2999 pa_assert(new_device_type);
3000 pa_assert(available);
3001 pa_assert((route_type == STREAM_ROUTE_TYPE_AUTO || route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED));
3005 if (!(si = pa_hashmap_get(m->stream_infos, role))) {
3006 pa_log_warn("not support this role[%s]", role);
3009 if (si->route_type != route_type) {
3010 pa_log_warn("skip this route_type[%d]", si->route_type);
3014 if (!(devices = (stream_type == STREAM_SINK_INPUT) ? si->idx_avail_out_devices : si->idx_avail_in_devices)) {
3015 pa_log_error("could not found a device list for this role[%s], stream type[%d]", role, stream_type);
3019 PA_IDXSET_FOREACH(device_type, devices, idx) {
3020 if (route_type == STREAM_ROUTE_TYPE_AUTO) {
3021 if (pa_safe_streq(device_type, cur_device_type)) {
3022 pa_log_debug("cur_device[%s]'s priority is more higher than new_device[%s]", cur_device_type, new_device_type);
3026 if (pa_safe_streq(device_type, new_device_type)) {
3032 pa_log_debug("is new_device[%s] available for role[%s]/stream_type[%d]:%d", new_device_type, role, stream_type, *available);
3035 /* Re-trigger for routing update for streams using auto route type */
3036 static void process_stream_as_device_change_for_auto_route(pa_stream_manager *m, void *stream, stream_type_t stream_type,
3037 bool is_connected, pa_tz_device *device) {
3038 stream_route_type_t route_type;
3039 const char *device_type;
3040 bool use_internal_codec = false;
3046 device_type = pa_tz_device_get_type(device);
3047 if (stream_type == STREAM_SINK_INPUT) {
3049 if ((sink = pa_tz_device_get_sink(device, NULL)))
3050 use_internal_codec = sink->use_internal_codec;
3052 pa_log_warn("sink is null");
3055 if ((source = pa_tz_device_get_source(device, NULL)))
3056 use_internal_codec = source->use_internal_codec;
3058 pa_log_warn("source is null");
3061 pa_log_info("[SM][PROCESS_STREAM_FOR_AUTO] stream(%p), stream_type(%d), is_connected(%d), device_type(%s), use_internal_codec(%d)",
3062 stream, stream_type, is_connected, device_type, use_internal_codec);
3064 if (get_route_type(stream, stream_type, false, &route_type) ||
3065 (route_type != STREAM_ROUTE_TYPE_AUTO && route_type != STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))
3069 /* it is caused by the connection of supported device for the stream */
3070 if (use_internal_codec) {
3071 if (((stream_type == STREAM_SINK_INPUT) && (!m->cur_highest_priority.sink_input || (m->cur_highest_priority.sink_input != stream))) ||
3072 ((stream_type == STREAM_SOURCE_OUTPUT) && (!m->cur_highest_priority.source_output || (m->cur_highest_priority.source_output != stream))))
3073 process_stream(m, stream, stream_type, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
3075 process_stream(m, stream, stream_type, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
3076 pa_proplist_sets(GET_STREAM_PROPLIST(stream, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
3079 /* it is caused by the disconnection of external device
3080 * and the supported next device of this stream using internal audio codec */
3081 if (use_internal_codec)
3082 process_stream(m, stream, stream_type, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
3086 static bool is_filter_apply_stream(void *s, stream_type_t stream_type) {
3087 const char *filter_apply = NULL;
3091 if ((filter_apply = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_FILTER_APPLY))) {
3092 pa_log_info("stream index(%u) is for filter.apply(%s)", GET_STREAM_INDEX(s, stream_type), filter_apply);
3098 static bool manage_filter_apply_stream(pa_stream_manager *m, pa_sink_input *si, pa_tz_device *device, bool is_connected) {
3099 stream_route_type_t route_type;
3105 if (get_route_type(si, STREAM_SINK_INPUT, false, &route_type))
3107 if (route_type != STREAM_ROUTE_TYPE_AUTO && route_type != STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED)
3110 if (is_filter_apply_stream(si, STREAM_SINK_INPUT)) {
3111 const char *stream_role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
3112 const char *cur_device_type = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
3113 char *device_type = pa_tz_device_get_type(device);
3114 pa_tz_device *cur_device = pa_device_manager_get_device(m->dm, cur_device_type, NULL);
3115 pa_sink *sink = pa_tz_device_get_sink(device, NULL);
3118 bool available = false;
3119 pa_sink *cur_sink = pa_tz_device_get_sink(cur_device, NULL);
3120 if (cur_sink == sink)
3123 is_available_device_for_auto_route(m, route_type, cur_device_type, device_type,
3124 stream_role, STREAM_SINK_INPUT, &available);
3126 if (!reload_filter(m, stream_role, sink)) {
3127 pa_proplist_sets(si->proplist, PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
3128 pa_log_info("[%s] is CONNECTED and will be set for the next device", device_type);
3129 process_stream_as_device_change_for_auto_route(m, si, STREAM_SINK_INPUT, is_connected, device);
3131 pa_log_error("failed to reload filter");
3134 pa_tz_device *next_device = NULL;
3135 char *new_device_type = NULL;
3136 pa_sink *cur_sink = NULL;
3139 cur_sink = pa_tz_device_get_sink(cur_device, NULL);
3140 if (cur_sink && (cur_sink != sink))
3143 if (is_active_device_of_stream(si, STREAM_SINK_INPUT, device_type)) {
3144 pa_sink *next_sink = NULL;
3145 find_next_device_for_auto_route(m, route_type, stream_role, STREAM_SINK_INPUT, cur_device_type, NULL, &next_device);
3147 pa_log_error("failed to get next_device");
3150 next_sink = pa_tz_device_get_sink(next_device, NULL);
3151 if (!reload_filter(m, stream_role, next_sink)) {
3152 new_device_type = pa_tz_device_get_type(next_device);
3153 pa_proplist_sets(si->proplist, PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, new_device_type);
3154 pa_log_info("[%s] is DISCONNECTED and [%s] will be set for the next device", device_type, new_device_type);
3155 process_stream_as_device_change_for_auto_route(m, si, STREAM_SINK_INPUT, is_connected, next_device);
3157 pa_log_error("failed to reload filter");
3166 static void check_and_move_streams_by_device_connection_change(pa_stream_manager *m, stream_route_type_t stream_route_type,
3167 pa_idxset *streams, stream_type_t stream_type, pa_tz_device *device, bool is_connected) {
3168 #define MAX_CACHED_LEN 128
3169 typedef struct _cached_device_list {
3170 const char *device_type;
3172 } cached_device_list;
3175 const char *stream_role = NULL;
3176 const char *device_type = NULL;
3177 const char *cur_device_type = NULL;
3178 const char *new_device_type = NULL;
3179 pa_tz_device *next_device = NULL;
3180 stream_route_type_t route_type;
3181 pa_sink *sink = NULL;
3182 pa_sink *next_sink = NULL;
3183 pa_sink *null_sink = NULL;
3184 pa_source *source = NULL;
3185 pa_source *next_source = NULL;
3186 pa_source *null_source = NULL;
3187 bool available = false;
3188 cached_device_list cached_prev_dev_list[MAX_CACHED_LEN] = {{NULL, 0}, };
3190 pa_sink *combine_sink = NULL;
3196 null_sink = (pa_sink*)pa_namereg_get(m->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
3197 null_source = (pa_source*)pa_namereg_get(m->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE);
3198 if (!null_sink || !null_source) {
3199 pa_log_error("[SM][UPDATE_SINK_SOURCE] could not get null_sink(%p) or null_source(%p)", null_sink, null_source);
3202 device_type = pa_tz_device_get_type(device);
3204 if (stream_route_type == STREAM_ROUTE_TYPE_AUTO || stream_route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
3205 pa_log_debug("[SM][UPDATE_SINK_SOURCE][AUTO] route_type(%d), deivce_type(%s), is_connected(%d))",
3206 stream_route_type, device_type, is_connected);
3207 if (stream_type == STREAM_SINK_INPUT)
3208 sink = pa_tz_device_get_sink(device, NULL);
3210 source = pa_tz_device_get_source(device, NULL);
3212 PA_IDXSET_FOREACH(s, streams, s_idx) { /* streams: core->source_outputs/core->sink_inputs */
3213 if (get_route_type(s, stream_type, false, &route_type))
3215 if (route_type != stream_route_type)
3217 if (stream_type == STREAM_SINK_INPUT && manage_filter_apply_stream(m, s, device, is_connected))
3220 stream_role = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROLE);
3221 pa_log_debug(" -- idx(%u), route_type(%d), stream_role(%s)", s_idx, route_type, stream_role);
3223 uint32_t preemptive_device_id = 0;
3224 if (!pa_stream_manager_get_preemptive_device_id(m, stream_type, stream_role, &preemptive_device_id)) {
3225 pa_tz_device *_device = pa_device_manager_get_device_by_id(m->dm, preemptive_device_id);
3227 pa_log_debug("Skip moving this stream[%s, idx:%u] set to the preemptive device(%s, id:%u)", stream_role,
3228 (stream_type == STREAM_SINK_INPUT) ? PA_SINK_INPUT(s)->index : PA_SOURCE_OUTPUT(s)->index,
3229 _device->type, preemptive_device_id);
3233 /* CONNECTED: move a stream to the new device if possible */
3234 if (sink && (sink != ((pa_sink_input*)s)->sink)) {
3235 if ((cur_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
3236 is_available_device_for_auto_route(m, route_type, cur_device_type, device_type, stream_role, stream_type, &available);
3238 if (check_name_is_vstream(s, STREAM_SINK_INPUT, false)) {
3239 pa_log_debug(" -- *** keep null sink for a virtual stream");
3241 pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
3242 pa_sink_input_move_to(s, sink, false);
3243 pa_log_debug(" -- *** sink-input(%p,%u) moves to sink(%p,%s), new device(%s)",
3244 s, ((pa_sink_input*)s)->index, sink, sink->name, device_type);
3248 pa_log_error(" -- could not find current device type for s->sink(%p)", ((pa_sink_input*)s)->sink);
3249 } else if (source && (source != ((pa_source_output*)s)->source)) {
3250 if ((cur_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
3251 is_available_device_for_auto_route(m, route_type, cur_device_type, device_type, stream_role, stream_type, &available);
3253 if (check_name_is_vstream(s, STREAM_SOURCE_OUTPUT, false)) {
3254 pa_log_debug(" -- *** keep null source for a virtual stream");
3256 pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, device_type);
3257 pa_source_output_move_to(s, source, false);
3258 pa_log_debug(" -- *** source-output(%p,%u) moves to source(%p,%s), new device(%s)",
3259 s, ((pa_source_output*)s)->index, source, source->name, device_type);
3263 pa_log_error(" -- could not find current device type for s->source(%p)", ((pa_source_output*)s)->source);
3265 pa_log_debug("no need to move for stream(%p, idx:%u)", s, GET_STREAM_INDEX(s, stream_type));
3268 cached_prev_dev_list[cnt++].device_type = cur_device_type;
3269 /* trigger to update routing path */
3270 process_stream_as_device_change_for_auto_route(m, s, stream_type, is_connected, device);
3272 } else if (!is_connected) {
3273 const char *pref_device_type = NULL;
3274 const char *pref_device_role = NULL;
3275 const char *device_role = NULL;
3277 /* get preferred role */
3278 get_preferred_device_type_and_role(m, s, stream_type, &pref_device_type, &pref_device_role);
3280 /* DISCONNECTED: find a connected device that has the next priority */
3281 if (sink && ((sink == ((pa_sink_input*)s)->sink) || check_name_is_vstream(s, STREAM_SINK_INPUT, false))) {
3282 if (!is_active_device_of_stream(s, stream_type, device_type))
3284 find_next_device_for_auto_route(m, route_type, stream_role, stream_type, device_type, pref_device_role, &next_device);
3286 if (pref_device_type && pa_safe_streq(pa_tz_device_get_type(next_device), pref_device_type))
3287 device_role = pref_device_role;
3289 if ((next_sink = pa_tz_device_get_sink(next_device, device_role))) {
3290 new_device_type = pa_tz_device_get_type(next_device);
3291 /* update activated device */
3292 pa_proplist_sets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, new_device_type);
3293 cached_prev_dev_list[cnt++].device_type = device_type;
3294 /* trigger to update routing path if the next device uses internal audio codec */
3295 process_stream_as_device_change_for_auto_route(m, s, stream_type, is_connected, next_device);
3297 if (check_name_is_vstream(s, STREAM_SINK_INPUT, false)) {
3298 pa_log_debug(" -- *** keep null sink for a virtual stream");
3300 pa_sink_input_move_to(s, next_sink, false);
3301 pa_log_debug(" -- *** sink-input(%p,%u) moves to sink(%p,%s), new device(%s)",
3302 s, ((pa_sink_input*)s)->index, next_sink, next_sink->name, new_device_type);
3307 if (!next_device || !next_sink) {
3308 pa_sink_input_move_to(s, null_sink, false);
3309 pa_log_warn(" -- *** sink-input(%p,%u) moves to sink(%p,%s)",
3310 s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
3313 } else if (source && ((source == ((pa_source_output*)s)->source) || check_name_is_vstream(s, STREAM_SOURCE_OUTPUT, false))) {
3314 if (!is_active_device_of_stream(s, stream_type, device_type))
3316 find_next_device_for_auto_route(m, route_type, stream_role, stream_type, device_type, pref_device_role, &next_device);
3318 if (pref_device_type && pa_safe_streq(pa_tz_device_get_type(next_device), pref_device_type))
3319 device_role = pref_device_role;
3321 if ((next_source = pa_tz_device_get_source(next_device, device_role))) {
3322 new_device_type = pa_tz_device_get_type(next_device);
3323 /* update activated device */
3324 pa_proplist_sets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, new_device_type);
3325 cached_prev_dev_list[cnt++].device_type = device_type;
3326 /* trigger to update routing path if the next device uses internal audio codec */
3327 if (next_source->use_internal_codec)
3328 process_stream_as_device_change_for_auto_route(m, s, stream_type, is_connected, next_device);
3330 if (check_name_is_vstream(s, STREAM_SOURCE_OUTPUT, false)) {
3331 pa_log_debug(" -- *** keep null source for a virtual stream");
3333 pa_source_output_move_to(s, next_source, false);
3334 pa_log_warn(" -- *** source-output(%p,%u) moves to source(%p,%s), new device(%s)",
3335 s, ((pa_source_output*)s)->index, next_source, next_source->name, new_device_type);
3339 if (!next_device || !next_source) {
3340 pa_source_output_move_to(s, null_source, false);
3341 pa_log_debug(" -- *** source-output(%p,%u) moves to source(%p,%s)",
3342 s, ((pa_source_output*)s)->index, null_source, null_source->name);
3348 /* set device state to be deactivated */
3350 if (stream_type == STREAM_SINK_INPUT) {
3351 combine_sink = (pa_sink*)pa_namereg_get(m->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
3352 if (combine_sink && pa_idxset_size(combine_sink->inputs)) {
3353 pa_log_warn(" -- combine sink has streams, skip it..");
3357 /* retrieve all the streams for checking current activated device */
3358 PA_IDXSET_FOREACH(s, streams, s_idx) { /* streams: core->source_outputs/core->sink_inputs */
3359 if ((cur_device_type = pa_proplist_gets(GET_STREAM_PROPLIST(s, stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV))) {
3360 for (cnt = 0; cached_prev_dev_list[cnt].device_type; cnt++) {
3361 if (pa_safe_streq(cur_device_type, cached_prev_dev_list[cnt].device_type))
3362 cached_prev_dev_list[cnt].count++;
3368 } else if (stream_route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) {
3369 pa_log_debug("[SM][UPDATE_SINK_SOURCE][EXT] deivce_type(%s), is_connected(%d))", device_type, is_connected);
3370 if (!is_connected) {
3371 PA_IDXSET_FOREACH(s, streams, s_idx) { /* streams: source->outputs/sink->inputs */
3372 if (!get_route_type(s, stream_type, false, &route_type) && route_type == stream_route_type) {
3373 if (stream_type == STREAM_SOURCE_OUTPUT) {
3374 /* move it to null source if this role is for external device */
3375 pa_source_output_move_to((pa_source_output*)s, null_source, false);
3376 pa_log_debug(" -- *** source-output(%p,%u) moves to source(%p,%s)", s, ((pa_source_output*)s)->index, null_source, null_source->name);
3378 /* move it to null sink if this role is for external device */
3379 pa_sink_input_move_to((pa_sink_input*)s, null_sink, false);
3380 pa_log_debug(" -- *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
3386 pa_log_error("[SM][UPDATE_SINK_SOURCE] could not handle it here, stream_route_type(%d)", stream_route_type);
3389 static void apply_volume_factor_to_streams(pa_idxset *all_streams, pa_idxset **applied_streams) {
3394 pa_assert(all_streams);
3395 pa_assert(applied_streams);
3397 pa_log_debug("apply volume factor");
3400 pa_parse_volume("0%", &vol.values[0]);
3401 *applied_streams = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3402 PA_IDXSET_FOREACH(i, all_streams, idx) {
3403 pa_log_info("found a stream(%p, %u) that should be muted.", i, i->index);
3404 /* remove MUTE_KEY before adding to avoid abort-case in pa_sink_input_add_volume_factor */
3405 pa_hashmap_remove(i->volume_factor_items, MUTE_KEY);
3406 pa_sink_input_add_volume_factor(i, MUTE_KEY, &vol);
3407 pa_idxset_put(*applied_streams, i, NULL);
3411 static void clear_volume_factor_from_streams(pa_idxset *applied_streams) {
3415 pa_assert(applied_streams);
3417 pa_log_debug("clear volume factor");
3419 PA_IDXSET_FOREACH(i, applied_streams, idx) {
3420 pa_idxset_remove_by_data(applied_streams, i, NULL);
3421 pa_sink_input_remove_volume_factor(i, MUTE_KEY);
3422 pa_log_info("found a stream(%p, %u) that should be un-muted.", i, i->index);
3424 pa_idxset_free(applied_streams, NULL);
3427 static void timed_unmute_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
3428 pa_stream_manager *m = userdata;
3429 pa_idxset *applied_streams;
3433 pa_assert(m->time_event_for_unmute == e);
3435 pa_log_info("time event(%p)", e);
3437 PA_HASHMAP_FOREACH(applied_streams, m->muted_streams, state) {
3438 clear_volume_factor_from_streams(applied_streams);
3439 pa_log_warn("remove volume factors forcedly...");
3441 pa_hashmap_remove_all(m->muted_streams);
3443 m->core->mainloop->time_free(m->time_event_for_unmute);
3444 m->time_event_for_unmute = NULL;
3447 static int active_device_filter_func(const void *i, const void *device_type) {
3449 pa_assert(device_type);
3451 if (is_active_device_of_stream(i, STREAM_SINK_INPUT, (const char *)device_type))
3457 static pa_idxset* get_streams_for_matching_active_device(pa_idxset *streams, const char *device_type) {
3458 pa_idxset *filtered_streams;
3462 filtered_streams = pa_idxset_filtered_copy(streams, NULL, active_device_filter_func, device_type);
3464 return filtered_streams;
3467 static void mute_sink_inputs_as_device_disconnection(pa_stream_manager *m, uint32_t event_id, bool need_to_mute, pa_idxset *streams_of_disconnected_device) {
3468 pa_idxset *applied_streams;
3472 pa_log_info("event_id(%u), need_to_mute(%d)", event_id, need_to_mute);
3475 if (!streams_of_disconnected_device) {
3476 pa_log_error("invalid argument, inputs are needed");
3479 apply_volume_factor_to_streams(streams_of_disconnected_device, &applied_streams);
3480 pa_hashmap_put(m->muted_streams, PA_UINT_TO_PTR(event_id), applied_streams);
3482 /* If PA_COMMUNICATOR_HOOK_EVENT_FULLY_HANDLED is not called for some reason,
3483 * volume factor should be removed forcedly. */
3484 if (!m->time_event_for_unmute)
3485 m->time_event_for_unmute = pa_core_rttime_new(m->core, pa_rtclock_now() + TIMED_UNMUTE_USEC, timed_unmute_cb, m);
3487 if (!(applied_streams = pa_hashmap_get(m->muted_streams, PA_UINT_TO_PTR(event_id)))) {
3488 pa_log_debug("could not find applied_streams for event_id(%u)", event_id);
3491 clear_volume_factor_from_streams(applied_streams);
3492 pa_hashmap_remove(m->muted_streams, PA_UINT_TO_PTR(event_id));
3498 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) {
3500 pa_assert(event_handled_hook_data);
3503 pa_log_info("[SM][HANDLED] event-id(%u), event-type(%d)",
3504 event_handled_hook_data->event_id, event_handled_hook_data->event_type);
3506 /* un-mute streams */
3507 mute_sink_inputs_as_device_disconnection(m, event_handled_hook_data->event_id, false, NULL);
3512 static void update_sink_or_source_by_device_connection_change(pa_stream_manager *m, pa_tz_device_hook_data_for_conn_changed *data,
3513 bool use_internal_codec, dm_device_direction_t device_direction) {
3514 pa_sink *sink = NULL;
3515 pa_source *source = NULL;
3520 /* Update streams belong to this external device that have MAUAL_EXT route type */
3521 if (!use_internal_codec) {
3522 if ((device_direction & DM_DEVICE_DIRECTION_IN) &&
3523 (source = pa_tz_device_get_source(data->device, NULL)))
3524 check_and_move_streams_by_device_connection_change(m, STREAM_ROUTE_TYPE_MANUAL_EXT, source->outputs,
3525 STREAM_SOURCE_OUTPUT, data->device, data->is_connected);
3526 if ((device_direction & DM_DEVICE_DIRECTION_OUT) &&
3527 (sink = pa_tz_device_get_sink(data->device, NULL)))
3528 check_and_move_streams_by_device_connection_change(m, STREAM_ROUTE_TYPE_MANUAL_EXT, sink->inputs,
3529 STREAM_SINK_INPUT, data->device, data->is_connected);
3532 /* Update all the streams that have AUTO route type */
3533 if (device_direction & DM_DEVICE_DIRECTION_IN) {
3534 check_and_move_streams_by_device_connection_change(m, STREAM_ROUTE_TYPE_AUTO, m->core->source_outputs,
3535 STREAM_SOURCE_OUTPUT, data->device, data->is_connected);
3536 check_and_move_streams_by_device_connection_change(m, STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->source_outputs,
3537 STREAM_SOURCE_OUTPUT, data->device, data->is_connected);
3539 if (device_direction & DM_DEVICE_DIRECTION_OUT) {
3540 check_and_move_streams_by_device_connection_change(m, STREAM_ROUTE_TYPE_AUTO, m->core->sink_inputs,
3541 STREAM_SINK_INPUT, data->device, data->is_connected);
3542 check_and_move_streams_by_device_connection_change(m, STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED, m->core->sink_inputs,
3543 STREAM_SINK_INPUT, data->device, data->is_connected);
3547 /* Reorganize routing when a device has been connected or disconnected */
3548 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) {
3549 const char *active_dev = NULL;
3550 const char *device_type = NULL;
3551 stream_route_type_t route_type;
3552 dm_device_direction_t device_direction = DM_DEVICE_DIRECTION_OUT;
3553 bool use_internal_codec = false;
3556 uint32_t device_id = 0;
3557 pa_sink *sink = NULL;
3558 pa_sink_input *si = NULL;
3559 pa_sink_input *highest_prior_si = NULL;
3560 pa_source_output *highest_prior_so = NULL;
3566 device_direction = pa_tz_device_get_direction(data->device);
3567 device_type = pa_tz_device_get_type(data->device);
3568 device_id = pa_tz_device_get_id(data->device);
3569 use_internal_codec = pa_tz_device_is_use_internal_codec(data->device);
3571 pa_log_info("[SM][CONN] evend_id(%u), is_connected(%d), device(%p, %s, %u), direction(0x%x), use_internal_codec(%d)",
3572 data->event_id, data->is_connected, data->device, device_type, device_id, device_direction, use_internal_codec);
3574 /* mute all the streams belong to this device, those will be un-muted in event_fully_handled_hook_cb */
3575 if (!data->is_connected && (device_direction & DM_DEVICE_DIRECTION_OUT)) {
3576 const char *media_role = NULL;
3577 hal_route_option route_option;
3579 if ((sink = pa_tz_device_get_sink(data->device, NULL))) {
3580 pa_idxset *filtered_streams = get_streams_for_matching_active_device(sink->inputs, device_type);
3581 mute_sink_inputs_as_device_disconnection(m, data->event_id, true, filtered_streams);
3582 pa_xfree(filtered_streams);
3585 /* If Earjack is disconnected, search for sink-input which has radio role,
3586 if found, let radio mute to avoid intermediate noise */
3587 if (pa_safe_streq(device_type, DEVICE_TYPE_AUDIO_JACK)) {
3588 PA_IDXSET_FOREACH(si, c->sink_inputs, s_idx) {
3589 media_role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
3590 if (pa_safe_streq(media_role, STREAM_ROLE_RADIO)) {
3591 pa_log_info(" sink-input(%d) is radio, let HAL mute for disconnect of earjack", si->index);
3592 memset(&route_option, 0, sizeof(hal_route_option));
3593 route_option.role = STREAM_ROLE_RADIO;
3594 route_option.name = "mute";
3595 route_option.value = 1;
3596 pa_hal_interface_update_route_option(m->hal, &route_option);
3602 update_sink_or_source_by_device_connection_change(m, data, use_internal_codec, device_direction);
3604 /* If the route type is AUTO SERIES, notify again */
3605 highest_prior_so = m->cur_highest_priority.source_output;
3606 if ((device_direction & DM_DEVICE_DIRECTION_IN) && highest_prior_so &&
3607 !get_route_type(highest_prior_so, STREAM_SOURCE_OUTPUT, false, &route_type)) {
3608 if (IS_AUTO_ROUTE_TYPE_SERIES(route_type) && use_internal_codec) {
3609 PA_IDXSET_FOREACH(s, highest_prior_so->source->outputs, s_idx) {
3610 if (!data->is_connected && !get_route_type(s, STREAM_SOURCE_OUTPUT, false, &route_type) &&
3611 ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
3612 /* remove activated device info. if it has the AUTO route type */
3613 active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
3614 if (pa_safe_streq(active_dev, device_type))
3615 pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
3618 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SOURCE_OUTPUT, false, highest_prior_so);
3619 if (!highest_prior_so->source->use_internal_codec &&
3620 !check_name_is_vstream(highest_prior_so, STREAM_SOURCE_OUTPUT, false)) {
3621 /* If the source of the cur_highest_priority stream uses external codec, it should be updated.
3622 * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
3623 * can update the cur_highest_priority, call it here */
3624 process_stream(m, highest_prior_so, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
3629 highest_prior_si = m->cur_highest_priority.sink_input;
3630 if ((device_direction & DM_DEVICE_DIRECTION_OUT) && highest_prior_si &&
3631 !get_route_type(highest_prior_si, STREAM_SINK_INPUT, false, &route_type)) {
3632 if (IS_AUTO_ROUTE_TYPE_SERIES(route_type) && use_internal_codec) {
3633 PA_IDXSET_FOREACH(s, highest_prior_si->sink->inputs, s_idx) {
3634 if (!data->is_connected && !get_route_type(s, STREAM_SINK_INPUT, false, &route_type) &&
3635 ((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED))) {
3636 /* remove activated device info. if it has the AUTO route type */
3637 active_dev = pa_proplist_gets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV);
3638 if (pa_safe_streq(active_dev, device_type))
3639 pa_proplist_sets(GET_STREAM_PROPLIST(s, STREAM_SINK_INPUT), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, ACTIVE_DEV_REMOVED);
3642 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, highest_prior_si);
3643 if (((route_type == STREAM_ROUTE_TYPE_AUTO) || (route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED)) &&
3644 !highest_prior_si->sink->use_internal_codec &&
3645 !check_name_is_vstream(highest_prior_si, STREAM_SINK_INPUT, false) &&
3646 !is_filter_apply_stream(highest_prior_si, STREAM_SINK_INPUT)) {
3647 /* If the sink of the cur_highest_priority stream uses external codec, it should be updated.
3648 * As only the process_stream(PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED)
3649 * can update the cur_highest_priority, call it here */
3650 process_stream(m, highest_prior_si, STREAM_SINK_INPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
3652 } else if (route_type == STREAM_ROUTE_TYPE_AUTO_ALL)
3653 do_notify(m, NOTIFY_COMMAND_CHANGE_ROUTE_START, STREAM_SINK_INPUT, false, highest_prior_si);
3657 pa_log_info("This is on_call state, do nothing about active device");
3659 pa_log_info("This is not on_call state, figure out to change active device");
3660 set_media_active_device(m);
3666 static void subscribe_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, pa_stream_manager *m) {
3667 stream_parent *sp = NULL;
3668 stream_ducking *sd = NULL;
3669 const char *name = NULL;
3671 pa_core_assert_ref(core);
3674 pa_log_info("subscribe_cb() is called, t(%x), idx(%u)", t, idx);
3676 if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE)) {
3677 pa_client *client = NULL;
3679 if ((client = pa_idxset_get_by_index(core->clients, idx)) == NULL) {
3680 pa_log_error(" - could not find any client that has idx(%u)", idx);
3684 name = pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME);
3686 if (pa_safe_streq(name, STREAM_MANAGER_CLIENT_INFO)) {
3687 /* add a stream parent */
3688 sp = pa_xmalloc0(sizeof(stream_parent));
3689 sp->idx_sink_inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3690 sp->idx_source_outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3691 sp->idx_route_in_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3692 sp->idx_route_out_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3693 pa_hashmap_put(m->stream_parents, PA_UINT_TO_PTR(idx), sp);
3694 pa_log_debug(" - add sp(%p), idx(%u)", sp, idx);
3696 } else if (pa_safe_streq(name, STREAM_MANAGER_CLIENT_DUCKING)) {
3697 /* add a stream ducking */
3698 sd = pa_xmalloc0(sizeof(stream_ducking));
3699 sd->idx_ducking_streams = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3700 sd->trigger_index = idx;
3701 pa_hashmap_put(m->stream_duckings, PA_UINT_TO_PTR(idx), sd);
3702 pa_log_debug(" - add sd(%p), trigger_index(%u)", sd, idx);
3705 } else if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE)) {
3706 /* try to find stream parent with idx */
3707 sp = pa_hashmap_get(m->stream_parents, PA_UINT_TO_PTR(idx));
3709 pa_log_debug(" - remove sp(%p), idx(%u)", sp, idx);
3710 pa_hashmap_remove(m->stream_parents, PA_UINT_TO_PTR(idx));
3711 pa_idxset_free(sp->idx_sink_inputs, NULL);
3712 pa_idxset_free(sp->idx_source_outputs, NULL);
3713 if (sp->idx_route_in_devices)
3714 pa_idxset_free(sp->idx_route_in_devices, pa_xfree);
3715 if (sp->idx_route_out_devices)
3716 pa_idxset_free(sp->idx_route_out_devices, pa_xfree);
3721 /* try to find sd with idx */
3722 sd = pa_hashmap_get(m->stream_duckings, PA_UINT_TO_PTR(idx));
3724 uint32_t ducking_idx = 0;
3725 pa_sink_input *stream = NULL;
3726 hal_ducking_activation_info ducking_activation_info = {
3727 sd->target_role, sd->duration, sd->ratio, false
3730 pa_log_info(" - remove sd(%p) state(%d), trigger_index(%u)", sd, sd->state, idx);
3732 if (sd->state == STREAM_DUCKING_STATE_UNDUCKED || sd->state == STREAM_DUCKING_STATE_UNDUCKING)
3733 goto skip_unducking;
3735 PA_IDXSET_FOREACH(stream, sd->idx_ducking_streams, ducking_idx) {
3736 /* Note: It is added temporarily to find missing index of idx_ducking_streams */
3737 if (!pa_idxset_get_by_data(m->core->sink_inputs, stream, NULL)) {
3738 pa_log_error("could not find stream(%p), skip it", stream);
3742 pa_log_info(" -- remove volume ramp, key(%s) from remained stream(idx:%u)", sd->vol_key, stream->index);
3743 pa_sink_input_remove_volume_factor(stream, sd->vol_key);
3744 pa_sink_input_remove_volume_ramp_factor(stream, sd->vol_key, true);
3747 pa_hal_interface_notify_ducking_activation_changed(m->hal, &ducking_activation_info);
3750 pa_idxset_free(sd->idx_ducking_streams, NULL);
3751 pa_hashmap_remove(m->stream_duckings, PA_UINT_TO_PTR(idx));
3757 pa_log_warn(" - this is not a client(%s) that we should take care of, skip it", name ? name : "NULL");
3760 /* Message callback from HAL interface */
3761 static void message_cb_func(const char *name, int value, void *user_data) {
3762 pa_stream_manager *m;
3763 pa_stream_manager_hook_data_for_update_info hook_call_data;
3765 pa_assert(user_data);
3768 m = (pa_stream_manager*)user_data;
3770 /* For module-loopback parameters */
3771 if (strstr(name, STREAM_ROLE_LOOPBACK)) {
3772 memset(&hook_call_data, 0, sizeof(pa_stream_manager_hook_data_for_update_info));
3773 hook_call_data.stream_role = STREAM_ROLE_LOOPBACK;
3774 hook_call_data.name = name;
3775 hook_call_data.value = value;
3776 pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_UPDATE_INFORMATION), &hook_call_data);
3781 send_command_signal(pa_dbus_connection_get(m->dbus_conn), name, value);
3786 static int32_t init_ipc(pa_stream_manager *m) {
3789 pa_log_info("Initialization for IPC");
3791 if ((init_sm_dbus(m)))
3794 pa_log_error("DBUS is not supported");
3800 static void deinit_ipc(pa_stream_manager *m) {
3808 bool pa_stream_manager_check_name_is_vstream(void *stream, stream_type_t type, bool is_new_data) {
3809 return check_name_is_vstream(stream, type, is_new_data);
3812 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) {
3813 return get_route_type(stream, stream_type, is_new_data, stream_route_type);
3816 int32_t pa_stream_manager_get_preemptive_device_id(pa_stream_manager *m, stream_type_t stream_type, const char *role, uint32_t *id) {
3817 stream_info *s = NULL;
3823 if (!(s = pa_hashmap_get(m->stream_infos, role))) {
3824 pa_log_warn("%s is not valid role, return false", role);
3828 if (s->preemptive_device[stream_type == STREAM_SINK_INPUT ? STREAM_DIRECTION_OUT : STREAM_DIRECTION_IN].type) {
3829 *id = s->preemptive_device[stream_type == STREAM_SINK_INPUT ? STREAM_DIRECTION_OUT : STREAM_DIRECTION_IN].id;
3836 bool pa_stream_manager_check_filter_apply_stream(void *stream, stream_type_t stream_type) {
3837 return is_filter_apply_stream(stream, stream_type);
3840 bool pa_stream_manager_is_valid_stream_role(pa_core *c, const char *role) {
3841 pa_stream_manager *m;
3846 if (!(m = pa_shared_get(c, SHARED_STREAM_MANAGER))) {
3847 pa_log_error("could not get shared data");
3850 if (!pa_hashmap_get(m->stream_infos, role)) {
3851 pa_log_error("%s is not valid role", role);
3858 const char* pa_stream_manager_get_volume_type(pa_stream_manager *m, stream_type_t stream_type, const char *role) {
3859 stream_info *s = NULL;
3864 if (!(s = pa_hashmap_get(m->stream_infos, role))) {
3865 pa_log_warn("%s is not valid role, return NULL", role);
3869 return s->volume_types[stream_type == STREAM_SINK_INPUT ? STREAM_DIRECTION_OUT : STREAM_DIRECTION_IN];
3872 static int stream_manager_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
3873 struct stream_manager_param *param = (struct stream_manager_param *)data;
3876 pa_assert(param->m);
3877 pa_assert(param->m->core);
3879 pa_log_info("code(%d), sink-input(%p, index:%u)", code, param->sink_input, param->index);
3881 if (param->m->core->state == PA_CORE_SHUTDOWN)
3885 case MESSAGE_RAMP_FINISHED:
3886 process_ramp_finish(param);
3890 pa_assert_not_reached();
3896 pa_stream_manager* pa_stream_manager_get(pa_core *c) {
3897 pa_stream_manager *m;
3901 pa_log_debug("pa_stream_manager_get");
3903 if ((m = pa_shared_get(c, SHARED_STREAM_MANAGER)))
3904 return pa_stream_manager_ref(m);
3906 m = pa_xnew0(pa_stream_manager, 1);
3910 m->rtpoll = pa_rtpoll_new();
3911 if (pa_thread_mq_init(&m->thread_mq, m->core->mainloop, m->rtpoll) < 0) {
3912 pa_log("pa_thread_mq_init() failed.");
3915 m->msg = pa_msgobject_new(stream_manager_msg);
3916 m->msg->parent.process_msg = stream_manager_process_msg;
3918 if (!(m->hal = pa_hal_interface_get(c)))
3921 if (pa_hal_interface_add_message_callback(m->hal, message_cb_func, m))
3922 pa_log_warn("skip adding message callback");
3923 m->dm = pa_device_manager_get(c);
3924 m->subs_ob = pa_subscribe_observer_get(c);
3926 #ifdef USE_DBUS_PROTOCOL
3927 m->dbus_protocol = NULL;
3929 m->dbus_conn = NULL;
3934 if (init_stream_map(m))
3936 if (init_volumes(m))
3939 m->stream_parents = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3940 m->muted_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3941 #ifndef __TIZEN_TV_EXTERNAL_TV_SOURCE__
3942 m->mirroring_streams = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3944 m->stream_duckings = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3946 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);
3947 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);
3948 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);
3949 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);
3950 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);
3951 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);
3952 m->sink_input_ramp_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_RAMP_FINISH], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_ramp_finish_cb, m);
3953 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);
3954 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);
3955 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);
3956 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);
3957 m->source_output_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);
3958 m->source_output_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);
3960 m->remote_sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) remote_client_put_cb, m);
3961 m->remote_sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) remote_client_unlink_cb, m);
3962 m->remote_source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) remote_client_put_cb, m);
3963 m->remote_source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) remote_client_unlink_cb, m);
3965 m->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CLIENT | PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, (pa_subscription_cb_t)subscribe_cb, m);
3967 m->comm.comm = pa_communicator_get(c);
3968 m->comm.comm_hook_device_connection_changed_slot = pa_hook_connect(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_DEVICE_CONNECTION_CHANGED),
3969 PA_HOOK_EARLY + 10, (pa_hook_cb_t)device_connection_changed_hook_cb, m);
3970 m->comm.comm_hook_event_fully_handled_slot = pa_hook_connect(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_EVENT_FULLY_HANDLED),
3971 PA_HOOK_EARLY + 10, (pa_hook_cb_t)event_fully_handled_hook_cb, m);
3973 set_initial_active_device(m);
3975 pa_shared_set(c, SHARED_STREAM_MANAGER, m);
3980 pa_log_error("failed to initialize stream-manager");
3982 pa_thread_mq_done(&m->thread_mq);
3983 pa_rtpoll_free(m->rtpoll);
3987 deinit_stream_map(m);
3991 pa_hal_interface_remove_message_callback(m->hal, message_cb_func);
3992 pa_hal_interface_unref(m->hal);
3995 pa_device_manager_unref(m->dm);
3997 pa_subscribe_observer_unref(m->subs_ob);
4002 pa_stream_manager* pa_stream_manager_ref(pa_stream_manager *m) {
4004 pa_assert(PA_REFCNT_VALUE(m) > 0);
4007 pa_log_info("pa_stream_manager_ref to %d", PA_REFCNT_VALUE(m));
4012 static void free_hook_slots(pa_stream_manager *m) {
4015 if (m->comm.comm_hook_device_connection_changed_slot)
4016 pa_hook_slot_free(m->comm.comm_hook_device_connection_changed_slot);
4017 if (m->comm.comm_hook_event_fully_handled_slot)
4018 pa_hook_slot_free(m->comm.comm_hook_event_fully_handled_slot);
4019 if (m->sink_input_new_slot)
4020 pa_hook_slot_free(m->sink_input_new_slot);
4021 if (m->sink_input_put_slot)
4022 pa_hook_slot_free(m->sink_input_put_slot);
4023 if (m->remote_sink_input_put_slot)
4024 pa_hook_slot_free(m->remote_sink_input_put_slot);
4025 if (m->sink_input_unlink_slot)
4026 pa_hook_slot_free(m->sink_input_unlink_slot);
4027 if (m->remote_sink_input_unlink_slot)
4028 pa_hook_slot_free(m->remote_sink_input_unlink_slot);
4029 if (m->sink_input_state_changed_slot)
4030 pa_hook_slot_free(m->sink_input_state_changed_slot);
4031 if (m->sink_input_move_start_slot)
4032 pa_hook_slot_free(m->sink_input_move_start_slot);
4033 if (m->sink_input_move_finish_slot)
4034 pa_hook_slot_free(m->sink_input_move_finish_slot);
4035 if (m->sink_input_ramp_finish_slot)
4036 pa_hook_slot_free(m->sink_input_ramp_finish_slot);
4038 if (m->source_output_new_slot)
4039 pa_hook_slot_free(m->source_output_new_slot);
4040 if (m->source_output_put_slot)
4041 pa_hook_slot_free(m->source_output_put_slot);
4042 if (m->remote_source_output_put_slot)
4043 pa_hook_slot_free(m->remote_source_output_put_slot);
4044 if (m->source_output_unlink_slot)
4045 pa_hook_slot_free(m->source_output_unlink_slot);
4046 if (m->remote_source_output_unlink_slot)
4047 pa_hook_slot_free(m->remote_source_output_unlink_slot);
4048 if (m->source_output_state_changed_slot)
4049 pa_hook_slot_free(m->source_output_state_changed_slot);
4050 if (m->source_output_move_start_slot)
4051 pa_hook_slot_free(m->source_output_move_start_slot);
4052 if (m->source_output_move_finish_slot)
4053 pa_hook_slot_free(m->source_output_move_finish_slot);
4056 void pa_stream_manager_unref(pa_stream_manager *m) {
4061 pa_assert(PA_REFCNT_VALUE(m) > 0);
4063 pa_log_info("pa_stream_manager_unref to %d", PA_REFCNT_VALUE(m) - 1);
4064 if (PA_REFCNT_DEC(m) > 0)
4067 pa_thread_mq_done(&m->thread_mq);
4068 pa_rtpoll_free(m->rtpoll);
4074 pa_communicator_unref(m->comm.comm);
4076 if (m->subscription)
4077 pa_subscription_free(m->subscription);
4079 if (m->muted_streams) {
4080 PA_HASHMAP_FOREACH(streams, m->muted_streams, state)
4081 pa_idxset_free(streams, NULL);
4082 pa_hashmap_free(m->muted_streams);
4085 #ifndef __TIZEN_TV_EXTERNAL_TV_SOURCE__
4086 if (m->mirroring_streams) {
4087 if (pa_idxset_size(m->mirroring_streams))
4088 update_forwarding_device(m, false);
4089 pa_idxset_free(m->mirroring_streams, NULL);
4093 if (m->stream_parents)
4094 pa_hashmap_free(m->stream_parents);
4096 if (m->stream_duckings)
4097 pa_hashmap_free(m->stream_duckings);
4100 deinit_stream_map(m);
4105 pa_subscribe_observer_unref(m->subs_ob);
4108 pa_device_manager_unref(m->dm);
4111 pa_hal_interface_remove_message_callback(m->hal, message_cb_func);
4112 pa_hal_interface_unref(m->hal);
4115 pa_shared_remove(m->core, SHARED_STREAM_MANAGER);