sink: Update sink with new common.
This commit is contained in:
parent
d4a8167c11
commit
5238bfd192
1 changed files with 121 additions and 358 deletions
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2018 Jolla Ltd.
|
||||
* Copyright (C) 2013-2022 Jolla Ltd.
|
||||
*
|
||||
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
|
||||
*
|
||||
|
|
@ -60,6 +60,7 @@
|
|||
#include "droid-sink.h"
|
||||
#include <droid/droid-util.h>
|
||||
#include <droid/conversion.h>
|
||||
#include <droid/sllist.h>
|
||||
|
||||
struct userdata {
|
||||
pa_core *core;
|
||||
|
|
@ -79,18 +80,10 @@ struct userdata {
|
|||
pa_usec_t buffer_time;
|
||||
pa_usec_t write_time;
|
||||
pa_usec_t write_threshold;
|
||||
audio_devices_t prewrite_devices;
|
||||
bool prewrite_always;
|
||||
uint32_t prewrite_silence;
|
||||
pa_hook_slot *sink_put_hook_slot;
|
||||
pa_hook_slot *sink_unlink_hook_slot;
|
||||
pa_hook_slot *sink_port_changed_hook_slot;
|
||||
pa_sink *primary_stream_sink;
|
||||
|
||||
audio_devices_t primary_devices;
|
||||
audio_devices_t extra_devices;
|
||||
pa_hashmap *extra_devices_map;
|
||||
bool mix_route;
|
||||
dm_config_port *active_device_port;
|
||||
dm_config_port *override_device_port;
|
||||
dm_list *extra_devices_stack;
|
||||
|
||||
bool use_hw_volume;
|
||||
bool use_voice_volume;
|
||||
|
|
@ -133,87 +126,84 @@ static void set_voice_volume(struct userdata *u, pa_sink_input *i);
|
|||
static void apply_volume(pa_sink *s);
|
||||
static pa_sink_input *find_volume_control_sink_input(struct userdata *u);
|
||||
|
||||
static void set_primary_devices(struct userdata *u, audio_devices_t devices) {
|
||||
pa_assert(u);
|
||||
pa_assert(devices);
|
||||
static bool add_extra_devices(struct userdata *u, audio_devices_t device) {
|
||||
dm_list_entry *prev;
|
||||
dm_config_port *device_port;
|
||||
|
||||
u->primary_devices = devices;
|
||||
pa_assert(u);
|
||||
pa_assert(u->extra_devices_stack);
|
||||
|
||||
if (!(device_port = dm_config_find_device_port(u->active_device_port, device))) {
|
||||
pa_log("Unknown device port %u", device);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool add_extra_devices(struct userdata *u, audio_devices_t devices) {
|
||||
void *value;
|
||||
uint32_t count;
|
||||
prev = dm_list_last(u->extra_devices_stack);
|
||||
|
||||
dm_list_push_back(u->extra_devices_stack, device_port);
|
||||
|
||||
if (prev) {
|
||||
dm_config_port *last_port = prev->data;
|
||||
if (dm_config_port_equal(last_port, device_port))
|
||||
return false;
|
||||
}
|
||||
|
||||
u->override_device_port = device_port;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool remove_extra_devices(struct userdata *u, audio_devices_t device) {
|
||||
dm_config_port *device_port;
|
||||
dm_list_entry *remove = NULL, *i = NULL;
|
||||
bool need_update = false;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(u->extra_devices_map);
|
||||
pa_assert(devices);
|
||||
pa_assert(u->extra_devices_stack);
|
||||
|
||||
if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
|
||||
count = PA_PTR_TO_UINT(value);
|
||||
count++;
|
||||
pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices));
|
||||
pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count));
|
||||
if (!(device_port = dm_config_find_device_port(u->active_device_port, device))) {
|
||||
pa_log("Unknown device port %u", device);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* added extra device already exists in hashmap, so no need to update route. */
|
||||
need_update = false;
|
||||
} else {
|
||||
pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(1));
|
||||
u->extra_devices |= devices;
|
||||
DM_LIST_FOREACH(i, u->extra_devices_stack) {
|
||||
if (dm_config_port_equal(i->data, device_port)) {
|
||||
remove = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove && dm_list_last(u->extra_devices_stack) == remove)
|
||||
need_update = true;
|
||||
}
|
||||
|
||||
return need_update;
|
||||
}
|
||||
|
||||
static bool remove_extra_devices(struct userdata *u, audio_devices_t devices) {
|
||||
void *value;
|
||||
uint32_t count;
|
||||
bool need_update = false;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(u->extra_devices_map);
|
||||
pa_assert(devices);
|
||||
|
||||
if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
|
||||
pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices));
|
||||
count = PA_PTR_TO_UINT(value);
|
||||
count--;
|
||||
if (count == 0) {
|
||||
u->extra_devices &= ~devices;
|
||||
need_update = true;
|
||||
} else {
|
||||
/* added extra devices still exists in hashmap, so no need to update route. */
|
||||
pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count));
|
||||
need_update = false;
|
||||
}
|
||||
}
|
||||
if (remove)
|
||||
dm_list_remove(u->extra_devices_stack, remove);
|
||||
|
||||
return need_update;
|
||||
}
|
||||
|
||||
static void clear_extra_devices(struct userdata *u) {
|
||||
pa_assert(u);
|
||||
pa_assert(u->extra_devices_map);
|
||||
pa_assert(u->extra_devices_stack);
|
||||
|
||||
pa_hashmap_remove_all(u->extra_devices_map);
|
||||
u->extra_devices = 0;
|
||||
while (dm_list_steal_first(u->extra_devices_stack));
|
||||
u->override_device_port = NULL;
|
||||
}
|
||||
|
||||
/* Called from main context during voice calls, and from IO context during media operation. */
|
||||
static void do_routing(struct userdata *u) {
|
||||
audio_devices_t routing;
|
||||
dm_config_port *routing = NULL;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(u->stream);
|
||||
|
||||
if (u->use_voice_volume && u->extra_devices)
|
||||
if (u->use_voice_volume && u->override_device_port)
|
||||
clear_extra_devices(u);
|
||||
|
||||
if (!u->mix_route && u->extra_devices)
|
||||
routing = u->extra_devices;
|
||||
if (u->override_device_port)
|
||||
routing = u->override_device_port;
|
||||
else
|
||||
routing = u->primary_devices | u->extra_devices;
|
||||
routing = u->active_device_port;
|
||||
|
||||
pa_droid_stream_set_route(u->stream, routing);
|
||||
}
|
||||
|
|
@ -244,30 +234,6 @@ static bool parse_device_list(const char *str, audio_devices_t *dst) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static int thread_write_silence(struct userdata *u) {
|
||||
const void *p;
|
||||
ssize_t wrote;
|
||||
|
||||
/* Drop our rendered audio and write silence to HAL. */
|
||||
pa_memblockq_drop(u->memblockq, u->buffer_size);
|
||||
u->write_time = pa_rtclock_now();
|
||||
|
||||
/* We should be able to write everything in one go as long as memblock size
|
||||
* is multiples of buffer_size. Even if we don't write whole buffer size
|
||||
* here it's okay, as long as mute time isn't configured too strictly. */
|
||||
|
||||
p = pa_memblock_acquire_chunk(&u->silence);
|
||||
wrote = pa_droid_stream_write(u->stream, p, u->silence.length);
|
||||
pa_memblock_release(u->silence.memblock);
|
||||
|
||||
u->write_time = pa_rtclock_now() - u->write_time;
|
||||
|
||||
if (wrote < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thread_write(struct userdata *u) {
|
||||
pa_memchunk c;
|
||||
const void *p;
|
||||
|
|
@ -281,9 +247,6 @@ static int thread_write(struct userdata *u) {
|
|||
u->write_time = pa_rtclock_now();
|
||||
|
||||
for (;;) {
|
||||
if (pa_droid_quirk(u->hw_module, QUIRK_OUTPUT_MAKE_WRITABLE))
|
||||
pa_memchunk_make_writable(&c, c.length);
|
||||
|
||||
p = pa_memblock_acquire_chunk(&c);
|
||||
wrote = pa_droid_stream_write(u->stream, p, c.length);
|
||||
pa_memblock_release(c.memblock);
|
||||
|
|
@ -454,8 +417,6 @@ static int suspend(struct userdata *u) {
|
|||
|
||||
/* Called from IO context */
|
||||
static int unsuspend(struct userdata *u) {
|
||||
uint32_t i;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(u->sink);
|
||||
|
||||
|
|
@ -466,13 +427,6 @@ static int unsuspend(struct userdata *u) {
|
|||
|
||||
apply_volume(u->sink);
|
||||
|
||||
if (u->prewrite_silence &&
|
||||
(u->primary_devices | u->extra_devices) & u->prewrite_devices &&
|
||||
(u->prewrite_always || pa_droid_output_stream_any_active(u->stream) == 0)) {
|
||||
for (i = 0; i < u->prewrite_silence; i++)
|
||||
thread_write_silence(u);
|
||||
}
|
||||
|
||||
pa_droid_stream_suspend(u->stream, false);
|
||||
|
||||
return 0;
|
||||
|
|
@ -548,7 +502,7 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
|
|||
|
||||
data = PA_DEVICE_PORT_DATA(p);
|
||||
|
||||
if (!data->device) {
|
||||
if (!data->device_port) {
|
||||
/* If there is no device defined, just return 0 to say everything is ok.
|
||||
* Then next port change can be whatever sink port, even the one enabled
|
||||
* before parking. */
|
||||
|
|
@ -556,9 +510,9 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
pa_log_debug("Sink set port %u", data->device);
|
||||
pa_log_debug("Sink set port %#010x (%s)", data->device_port->type, data->device_port->name);
|
||||
|
||||
set_primary_devices(u, data->device);
|
||||
u->active_device_port = data->device_port;
|
||||
do_routing(u);
|
||||
|
||||
return 0;
|
||||
|
|
@ -633,9 +587,9 @@ static void update_volumes(struct userdata *u) {
|
|||
u->use_hw_volume = (ret == 0);
|
||||
if (u->use_hw_volume &&
|
||||
#if defined(HAVE_ENUM_AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
|
||||
!(u->stream->output->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
|
||||
!(u->stream->mix_port->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
|
||||
#endif
|
||||
pa_droid_quirk(u->hw_module, QUIRK_NO_HW_VOLUME)) {
|
||||
!pa_droid_option(u->hw_module, DM_OPTION_HW_VOLUME)) {
|
||||
pa_log_info("Forcing software volume control with %s", u->sink->name);
|
||||
u->use_hw_volume = false;
|
||||
} else {
|
||||
|
|
@ -649,7 +603,7 @@ static void update_volumes(struct userdata *u) {
|
|||
}
|
||||
}
|
||||
|
||||
static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, const char *module_id) {
|
||||
static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, pa_droid_mapping *am, const char *name) {
|
||||
const char *tmp;
|
||||
|
||||
pa_assert(ma);
|
||||
|
|
@ -660,13 +614,14 @@ static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, const char *mo
|
|||
data->namereg_fail = true;
|
||||
pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink");
|
||||
} else {
|
||||
char *tt;
|
||||
pa_assert(module_id);
|
||||
tt = pa_sprintf_malloc("sink.%s", module_id);
|
||||
pa_sink_new_data_set_name(data, tt);
|
||||
pa_xfree(tt);
|
||||
char *full_name;
|
||||
pa_assert(name);
|
||||
pa_assert(am);
|
||||
full_name = pa_sprintf_malloc("sink.%s", name);
|
||||
pa_sink_new_data_set_name(data, full_name);
|
||||
pa_xfree(full_name);
|
||||
data->namereg_fail = false;
|
||||
pa_proplist_setf(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink %s", module_id);
|
||||
pa_proplist_setf(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink %s", am->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -786,7 +741,7 @@ static pa_hook_result_t sink_input_put_hook_cb(pa_core *c, pa_sink_input *sink_i
|
|||
|
||||
if (parse_device_list(dev_str, &devices) && devices) {
|
||||
|
||||
pa_log_debug("Add extra route %s (%u).", dev_str, devices);
|
||||
pa_log_debug("%s: Add extra route %s (%u).", u->sink->name, dev_str, devices);
|
||||
|
||||
/* if this device was not routed to previously post routing change */
|
||||
if (add_extra_devices(u, devices))
|
||||
|
|
@ -882,167 +837,6 @@ static pa_hook_result_t sink_proplist_changed_hook_cb(pa_core *c, pa_sink *sink,
|
|||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static pa_hook_result_t sink_port_changed_hook_cb(pa_core *c, pa_sink *sink, struct userdata *u) {
|
||||
pa_device_port *port;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(sink);
|
||||
pa_assert(u);
|
||||
|
||||
if (sink != u->primary_stream_sink)
|
||||
return PA_HOOK_OK;
|
||||
|
||||
port = sink->active_port;
|
||||
pa_log_info("Set slave sink port to %s", port->name);
|
||||
pa_sink_set_port(u->sink, port->name, false);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static void unset_primary_stream_sink(struct userdata *u) {
|
||||
pa_assert(u);
|
||||
pa_assert(u->primary_stream_sink);
|
||||
pa_assert(u->sink_port_changed_hook_slot);
|
||||
|
||||
pa_hook_slot_free(u->sink_port_changed_hook_slot);
|
||||
u->sink_port_changed_hook_slot = NULL;
|
||||
u->primary_stream_sink = NULL;
|
||||
}
|
||||
|
||||
static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *sink, struct userdata *u) {
|
||||
pa_assert(c);
|
||||
pa_assert(sink);
|
||||
pa_assert(u);
|
||||
|
||||
if (sink != u->primary_stream_sink)
|
||||
return PA_HOOK_OK;
|
||||
|
||||
pa_log_info("Primary stream sink disappeared.");
|
||||
unset_primary_stream_sink(u);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *sink, struct userdata *u) {
|
||||
struct userdata *sink_u;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(sink);
|
||||
pa_assert(u);
|
||||
|
||||
if (!pa_sink_is_droid_sink(sink))
|
||||
return PA_HOOK_OK;
|
||||
|
||||
sink_u = sink->userdata;
|
||||
|
||||
if (!pa_droid_stream_is_primary(sink_u->stream))
|
||||
return PA_HOOK_OK;
|
||||
|
||||
u->primary_stream_sink = sink;
|
||||
|
||||
pa_assert(!u->sink_port_changed_hook_slot);
|
||||
u->sink_port_changed_hook_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], PA_HOOK_NORMAL,
|
||||
(pa_hook_cb_t) sink_port_changed_hook_cb, u);
|
||||
|
||||
pa_log_info("Primary stream sink setup for slave.");
|
||||
|
||||
sink_port_changed_hook_cb(c, sink, u);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
static void setup_track_primary(struct userdata *u) {
|
||||
pa_sink *sink;
|
||||
struct userdata *sink_u;
|
||||
uint32_t idx;
|
||||
|
||||
pa_assert(u);
|
||||
|
||||
u->sink_put_hook_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL,
|
||||
(pa_hook_cb_t) sink_put_hook_cb, u);
|
||||
u->sink_unlink_hook_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_NORMAL,
|
||||
(pa_hook_cb_t) sink_unlink_hook_cb, u);
|
||||
|
||||
PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
|
||||
if (pa_sink_is_droid_sink(sink)) {
|
||||
sink_u = sink->userdata;
|
||||
if (pa_droid_stream_is_primary(sink_u->stream)) {
|
||||
sink_put_hook_cb(u->core, sink, u);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_prewrite_on_resume(struct userdata *u, const char *prewrite_resume, const char *name) {
|
||||
const char *state = NULL;
|
||||
char *entry = NULL;
|
||||
char *devices, *stream, *value;
|
||||
uint32_t devices_len, devices_index, value_index, entry_len;
|
||||
uint32_t b;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(prewrite_resume);
|
||||
pa_assert(name);
|
||||
|
||||
/* Argument is string of for example "deep_buffer=AUDIO_DEVICE_OUT_SPEAKER:1,primary=FOO:5" */
|
||||
|
||||
while ((entry = pa_split(prewrite_resume, ",", &state))) {
|
||||
audio_devices_t prewrite_devices = 0;
|
||||
bool prewrite_always = false;
|
||||
char *tmp;
|
||||
|
||||
entry_len = strlen(entry);
|
||||
devices_index = strcspn(entry, "=");
|
||||
|
||||
if (devices_index == 0 || devices_index >= entry_len - 1)
|
||||
goto error;
|
||||
|
||||
entry[devices_index] = '\0';
|
||||
devices = entry + devices_index + 1;
|
||||
stream = entry;
|
||||
|
||||
devices_len = strlen(devices);
|
||||
value_index = strcspn(devices, ":");
|
||||
|
||||
if (value_index == 0 || value_index >= devices_len - 1)
|
||||
goto error;
|
||||
|
||||
devices[value_index] = '\0';
|
||||
value = devices + value_index + 1;
|
||||
|
||||
if (!parse_device_list(devices, &prewrite_devices))
|
||||
goto error;
|
||||
|
||||
if ((tmp = strstr(value, "/always"))) {
|
||||
prewrite_always = true;
|
||||
*tmp = '\0';
|
||||
}
|
||||
|
||||
if (strlen(value) == 0 || pa_atou(value, &b) < 0)
|
||||
goto error;
|
||||
|
||||
if (pa_streq(stream, name)) {
|
||||
pa_log_info("Using requested prewrite%s size for %s: %zu (%u * %zu).",
|
||||
prewrite_always ? "_always" : "",
|
||||
name, u->buffer_size * b, b, u->buffer_size);
|
||||
u->prewrite_devices = prewrite_devices;
|
||||
u->prewrite_always = prewrite_always;
|
||||
u->prewrite_silence = b;
|
||||
pa_xfree(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
pa_xfree(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
pa_xfree(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
pa_sink *pa_droid_sink_new(pa_module *m,
|
||||
pa_modargs *ma,
|
||||
const char *driver,
|
||||
|
|
@ -1052,23 +846,21 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
pa_card *card) {
|
||||
|
||||
struct userdata *u = NULL;
|
||||
const pa_droid_config_device *output = NULL;
|
||||
dm_config_port *mix_port = NULL;
|
||||
dm_config_port *device_port = NULL;
|
||||
bool deferred_volume = false;
|
||||
char *thread_name = NULL;
|
||||
pa_sink_new_data data;
|
||||
const char *module_id = NULL;
|
||||
const char *tmp;
|
||||
char *list = NULL;
|
||||
uint32_t alternate_sample_rate;
|
||||
const char *format;
|
||||
audio_devices_t dev_out;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_channel_map channel_map;
|
||||
bool namereg_fail = false;
|
||||
pa_usec_t latency;
|
||||
uint32_t sink_buffer = 0;
|
||||
const char *prewrite_resume = NULL;
|
||||
bool mix_route = false;
|
||||
char *sink_name = NULL;
|
||||
|
||||
pa_assert(m);
|
||||
pa_assert(ma);
|
||||
|
|
@ -1083,8 +875,8 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
}
|
||||
|
||||
if (card && am) {
|
||||
output = am->output;
|
||||
module_id = output->module->name;
|
||||
mix_port = am->mix_port;
|
||||
module_id = mix_port->name;
|
||||
} else
|
||||
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
|
||||
|
||||
|
|
@ -1093,18 +885,22 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
|
||||
/* First parse both sample spec and channel map, then see if sink_* override some
|
||||
* of the values. */
|
||||
if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) {
|
||||
pa_log("Failed to parse sink sample specification and channel map.");
|
||||
|
||||
if (pa_modargs_get_sample_spec(ma, &sample_spec) < 0) {
|
||||
pa_log("Failed to parse sink sample specification.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value(ma, "sink_channel_map", NULL)) {
|
||||
if (pa_modargs_get_channel_map(ma, "sink_channel_map", &channel_map) < 0) {
|
||||
if (pa_modargs_get_channel_map(ma, NULL, &channel_map) < 0) {
|
||||
pa_log("Failed to parse sink channel map.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sample_spec.channels = channel_map.channels;
|
||||
/* Possible overrides. */
|
||||
|
||||
if (pa_modargs_get_channel_map(ma, "sink_channel_map", &channel_map) < 0) {
|
||||
pa_log("Failed to parse sink channel map.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((format = pa_modargs_get_value(ma, "sink_format", NULL))) {
|
||||
|
|
@ -1114,6 +910,11 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
}
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_u32(ma, "rate", &sample_spec.rate) < 0) {
|
||||
pa_log("Failed to parse sink samplerate");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_u32(ma, "sink_rate", &sample_spec.rate) < 0) {
|
||||
pa_log("Failed to parse sink samplerate");
|
||||
goto fail;
|
||||
|
|
@ -1135,11 +936,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_boolean(ma, "sink_mix_route", &mix_route) < 0) {
|
||||
pa_log("Failed to parse sink_mix_route, expects boolean argument.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u = pa_xnew0(struct userdata, 1);
|
||||
u->core = m->core;
|
||||
u->module = m;
|
||||
|
|
@ -1151,8 +947,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
NULL, (pa_free_cb_t) parameter_free);
|
||||
u->voice_property_key = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_key", DEFAULT_VOICE_CONTROL_PROPERTY_KEY));
|
||||
u->voice_property_value = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_value", DEFAULT_VOICE_CONTROL_PROPERTY_VALUE));
|
||||
u->extra_devices_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
||||
u->mix_route = mix_route;
|
||||
u->extra_devices_stack = dm_list_new();
|
||||
|
||||
if (card_data) {
|
||||
u->card_data = card_data;
|
||||
|
|
@ -1166,34 +961,25 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (!(output = pa_droid_config_find_output(u->hw_module->enabled_module, output_name))) {
|
||||
pa_log("Could not find output %s from module %s.", output_name, u->hw_module->enabled_module->name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Sink wasn't created from inside card module, so we'll need to open
|
||||
* hw module ourself. */
|
||||
|
||||
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
|
||||
goto fail;
|
||||
|
||||
if (!(mix_port = dm_config_find_port(u->hw_module->enabled_module, output_name)) ||
|
||||
mix_port->port_type != DM_CONFIG_TYPE_MIX_PORT) {
|
||||
pa_log("Could not find output %s from module %s.", output_name, u->hw_module->enabled_module->name);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default routing */
|
||||
dev_out = output->module->global_config ? output->module->global_config->default_output_device
|
||||
: u->hw_module->config->global_config->default_output_device;
|
||||
pa_assert(mix_port);
|
||||
|
||||
if ((tmp = pa_modargs_get_value(ma, "output_devices", NULL))) {
|
||||
audio_devices_t tmp_dev;
|
||||
/* Start with default output device */
|
||||
device_port = dm_config_default_output_device(mix_port->module);
|
||||
|
||||
if (parse_device_list(tmp, &tmp_dev) && tmp_dev)
|
||||
dev_out = tmp_dev;
|
||||
|
||||
pa_log_debug("Set initial devices %s", tmp);
|
||||
}
|
||||
|
||||
flags = output->flags;
|
||||
|
||||
u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, output->name, dev_out);
|
||||
u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, mix_port, device_port);
|
||||
|
||||
if (!u->stream) {
|
||||
pa_log("Failed to open output stream.");
|
||||
|
|
@ -1207,13 +993,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
} else
|
||||
pa_log_info("Using buffer size %zu.", u->buffer_size);
|
||||
|
||||
if ((prewrite_resume = pa_modargs_get_value(ma, "prewrite_on_resume", NULL))) {
|
||||
if (!parse_prewrite_on_resume(u, prewrite_resume, output->name)) {
|
||||
pa_log("Failed to parse prewrite_on_resume (%s)", prewrite_resume);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
u->buffer_time = pa_bytes_to_usec(u->buffer_size, &u->stream->output->sample_spec);
|
||||
u->write_threshold = u->buffer_time - u->buffer_time / 6;
|
||||
|
||||
|
|
@ -1225,7 +1004,8 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
data.module = m;
|
||||
data.card = card;
|
||||
|
||||
set_sink_name(ma, &data, output->name);
|
||||
sink_name = dm_config_escape_string(module_id);
|
||||
set_sink_name(ma, &data, am, sink_name);
|
||||
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
|
||||
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, PROP_DROID_API_STRING);
|
||||
|
||||
|
|
@ -1245,15 +1025,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
pa_sink_new_data_set_channel_map(&data, &u->stream->output->channel_map);
|
||||
pa_sink_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
|
||||
|
||||
/*
|
||||
if (!(list = pa_list_string_output_device(dev_out))) {
|
||||
pa_log("Couldn't format device list string.");
|
||||
goto fail;
|
||||
}
|
||||
pa_proplist_sets(data.proplist, PROP_DROID_DEVICES, list);
|
||||
pa_xfree(list);
|
||||
*/
|
||||
|
||||
if (flags) {
|
||||
if (!(list = pa_list_string_flags(flags))) {
|
||||
pa_log("Couldn't format flag list string.");
|
||||
|
|
@ -1281,15 +1052,13 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
u->sink->parent.process_msg = sink_process_msg;
|
||||
u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
|
||||
|
||||
u->sink->set_port = sink_set_port_cb;
|
||||
|
||||
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
|
||||
pa_sink_set_rtpoll(u->sink, u->rtpoll);
|
||||
|
||||
/* Rewind internal memblockq */
|
||||
pa_sink_set_max_rewind(u->sink, 0);
|
||||
|
||||
thread_name = pa_sprintf_malloc("droid-sink-%s", output->name);
|
||||
thread_name = pa_sprintf_malloc("droid-sink-%s", sink_name);
|
||||
if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
|
||||
pa_log("Failed to create thread.");
|
||||
goto fail;
|
||||
|
|
@ -1306,8 +1075,10 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
if (u->sink->active_port)
|
||||
sink_set_port_cb(u->sink, u->sink->active_port);
|
||||
|
||||
/* Hooks to track appearance and disappearance of sink-inputs. */
|
||||
/* Hook a little bit earlier and later than module-role-ducking. */
|
||||
if (pa_droid_stream_is_primary(u->stream)) {
|
||||
/* Hooks to track appearance and disappearance of sink-inputs.
|
||||
* Hook a little bit earlier and later than module-role-ducking.
|
||||
* Used only in primary sink. */
|
||||
u->sink_input_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10,
|
||||
(pa_hook_cb_t) sink_input_put_hook_cb, u);
|
||||
u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY-10,
|
||||
|
|
@ -1315,19 +1086,23 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
|||
u->sink_proplist_changed_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_EARLY,
|
||||
(pa_hook_cb_t) sink_proplist_changed_hook_cb, u);
|
||||
|
||||
update_volumes(u);
|
||||
/* Port changes are done only in primary sink. */
|
||||
u->sink->set_port = sink_set_port_cb;
|
||||
}
|
||||
|
||||
if (!pa_droid_stream_is_primary(u->stream))
|
||||
setup_track_primary(u);
|
||||
update_volumes(u);
|
||||
|
||||
pa_droid_stream_suspend(u->stream, false);
|
||||
pa_droid_stream_set_data(u->stream, u->sink);
|
||||
pa_sink_put(u->sink);
|
||||
|
||||
pa_xfree(sink_name);
|
||||
|
||||
return u->sink;
|
||||
|
||||
fail:
|
||||
pa_xfree(thread_name);
|
||||
pa_xfree(sink_name);
|
||||
|
||||
if (u)
|
||||
userdata_free(u);
|
||||
|
|
@ -1354,18 +1129,6 @@ static void parameter_free(droid_parameter_mapping *m) {
|
|||
|
||||
static void userdata_free(struct userdata *u) {
|
||||
|
||||
if (u->primary_stream_sink)
|
||||
unset_primary_stream_sink(u);
|
||||
|
||||
if (u->sink_put_hook_slot)
|
||||
pa_hook_slot_free(u->sink_put_hook_slot);
|
||||
|
||||
if (u->sink_unlink_hook_slot)
|
||||
pa_hook_slot_free(u->sink_unlink_hook_slot);
|
||||
|
||||
if (u->sink_port_changed_hook_slot)
|
||||
pa_hook_slot_free(u->sink_port_changed_hook_slot);
|
||||
|
||||
if (u->sink)
|
||||
pa_sink_unlink(u->sink);
|
||||
|
||||
|
|
@ -1411,8 +1174,8 @@ static void userdata_free(struct userdata *u) {
|
|||
if (u->voice_property_value)
|
||||
pa_xfree(u->voice_property_value);
|
||||
|
||||
if (u->extra_devices_map)
|
||||
pa_hashmap_free(u->extra_devices_map);
|
||||
if (u->extra_devices_stack)
|
||||
dm_list_free(u->extra_devices_stack, NULL);
|
||||
|
||||
pa_xfree(u);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue