static void handle_control_filter(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void handle_check_stream_exist_by_pid(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void handle_get_pid_of_latest_stream(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_activate_ducking(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_ducking_state(DBusConnection *conn, DBusMessage *msg, void *userdata);
static void send_volume_changed_signal(DBusConnection *conn, const char *direction, const char *volume_type, const uint32_t volume_level);
static pa_dbus_arg_info get_stream_info_args[] = { { "stream_type", "s", "in" },
{ "stream_types", "as", "in" },
{ "pid", "u", "out" },
{ "ret_msg", "s", "out" } };
-static const char* signature_args_for_in[] = { "s", "", "uauau", "usi", "uu","ssu", "ss", "ss", "ssu", "ss", "sud", "su", "s", "s", "uu", "iu", "su", "s", "ssss", "s", "sss", "uss","sas"};
+static pa_dbus_arg_info activate_ducking_args[] = { { "index", "u", "in" },
+ { "enable", "b", "in" },
+ { "target_stream", "s", "in" },
+ { "duration", "u", "in" },
+ { "ratio", "d", "in" },
+ { "ret_msg", "s", "out" } };
+
+static pa_dbus_arg_info get_ducking_state_args[] = { { "index", "u", "in" },
+ { "is_ducked", "b", "out" },
+ { "ret_msg", "s", "out" } };
+
+static const char* signature_args_for_in[] = {
+ "s", /* METHOD_HANDLER_GET_STREAM_INFO */
+ "", /* METHOD_HANDLER_GET_STREAM_LIST */
+ "uauau", /* METHOD_HANDLER_SET_STREAM_ROUTE_DEVICES */
+ "usi", /* METHOD_HANDLER_SET_STREAM_ROUTE_OPTION */
+ "uu", /* METHOD_HANDLER_SET_STREAM_PREFERRED_DEVICE */
+ "ssu", /* METHOD_HANDLER_SET_VOLUME_LEVEL */
+ "ss", /* METHOD_HANDLER_GET_VOLUME_LEVEL */
+ "ss", /* METHOD_HANDLER_GET_VOLUME_MAX_LEVEL */
+ "ssu", /* METHOD_HANDLER_SET_VOLUME_MUTE */
+ "ss", /* METHOD_HANDLER_GET_VOLUME_MUTE */
+ "sud", /* METHOD_HANDLER_SET_VOLUME_RATIO */
+ "su", /* METHOD_HANDLER_GET_VOLUME_RATIO */
+ "s", /* METHOD_HANDLER_GET_CURRENT_VOLUME_TYPE */
+ "s", /* METHOD_HANDLER_GET_CURRENT_MEDIA_ROUTING_PATH */
+ "uu", /* METHOD_HANDLER_UPDATE_FOCUS_STATUS */
+ "iu", /* METHOD_HANDLER_UPDATE_FOCUS_STATUS_BY_FOCUS_ID */
+ "su", /* METHOD_HANDLER_UPDATE_RESTRICTION */
+ "s", /* METHOD_HANDLER_UPDATE_CALL_PARAMETERS */
+ "ssss", /* METHOD_HANDLER_SET_FILTER */
+ "s", /* METHOD_HANDLER_UNSET_FILTER */
+ "sss", /* METHOD_HANDLER_CONTROL_FILTER */
+ "uss", /* METHOD_HANDLER_CHECK_STREAM_EXIST_BY_PID */
+ "sas", /* METHOD_HANDLER_GET_PID_OF_LATEST_STREAM */
+ "ubsud", /* METHOD_HANDLER_ACTIVATE_DUCKING */
+ "u" /* METHOD_HANDLER_GET_DUCKING_STATE */
+ };
static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
[METHOD_HANDLER_GET_STREAM_INFO] = {
.arguments = get_pid_of_latest_stream_args,
.n_arguments = sizeof(get_pid_of_latest_stream_args) / sizeof(pa_dbus_arg_info),
.receive_cb = handle_get_pid_of_latest_stream },
+ [METHOD_HANDLER_ACTIVATE_DUCKING] = {
+ .method_name = STREAM_MANAGER_METHOD_NAME_ACTIVATE_DUCKING,
+ .arguments = activate_ducking_args,
+ .n_arguments = sizeof(activate_ducking_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_activate_ducking },
+ [METHOD_HANDLER_GET_DUCKING_STATE] = {
+ .method_name = STREAM_MANAGER_METHOD_NAME_GET_DUCKING_STATE,
+ .arguments = get_ducking_state_args,
+ .n_arguments = sizeof(get_ducking_state_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_ducking_state },
};
static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, void *userdata) {
dbus_message_unref(reply);
}
+static void handle_activate_ducking(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ dbus_uint32_t id = 0;
+ dbus_bool_t enable = 0;
+ const char *target_stream = NULL;
+ uint32_t idx = 0;
+ pa_sink_input *stream = NULL;
+ dbus_uint32_t duration = 0;
+ double ratio = 0.0;
+ DBusMessage *reply = NULL;
+ pa_stream_manager *m = (pa_stream_manager*)userdata;
+ stream_ducking *sd = NULL;
+ bool target_matched = false;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_BOOLEAN, &enable,
+ DBUS_TYPE_STRING, &target_stream,
+ DBUS_TYPE_UINT32, &duration,
+ DBUS_TYPE_DOUBLE, &ratio,
+ DBUS_TYPE_INVALID));
+
+ pa_log_info("id[%u], enable[%u], target stream[%s], duration[%u], ratio[%lf]",
+ id, enable, target_stream, duration, ratio);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ /* get stream_ducking */
+ pa_assert_se((sd = pa_hashmap_get(m->stream_duckings, (const void*)id)));
+
+ /* set volume ramp factor to target stream */
+ PA_IDXSET_FOREACH(stream, m->core->sink_inputs, idx) {
+ if (!pa_safe_streq(target_stream, pa_proplist_gets(stream->proplist, PA_PROP_MEDIA_ROLE)))
+ continue;
+
+ target_matched = true;
+ sd->ducking_stream_count++;
+
+ if (enable) {
+ pa_volume_t set_vol;
+ pa_cvolume_ramp vol_ramp;
+
+ snprintf(sd->vol_ramp_key, VOLUME_RAMP_KEY_LENGTH, "stream_ducking_%u_%s", id, target_stream);
+
+ pa_idxset_put(sd->idx_ducking_streams, (void *)stream, NULL);
+
+ set_vol = PA_VOLUME_NORM * ratio;
+
+ pa_log_info("ducking: [%p] set vol [%u], key [%s]", stream, set_vol, sd->vol_ramp_key);
+
+ pa_cvolume_ramp_set(&vol_ramp, stream->volume.channels,
+ PA_VOLUME_RAMP_TYPE_LINEAR, (long)duration, set_vol);
+
+ pa_sink_input_add_volume_ramp_factor(stream, sd->vol_ramp_key, &vol_ramp, true);
+ } else {
+ pa_log_info("unducking : remove volume ramp factor [key:%s]", sd->vol_ramp_key);
+
+ pa_sink_input_remove_volume_ramp_factor(stream, sd->vol_ramp_key, true);
+ }
+ }
+
+ pa_log_info("ducking stream count %d", sd->ducking_stream_count);
+
+ pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_OK], DBUS_TYPE_INVALID));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+
+ if (target_matched == false) {
+ /* change ducking state and send signal here,
+ because ramp_finish_cb could not be called in this case */
+ sd->is_ducked = enable;
+
+ pa_log_info("send signal for ramp finished(but, no stream matched) - is_ducked %d", sd->is_ducked);
+
+ send_ducking_state_changed_signal(pa_dbus_connection_get(m->dbus_conn), sd->trigger_index, sd->is_ducked);
+ }
+}
+
+static void handle_get_ducking_state(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ dbus_uint32_t id = 0;
+ DBusMessage *reply = NULL;
+ pa_stream_manager *m = (pa_stream_manager*)userdata;
+ stream_ducking *sd = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID));
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ /* get stream_ducking */
+ pa_assert_se((sd = pa_hashmap_get(m->stream_duckings, (const void*)id)));
+
+ pa_log_info("id[%u], is_ducked[%u]", id, sd->is_ducked);
+
+ pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &sd->is_ducked, DBUS_TYPE_INVALID));
+ pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &stream_manager_dbus_ret_str[RET_MSG_OK], DBUS_TYPE_INVALID));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
static DBusHandlerResult handle_methods(DBusConnection *conn, DBusMessage *msg, void *userdata) {
int idx = 0;
pa_stream_manager *m = (pa_stream_manager*)userdata;
return;
}
+void send_ducking_state_changed_signal(DBusConnection *conn, const int index, const int is_ducked)
+{
+ DBusMessage *signal_msg;
+ DBusMessageIter msg_iter;
+
+ pa_assert(conn);
+
+ pa_log_debug("trigger index %d : is_ducked[%d]", index, is_ducked);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(STREAM_MANAGER_OBJECT_PATH, STREAM_MANAGER_INTERFACE, STREAM_MANAGER_SIGNAL_NAME_DUCKING_STATE_CHANGED)));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+
+ dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &index);
+ dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &is_ducked);
+
+ pa_assert_se(dbus_connection_send(conn, signal_msg, NULL));
+ dbus_message_unref(signal_msg);
+
+ return;
+}
+
void send_command_signal(DBusConnection *conn, const char *name, int value) {
DBusMessage *signal_msg;
DBusMessageIter msg_iter;
#define CONVERT_TO_DEVICE_DIRECTION(stream_type) \
((stream_type == STREAM_SINK_INPUT) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
-#define STREAM_MANAGER_CLIENT_NAME "SOUND_MANAGER_STREAM_INFO" /* The client via sound-manager */
+#define STREAM_MANAGER_CLIENT_INFO "SOUND_MANAGER_STREAM_INFO" /* The stream info client via sound-manager */
+#define STREAM_MANAGER_CLIENT_DUCKING "SOUND_MANAGER_STREAM_DUCKING" /* The ducking client via sound-manager */
#define VIRTUAL_STREAM_NAME "VIRTUAL_STREAM" /* The virtual stream created by sound-manager */
#define DEFAULT_ROLE "media"
#define SKIP_ROLE "skip"
}
static pa_hook_result_t sink_input_ramp_finish_cb(pa_core *core, pa_sink_input *i, pa_stream_manager *m) {
+ bool is_ducked = false;
+ stream_ducking *sd = NULL;
+ pa_sink_input *stream = NULL;
+ uint32_t idx;
+ void *state;
+
pa_core_assert_ref(core);
pa_sink_input_assert_ref(i);
/* Find a context id from all the ducked stream list by this stream index.
* Check the number of managed streams of the context id, if it is the last one
* then broadcast a signal with context id.*/
- // send_command_signal(pa_dbus_connection_get(m->dbus_conn), "ducking_finished", context_id);
+ PA_HASHMAP_FOREACH(sd, m->stream_duckings, state) {
+ PA_IDXSET_FOREACH(stream, sd->idx_ducking_streams, idx) {
+ if (stream == i) {
+ pa_log_info("matched");
+ break;
+ }
+ }
+
+ if (stream == i)
+ break;
+ }
+
+ if (stream != i) {
+ pa_log_error("not found matched stream for %p", i);
+ return PA_HOOK_OK;
+ }
+
+ is_ducked = i->thread_info.ramp.ramps[0].start > i->thread_info.ramp.ramps[0].end;
+
+ /* remove trigger when unducked */
+ if (is_ducked == false)
+ pa_idxset_remove_by_data(sd->idx_ducking_streams, (void *)i, NULL);
+
+ /* send signal when all streams are ducked */
+ if (--sd->ducking_stream_count == 0) {
+ pa_log_info("send signal for ramp finished - is_ducked %d", is_ducked);
+ sd->is_ducked = is_ducked;
+ send_ducking_state_changed_signal(pa_dbus_connection_get(m->dbus_conn), sd->trigger_index, sd->is_ducked);
+ }
return PA_HOOK_OK;
}
}
static void subscribe_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, pa_stream_manager *m) {
- pa_client *client = NULL;
stream_parent *sp = NULL;
+ stream_ducking *sd = NULL;
const char *name = NULL;
pa_core_assert_ref(core);
pa_log_info("subscribe_cb() is called, t(%x), idx(%u)", t, idx);
if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE)) {
+ pa_client *client = NULL;
+
if ((client = pa_idxset_get_by_index(core->clients, idx)) == NULL) {
pa_log_error(" - could not find any client that has idx(%u)", idx);
return;
}
+
name = pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME);
- if (!pa_safe_streq(name, STREAM_MANAGER_CLIENT_NAME)) {
- pa_log_warn(" - this is not a client(%s) that we should take care of, skip it", name);
+
+ if (pa_safe_streq(name, STREAM_MANAGER_CLIENT_INFO)) {
+ /* add a stream parent */
+ sp = pa_xmalloc0(sizeof(stream_parent));
+ sp->idx_sink_inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ sp->idx_source_outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ sp->idx_route_in_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ sp->idx_route_out_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ pa_hashmap_put(m->stream_parents, (void*)idx, sp);
+ pa_log_debug(" - add sp(%p), idx(%u)", sp, idx);
+ return;
+ } else if (pa_safe_streq(name, STREAM_MANAGER_CLIENT_DUCKING)) {
+ /* add a stream ducking */
+ sd = pa_xmalloc0(sizeof(stream_ducking));
+ sd->idx_ducking_streams = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ sd->trigger_index = idx;
+ pa_hashmap_put(m->stream_duckings, (void *)idx, sd);
+ pa_log_debug(" - add sd(%p), idx(%u)", sd, idx);
return;
}
- /* add a stream parent */
- sp = pa_xmalloc0(sizeof(stream_parent));
- sp->idx_sink_inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- sp->idx_source_outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- sp->idx_route_in_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- sp->idx_route_out_devices = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- pa_hashmap_put(m->stream_parents, (void*)idx, sp);
- pa_log_debug(" - add sp(%p), idx(%u)", sp, idx);
-
} else if (t == (PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE)) {
- /* remove the stream parent */
+ /* try to find stream parent with idx */
sp = pa_hashmap_get(m->stream_parents, (const void*)idx);
if (sp) {
pa_log_debug(" - remove sp(%p), idx(%u)", sp, idx);
if (sp->idx_route_out_devices)
pa_idxset_free(sp->idx_route_out_devices, pa_xfree);
pa_xfree(sp);
- } else {
- pa_log_error(" - could not find any stream_parent that has idx(%u)", idx);
+ return;
+ }
+
+ /* try to find sd with idx */
+ sd = pa_hashmap_get(m->stream_duckings, (const void*)idx);
+ if (sd) {
+ uint32_t ducking_idx = 0;
+ pa_sink_input *stream = NULL;
+
+ pa_log_info(" - remove sd(%p), idx(%u)", sd, idx);
+
+ PA_IDXSET_FOREACH(stream, sd->idx_ducking_streams, ducking_idx) {
+ pa_log_info("remove volume ramp for remained stream %p, key %s", stream, sd->vol_ramp_key);
+ pa_sink_input_remove_volume_ramp_factor(stream, sd->vol_ramp_key, true);
+ }
+
+ pa_idxset_free(sd->idx_ducking_streams, NULL);
+ pa_hashmap_remove(m->stream_duckings, (const void*)idx);
+ pa_xfree(sd);
+
+ pa_log_info("done");
+ return;
}
}
+
+ pa_log_warn(" - this is not a client(%s) that we should take care of, skip it", name ? name : "NULL");
}
/* Message callback from HAL interface */
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->stream_duckings = pa_hashmap_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);
if (m->stream_parents)
pa_hashmap_free(m->stream_parents);
+ if (m->stream_duckings)
+ pa_hashmap_free(m->stream_duckings);
+
deinit_volumes(m);
deinit_stream_map(m);
deinit_ipc(m);