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>
|
* Contact: Juho Hämäläinen <juho.hamalainen@jolla.com>
|
||||||
*
|
*
|
||||||
|
|
@ -60,6 +60,7 @@
|
||||||
#include "droid-sink.h"
|
#include "droid-sink.h"
|
||||||
#include <droid/droid-util.h>
|
#include <droid/droid-util.h>
|
||||||
#include <droid/conversion.h>
|
#include <droid/conversion.h>
|
||||||
|
#include <droid/sllist.h>
|
||||||
|
|
||||||
struct userdata {
|
struct userdata {
|
||||||
pa_core *core;
|
pa_core *core;
|
||||||
|
|
@ -79,18 +80,10 @@ struct userdata {
|
||||||
pa_usec_t buffer_time;
|
pa_usec_t buffer_time;
|
||||||
pa_usec_t write_time;
|
pa_usec_t write_time;
|
||||||
pa_usec_t write_threshold;
|
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;
|
dm_config_port *active_device_port;
|
||||||
audio_devices_t extra_devices;
|
dm_config_port *override_device_port;
|
||||||
pa_hashmap *extra_devices_map;
|
dm_list *extra_devices_stack;
|
||||||
bool mix_route;
|
|
||||||
|
|
||||||
bool use_hw_volume;
|
bool use_hw_volume;
|
||||||
bool use_voice_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 void apply_volume(pa_sink *s);
|
||||||
static pa_sink_input *find_volume_control_sink_input(struct userdata *u);
|
static pa_sink_input *find_volume_control_sink_input(struct userdata *u);
|
||||||
|
|
||||||
static void set_primary_devices(struct userdata *u, audio_devices_t devices) {
|
static bool add_extra_devices(struct userdata *u, audio_devices_t device) {
|
||||||
pa_assert(u);
|
dm_list_entry *prev;
|
||||||
pa_assert(devices);
|
dm_config_port *device_port;
|
||||||
|
|
||||||
u->primary_devices = devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool add_extra_devices(struct userdata *u, audio_devices_t devices) {
|
|
||||||
void *value;
|
|
||||||
uint32_t count;
|
|
||||||
bool need_update = false;
|
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->extra_devices_map);
|
pa_assert(u->extra_devices_stack);
|
||||||
pa_assert(devices);
|
|
||||||
|
|
||||||
if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
|
if (!(device_port = dm_config_find_device_port(u->active_device_port, device))) {
|
||||||
count = PA_PTR_TO_UINT(value);
|
pa_log("Unknown device port %u", device);
|
||||||
count++;
|
return false;
|
||||||
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));
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
need_update = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return need_update;
|
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 devices) {
|
static bool remove_extra_devices(struct userdata *u, audio_devices_t device) {
|
||||||
void *value;
|
dm_config_port *device_port;
|
||||||
uint32_t count;
|
dm_list_entry *remove = NULL, *i = NULL;
|
||||||
bool need_update = false;
|
bool need_update = false;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->extra_devices_map);
|
pa_assert(u->extra_devices_stack);
|
||||||
pa_assert(devices);
|
|
||||||
|
|
||||||
if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
|
if (!(device_port = dm_config_find_device_port(u->active_device_port, device))) {
|
||||||
pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices));
|
pa_log("Unknown device port %u", device);
|
||||||
count = PA_PTR_TO_UINT(value);
|
return false;
|
||||||
count--;
|
}
|
||||||
if (count == 0) {
|
|
||||||
u->extra_devices &= ~devices;
|
DM_LIST_FOREACH(i, u->extra_devices_stack) {
|
||||||
need_update = true;
|
if (dm_config_port_equal(i->data, device_port)) {
|
||||||
} else {
|
remove = i;
|
||||||
/* added extra devices still exists in hashmap, so no need to update route. */
|
break;
|
||||||
pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count));
|
|
||||||
need_update = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (remove && dm_list_last(u->extra_devices_stack) == remove)
|
||||||
|
need_update = true;
|
||||||
|
|
||||||
|
if (remove)
|
||||||
|
dm_list_remove(u->extra_devices_stack, remove);
|
||||||
|
|
||||||
return need_update;
|
return need_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_extra_devices(struct userdata *u) {
|
static void clear_extra_devices(struct userdata *u) {
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->extra_devices_map);
|
pa_assert(u->extra_devices_stack);
|
||||||
|
|
||||||
pa_hashmap_remove_all(u->extra_devices_map);
|
while (dm_list_steal_first(u->extra_devices_stack));
|
||||||
u->extra_devices = 0;
|
u->override_device_port = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called from main context during voice calls, and from IO context during media operation. */
|
/* Called from main context during voice calls, and from IO context during media operation. */
|
||||||
static void do_routing(struct userdata *u) {
|
static void do_routing(struct userdata *u) {
|
||||||
audio_devices_t routing;
|
dm_config_port *routing = NULL;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->stream);
|
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);
|
clear_extra_devices(u);
|
||||||
|
|
||||||
if (!u->mix_route && u->extra_devices)
|
if (u->override_device_port)
|
||||||
routing = u->extra_devices;
|
routing = u->override_device_port;
|
||||||
else
|
else
|
||||||
routing = u->primary_devices | u->extra_devices;
|
routing = u->active_device_port;
|
||||||
|
|
||||||
pa_droid_stream_set_route(u->stream, routing);
|
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;
|
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) {
|
static int thread_write(struct userdata *u) {
|
||||||
pa_memchunk c;
|
pa_memchunk c;
|
||||||
const void *p;
|
const void *p;
|
||||||
|
|
@ -281,9 +247,6 @@ static int thread_write(struct userdata *u) {
|
||||||
u->write_time = pa_rtclock_now();
|
u->write_time = pa_rtclock_now();
|
||||||
|
|
||||||
for (;;) {
|
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);
|
p = pa_memblock_acquire_chunk(&c);
|
||||||
wrote = pa_droid_stream_write(u->stream, p, c.length);
|
wrote = pa_droid_stream_write(u->stream, p, c.length);
|
||||||
pa_memblock_release(c.memblock);
|
pa_memblock_release(c.memblock);
|
||||||
|
|
@ -454,8 +417,6 @@ static int suspend(struct userdata *u) {
|
||||||
|
|
||||||
/* Called from IO context */
|
/* Called from IO context */
|
||||||
static int unsuspend(struct userdata *u) {
|
static int unsuspend(struct userdata *u) {
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->sink);
|
pa_assert(u->sink);
|
||||||
|
|
||||||
|
|
@ -466,13 +427,6 @@ static int unsuspend(struct userdata *u) {
|
||||||
|
|
||||||
apply_volume(u->sink);
|
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);
|
pa_droid_stream_suspend(u->stream, false);
|
||||||
|
|
||||||
return 0;
|
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);
|
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.
|
/* 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
|
* Then next port change can be whatever sink port, even the one enabled
|
||||||
* before parking. */
|
* before parking. */
|
||||||
|
|
@ -556,9 +510,9 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
|
||||||
return 0;
|
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);
|
do_routing(u);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -633,9 +587,9 @@ static void update_volumes(struct userdata *u) {
|
||||||
u->use_hw_volume = (ret == 0);
|
u->use_hw_volume = (ret == 0);
|
||||||
if (u->use_hw_volume &&
|
if (u->use_hw_volume &&
|
||||||
#if defined(HAVE_ENUM_AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
|
#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
|
#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);
|
pa_log_info("Forcing software volume control with %s", u->sink->name);
|
||||||
u->use_hw_volume = false;
|
u->use_hw_volume = false;
|
||||||
} else {
|
} 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;
|
const char *tmp;
|
||||||
|
|
||||||
pa_assert(ma);
|
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;
|
data->namereg_fail = true;
|
||||||
pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink");
|
pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink");
|
||||||
} else {
|
} else {
|
||||||
char *tt;
|
char *full_name;
|
||||||
pa_assert(module_id);
|
pa_assert(name);
|
||||||
tt = pa_sprintf_malloc("sink.%s", module_id);
|
pa_assert(am);
|
||||||
pa_sink_new_data_set_name(data, tt);
|
full_name = pa_sprintf_malloc("sink.%s", name);
|
||||||
pa_xfree(tt);
|
pa_sink_new_data_set_name(data, full_name);
|
||||||
|
pa_xfree(full_name);
|
||||||
data->namereg_fail = false;
|
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) {
|
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 this device was not routed to previously post routing change */
|
||||||
if (add_extra_devices(u, devices))
|
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;
|
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_sink *pa_droid_sink_new(pa_module *m,
|
||||||
pa_modargs *ma,
|
pa_modargs *ma,
|
||||||
const char *driver,
|
const char *driver,
|
||||||
|
|
@ -1052,23 +846,21 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
pa_card *card) {
|
pa_card *card) {
|
||||||
|
|
||||||
struct userdata *u = NULL;
|
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;
|
bool deferred_volume = false;
|
||||||
char *thread_name = NULL;
|
char *thread_name = NULL;
|
||||||
pa_sink_new_data data;
|
pa_sink_new_data data;
|
||||||
const char *module_id = NULL;
|
const char *module_id = NULL;
|
||||||
const char *tmp;
|
|
||||||
char *list = NULL;
|
char *list = NULL;
|
||||||
uint32_t alternate_sample_rate;
|
uint32_t alternate_sample_rate;
|
||||||
const char *format;
|
const char *format;
|
||||||
audio_devices_t dev_out;
|
|
||||||
pa_sample_spec sample_spec;
|
pa_sample_spec sample_spec;
|
||||||
pa_channel_map channel_map;
|
pa_channel_map channel_map;
|
||||||
bool namereg_fail = false;
|
bool namereg_fail = false;
|
||||||
pa_usec_t latency;
|
pa_usec_t latency;
|
||||||
uint32_t sink_buffer = 0;
|
uint32_t sink_buffer = 0;
|
||||||
const char *prewrite_resume = NULL;
|
char *sink_name = NULL;
|
||||||
bool mix_route = false;
|
|
||||||
|
|
||||||
pa_assert(m);
|
pa_assert(m);
|
||||||
pa_assert(ma);
|
pa_assert(ma);
|
||||||
|
|
@ -1083,8 +875,8 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card && am) {
|
if (card && am) {
|
||||||
output = am->output;
|
mix_port = am->mix_port;
|
||||||
module_id = output->module->name;
|
module_id = mix_port->name;
|
||||||
} else
|
} else
|
||||||
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
|
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
|
/* First parse both sample spec and channel map, then see if sink_* override some
|
||||||
* of the values. */
|
* 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;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_modargs_get_value(ma, "sink_channel_map", NULL)) {
|
if (pa_modargs_get_channel_map(ma, NULL, &channel_map) < 0) {
|
||||||
if (pa_modargs_get_channel_map(ma, "sink_channel_map", &channel_map) < 0) {
|
pa_log("Failed to parse sink channel map.");
|
||||||
pa_log("Failed to parse sink channel map.");
|
goto fail;
|
||||||
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))) {
|
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) {
|
if (pa_modargs_get_value_u32(ma, "sink_rate", &sample_spec.rate) < 0) {
|
||||||
pa_log("Failed to parse sink samplerate");
|
pa_log("Failed to parse sink samplerate");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1135,11 +936,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
goto fail;
|
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 = pa_xnew0(struct userdata, 1);
|
||||||
u->core = m->core;
|
u->core = m->core;
|
||||||
u->module = m;
|
u->module = m;
|
||||||
|
|
@ -1151,8 +947,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
NULL, (pa_free_cb_t) parameter_free);
|
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_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->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->extra_devices_stack = dm_list_new();
|
||||||
u->mix_route = mix_route;
|
|
||||||
|
|
||||||
if (card_data) {
|
if (card_data) {
|
||||||
u->card_data = card_data;
|
u->card_data = card_data;
|
||||||
|
|
@ -1166,34 +961,25 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
goto fail;
|
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
|
/* Sink wasn't created from inside card module, so we'll need to open
|
||||||
* hw module ourself. */
|
* hw module ourself. */
|
||||||
|
|
||||||
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
|
if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id)))
|
||||||
goto fail;
|
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 */
|
pa_assert(mix_port);
|
||||||
dev_out = output->module->global_config ? output->module->global_config->default_output_device
|
|
||||||
: u->hw_module->config->global_config->default_output_device;
|
|
||||||
|
|
||||||
if ((tmp = pa_modargs_get_value(ma, "output_devices", NULL))) {
|
/* Start with default output device */
|
||||||
audio_devices_t tmp_dev;
|
device_port = dm_config_default_output_device(mix_port->module);
|
||||||
|
|
||||||
if (parse_device_list(tmp, &tmp_dev) && tmp_dev)
|
u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, mix_port, device_port);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!u->stream) {
|
if (!u->stream) {
|
||||||
pa_log("Failed to open output stream.");
|
pa_log("Failed to open output stream.");
|
||||||
|
|
@ -1207,13 +993,6 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
} else
|
} else
|
||||||
pa_log_info("Using buffer size %zu.", u->buffer_size);
|
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->buffer_time = pa_bytes_to_usec(u->buffer_size, &u->stream->output->sample_spec);
|
||||||
u->write_threshold = u->buffer_time - u->buffer_time / 6;
|
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.module = m;
|
||||||
data.card = card;
|
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_CLASS, "sound");
|
||||||
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, PROP_DROID_API_STRING);
|
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_channel_map(&data, &u->stream->output->channel_map);
|
||||||
pa_sink_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
|
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 (flags) {
|
||||||
if (!(list = pa_list_string_flags(flags))) {
|
if (!(list = pa_list_string_flags(flags))) {
|
||||||
pa_log("Couldn't format flag list string.");
|
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->parent.process_msg = sink_process_msg;
|
||||||
u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
|
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_asyncmsgq(u->sink, u->thread_mq.inq);
|
||||||
pa_sink_set_rtpoll(u->sink, u->rtpoll);
|
pa_sink_set_rtpoll(u->sink, u->rtpoll);
|
||||||
|
|
||||||
/* Rewind internal memblockq */
|
/* Rewind internal memblockq */
|
||||||
pa_sink_set_max_rewind(u->sink, 0);
|
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))) {
|
if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
|
||||||
pa_log("Failed to create thread.");
|
pa_log("Failed to create thread.");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1306,28 +1075,34 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
if (u->sink->active_port)
|
if (u->sink->active_port)
|
||||||
sink_set_port_cb(u->sink, u->sink->active_port);
|
sink_set_port_cb(u->sink, u->sink->active_port);
|
||||||
|
|
||||||
/* Hooks to track appearance and disappearance of sink-inputs. */
|
if (pa_droid_stream_is_primary(u->stream)) {
|
||||||
/* Hook a little bit earlier and later than module-role-ducking. */
|
/* Hooks to track appearance and disappearance of sink-inputs.
|
||||||
u->sink_input_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10,
|
* Hook a little bit earlier and later than module-role-ducking.
|
||||||
(pa_hook_cb_t) sink_input_put_hook_cb, u);
|
* Used only in primary sink. */
|
||||||
u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY-10,
|
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_unlink_hook_cb, u);
|
(pa_hook_cb_t) sink_input_put_hook_cb, u);
|
||||||
u->sink_proplist_changed_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_EARLY,
|
u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY-10,
|
||||||
(pa_hook_cb_t) sink_proplist_changed_hook_cb, u);
|
(pa_hook_cb_t) sink_input_unlink_hook_cb, u);
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* Port changes are done only in primary sink. */
|
||||||
|
u->sink->set_port = sink_set_port_cb;
|
||||||
|
}
|
||||||
|
|
||||||
update_volumes(u);
|
update_volumes(u);
|
||||||
|
|
||||||
if (!pa_droid_stream_is_primary(u->stream))
|
|
||||||
setup_track_primary(u);
|
|
||||||
|
|
||||||
pa_droid_stream_suspend(u->stream, false);
|
pa_droid_stream_suspend(u->stream, false);
|
||||||
pa_droid_stream_set_data(u->stream, u->sink);
|
pa_droid_stream_set_data(u->stream, u->sink);
|
||||||
pa_sink_put(u->sink);
|
pa_sink_put(u->sink);
|
||||||
|
|
||||||
|
pa_xfree(sink_name);
|
||||||
|
|
||||||
return u->sink;
|
return u->sink;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
pa_xfree(thread_name);
|
pa_xfree(thread_name);
|
||||||
|
pa_xfree(sink_name);
|
||||||
|
|
||||||
if (u)
|
if (u)
|
||||||
userdata_free(u);
|
userdata_free(u);
|
||||||
|
|
@ -1354,18 +1129,6 @@ static void parameter_free(droid_parameter_mapping *m) {
|
||||||
|
|
||||||
static void userdata_free(struct userdata *u) {
|
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)
|
if (u->sink)
|
||||||
pa_sink_unlink(u->sink);
|
pa_sink_unlink(u->sink);
|
||||||
|
|
||||||
|
|
@ -1411,8 +1174,8 @@ static void userdata_free(struct userdata *u) {
|
||||||
if (u->voice_property_value)
|
if (u->voice_property_value)
|
||||||
pa_xfree(u->voice_property_value);
|
pa_xfree(u->voice_property_value);
|
||||||
|
|
||||||
if (u->extra_devices_map)
|
if (u->extra_devices_stack)
|
||||||
pa_hashmap_free(u->extra_devices_map);
|
dm_list_free(u->extra_devices_stack, NULL);
|
||||||
|
|
||||||
pa_xfree(u);
|
pa_xfree(u);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue