stream-manager: Support loopback-mirroring stream type and add logics to load/unload... 53/89953/4 accepted/tizen/3.0/ivi/20161011.062233 accepted/tizen/common/20160928.164017 accepted/tizen/ivi/20160930.040809 accepted/tizen/mobile/20160930.040503 accepted/tizen/tv/20160930.040619 accepted/tizen/wearable/20160930.040712 submit/tizen/20160928.043825 submit/tizen_3.0_ivi/20161010.000006
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 27 Sep 2016 06:59:07 +0000 (15:59 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 28 Sep 2016 04:42:12 +0000 (13:42 +0900)
[Version] 5.0.81
[Profile] Common
[Issue Type] Feature enhancement

Change-Id: I70a93307b3d85c727c5d6bb8443e7ec0a76bdcdd
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
packaging/pulseaudio-modules-tizen.spec
src/module-tizenaudio-policy.c
src/stream-manager-priv.h
src/stream-manager-volume.c
src/stream-manager.c
src/stream-manager.h

index 24dda5e..3a6a20f 100644 (file)
@@ -1,6 +1,6 @@
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          5.0.80
+Version:          5.0.81
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
index 51456c6..b3976e6 100644 (file)
@@ -208,6 +208,121 @@ static void __load_dump_config(struct userdata *u)
     }
 }
 
+/* The state of a device using internal audio codec is handled here.
+ * Regarding the state of an external device, it is handled in device-manager.c */
+static void set_device_state_if_using_internal_codec(pa_tz_device *device, stream_type_t stream_type, dm_device_state_t device_state) {
+    dm_device_direction_t direction;
+
+    pa_assert(device);
+
+    direction = pa_tz_device_get_direction(device);
+    if (IS_AVAILABLE_DIRECTION(stream_type, direction))
+        if (pa_tz_device_is_use_internal_codec(device))
+            pa_tz_device_set_state(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), device_state);
+}
+
+/* threre is only one sco connected device */
+static pa_tz_device* _get_sco_connected_device(pa_device_manager *dm) {
+    pa_idxset *device_list;
+    pa_tz_device *device;
+    uint32_t device_idx;
+
+    pa_assert(dm);
+
+    device_list = pa_device_manager_get_device_list(dm);
+
+    PA_IDXSET_FOREACH(device, device_list, device_idx) {
+        if (pa_streq(device->type, DEVICE_TYPE_BT)) {
+            /* FIXME : not works */
+            if (pa_tz_device_have_profile(device, DEVICE_PROFILE_BT_SCO)) {
+                return device;
+            }
+        }
+    }
+    return NULL;
+}
+
+/* Open/Close BT SCO if it is possible */
+static int update_bt_sco_state(pa_device_manager *dm, bool open) {
+    dm_device_bt_sco_status_t sco_status;
+    pa_tz_device *bt_device;
+
+    pa_assert(dm);
+
+    bt_device = _get_sco_connected_device(dm);
+    if (bt_device == NULL) {
+        pa_log_debug("No SCO connected bt device");
+        return 0;
+    } else
+        pa_log_info("Got BT SCO connected device(%u)", bt_device->id);
+
+    if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) {
+        pa_log_error("get BT SCO status failed");
+        return -1;
+    }
+
+    if (sco_status == DM_DEVICE_BT_SCO_STATUS_DISCONNECTED) {
+        pa_log_info("BT SCO is not available for this BT device");
+        return 0;
+    }
+    if (open) {
+        if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED) {
+            if (pa_tz_device_sco_open(bt_device) < 0) {
+                pa_log_error("failed to open BT SCO");
+                return -1;
+            } else
+                pa_log_debug("BT SCO is now opened");
+        } else
+            pa_log_error("BT SCO is already opened for this BT device");
+    } else {
+        if (sco_status == DM_DEVICE_BT_SCO_STATUS_OPENED) {
+            if (pa_tz_device_sco_close(bt_device) < 0) {
+                pa_log_error("BT SCO was opened, but failed to close SCO");
+                return -1;
+            } else
+                pa_log_debug("BT SCO is now closed");
+        } else
+            pa_log_error("BT SCO is already closed for this BT device");
+    }
+
+    return 0;
+}
+
+/* Load/Unload module-loopback */
+static void update_loopback_module(struct userdata *u, bool load) {
+    char *args = NULL;
+
+    pa_assert(u);
+
+    if (load && u->loopback_args.sink && u->loopback_args.source) {
+        if (!u->loopback_args.latency_msec || !u->loopback_args.latency_msec) {
+            u->loopback_args.latency_msec = LOOPBACK_DEFAULT_LATENCY_MSEC;
+            u->loopback_args.adjust_sec = LOOPBACK_DEFAULT_ADJUST_SEC;
+        }
+        args = pa_sprintf_malloc("sink=%s source=%s latency_msec=%d adjust_time=%d",
+                                 u->loopback_args.sink->name, u->loopback_args.source->name,
+                                 u->loopback_args.latency_msec, u->loopback_args.adjust_sec);
+        if (u->module_loopback)
+            pa_module_unload(u->core, u->module_loopback, true);
+
+        u->module_loopback = pa_module_load(u->core, MODULE_LOOPBACK, args);
+
+        pa_log_info("  -- load module-loopback with (%s)", args);
+        pa_xfree(args);
+
+    } else if (!load) {
+        if (u->module_loopback) {
+            pa_module_unload(u->core, u->module_loopback, true);
+            u->module_loopback = NULL;
+            u->loopback_args.sink = NULL;
+            u->loopback_args.source = NULL;
+            pa_log_info("  -- unload module-loopback");
+        }
+    } else {
+        pa_log_error("  -- failed to update loopback module");
+    }
+}
+
 /* Set the proper sink(source) according to the data of the parameter.
  * - ROUTE_TYPE_AUTO(_ALL)
  *     1. Find the proper sink/source comparing between avail_devices
@@ -441,124 +556,6 @@ static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stre
     return PA_HOOK_OK;
 }
 
-/* The state of a device using internal audio codec is handled here.
- * Regarding the state of an external device, those is handled in device-manager.c */
-static void set_device_state_if_using_internal_codec(pa_tz_device *device, stream_type_t stream_type, dm_device_state_t device_state) {
-    bool use_internal_codec = false;
-    dm_device_direction_t direction;
-
-    pa_assert(device);
-
-    direction = pa_tz_device_get_direction(device);
-    if (IS_AVAILABLE_DIRECTION(stream_type, direction))
-        if ((use_internal_codec = pa_tz_device_is_use_internal_codec(device)))
-            pa_tz_device_set_state(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), device_state);
-
-    return;
-}
-
-/* threre is only one sco connected device */
-static pa_tz_device* _get_sco_connected_device(pa_device_manager *dm) {
-    pa_idxset *device_list;
-    pa_tz_device *device;
-    uint32_t device_idx;
-
-    pa_assert(dm);
-
-    device_list = pa_device_manager_get_device_list(dm);
-
-    PA_IDXSET_FOREACH(device, device_list, device_idx) {
-        if (pa_streq(device->type, DEVICE_TYPE_BT)) {
-            /* FIXME : not works */
-            if (pa_tz_device_have_profile(device, DEVICE_PROFILE_BT_SCO)) {
-                return device;
-            }
-        }
-    }
-    return NULL;
-}
-
-/* Open/Close BT SCO if it is possible */
-static int update_bt_sco_state(pa_device_manager *dm, bool open) {
-    dm_device_bt_sco_status_t sco_status;
-    pa_tz_device *bt_device;
-
-    pa_assert(dm);
-
-    bt_device = _get_sco_connected_device(dm);
-    if (bt_device == NULL) {
-        pa_log_debug("No SCO connected bt device");
-        return 0;
-    } else
-        pa_log_info("Got BT SCO connected device(%u)", bt_device->id);
-
-    if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) {
-        pa_log_error("get BT SCO status failed");
-        return -1;
-    }
-
-    if (sco_status == DM_DEVICE_BT_SCO_STATUS_DISCONNECTED) {
-        pa_log_info("BT SCO is not available for this BT device");
-        return 0;
-    }
-    if (open) {
-        if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED) {
-            if (pa_tz_device_sco_open(bt_device) < 0) {
-                pa_log_error("failed to open BT SCO");
-                return -1;
-            } else
-                pa_log_debug("BT SCO is now opened");
-        } else
-            pa_log_error("BT SCO is already opened for this BT device");
-    } else {
-        if (sco_status == DM_DEVICE_BT_SCO_STATUS_OPENED) {
-            if (pa_tz_device_sco_close(bt_device) < 0) {
-                pa_log_error("BT SCO was opened, but failed to close SCO");
-                return -1;
-            } else
-                pa_log_debug("BT SCO is now closed");
-        } else
-            pa_log_error("BT SCO is already closed for this BT device");
-    }
-
-    return 0;
-}
-
-/* Load/Unload module-loopback */
-static void update_loopback_module(struct userdata *u, bool load) {
-    char *args = NULL;
-
-    pa_assert(u);
-
-    if (load && u->loopback_args.sink && u->loopback_args.source) {
-        if (!u->loopback_args.latency_msec || !u->loopback_args.latency_msec) {
-            u->loopback_args.latency_msec = LOOPBACK_DEFAULT_LATENCY_MSEC;
-            u->loopback_args.adjust_sec = LOOPBACK_DEFAULT_ADJUST_SEC;
-        }
-        args = pa_sprintf_malloc("sink=%s source=%s latency_msec=%d adjust_time=%d",
-                                 u->loopback_args.sink->name, u->loopback_args.source->name,
-                                 u->loopback_args.latency_msec, u->loopback_args.adjust_sec);
-        if (u->module_loopback)
-            pa_module_unload(u->core, u->module_loopback, true);
-
-        u->module_loopback = pa_module_load(u->core, MODULE_LOOPBACK, args);
-
-        pa_log_info("  -- load module-loopback with (%s)", args);
-        pa_xfree(args);
-
-    } else if (!load) {
-        if (u->module_loopback) {
-            pa_module_unload(u->core, u->module_loopback, true);
-            u->module_loopback = NULL;
-            u->loopback_args.sink = NULL;
-            u->loopback_args.source = NULL;
-            pa_log_info("  -- unload module-loopback");
-        }
-    } else {
-        pa_log_error("  -- failed to update loopback module");
-    }
-}
-
 /* Change the route setting according to the data from argument.
  * This function is called only when it needs to change routing path via HAL.
  * - stream is null
@@ -706,6 +703,9 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
                         pa_log_debug("  ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
                         use_internal_codec = pa_tz_device_is_use_internal_codec(device);
                         if (use_internal_codec) {
+                            /* if the device type is forwarding, keep going to next device for proper UCM setting */
+                            if (pa_streq(dm_device_type, DEVICE_TYPE_FORWARDING))
+                                continue;
                             hal_direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
                             route_info.num_of_devices++;
                             route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
@@ -1112,8 +1112,6 @@ static void update_connected_devices(const char *device_type, dm_device_directio
         *ptr_in = 0;
     if (*ptr_out < 0)
         *ptr_out = 0;
-
-    return;
 }
 
 static void dump_connected_devices()
@@ -1126,7 +1124,6 @@ static void dump_connected_devices()
         pa_log_debug("in: %d, out: %d", cached_connected_devices[i][CACHED_DEVICE_DIRECTION_IN], cached_connected_devices[i][CACHED_DEVICE_DIRECTION_OUT]);
     pa_log_debug("================================");
 #endif
-    return;
 }
 
 /* Reorganize routing when a device has been connected or disconnected */
@@ -1356,6 +1353,5 @@ void pa__done(pa_module *m)
 
     pa_xfree(u);
 
-
     pa_log_info("Tizen Audio Policy module is unloaded\n");
 }
index e595d77..343d9e5 100644 (file)
@@ -111,6 +111,7 @@ struct _stream_manager {
     pa_hashmap *latency_infos;
     pa_hashmap *stream_parents;
     pa_hashmap *muted_streams;
+    pa_idxset *mirroring_streams;
     cur_max_priority_stream cur_highest_priority;
     stream_restrictions restrictions;
 
index 3c0c062..051a287 100644 (file)
@@ -701,8 +701,6 @@ static void dump_volumes(pa_stream_manager *m) {
     while (m->volume_modifiers && (gain_value = pa_hashmap_iterate(m->volume_modifiers, &state, (const void**)&modifier_gain)))
         pa_log_debug("[modifier gain:%s, value:%f]", modifier_gain, *gain_value);
     pa_log_debug("===========[END volume infos dump]===========");
-
-    return;
 }
 
 int32_t init_volumes(pa_stream_manager *m) {
@@ -774,8 +772,6 @@ void deinit_volumes(pa_stream_manager *m) {
             pa_xfree(gain);
         pa_hashmap_free(m->volume_modifiers);
     }
-
-    return;
 }
 
 int32_t pa_stream_manager_volume_get_max_level(pa_stream_manager *m, stream_type_t stream_type, const char *volume_type, uint32_t *volume_level) {
index 13b80eb..d2f7918 100644 (file)
@@ -769,7 +769,6 @@ fail:
     pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_INDEX_ERROR], DBUS_TYPE_INVALID));
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_set_stream_route_option(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -835,7 +834,6 @@ static void handle_set_stream_route_option(DBusConnection *conn, DBusMessage *ms
 
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_set_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -881,8 +879,6 @@ finish:
 
     if (!ret)
         send_volume_changed_signal(conn, direction, type, level);
-
-    return;
 }
 
 static void handle_get_volume_level(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -926,7 +922,6 @@ static void handle_get_volume_level(DBusConnection *conn, DBusMessage *msg, void
 finish:
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_get_volume_max_level(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -969,7 +964,6 @@ static void handle_get_volume_max_level(DBusConnection *conn, DBusMessage *msg,
 finish:
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_set_volume_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1010,7 +1004,6 @@ static void handle_set_volume_mute(DBusConnection *conn, DBusMessage *msg, void
 finish:
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_get_volume_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1054,7 +1047,6 @@ static void handle_get_volume_mute(DBusConnection *conn, DBusMessage *msg, void
 fail:
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_get_current_volume_type(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1109,7 +1101,6 @@ static void handle_get_current_volume_type(DBusConnection *conn, DBusMessage *ms
 fail:
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_update_focus_status(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1169,7 +1160,6 @@ static void handle_update_focus_status(DBusConnection *conn, DBusMessage *msg, v
 fail:
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static void handle_update_restriction(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1199,7 +1189,6 @@ static void handle_update_restriction(DBusConnection *conn, DBusMessage *msg, vo
 fail:
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
-    return;
 }
 
 static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1366,7 +1355,6 @@ static void dump_stream_map(pa_stream_manager *m) {
             pa_log_debug("      name[%d]  : %s", idx, name);
     }
     pa_log_debug("===========[END stream-map dump]===========");
-    return;
 }
 
 static int init_stream_map(pa_stream_manager *m) {
@@ -1646,8 +1634,6 @@ static void deinit_stream_map(pa_stream_manager *m) {
         }
         pa_hashmap_free(m->latency_infos);
     }
-
-    return;
 }
 
 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) {
@@ -1682,7 +1668,7 @@ static bool check_name_to_skip(pa_stream_manager *m, process_command_type_t comm
     return ret;
 }
 
-static bool check_role_to_skip(pa_stream_manager *m, const char *role) {
+static bool check_role_to_skip(pa_stream_manager *m, process_command_type_t command, const char *role) {
     bool ret = true;
     stream_info *s = NULL;
 
@@ -1691,7 +1677,11 @@ static bool check_role_to_skip(pa_stream_manager *m, const char *role) {
     pa_assert(role);
 
     if ((s = pa_hashmap_get(m->stream_infos, role)))
-        ret = false;
+      ret = false;
+    if (s && pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING) &&
+       (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED ||
+       command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED))
+      ret = true;
 
     pa_log_debug("role is [%s], skip(%d)", role, ret);
 
@@ -2138,8 +2128,6 @@ static void update_buffer_attribute(pa_stream_manager *m, void *new_data, stream
 
         pa_log_info("*** maxlength:%d, fragsize:%d", maxlength, fragsize);
     }
-
-    return;
 }
 
 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) {
@@ -2232,7 +2220,6 @@ static void fill_device_info_to_hook_data(pa_stream_manager *m, void *hook_data,
     default:
         break;
     }
-    return;
 }
 
 static void do_notify(pa_stream_manager *m, notify_command_type_t command, stream_type_t type, bool is_new_data, void *user_data) {
@@ -2377,7 +2364,47 @@ static void do_notify(pa_stream_manager *m, notify_command_type_t command, strea
         break;
     }
     }
-    return;
+}
+
+static void update_mirroring_streams(pa_stream_manager *m, pa_source_output *o, bool put) {
+    const char *role;
+
+    pa_assert(m);
+    pa_assert(o);
+
+    if ((role = pa_proplist_gets(GET_STREAM_PROPLIST(o, STREAM_SOURCE_OUTPUT), PA_PROP_MEDIA_ROLE)) &&
+        pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING)) {
+        if (put)
+            pa_idxset_put(m->mirroring_streams, o, NULL);
+        else
+            pa_idxset_remove_by_data(m->mirroring_streams, o, NULL);
+    }
+}
+
+/* Load/unload forwarding device */
+static int update_forwarding_device(pa_stream_manager *m, bool load) {
+    pa_assert(m);
+
+    if (load) {
+        if (pa_idxset_size(m->mirroring_streams) == 0) {
+            if (!pa_device_manager_load_forwarding(m->dm)) {
+                pa_log_error("could not load forwarding device");
+                return -1;
+            } else
+                pa_log_info("forwarding device is now loaded");
+        } else
+            pa_log_debug("forwarding device has been already loaded");
+
+    } else {
+        if (pa_idxset_size(m->mirroring_streams) > 0)
+            pa_log_debug("no need to unload forwarding device");
+        else {
+            pa_device_manager_unload_forwarding(m->dm);
+            pa_log_info("forwarding device is now unloaded");
+        }
+    }
+
+    return 0;
 }
 
 static process_stream_result_t process_stream(pa_stream_manager *m, void *stream, stream_type_t type, process_command_type_t command, bool is_new_data) {
@@ -2447,10 +2474,13 @@ static process_stream_result_t process_stream(pa_stream_manager *m, void *stream
             pa_log_warn("role is null, set default to [%s]", role);
         } else {
             /* skip roles */
-            if (check_role_to_skip(m, role)) {
+            if (check_role_to_skip(m, command, role)) {
                 result = PROCESS_STREAM_RESULT_SKIP;
                 goto finish;
             }
+            /* load forwarding device */
+            if (type == STREAM_SOURCE_OUTPUT && pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING))
+                update_forwarding_device(m, true);
         }
         /* update the priority of this stream */
         ret = update_priority_of_stream(m, stream, type, role, is_new_data);
@@ -2501,7 +2531,7 @@ static process_stream_result_t process_stream(pa_stream_manager *m, void *stream
         }
 
         /* skip roles */
-        if (check_role_to_skip(m, role)) {
+        if (check_role_to_skip(m, command, role)) {
             result = PROCESS_STREAM_RESULT_SKIP;
             goto finish;
         }
@@ -2562,8 +2592,12 @@ static process_stream_result_t process_stream(pa_stream_manager *m, void *stream
     } else if (command == PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED) {
         role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
         if (role) {
+            /* unload forwarding device */
+            if (type == STREAM_SOURCE_OUTPUT && pa_streq(role, STREAM_ROLE_LOOPBACK_MIRRORING))
+                update_forwarding_device(m, false);
+
             /* skip roles */
-            if (check_role_to_skip(m, role)) {
+            if (check_role_to_skip(m, command, role)) {
                 result = PROCESS_STREAM_RESULT_SKIP;
                 goto finish;
             }
@@ -2606,7 +2640,7 @@ static process_stream_result_t process_stream(pa_stream_manager *m, void *stream
         role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
         if (role) {
             /* skip roles */
-            if (check_role_to_skip(m, role)) {
+            if (check_role_to_skip(m, command, role)) {
                 result = PROCESS_STREAM_RESULT_SKIP;
                 goto finish;
             }
@@ -2654,7 +2688,7 @@ static process_stream_result_t process_stream(pa_stream_manager *m, void *stream
         role = pa_proplist_gets(GET_STREAM_PROPLIST(stream, type), PA_PROP_MEDIA_ROLE);
         if (role) {
             /* skip roles */
-            if (check_role_to_skip(m, role)) {
+            if (check_role_to_skip(m, command, role)) {
                 result = PROCESS_STREAM_RESULT_SKIP;
                 goto finish;
             }
@@ -2712,8 +2746,6 @@ static void remove_sink_input_from_muted_streams(pa_stream_manager *m, pa_sink_i
         PA_IDXSET_FOREACH(si, streams, idx)
             if (si == i)
                 pa_idxset_remove_by_data(streams, i, NULL);
-
-    return;
 }
 
 static pa_hook_result_t sink_input_new_cb(pa_core *core, pa_sink_input_new_data *new_data, pa_stream_manager *m) {
@@ -2830,6 +2862,7 @@ static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o,
 
     pa_log_info("start source_output_put_cb, o(%p, index:%u)", o, o->index);
 
+    update_mirroring_streams(m, o, true);
     process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_ADD_PARENT_ID, false);
 
     return PA_HOOK_OK;
@@ -2841,6 +2874,7 @@ static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output
 
     pa_log_info("start source_output_unlink_cb, o(%p, index:%u)", o, o->index);
 
+    update_mirroring_streams(m, o, false);
     process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_ENDED, false);
     process_stream(m, o, STREAM_SOURCE_OUTPUT, PROCESS_COMMAND_REMOVE_PARENT_ID, false);
 
@@ -2964,8 +2998,6 @@ static void find_next_device_for_auto_route(pa_stream_manager *m, stream_route_t
     }
 
     pa_log_debug("next_device is [%p] for role[%s]/route_type[%d]/stream_type[%d]", *next_device, role, route_type, stream_type);
-
-    return;
 }
 
 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) {
@@ -3012,8 +3044,6 @@ static void is_available_device_for_auto_route(pa_stream_manager *m, stream_rout
     }
 
     pa_log_debug("is new_device[%s] available for role[%s]/stream_type[%d]:%d", new_device_type, role, stream_type, *available);
-
-    return;
 }
 
 /* Re-trigger for routing update for streams using auto route type */
@@ -3045,8 +3075,6 @@ static void process_stream_as_device_change_for_auto_route(pa_stream_manager *m,
         if (use_internal_codec)
             process_stream(m, stream, stream_type, PROCESS_COMMAND_CHANGE_ROUTE_BY_STREAM_STARTED, false);
     }
-
-    return;
 }
 
 /* The state of a device using internal audio codec is handled here.
@@ -3058,8 +3086,6 @@ static void set_device_state_if_using_internal_codec(pa_tz_device *device, strea
 
     if ((use_internal_codec = pa_tz_device_is_use_internal_codec(device)))
         pa_tz_device_set_state(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), device_state);
-
-    return;
 }
 
 static void update_sink_or_source_as_device_change(stream_route_type_t stream_route_type, pa_idxset *streams,
@@ -3251,8 +3277,6 @@ static void update_sink_or_source_as_device_change(stream_route_type_t stream_ro
         }
     } else
         pa_log_error("[SM][UPDATE_SINK_SOURCE] could not handle it here, stream_route_type(%d)", stream_route_type);
-
-    return;
 }
 
 static void mute_sink_inputs_as_device_disconnection(pa_stream_manager *m, uint32_t event_id, bool need_to_mute, void *user_data) {
@@ -3286,6 +3310,7 @@ static void mute_sink_inputs_as_device_disconnection(pa_stream_manager *m, uint3
         PA_IDXSET_FOREACH(i, muted_streams, s_idx) {
             pa_idxset_remove_by_data(muted_streams, i, NULL);
             pa_sink_input_remove_volume_factor(i, mute_key);
+            pa_log_debug("found a stream(%p, %u) that should be un-muted.", i, i->index);
         }
         pa_hashmap_remove(m->muted_streams, (void*)event_id);
         pa_idxset_free(muted_streams, NULL);
@@ -3502,8 +3527,6 @@ static void message_cb(const char *name, int value, void *user_data) {
         send_command_signal(pa_dbus_connection_get(m->dbus_conn), name, value);
     }
 #endif
-
-    return;
 }
 
 static int32_t init_ipc(pa_stream_manager *m) {
@@ -3567,7 +3590,6 @@ static void deinit_ipc(pa_stream_manager *m) {
     }
 #endif
 #endif
-    return;
 }
 
 bool pa_stream_manager_check_name_is_vstream(void *stream, stream_type_t type, bool is_new_data) {
@@ -3606,6 +3628,7 @@ pa_stream_manager* pa_stream_manager_init(pa_core *c) {
         goto fail;
     m->stream_parents = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
     m->muted_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    m->mirroring_streams = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
     m->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_cb, m);
     m->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_put_cb, m);
@@ -3691,6 +3714,12 @@ void pa_stream_manager_done(pa_stream_manager *m) {
         pa_hashmap_free(m->muted_streams);
     }
 
+    if (m->mirroring_streams) {
+        if (pa_idxset_size(m->mirroring_streams))
+            update_forwarding_device(m, false);
+        pa_idxset_free(m->mirroring_streams, NULL);
+    }
+
     if (m->stream_parents)
         pa_hashmap_free(m->stream_parents);
 
index 2e50746..0fb6f23 100644 (file)
 #define GET_STREAM_PROPLIST(stream, type) \
       (type == STREAM_SINK_INPUT ? ((pa_sink_input*)stream)->proplist : ((pa_source_output*)stream)->proplist)
 
-#define STREAM_ROLE_CALL_VOICE        "call-voice"
-#define STREAM_ROLE_CALL_VIDEO        "call-video"
-#define STREAM_ROLE_VOIP              "voip"
-#define STREAM_ROLE_LOOPBACK          "loopback"
-#define STREAM_ROLE_RADIO             "radio"
-
-#define SINK_NAME_COMBINED            "sink_combined"
-#define SINK_NAME_NULL                "sink_null"
-#define SOURCE_NAME_NULL              "source_null"
+#define STREAM_ROLE_CALL_VOICE          "call-voice"
+#define STREAM_ROLE_CALL_VIDEO          "call-video"
+#define STREAM_ROLE_VOIP                "voip"
+#define STREAM_ROLE_LOOPBACK            "loopback"
+#define STREAM_ROLE_LOOPBACK_MIRRORING  "loopback-mirroring"
+#define STREAM_ROLE_RADIO               "radio"
+
+#define SINK_NAME_COMBINED              "sink_combined"
+#define SINK_NAME_NULL                  "sink_null"
+#define SOURCE_NAME_NULL                "source_null"
 
 typedef struct _stream_manager pa_stream_manager;