mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
message-params: use JSON instead of custom format
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>
This commit is contained in:
parent
0ba768b2e9
commit
1dd05f4a9b
6 changed files with 93 additions and 69 deletions
|
|
@ -4,26 +4,18 @@ The message API allows any object within pulseaudio to register a message
|
||||||
handler. A message handler is a function that can be called by clients using
|
handler. A message handler is a function that can be called by clients using
|
||||||
PA_COMMAND_SEND_OBJECT_MESSAGE. A message consists at least of an object path
|
PA_COMMAND_SEND_OBJECT_MESSAGE. A message consists at least of an object path
|
||||||
and a message command, both specified as strings. Additional parameters can
|
and a message command, both specified as strings. Additional parameters can
|
||||||
be specified using a single string, but are not mandatory. The message handler
|
be specified using a single string in JSON format, but are not mandatory.
|
||||||
returns an error number as defined in def.h and also returns a string in
|
|
||||||
the "response" variable. If the string is not empty it consists of elements.
|
|
||||||
Curly braces are used to separate elements. Each element can itself contain
|
|
||||||
further elements. For example consider a message that returns multiple elements
|
|
||||||
which each contain an integer and an array of float. A response string would
|
|
||||||
look like that:
|
|
||||||
{{Integer} {{1st float} {2nd float} ...}}{...}
|
|
||||||
Any characters that are not enclosed in curly braces are ignored (all characters
|
|
||||||
between { and {, between } and } and between } and {). The same syntax is used
|
|
||||||
to specify message parameters. The reference further down lists available messages,
|
|
||||||
their parameters and return values. If a return value is enclosed in {}, this
|
|
||||||
means that multiple elements of the same type may be returned.
|
|
||||||
|
|
||||||
For string parameters that contain curly braces or backslashes, those characters
|
The message handler returns an error number as defined in def.h and also returns
|
||||||
must be escaped by adding a "\" before them.
|
a string in the "response" variable. Non-empty response will be in JSON format.
|
||||||
|
|
||||||
|
The reference further down lists available messages, their parameters
|
||||||
|
and return values.
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
Object path: /core
|
Object path: /core
|
||||||
Message: list-handlers
|
Message: list-handlers
|
||||||
Parameters: None
|
Parameters: None
|
||||||
Return value: {{{Handler name} {Description}} ...}
|
Return value: JSON array of handler description objects
|
||||||
|
[{"name":"Handler name","description":"Description"} ...]
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <pulse/json.h>
|
||||||
#include <pulse/rtclock.h>
|
#include <pulse/rtclock.h>
|
||||||
#include <pulse/timeval.h>
|
#include <pulse/timeval.h>
|
||||||
#include <pulse/utf8.h>
|
#include <pulse/utf8.h>
|
||||||
|
|
@ -2458,7 +2459,7 @@ static char *list_codecs(struct userdata *u) {
|
||||||
const pa_a2dp_codec_capabilities *a2dp_capabilities;
|
const pa_a2dp_codec_capabilities *a2dp_capabilities;
|
||||||
const pa_a2dp_codec_id *key;
|
const pa_a2dp_codec_id *key;
|
||||||
pa_hashmap *a2dp_endpoints;
|
pa_hashmap *a2dp_endpoints;
|
||||||
pa_message_params *param;
|
pa_json_encoder *encoder;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
bool is_a2dp_sink;
|
bool is_a2dp_sink;
|
||||||
void *state;
|
void *state;
|
||||||
|
|
@ -2467,9 +2468,9 @@ static char *list_codecs(struct userdata *u) {
|
||||||
|
|
||||||
a2dp_endpoints = is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints;
|
a2dp_endpoints = is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints;
|
||||||
|
|
||||||
param = pa_message_params_new();
|
encoder = pa_json_encoder_new();
|
||||||
|
|
||||||
pa_message_params_begin_list(param);
|
pa_json_encoder_begin_element_array(encoder);
|
||||||
|
|
||||||
PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, a2dp_endpoints, state) {
|
PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, a2dp_endpoints, state) {
|
||||||
for (i = 0; i < pa_bluetooth_a2dp_codec_count(); i++) {
|
for (i = 0; i < pa_bluetooth_a2dp_codec_count(); i++) {
|
||||||
|
|
@ -2479,23 +2480,23 @@ static char *list_codecs(struct userdata *u) {
|
||||||
|
|
||||||
if (memcmp(key, &a2dp_codec->id, sizeof(pa_a2dp_codec_id)) == 0) {
|
if (memcmp(key, &a2dp_codec->id, sizeof(pa_a2dp_codec_id)) == 0) {
|
||||||
if (a2dp_codec->can_be_supported(is_a2dp_sink)) {
|
if (a2dp_codec->can_be_supported(is_a2dp_sink)) {
|
||||||
pa_message_params_begin_list(param);
|
pa_json_encoder_begin_element_object(encoder);
|
||||||
|
|
||||||
pa_message_params_write_string(param, a2dp_codec->name);
|
pa_json_encoder_add_member_string(encoder, "name", a2dp_codec->name);
|
||||||
pa_message_params_write_string(param, a2dp_codec->description);
|
pa_json_encoder_add_member_string(encoder, "description", a2dp_codec->description);
|
||||||
|
|
||||||
pa_message_params_end_list(param);
|
pa_json_encoder_end_object(encoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_message_params_end_list(param);
|
pa_json_encoder_end_array(encoder);
|
||||||
|
|
||||||
return pa_message_params_to_string_free(param);
|
return pa_json_encoder_to_string_free(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bluez5_device_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
|
static int bluez5_device_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
|
||||||
char *message_handler_path;
|
char *message_handler_path;
|
||||||
pa_hashmap *capabilities_hashmap;
|
pa_hashmap *capabilities_hashmap;
|
||||||
pa_bluetooth_profile_t profile;
|
pa_bluetooth_profile_t profile;
|
||||||
|
|
@ -2503,8 +2504,6 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
|
||||||
const char *codec_name;
|
const char *codec_name;
|
||||||
struct userdata *u;
|
struct userdata *u;
|
||||||
bool is_a2dp_sink;
|
bool is_a2dp_sink;
|
||||||
void *state = NULL;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
pa_assert(u = (struct userdata *)userdata);
|
pa_assert(u = (struct userdata *)userdata);
|
||||||
pa_assert(message);
|
pa_assert(message);
|
||||||
|
|
@ -2539,9 +2538,17 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_streq(message, "switch-codec")) {
|
if (pa_streq(message, "switch-codec")) {
|
||||||
err = pa_message_params_read_string(message_parameters, &codec_name, &state);
|
if (!parameters) {
|
||||||
if (err < 0)
|
pa_log_info("Codec switching operation requires codec name string parameter");
|
||||||
return err;
|
return -PA_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa_json_object_get_type(parameters) != PA_JSON_TYPE_STRING) {
|
||||||
|
pa_log_info("Codec name object parameter must be a string");
|
||||||
|
return -PA_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
codec_name = pa_json_object_get_string(parameters);
|
||||||
|
|
||||||
if (u->a2dp_codec && pa_streq(codec_name, u->a2dp_codec->name)) {
|
if (u->a2dp_codec && pa_streq(codec_name, u->a2dp_codec->name)) {
|
||||||
pa_log_info("Requested codec is currently selected codec");
|
pa_log_info("Requested codec is currently selected codec");
|
||||||
|
|
@ -2599,15 +2606,13 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
|
||||||
*response = list_codecs(u);
|
*response = list_codecs(u);
|
||||||
return PA_OK;
|
return PA_OK;
|
||||||
} else if (pa_streq(message, "get-codec")) {
|
} else if (pa_streq(message, "get-codec")) {
|
||||||
pa_message_params *param;
|
pa_json_encoder *encoder;
|
||||||
param = pa_message_params_new();
|
encoder = pa_json_encoder_new();
|
||||||
|
|
||||||
if (u->a2dp_codec)
|
if (u->a2dp_codec)
|
||||||
pa_message_params_write_string(param, u->a2dp_codec->name);
|
pa_json_encoder_add_element_string(encoder, u->a2dp_codec->name);
|
||||||
else
|
|
||||||
pa_message_params_write_string(param, "none");
|
|
||||||
|
|
||||||
*response = pa_message_params_to_string_free(param);
|
*response = pa_json_encoder_to_string_free(encoder);
|
||||||
|
|
||||||
return PA_OK;
|
return PA_OK;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,29 +66,27 @@ static void core_free(pa_object *o);
|
||||||
|
|
||||||
/* Returns a list of handlers. */
|
/* Returns a list of handlers. */
|
||||||
static char *message_handler_list(pa_core *c) {
|
static char *message_handler_list(pa_core *c) {
|
||||||
pa_message_params *param;
|
pa_json_encoder *encoder;
|
||||||
void *state = NULL;
|
void *state = NULL;
|
||||||
struct pa_message_handler *handler;
|
struct pa_message_handler *handler;
|
||||||
|
|
||||||
param = pa_message_params_new();
|
encoder = pa_json_encoder_new();
|
||||||
|
|
||||||
pa_message_params_begin_list(param);
|
pa_json_encoder_begin_element_array(encoder);
|
||||||
PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
|
PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
|
||||||
pa_message_params_begin_list(param);
|
pa_json_encoder_begin_element_object(encoder);
|
||||||
|
|
||||||
/* object_path cannot contain characters that need escaping, therefore
|
pa_json_encoder_add_member_string(encoder, "name", handler->object_path);
|
||||||
* pa_message_params_write_raw() can safely be used here. */
|
pa_json_encoder_add_member_string(encoder, "description", handler->description);
|
||||||
pa_message_params_write_raw(param, handler->object_path, true);
|
|
||||||
pa_message_params_write_string(param, handler->description);
|
|
||||||
|
|
||||||
pa_message_params_end_list(param);
|
pa_json_encoder_end_object(encoder);
|
||||||
}
|
}
|
||||||
pa_message_params_end_list(param);
|
pa_json_encoder_end_array(encoder);
|
||||||
|
|
||||||
return pa_message_params_to_string_free(param);
|
return pa_json_encoder_to_string_free(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int core_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
|
static int core_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
|
||||||
pa_core *c;
|
pa_core *c;
|
||||||
|
|
||||||
pa_assert(c = (pa_core *) userdata);
|
pa_assert(c = (pa_core *) userdata);
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,8 @@ void pa_message_handler_unregister(pa_core *c, const char *object_path) {
|
||||||
int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) {
|
int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) {
|
||||||
struct pa_message_handler *handler;
|
struct pa_message_handler *handler;
|
||||||
int ret;
|
int ret;
|
||||||
char *parameter_copy, *path_copy;
|
char *path_copy;
|
||||||
|
pa_json_object *parameters = NULL;
|
||||||
|
|
||||||
pa_assert(c);
|
pa_assert(c);
|
||||||
pa_assert(object_path);
|
pa_assert(object_path);
|
||||||
|
|
@ -125,14 +126,21 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c
|
||||||
return -PA_ERR_NOENTITY;
|
return -PA_ERR_NOENTITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_copy = pa_xstrdup(message_parameters);
|
pa_xfree(path_copy);
|
||||||
|
|
||||||
|
if (message_parameters) {
|
||||||
|
parameters = pa_json_parse(message_parameters);
|
||||||
|
|
||||||
|
if (!parameters)
|
||||||
|
return -PA_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
/* The handler is expected to return an error code and may also
|
/* The handler is expected to return an error code and may also
|
||||||
return an error string in response */
|
return an error string in response */
|
||||||
ret = handler->callback(handler->object_path, message, parameter_copy, response, handler->userdata);
|
ret = handler->callback(handler->object_path, message, parameters, response, handler->userdata);
|
||||||
|
|
||||||
pa_xfree(parameter_copy);
|
if (parameters)
|
||||||
pa_xfree(path_copy);
|
pa_json_object_free(parameters);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include <pulsecore/core.h>
|
#include <pulsecore/core.h>
|
||||||
|
#include <pulse/json.h>
|
||||||
|
|
||||||
/* Message handler types and functions */
|
/* Message handler types and functions */
|
||||||
|
|
||||||
|
|
@ -26,7 +27,7 @@
|
||||||
typedef int (*pa_message_handler_cb_t)(
|
typedef int (*pa_message_handler_cb_t)(
|
||||||
const char *object_path,
|
const char *object_path,
|
||||||
const char *message,
|
const char *message,
|
||||||
char *message_parameters,
|
const pa_json_object *parameters,
|
||||||
char **response,
|
char **response,
|
||||||
void *userdata);
|
void *userdata);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
#include <pulse/message-params.h>
|
#include <pulse/message-params.h>
|
||||||
#include <pulse/ext-device-restore.h>
|
#include <pulse/ext-device-restore.h>
|
||||||
|
#include <pulse/json.h>
|
||||||
|
|
||||||
#include <pulsecore/i18n.h>
|
#include <pulsecore/i18n.h>
|
||||||
#include <pulsecore/macro.h>
|
#include <pulsecore/macro.h>
|
||||||
|
|
@ -897,10 +898,10 @@ static void send_message_callback(pa_context *c, int success, char *response, vo
|
||||||
}
|
}
|
||||||
|
|
||||||
static void list_handlers_callback(pa_context *c, int success, char *response, void *userdata) {
|
static void list_handlers_callback(pa_context *c, int success, char *response, void *userdata) {
|
||||||
void *state = NULL;
|
|
||||||
char *handler_list;
|
|
||||||
char *handler_struct;
|
|
||||||
int err;
|
int err;
|
||||||
|
pa_json_object *o;
|
||||||
|
int i;
|
||||||
|
const pa_json_object *v, *path, *description;
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c)));
|
pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c)));
|
||||||
|
|
@ -908,29 +909,45 @@ static void list_handlers_callback(pa_context *c, int success, char *response, v
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_message_params_read_raw(response, &handler_list, &state) <= 0) {
|
o = pa_json_parse(response);
|
||||||
|
|
||||||
|
if (!o) {
|
||||||
pa_log(_("list-handlers message response could not be parsed correctly"));
|
pa_log(_("list-handlers message response could not be parsed correctly"));
|
||||||
|
pa_json_object_free(o);
|
||||||
quit(1);
|
quit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state = NULL;
|
if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) {
|
||||||
while ((err = pa_message_params_read_raw(handler_list, &handler_struct, &state)) > 0) {
|
pa_log(_("list-handlers message response is not a JSON array"));
|
||||||
void *state2 = NULL;
|
pa_json_object_free(o);
|
||||||
const char *path;
|
quit(1);
|
||||||
const char *description;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pa_message_params_read_string(handler_struct, &path, &state2) <= 0) {
|
err = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < pa_json_object_get_array_length(o); ++i) {
|
||||||
|
v = pa_json_object_get_array_member(o, i);
|
||||||
|
if (pa_json_object_get_type(v) != PA_JSON_TYPE_OBJECT) {
|
||||||
|
pa_log(_("list-handlers message response array element %d is not a JSON object"), i);
|
||||||
err = -1;
|
err = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pa_message_params_read_string(handler_struct, &description, &state2) <= 0) {
|
|
||||||
|
path = pa_json_object_get_object_member(v, "name");
|
||||||
|
if (!path || pa_json_object_get_type(path) != PA_JSON_TYPE_STRING) {
|
||||||
|
err = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
description = pa_json_object_get_object_member(v, "description");
|
||||||
|
if (!description || pa_json_object_get_type(description) != PA_JSON_TYPE_STRING) {
|
||||||
err = -1;
|
err = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (short_list_format)
|
if (short_list_format)
|
||||||
printf("%s\n", path);
|
printf("%s\n", pa_json_object_get_string(path));
|
||||||
else {
|
else {
|
||||||
if (nl)
|
if (nl)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
@ -938,17 +955,20 @@ static void list_handlers_callback(pa_context *c, int success, char *response, v
|
||||||
|
|
||||||
printf("Message Handler %s\n"
|
printf("Message Handler %s\n"
|
||||||
"\tDescription: %s\n",
|
"\tDescription: %s\n",
|
||||||
path,
|
pa_json_object_get_string(path),
|
||||||
description);
|
pa_json_object_get_string(description));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
pa_log(_("list-handlers message response could not be parsed correctly"));
|
pa_log(_("list-handlers message response could not be parsed correctly"));
|
||||||
|
pa_json_object_free(o);
|
||||||
quit(1);
|
quit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_json_object_free(o);
|
||||||
|
|
||||||
complete_action();
|
complete_action();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue