filter_plugin: allow open() to force an input format
Make the audio_format argument non-const. Allow the open() method to modify it, to indicate that it wants a different input audio format than the one specified. Check that condition in chain_filter_open(), and fail.
This commit is contained in:
		| @@ -23,6 +23,7 @@ | |||||||
| #include "filter_plugin.h" | #include "filter_plugin.h" | ||||||
| #include "filter_internal.h" | #include "filter_internal.h" | ||||||
| #include "filter_registry.h" | #include "filter_registry.h" | ||||||
|  | #include "audio_format.h" | ||||||
|  |  | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  |  | ||||||
| @@ -33,6 +34,12 @@ struct filter_chain { | |||||||
| 	GSList *children; | 	GSList *children; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static inline GQuark | ||||||
|  | filter_quark(void) | ||||||
|  | { | ||||||
|  | 	return g_quark_from_static_string("filter"); | ||||||
|  | } | ||||||
|  |  | ||||||
| static struct filter * | static struct filter * | ||||||
| chain_filter_init(G_GNUC_UNUSED const struct config_param *param, | chain_filter_init(G_GNUC_UNUSED const struct config_param *param, | ||||||
| 		  G_GNUC_UNUSED GError **error_r) | 		  G_GNUC_UNUSED GError **error_r) | ||||||
| @@ -92,16 +99,42 @@ chain_close_until(struct filter_chain *chain, const struct filter *until) | |||||||
| } | } | ||||||
|  |  | ||||||
| static const struct audio_format * | static const struct audio_format * | ||||||
| chain_filter_open(struct filter *_filter, | chain_open_child(struct filter *filter, | ||||||
| 		  const struct audio_format *audio_format, | 		 const struct audio_format *prev_audio_format, | ||||||
|  | 		 GError **error_r) | ||||||
|  | { | ||||||
|  | 	struct audio_format conv_audio_format = *prev_audio_format; | ||||||
|  | 	const struct audio_format *next_audio_format; | ||||||
|  |  | ||||||
|  | 	next_audio_format = filter_open(filter, &conv_audio_format, error_r); | ||||||
|  | 	if (next_audio_format == NULL) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (!audio_format_equals(&conv_audio_format, prev_audio_format)) { | ||||||
|  | 		struct audio_format_string s; | ||||||
|  |  | ||||||
|  | 		filter_close(filter); | ||||||
|  | 		g_set_error(error_r, filter_quark(), 0, | ||||||
|  | 			    "Audio format not supported by filter '%s': %s", | ||||||
|  | 			    filter->plugin->name, | ||||||
|  | 			    audio_format_to_string(prev_audio_format, &s)); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return next_audio_format; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const struct audio_format * | ||||||
|  | chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format, | ||||||
| 		  GError **error_r) | 		  GError **error_r) | ||||||
| { | { | ||||||
| 	struct filter_chain *chain = (struct filter_chain *)_filter; | 	struct filter_chain *chain = (struct filter_chain *)_filter; | ||||||
|  | 	const struct audio_format *audio_format = in_audio_format; | ||||||
|  |  | ||||||
| 	for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) { | 	for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) { | ||||||
| 		struct filter *filter = i->data; | 		struct filter *filter = i->data; | ||||||
|  |  | ||||||
| 		audio_format = filter_open(filter, audio_format, error_r); | 		audio_format = chain_open_child(filter, audio_format, error_r); | ||||||
| 		if (audio_format == NULL) { | 		if (audio_format == NULL) { | ||||||
| 			/* rollback, close all children */ | 			/* rollback, close all children */ | ||||||
| 			chain_close_until(chain, filter); | 			chain_close_until(chain, filter); | ||||||
|   | |||||||
| @@ -71,8 +71,7 @@ convert_filter_finish(struct filter *filter) | |||||||
| } | } | ||||||
|  |  | ||||||
| static const struct audio_format * | static const struct audio_format * | ||||||
| convert_filter_open(struct filter *_filter, | convert_filter_open(struct filter *_filter, struct audio_format *audio_format, | ||||||
| 		    const struct audio_format *audio_format, |  | ||||||
| 		    G_GNUC_UNUSED GError **error_r) | 		    G_GNUC_UNUSED GError **error_r) | ||||||
| { | { | ||||||
| 	struct convert_filter *filter = (struct convert_filter *)_filter; | 	struct convert_filter *filter = (struct convert_filter *)_filter; | ||||||
| @@ -82,7 +81,7 @@ convert_filter_open(struct filter *_filter, | |||||||
| 	filter->in_audio_format = filter->out_audio_format = *audio_format; | 	filter->in_audio_format = filter->out_audio_format = *audio_format; | ||||||
| 	pcm_convert_init(&filter->state); | 	pcm_convert_init(&filter->state); | ||||||
|  |  | ||||||
| 	return audio_format; | 	return &filter->in_audio_format; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
|   | |||||||
| @@ -61,23 +61,13 @@ normalize_filter_finish(struct filter *filter) | |||||||
|  |  | ||||||
| static const struct audio_format * | static const struct audio_format * | ||||||
| normalize_filter_open(struct filter *_filter, | normalize_filter_open(struct filter *_filter, | ||||||
| 		      const struct audio_format *audio_format, | 		      struct audio_format *audio_format, | ||||||
| 		      GError **error_r) | 		      G_GNUC_UNUSED GError **error_r) | ||||||
| { | { | ||||||
| 	struct normalize_filter *filter = (struct normalize_filter *)_filter; | 	struct normalize_filter *filter = (struct normalize_filter *)_filter; | ||||||
|  |  | ||||||
| 	if (audio_format->format != SAMPLE_FORMAT_S16) { | 	audio_format->format = SAMPLE_FORMAT_S16; | ||||||
| 		g_set_error(error_r, normalize_quark(), 0, | 	audio_format->reverse_endian = false; | ||||||
| 			    "Unsupported audio format"); |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (audio_format->reverse_endian) { |  | ||||||
| 		g_set_error(error_r, normalize_quark(), 0, |  | ||||||
| 			    "Normalize for reverse endian " |  | ||||||
| 			    "samples is not implemented"); |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	filter->compressor = Compressor_new(0); | 	filter->compressor = Compressor_new(0); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,8 +54,7 @@ null_filter_finish(struct filter *_filter) | |||||||
| } | } | ||||||
|  |  | ||||||
| static const struct audio_format * | static const struct audio_format * | ||||||
| null_filter_open(struct filter *_filter, | null_filter_open(struct filter *_filter, struct audio_format *audio_format, | ||||||
| 		 const struct audio_format *audio_format, |  | ||||||
| 		 G_GNUC_UNUSED GError **error_r) | 		 G_GNUC_UNUSED GError **error_r) | ||||||
| { | { | ||||||
| 	struct null_filter *filter = (struct null_filter *)_filter; | 	struct null_filter *filter = (struct null_filter *)_filter; | ||||||
|   | |||||||
| @@ -248,8 +248,7 @@ route_filter_finish(struct filter *_filter) | |||||||
| } | } | ||||||
|  |  | ||||||
| static const struct audio_format * | static const struct audio_format * | ||||||
| route_filter_open(struct filter *_filter, | route_filter_open(struct filter *_filter, struct audio_format *audio_format, | ||||||
| 		 const struct audio_format *audio_format, |  | ||||||
| 		  G_GNUC_UNUSED GError **error_r) | 		  G_GNUC_UNUSED GError **error_r) | ||||||
| { | { | ||||||
| 	struct route_filter *filter = (struct route_filter *)_filter; | 	struct route_filter *filter = (struct route_filter *)_filter; | ||||||
|   | |||||||
| @@ -69,18 +69,12 @@ volume_filter_finish(struct filter *filter) | |||||||
| } | } | ||||||
|  |  | ||||||
| static const struct audio_format * | static const struct audio_format * | ||||||
| volume_filter_open(struct filter *_filter, | volume_filter_open(struct filter *_filter, struct audio_format *audio_format, | ||||||
| 		   const struct audio_format *audio_format, | 		   G_GNUC_UNUSED GError **error_r) | ||||||
| 		   GError **error_r) |  | ||||||
| { | { | ||||||
| 	struct volume_filter *filter = (struct volume_filter *)_filter; | 	struct volume_filter *filter = (struct volume_filter *)_filter; | ||||||
|  |  | ||||||
| 	if (audio_format->reverse_endian) { | 	audio_format->reverse_endian = false; | ||||||
| 		g_set_error(error_r, volume_quark(), 0, |  | ||||||
| 			    "Software volume for reverse endian " |  | ||||||
| 			    "samples is not implemented"); |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	filter->audio_format = *audio_format; | 	filter->audio_format = *audio_format; | ||||||
| 	pcm_buffer_init(&filter->buffer); | 	pcm_buffer_init(&filter->buffer); | ||||||
|   | |||||||
| @@ -74,18 +74,23 @@ filter_free(struct filter *filter) | |||||||
| } | } | ||||||
|  |  | ||||||
| const struct audio_format * | const struct audio_format * | ||||||
| filter_open(struct filter *filter, const struct audio_format *audio_format, | filter_open(struct filter *filter, struct audio_format *audio_format, | ||||||
| 	    GError **error_r) | 	    GError **error_r) | ||||||
| { | { | ||||||
|  | 	const struct audio_format *out_audio_format; | ||||||
|  |  | ||||||
| 	assert(filter != NULL); | 	assert(filter != NULL); | ||||||
| 	assert(audio_format != NULL); | 	assert(audio_format != NULL); | ||||||
| 	assert(audio_format_valid(audio_format)); | 	assert(audio_format_valid(audio_format)); | ||||||
| 	assert(error_r == NULL || *error_r == NULL); | 	assert(error_r == NULL || *error_r == NULL); | ||||||
|  |  | ||||||
| 	audio_format = filter->plugin->open(filter, audio_format, error_r); | 	out_audio_format = filter->plugin->open(filter, audio_format, error_r); | ||||||
| 	assert(audio_format == NULL || audio_format_valid(audio_format)); |  | ||||||
|  |  | ||||||
| 	return audio_format; | 	assert(out_audio_format == NULL || audio_format_valid(audio_format)); | ||||||
|  | 	assert(out_audio_format == NULL || | ||||||
|  | 	       audio_format_valid(out_audio_format)); | ||||||
|  |  | ||||||
|  | 	return out_audio_format; | ||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
|   | |||||||
| @@ -50,10 +50,14 @@ struct filter_plugin { | |||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Opens a filter. | 	 * Opens a filter. | ||||||
|  | 	 * | ||||||
|  | 	 * @param audio_format the audio format of incoming data; the | ||||||
|  | 	 * plugin may modify the object to enforce another input | ||||||
|  | 	 * format | ||||||
| 	 */ | 	 */ | ||||||
| 	const struct audio_format * | 	const struct audio_format * | ||||||
| 	(*open)(struct filter *filter, | 	(*open)(struct filter *filter, | ||||||
| 		const struct audio_format *audio_format, | 		struct audio_format *audio_format, | ||||||
| 		GError **error_r); | 		GError **error_r); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| @@ -108,13 +112,14 @@ filter_free(struct filter *filter); | |||||||
|  * Opens the filter, preparing it for filter_filter(). |  * Opens the filter, preparing it for filter_filter(). | ||||||
|  * |  * | ||||||
|  * @param filter the filter object |  * @param filter the filter object | ||||||
|  * @param audio_format the audio format of incoming data |  * @param audio_format the audio format of incoming data; the plugin | ||||||
|  |  * may modify the object to enforce another input format | ||||||
|  * @param error location to store the error occuring, or NULL to |  * @param error location to store the error occuring, or NULL to | ||||||
|  * ignore errors. |  * ignore errors. | ||||||
|  * @return the format of outgoing data |  * @return the format of outgoing data | ||||||
|  */ |  */ | ||||||
| const struct audio_format * | const struct audio_format * | ||||||
| filter_open(struct filter *filter, const struct audio_format *audio_format, | filter_open(struct filter *filter, struct audio_format *audio_format, | ||||||
| 	    GError **error_r); | 	    GError **error_r); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann