policy: Support scenario for using module-loopback 96/56996/4 accepted/tizen/mobile/20160115.114140 accepted/tizen/tv/20160115.114205 accepted/tizen/wearable/20160115.114232 submit/tizen/20160115.085807
authorSangchul Lee <sc11.lee@samsung.com>
Thu, 14 Jan 2016 07:39:23 +0000 (16:39 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Fri, 15 Jan 2016 08:52:45 +0000 (17:52 +0900)
[Version] 5.0.31
[Profile] Common
[Issue Type] Feature Enhancement

Change-Id: Ie03bb0394d61557d77a9f6be447da97ff3afd219

packaging/pulseaudio-modules-tizen.spec
src/hal-manager.h
src/module-policy.c
src/stream-manager.c
src/stream-manager.h

index 99ddd3a..f5f11fd 100644 (file)
@@ -1,6 +1,6 @@
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          5.0.30
+Version:          5.0.31
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
index 583e6ca..e0ac902 100644 (file)
@@ -43,6 +43,8 @@ typedef struct _hal_stream_info {
 
 typedef void* pcm_handle;
 
+#define MSG_FOR_LOOPBACK_ARG_LATENCY      "loopback::latency"
+#define MSG_FOR_LOOPBACK_ARG_ADJUST_TIME  "loopback::adjust_time"
 typedef void (*hal_message_callback)(const char *name, int value, void *user_data);
 
 pa_hal_manager* pa_hal_manager_get(pa_core *core);
index a3ccf21..ba0818c 100644 (file)
@@ -205,12 +205,19 @@ enum signal_index {
 #define MODULE_COMBINE_SINK           "module-combine-sink"
 #define MODULE_NULL_SINK              "module-null-sink"
 #define MODULE_NULL_SOURCE            "module-null-source"
+#define MODULE_LOOPBACK               "module-loopback"
 
 /* Name of combine sink for external route type */
 #define SINK_NAME_COMBINED_EX         SINK_NAME_COMBINED"_ex"
 
+/* Default values */
+#define LOOPBACK_DEFAULT_LATENCY_MSEC    40
+#define LOOPBACK_DEFAULT_ADJUST_SEC      3
+
 /* Macros */
-#define CONVERT_TO_DEVICE_DIRECTION(stream_type)\
+#define CONVERT_TO_HAL_DIRECTION(stream_type) \
+    ((stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN)
+#define CONVERT_TO_DEVICE_DIRECTION(stream_type) \
     ((stream_type == STREAM_SINK_INPUT) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
 #define IS_AVAILABLE_DIRECTION(stream_type, device_direction) \
     ((stream_type == STREAM_SINK_INPUT) ? (device_direction & DM_DEVICE_DIRECTION_OUT) : (device_direction & DM_DEVICE_DIRECTION_IN))
@@ -249,6 +256,13 @@ struct userdata {
     pa_module *module_combine_sink_for_ex;
     pa_module *module_null_sink;
     pa_module *module_null_source;
+    pa_module *module_loopback;
+    struct {
+        int32_t latency_msec;
+        int32_t adjust_sec;
+        pa_sink *sink;
+        pa_source *source;
+    } loopback_args;
 };
 
 static void __load_dump_config(struct userdata *u)
@@ -524,7 +538,7 @@ static void set_device_state_if_using_internal_codec(dm_device *device, stream_t
 }
 
 /* Open/Close BT SCO if it is possible */
-static int set_bt_sco_state(pa_device_manager *dm, pa_bool_t open) {
+static int update_bt_sco_state(pa_device_manager *dm, pa_bool_t open) {
     dm_device_bt_sco_status_t sco_status;
 
     pa_assert(dm);
@@ -556,6 +570,41 @@ static int set_bt_sco_state(pa_device_manager *dm, pa_bool_t open) {
     return 0;
 }
 
+/* Load/Unload module-loopback */
+static void update_loopback_module(struct userdata *u, pa_bool_t 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", args);
+        }
+    } 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
@@ -620,13 +669,21 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
     pa_assert(u);
 
     pa_log_info("[ROUTE] route_change_hook_cb is called. (%p), stream_type(%d), stream_role(%s), route_type(%d)",
-            data, data->stream_type, data->stream_role, data->route_type);
+                data, data->stream_type, data->stream_role, data->route_type);
 
     route_info.role = data->stream_role;
 
     if (data->stream == NULL) {
+        /* unload module-loopback */
+        if (u->module_loopback) {
+            if (data->stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec) {
+                update_loopback_module(u, FALSE);
+            } else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec) {
+                update_loopback_module(u, FALSE);
+            }
+        }
         /* update BT SCO: close */
-        set_bt_sco_state(u->device_manager, FALSE);
+        update_bt_sco_state(u->device_manager, FALSE);
 
         /* get current connected devices */
         conn_devices = pa_device_manager_get_device_list(u->device_manager);
@@ -636,14 +693,15 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
             dm_device_state = pa_device_manager_get_device_state(device, CONVERT_TO_DEVICE_DIRECTION(data->stream_type));
             dm_device_direction = pa_device_manager_get_device_direction(device);
             if (dm_device_state == DM_DEVICE_STATE_ACTIVATED && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                pa_log_info("[ROUTE][RESET] found a matched device and set state to DE-ACTIVATED: type[%s], direction[0x%x]", dm_device_type, dm_device_direction);
+                pa_log_info("[ROUTE][RESET] found a matched device and set state to DE-ACTIVATED: type[%s], direction[0x%x]",
+                            dm_device_type, dm_device_direction);
                 set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
             }
         }
         route_info.role = "reset";
         route_info.num_of_devices = 1;
         route_info.device_infos = pa_xmalloc0(sizeof(hal_device_info)*route_info.num_of_devices);
-        route_info.device_infos[0].direction = (data->stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN;
+        route_info.device_infos[0].direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
 
         /* unload combine sink */
         if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
@@ -655,7 +713,8 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
             else {
                 PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
                     pa_sink_input_move_to(s, null_sink, FALSE);
-                    pa_log_debug("[ROUTE][RESET] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
+                    pa_log_debug("[ROUTE][RESET] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                 s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
                 }
                 pa_sink_suspend(combine_sink, TRUE, PA_SUSPEND_USER);
             }
@@ -664,8 +723,16 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
         }
 
     } else if ((data->route_type <= STREAM_ROUTE_TYPE_AUTO_ALL) && data->idx_avail_devices) {
+        /* unload module-loopback */
+        if (u->module_loopback) {
+            if (data->stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec) {
+                update_loopback_module(u, FALSE);
+            } else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec) {
+                update_loopback_module(u, FALSE);
+            }
+        }
         /* update BT SCO: close */
-        set_bt_sco_state(u->device_manager, FALSE);
+        update_bt_sco_state(u->device_manager, FALSE);
 
         /* get current connected devices */
         conn_devices = pa_device_manager_get_device_list(u->device_manager);
@@ -681,16 +748,18 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
                                  dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id);
                     if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
                         pa_log_debug("  ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
-                        use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, CONVERT_TO_DEVICE_DIRECTION(data->stream_type), data->device_role);
+                        use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
+                                                                                            CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                            data->device_role);
                         if (use_internal_codec) {
-                            hal_direction = (data->stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN;
+                            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);
                             route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
                             route_info.device_infos[route_info.num_of_devices-1].direction = hal_direction;
                             route_info.device_infos[route_info.num_of_devices-1].id = dm_device_id;
                             pa_log_info("  ** found a matched device and set state to ACTIVATED: type[%-16s], direction[0x%x], id[%u]",
-                            route_info.device_infos[route_info.num_of_devices-1].type, hal_direction, dm_device_id);
+                                        route_info.device_infos[route_info.num_of_devices-1].type, hal_direction, dm_device_id);
                             /* set device state to activated */
                             set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
                         } else
@@ -703,7 +772,9 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
 
                 if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
                     /* check if this device uses internal codec */
-                    use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, CONVERT_TO_DEVICE_DIRECTION(data->stream_type), DEVICE_ROLE_NORMAL);
+                    use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
+                                                                                        CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                        data->device_role);
                     if (use_internal_codec) {
                         /* set other device's state to deactivated */
                         PA_IDXSET_FOREACH(_device, conn_devices, conn_idx) {
@@ -726,7 +797,8 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
                         if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
                             PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
                                 pa_sink_input_move_to(s, sink, FALSE);
-                                pa_log_debug("[ROUTE][AUTO] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
+                                pa_log_debug("[ROUTE][AUTO] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                             s, ((pa_sink_input*)s)->index, sink, sink->name);
                             }
                             pa_sink_suspend(combine_sink, TRUE, PA_SUSPEND_USER);
                         } else
@@ -822,9 +894,11 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
             if (latest_device) {
                 dm_device_type = pa_device_manager_get_device_type(latest_device);
                 dm_device_id = pa_device_manager_get_device_id(latest_device);
-                use_internal_codec = pa_device_manager_is_device_use_internal_codec(latest_device, CONVERT_TO_DEVICE_DIRECTION(data->stream_type), data->device_role);
+                use_internal_codec = pa_device_manager_is_device_use_internal_codec(latest_device,
+                                                                                    CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                    data->device_role);
                 if (use_internal_codec) {
-                    hal_direction = (data->stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN;
+                    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);
                     route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
@@ -854,7 +928,8 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
                     if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
                         PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
                             pa_sink_input_move_to(s, sink, FALSE);
-                            pa_log_debug("[ROUTE][AUTO_LAST_CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
+                            pa_log_debug("[ROUTE][AUTO_LAST_CONN] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                         s, ((pa_sink_input*)s)->index, sink, sink->name);
                         }
                         pa_sink_suspend(combine_sink, TRUE, PA_SUSPEND_USER);
                     } else
@@ -891,29 +966,40 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
                 if ((device = pa_device_manager_get_device_by_id(u->device_manager, *device_id))) {
                     dm_device_type = pa_device_manager_get_device_type(device);
                     if (pa_streq(device_type, dm_device_type)) {
-                        pa_log_debug("  ** found a matched device: type[%-16s]", device_type);
-                        if (pa_streq(dm_device_type, DEVICE_TYPE_BT)) {
+                        dm_device_direction = pa_device_manager_get_device_direction(device);
+                        dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                        pa_log_debug("  ** found a matched device: type[%-16s], subtype[%-5s], direction[0x%x]",
+                                     dm_device_type, dm_device_subtype, dm_device_direction);
+                        /* Check for BT SCO in case of call routing */
+                        if (pa_streq(route_info.role, STREAM_ROLE_CALL_VOICE) && pa_streq(dm_device_type, DEVICE_TYPE_BT)) {
                             /* update BT SCO: open */
-                            if (set_bt_sco_state(u->device_manager, TRUE)) {
+                            if (update_bt_sco_state(u->device_manager, TRUE)) {
                                 pa_log_error("  ** could not open BT SCO");
                                 continue;
                             }
                         } else {
                             /* update BT SCO: close */
-                            set_bt_sco_state(u->device_manager, FALSE);
+                            update_bt_sco_state(u->device_manager, FALSE);
                         }
-                        dm_device_direction = pa_device_manager_get_device_direction(device);
-                        dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                        pa_log_debug("  -- manual_device, type[%-16s], subtype[%-5s], direction[0x%x]", dm_device_type, dm_device_subtype, dm_device_direction);
+                        /* Check for in/out devices in case of loopback */
+                        if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
+                            if (dm_device_direction & DM_DEVICE_DIRECTION_OUT)
+                                u->loopback_args.sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
+                            else if (dm_device_direction & DM_DEVICE_DIRECTION_IN)
+                                u->loopback_args.source = pa_device_manager_get_source(device, DEVICE_ROLE_NORMAL);
+                        }
+
                         if (IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                            use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, CONVERT_TO_DEVICE_DIRECTION(data->stream_type), DEVICE_ROLE_NORMAL);
+                            use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
+                                                                                                CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                                DEVICE_ROLE_NORMAL);
                             if (use_internal_codec) {
                                 route_info.num_of_devices++;
                                 route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
                                 route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
-                                route_info.device_infos[route_info.num_of_devices-1].direction = (data->stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN;
+                                route_info.device_infos[route_info.num_of_devices-1].direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
                                 pa_log_info("  ** found a matched device and set state to ACTIVATED: type[%-16s], direction[0x%x]",
-                                    route_info.device_infos[route_info.num_of_devices-1].type, dm_device_direction);
+                                            route_info.device_infos[route_info.num_of_devices-1].type, dm_device_direction);
                                 /* set device state to activated */
                                 set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
                             } else
@@ -923,6 +1009,11 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
                 }
             }
         }
+        if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
+            /* load module-loopback */
+            if (u->loopback_args.sink && u->loopback_args.source)
+                update_loopback_module(u, TRUE);
+        }
     }
 
     /* move other streams that are belong to device of NORMAL role
@@ -986,6 +1077,9 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_
         if (data->idx_streams) {
             PA_IDXSET_FOREACH(s, data->idx_streams, idx) {
                 if (sink && sink != ((pa_sink_input*)s)->sink) {
+                    if (((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK)) &&
+                        ((pa_sink_input*)s)->sink == combine_sink))
+                        break;
                     pa_sink_input_move_to(s, sink, FALSE);
                     pa_log_debug("[ROUTE][ROLLBACK] *** sink-input(%p,%u) moves to sink(%p,%s)",
                                  s, ((pa_sink_input*)s)->index, sink, sink->name);
@@ -1015,6 +1109,14 @@ static pa_hook_result_t route_option_update_hook_cb(pa_core *c, pa_stream_manage
     route_option.name = data->name;
     route_option.value = data->value;
 
+    if (pa_streq(route_option.role, STREAM_ROLE_LOOPBACK)) {
+        if (pa_streq(route_option.name, MSG_FOR_LOOPBACK_ARG_LATENCY))
+            u->loopback_args.latency_msec = route_option.value;
+        else if (pa_streq(route_option.name, MSG_FOR_LOOPBACK_ARG_ADJUST_TIME))
+            u->loopback_args.adjust_sec = route_option.value;
+        return PA_HOOK_OK;
+    }
+
     /* send information to HAL to update routing option */
     if (pa_hal_manager_update_route_option(u->hal_manager, &route_option))
         pa_log_error("[ROUTE_OPT] Failed to pa_hal_manager_update_route_option()");
@@ -1050,51 +1152,62 @@ static pa_hook_result_t device_connection_changed_hook_cb(pa_core *c, pa_device_
     }
 
     use_internal_codec = pa_device_manager_is_device_use_internal_codec(conn->device, device_direction, DEVICE_ROLE_NORMAL);
-    /* update for using combine sink case when external device is disconnected */
-    if (!use_internal_codec && !conn->is_connected && (device_direction & DM_DEVICE_DIRECTION_OUT)) {
-        if (u->module_combine_sink) {
-            /* unload combine sink */
-            if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
-                conn_devices = pa_device_manager_get_device_list(u->device_manager);
-                PA_IDXSET_FOREACH(device, conn_devices, idx) {
-                    device_direction = pa_device_manager_get_device_direction(device);
-                    if (device_direction == DM_DEVICE_DIRECTION_OUT) {
-                        use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, device_direction, DEVICE_ROLE_NORMAL);
-                        if (use_internal_codec) {
-                            sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
-                            break;
+    /* update for unloading modules when external device is disconnected */
+    if (!use_internal_codec && !conn->is_connected) {
+        if (device_direction & DM_DEVICE_DIRECTION_OUT) {
+            if (u->module_combine_sink) {
+                /* unload combine sink */
+                if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
+                    conn_devices = pa_device_manager_get_device_list(u->device_manager);
+                    PA_IDXSET_FOREACH(device, conn_devices, idx) {
+                        device_direction = pa_device_manager_get_device_direction(device);
+                        if (device_direction == DM_DEVICE_DIRECTION_OUT) {
+                            if ((use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, device_direction, DEVICE_ROLE_NORMAL))) {
+                                sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
+                                break;
+                            }
                         }
                     }
-                }
-                if (combine_sink->inputs) {
-                    if (!sink)
-                        sink = null_sink;
-                    PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
-                        /* re-route this stream to the remaining device using internal codec */
-                        pa_sink_input_move_to(s, sink, FALSE);
-                        pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
+                    if (combine_sink->inputs) {
+                        if (!sink)
+                            sink = null_sink;
+                        PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
+                            /* re-routing this stream to the remaining device using internal codec */
+                            pa_sink_input_move_to(s, sink, FALSE);
+                            pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
+                        }
                     }
-                }
-                pa_sink_suspend(combine_sink, TRUE, PA_SUSPEND_USER);
-                pa_module_unload(u->core, u->module_combine_sink, TRUE);
-                u->module_combine_sink = NULL;
-            } else
-                pa_log_error("[CONN] could not get combine_sink");
-        }
-        if (u->module_combine_sink_for_ex) {
-            /* unload combine sink for external devices */
-            if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED_EX, PA_NAMEREG_SINK))) {
-                if (combine_sink->inputs) {
-                    PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
-                        pa_sink_input_move_to(s, null_sink, FALSE);
-                        pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
+                    pa_sink_suspend(combine_sink, TRUE, PA_SUSPEND_USER);
+                    pa_module_unload(u->core, u->module_combine_sink, TRUE);
+                    u->module_combine_sink = NULL;
+                } else
+                    pa_log_error("[CONN] could not get combine_sink");
+            }
+            if (u->module_combine_sink_for_ex) {
+                /* unload combine sink for external devices */
+                if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED_EX, PA_NAMEREG_SINK))) {
+                    if (combine_sink->inputs) {
+                        PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
+                            pa_sink_input_move_to(s, null_sink, FALSE);
+                            pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
+                        }
                     }
-                }
-                pa_sink_suspend(combine_sink, TRUE, PA_SUSPEND_USER);
-                pa_module_unload(u->core, u->module_combine_sink_for_ex, TRUE);
-                u->module_combine_sink_for_ex = NULL;
-            } else
-                pa_log_error("[CONN] could not get combine_sink_ex");
+                    pa_sink_suspend(combine_sink, TRUE, PA_SUSPEND_USER);
+                    pa_module_unload(u->core, u->module_combine_sink_for_ex, TRUE);
+                    u->module_combine_sink_for_ex = NULL;
+                } else
+                    pa_log_error("[CONN] could not get combine_sink_ex");
+            }
+            /* unload loopback module */
+            if (u->module_loopback)
+                if (u->loopback_args.sink == pa_device_manager_get_sink(conn->device, DEVICE_ROLE_NORMAL))
+                    update_loopback_module(u, FALSE);
+        }
+    if (device_direction & DM_DEVICE_DIRECTION_IN) {
+        /* unload loopback module */
+        if (u->module_loopback)
+            if (u->loopback_args.source == pa_device_manager_get_source(conn->device, DEVICE_ROLE_NORMAL))
+                update_loopback_module(u, FALSE);
         }
     }
 
index 6f54f7c..2f0340e 100644 (file)
@@ -3216,13 +3216,24 @@ static void subscribe_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t
 
 static void message_cb(const char *name, int value, void *user_data) {
     pa_stream_manager *m;
+    pa_stream_manager_hook_data_for_option hook_call_option_data;
 
     pa_assert(user_data);
+    pa_assert(name);
 
     m = (pa_stream_manager*)user_data;
 
+    if (strstr(name, STREAM_ROLE_LOOPBACK)) {
+        memset(&hook_call_option_data, 0, sizeof(pa_stream_manager_hook_data_for_option));
+        hook_call_option_data.stream_role = STREAM_ROLE_LOOPBACK;
+        hook_call_option_data.name = name;
+        hook_call_option_data.value = value;
+        pa_hook_fire(pa_communicator_hook(m->comm.comm, PA_COMMUNICATOR_HOOK_UPDATE_ROUTE_OPTION), &hook_call_option_data);
+    }
 #ifdef HAVE_DBUS
-    send_command_signal(pa_dbus_connection_get(m->dbus_conn), name, value);
+    else {
+        send_command_signal(pa_dbus_connection_get(m->dbus_conn), name, value);
+    }
 #endif
 
     return;
index c63e2da..d4e28e0 100644 (file)
@@ -17,6 +17,7 @@
 #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 SINK_NAME_COMBINED            "sink_combined"
 #define SINK_NAME_NULL                "sink_null"