pw-cat: remove code to list targets

Introspection of default devices should be done elsewhere.
This commit is contained in:
Wim Taymans 2022-04-13 16:02:46 +02:00
parent 64cf5e80e6
commit ecff225b11

View file

@ -88,18 +88,6 @@ struct data;
typedef int (*fill_fn)(struct data *d, void *dest, unsigned int n_frames); typedef int (*fill_fn)(struct data *d, void *dest, unsigned int n_frames);
struct target {
struct spa_list link;
uint32_t id;
#define TARGET_TYPE_SINK 0
#define TARGET_TYPE_SOURCE 1
#define TARGET_TYPE_STREAM 2
uint32_t type;
char *name;
char *desc;
int prio;
};
struct channelmap { struct channelmap {
int n_channels; int n_channels;
int channels[SPA_AUDIO_MAX_CHANNELS]; int channels[SPA_AUDIO_MAX_CHANNELS];
@ -110,12 +98,6 @@ struct data {
struct pw_context *context; struct pw_context *context;
struct pw_core *core; struct pw_core *core;
struct spa_hook core_listener; struct spa_hook core_listener;
struct pw_registry *registry;
struct spa_hook registry_listener;
struct pw_metadata *metadata;
struct spa_hook metadata_listener;
char default_sink[1024];
char default_source[1024];
struct pw_stream *stream; struct pw_stream *stream;
struct spa_hook stream_listener; struct spa_hook stream_listener;
@ -157,12 +139,6 @@ struct data {
fill_fn fill; fill_fn fill;
uint32_t target_id;
bool list_targets;
bool targets_listed;
struct spa_list targets;
int sync;
struct spa_io_position *position; struct spa_io_position *position;
bool drained; bool drained;
uint64_t clock_time; uint64_t clock_time;
@ -612,39 +588,6 @@ static void channelmap_print(struct channelmap *map)
} }
} }
static void
target_destroy(struct target *target)
{
if (!target)
return;
if (target->name)
free(target->name);
if (target->desc)
free(target->desc);
free(target);
}
static struct target *
target_create(uint32_t id, uint32_t type, const char *name, const char *desc, int prio)
{
struct target *target;
target = malloc(sizeof(*target));
if (!target)
return NULL;
target->id = id;
target->type = type;
target->name = strdup(name);
target->desc = strdup(desc ? : "");
target->prio = prio;
if (!target->name || !target->desc) {
target_destroy(target);
return NULL;
}
return target;
}
static void on_core_info(void *userdata, const struct pw_core_info *info) static void on_core_info(void *userdata, const struct pw_core_info *info)
{ {
struct data *data = userdata; struct data *data = userdata;
@ -654,17 +597,6 @@ static void on_core_info(void *userdata, const struct pw_core_info *info)
info->id, info->name); info->id, info->name);
} }
static void on_core_done(void *userdata, uint32_t id, int seq)
{
struct data *data = userdata;
/* if we're listing targets just exist */
if (data->sync == seq && data->list_targets) {
data->targets_listed = true;
pw_main_loop_quit(data->loop);
}
}
static void on_core_error(void *userdata, uint32_t id, int seq, int res, const char *message) static void on_core_error(void *userdata, uint32_t id, int seq, int res, const char *message)
{ {
struct data *data = userdata; struct data *data = userdata;
@ -679,158 +611,9 @@ static void on_core_error(void *userdata, uint32_t id, int seq, int res, const c
static const struct pw_core_events core_events = { static const struct pw_core_events core_events = {
PW_VERSION_CORE_EVENTS, PW_VERSION_CORE_EVENTS,
.info = on_core_info, .info = on_core_info,
.done = on_core_done,
.error = on_core_error, .error = on_core_error,
}; };
static int json_object_find(const char *obj, const char *key, char *value, size_t len)
{
struct spa_json it[2];
const char *v;
char k[128];
spa_json_init(&it[0], obj, strlen(obj));
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
return -EINVAL;
while (spa_json_get_string(&it[1], k, sizeof(k)) > 0) {
if (spa_streq(k, key)) {
if (spa_json_get_string(&it[1], value, len) <= 0)
continue;
return 0;
} else {
if (spa_json_next(&it[1], &v) <= 0)
break;
}
}
return -ENOENT;
}
static int metadata_property(void *object,
uint32_t subject, const char *key, const char *type, const char *value)
{
struct data *data = object;
if (subject == PW_ID_CORE) {
if (key == NULL || spa_streq(key, "default.audio.sink")) {
if (value == NULL ||
json_object_find(value, "name",
data->default_sink, sizeof(data->default_sink)) < 0)
data->default_sink[0] = '\0';
}
if (key == NULL || spa_streq(key, "default.audio.source")) {
if (value == NULL ||
json_object_find(value, "name",
data->default_source, sizeof(data->default_source)) < 0)
data->default_source[0] = '\0';
}
}
return 0;
}
static const struct pw_metadata_events metadata_events = {
PW_VERSION_METADATA_EVENTS,
.property = metadata_property,
};
static void registry_event_global(void *userdata, uint32_t id,
uint32_t permissions, const char *type, uint32_t version,
const struct spa_dict *props)
{
struct data *data = userdata;
const struct spa_dict_item *item;
const char *name, *desc, *media_class, *prio_session;
int prio;
struct target *target;
uint32_t ttype;
/* only once */
if (data->targets_listed)
return;
/* must be listing targets and interface must be a node */
if (!data->list_targets)
return;
if (spa_streq(type, PW_TYPE_INTERFACE_Metadata)) {
if (data->metadata != NULL)
return;
if ((name = spa_dict_lookup(props, PW_KEY_METADATA_NAME)) != NULL &&
!spa_streq(name, "default"))
return;
data->metadata = pw_registry_bind(data->registry,
id, type, PW_VERSION_METADATA, 0);
pw_metadata_add_listener(data->metadata,
&data->metadata_listener,
&metadata_events, data);
data->sync = pw_core_sync(data->core, 0, data->sync);
} else if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
name = spa_dict_lookup(props, PW_KEY_NODE_NAME);
desc = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION);
media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
prio_session = spa_dict_lookup(props, PW_KEY_PRIORITY_SESSION);
/* name and media class must exist */
if (!name || !media_class)
return;
if (desc == NULL)
desc = name;
/* get allowed mode from the media class */
if (spa_streq(media_class, "Audio/Source")) {
if (data->mode != mode_record)
return;
ttype = TARGET_TYPE_SOURCE;
}
else if (spa_streq(media_class, "Stream/Output/Audio")) {
if (data->mode != mode_record)
return;
ttype = TARGET_TYPE_STREAM;
}
else if (spa_streq(media_class, "Audio/Sink")) {
if (data->mode != mode_playback &&
data->mode != mode_record)
return;
ttype = TARGET_TYPE_SINK;
}
else
return;
prio = prio_session ? atoi(prio_session) : -1;
if (data->verbose) {
printf("registry: id=%"PRIu32" type=%s name=\"%s\" media_class=\"%s\" desc=\"%s\" prio=%d\n",
id, type, name, media_class, desc ? : "", prio);
spa_dict_for_each(item, props) {
printf("\t\t%s = \"%s\"\n", item->key, item->value);
}
}
target = target_create(id, ttype, name, desc, prio);
if (target)
spa_list_append(&data->targets, &target->link);
}
}
static void registry_event_global_remove(void *userdata, uint32_t id)
{
struct data *data = userdata;
if (data->verbose)
printf("registry: remove id=%"PRIu32"\n", id);
}
static const struct pw_registry_events registry_events = {
PW_VERSION_REGISTRY_EVENTS,
.global = registry_event_global,
.global_remove = registry_event_global_remove,
};
static void static void
on_state_changed(void *userdata, enum pw_stream_state old, on_state_changed(void *userdata, enum pw_stream_state old,
enum pw_stream_state state, const char *error) enum pw_stream_state state, const char *error)
@ -1045,7 +828,6 @@ enum {
OPT_CHANNELMAP, OPT_CHANNELMAP,
OPT_FORMAT, OPT_FORMAT,
OPT_VOLUME, OPT_VOLUME,
OPT_LIST_TARGETS,
}; };
static const struct option long_options[] = { static const struct option long_options[] = {
@ -1072,8 +854,6 @@ static const struct option long_options[] = {
{ "volume", required_argument, NULL, OPT_VOLUME }, { "volume", required_argument, NULL, OPT_VOLUME },
{ "quality", required_argument, NULL, 'q' }, { "quality", required_argument, NULL, 'q' },
{ "list-targets", no_argument, NULL, OPT_LIST_TARGETS },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
@ -1101,7 +881,6 @@ static void show_usage(const char *name, bool is_error)
" Xunit (unit = s, ms, us, ns)\n" " Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n" " or direct samples (256)\n"
" the rate is the one of the source file\n" " the rate is the one of the source file\n"
" --list-targets List available targets for --target\n"
"\n"), "\n"),
DEFAULT_MEDIA_TYPE, DEFAULT_MEDIA_TYPE,
DEFAULT_MEDIA_CATEGORY_PLAYBACK, DEFAULT_MEDIA_CATEGORY_PLAYBACK,
@ -1535,9 +1314,6 @@ int main(int argc, char *argv[])
data.volume = -1.0; data.volume = -1.0;
data.quality = -1; data.quality = -1;
/* initialize list every time */
spa_list_init(&data.targets);
while ((c = getopt_long(argc, argv, "hvprmdR:q:", long_options, NULL)) != -1) { while ((c = getopt_long(argc, argv, "hvprmdR:q:", long_options, NULL)) != -1) {
switch (c) { switch (c) {
@ -1597,17 +1373,8 @@ int main(int argc, char *argv[])
case OPT_TARGET: case OPT_TARGET:
data.target = optarg; data.target = optarg;
if (spa_streq(optarg, "auto")) { if (spa_streq(data.target, "0")) {
data.target_id = PW_ID_ANY; data.target = NULL;
break;
}
if (!isdigit(optarg[0])) {
fprintf(stderr, "error: bad target option \"%s\"\n", optarg);
goto error_usage;
}
data.target_id = atoi(optarg);
if (data.target_id == 0) {
data.target_id = PW_ID_ANY;
flags &= ~PW_STREAM_FLAG_AUTOCONNECT; flags &= ~PW_STREAM_FLAG_AUTOCONNECT;
} }
break; break;
@ -1646,10 +1413,6 @@ int main(int argc, char *argv[])
data.volume = atof(optarg); data.volume = atof(optarg);
break; break;
case OPT_LIST_TARGETS:
data.list_targets = true;
break;
default: default:
fprintf(stderr, "error: unknown option '%c'\n", c); fprintf(stderr, "error: unknown option '%c'\n", c);
goto error_usage; goto error_usage;
@ -1677,10 +1440,7 @@ int main(int argc, char *argv[])
DEFAULT_MEDIA_CATEGORY_RECORD; DEFAULT_MEDIA_CATEGORY_RECORD;
if (!data.media_role) if (!data.media_role)
data.media_role = DEFAULT_MEDIA_ROLE; data.media_role = DEFAULT_MEDIA_ROLE;
if (!data.target) {
data.target = DEFAULT_TARGET;
data.target_id = PW_ID_ANY;
}
if (!data.latency) if (!data.latency)
data.latency = data.mode == mode_playback ? data.latency = data.mode == mode_playback ?
DEFAULT_LATENCY_PLAY : DEFAULT_LATENCY_PLAY :
@ -1701,7 +1461,7 @@ int main(int argc, char *argv[])
if (data.volume < 0) if (data.volume < 0)
data.volume = DEFAULT_VOLUME; data.volume = DEFAULT_VOLUME;
if (!data.list_targets && optind >= argc) { if (optind >= argc) {
fprintf(stderr, "error: filename argument missing\n"); fprintf(stderr, "error: filename argument missing\n");
goto error_usage; goto error_usage;
} }
@ -1715,6 +1475,7 @@ int main(int argc, char *argv[])
PW_KEY_MEDIA_FILENAME, data.filename, PW_KEY_MEDIA_FILENAME, data.filename,
PW_KEY_MEDIA_NAME, data.filename, PW_KEY_MEDIA_NAME, data.filename,
PW_KEY_NODE_NAME, prog, PW_KEY_NODE_NAME, prog,
PW_KEY_TARGET_OBJECT, data.target,
NULL); NULL);
if (data.props == NULL) { if (data.props == NULL) {
@ -1755,17 +1516,6 @@ int main(int argc, char *argv[])
} }
pw_core_add_listener(data.core, &data.core_listener, &core_events, &data); pw_core_add_listener(data.core, &data.core_listener, &core_events, &data);
data.registry = pw_core_get_registry(data.core, PW_VERSION_REGISTRY, 0);
if (!data.registry) {
fprintf(stderr, "error: pw_core_get_registry() failed: %m\n");
goto error_no_registry;
}
pw_registry_add_listener(data.registry, &data.registry_listener, &registry_events, &data);
data.sync = pw_core_sync(data.core, 0, data.sync);
if (!data.list_targets) {
switch (data.data_type) { switch (data.data_type) {
case TYPE_PCM: case TYPE_PCM:
ret = setup_sndfile(&data); ret = setup_sndfile(&data);
@ -1846,16 +1596,16 @@ int main(int argc, char *argv[])
pw_stream_add_listener(data.stream, &data.stream_listener, &stream_events, &data); pw_stream_add_listener(data.stream, &data.stream_listener, &stream_events, &data);
if (data.verbose) if (data.verbose)
printf("connecting %s stream; target_id=%"PRIu32"\n", printf("connecting %s stream; target=%s\n",
data.mode == mode_playback ? "playback" : "record", data.mode == mode_playback ? "playback" : "record",
data.target_id); data.target);
if (data.verbose) if (data.verbose)
data.timer = pw_loop_add_timer(l, do_print_delay, &data); data.timer = pw_loop_add_timer(l, do_print_delay, &data);
ret = pw_stream_connect(data.stream, ret = pw_stream_connect(data.stream,
data.mode == mode_playback ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT, data.mode == mode_playback ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT,
data.target_id, PW_ID_ANY,
flags | flags |
PW_STREAM_FLAG_MAP_BUFFERS, PW_STREAM_FLAG_MAP_BUFFERS,
params, 1); params, 1);
@ -1878,67 +1628,13 @@ int main(int argc, char *argv[])
} }
} }
} }
}
/* and wait while we let things run */ /* and wait while we let things run */
pw_main_loop_run(data.loop); pw_main_loop_run(data.loop);
/* we're returning OK only if got to the point to drain */ /* we're returning OK only if got to the point to drain */
if (!data.list_targets) {
if (data.drained) if (data.drained)
exit_code = EXIT_SUCCESS; exit_code = EXIT_SUCCESS;
} else {
if (data.targets_listed) {
struct target *target, *target_default;
char *default_name;
default_name = (data.mode == mode_record) ?
data.default_source : data.default_sink;
exit_code = EXIT_SUCCESS;
/* first find the highest priority */
target_default = NULL;
spa_list_for_each(target, &data.targets, link) {
if (target_default == NULL ||
spa_streq(default_name, target->name) ||
(default_name[0] == '\0' &&
target->prio > target_default->prio))
target_default = target;
}
printf("Available targets (\"*\" denotes default): %s\n", default_name);
spa_list_for_each(target, &data.targets, link) {
if (target->type != TARGET_TYPE_SOURCE)
continue;
printf("%s\t%"PRIu32": source description=\"%s\" prio=%d\n",
target == target_default ? "*" : "",
target->id, target->desc, target->prio);
}
spa_list_for_each(target, &data.targets, link) {
if (target->type != TARGET_TYPE_SINK)
continue;
printf("%s\t%"PRIu32": %s description=\"%s\" prio=%d\n",
target == target_default ? "*" : "",
target->id, data.mode == mode_record ? "monitor" : "sink",
target->desc, target->prio);
}
spa_list_for_each(target, &data.targets, link) {
if (target->type != TARGET_TYPE_STREAM)
continue;
printf("%s\t%"PRIu32": stream description=\"%s\" prio=%d\n",
target == target_default ? "*" : "",
target->id, target->desc, target->prio);
}
}
}
/* destroy targets */
while (!spa_list_is_empty(&data.targets)) {
struct target *target;
target = spa_list_last(&data.targets, struct target, link);
spa_list_remove(&target->link);
target_destroy(target);
}
error_connect_fail: error_connect_fail:
if (data.stream) { if (data.stream) {
@ -1946,15 +1642,6 @@ error_connect_fail:
pw_stream_destroy(data.stream); pw_stream_destroy(data.stream);
} }
error_no_stream: error_no_stream:
if (data.metadata) {
spa_hook_remove(&data.metadata_listener);
pw_proxy_destroy((struct pw_proxy*)data.metadata);
}
if (data.registry) {
spa_hook_remove(&data.registry_listener);
pw_proxy_destroy((struct pw_proxy*)data.registry);
}
error_no_registry:
spa_hook_remove(&data.core_listener); spa_hook_remove(&data.core_listener);
pw_core_disconnect(data.core); pw_core_disconnect(data.core);
error_ctx_connect_failed: error_ctx_connect_failed: