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
	
	 Igor V. Kovalenko
						Igor V. Kovalenko