+
+/* Called from the main thread */
+/* Gets the list of formats supported by the sink. The members and idxset must
+ * be freed by the caller. */
+pa_idxset* pa_sink_get_formats(pa_sink *s) {
+ pa_idxset *ret;
+
+ pa_assert(s);
+
+ if (s->get_formats) {
+ /* Sink supports format query, all is good */
+ ret = s->get_formats(s);
+ } else {
+ /* Sink doesn't support format query, so assume it does PCM */
+ pa_format_info *f = pa_format_info_new();
+ f->encoding = PA_ENCODING_PCM;
+
+ ret = pa_idxset_new(NULL, NULL);
+ pa_idxset_put(ret, f, NULL);
+ }
+
+ return ret;
+}
+
+/* Called from the main thread */
+/* Allows an external source to set what formats a sink supports if the sink
+ * permits this. The function makes a copy of the formats on success. */
+bool pa_sink_set_formats(pa_sink *s, pa_idxset *formats) {
+ pa_assert(s);
+ pa_assert(formats);
+
+ if (s->set_formats)
+ /* Sink supports setting formats -- let's give it a shot */
+ return s->set_formats(s, formats);
+ else
+ /* Sink doesn't support setting this -- bail out */
+ return false;
+}
+
+/* Called from the main thread */
+/* Checks if the sink can accept this format */
+bool pa_sink_check_format(pa_sink *s, pa_format_info *f) {
+ pa_idxset *formats = NULL;
+ bool ret = false;
+
+ pa_assert(s);
+ pa_assert(f);
+
+ formats = pa_sink_get_formats(s);
+
+ if (formats) {
+ pa_format_info *finfo_device;
+ uint32_t i;
+
+ PA_IDXSET_FOREACH(finfo_device, formats, i) {
+ if (pa_format_info_is_compatible(finfo_device, f)) {
+ ret = true;
+ break;
+ }
+ }
+
+ pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+ }
+
+ return ret;
+}
+
+/* Called from the main thread */
+/* Calculates the intersection between formats supported by the sink and
+ * in_formats, and returns these, in the order of the sink's formats. */
+pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
+ pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats = NULL;
+ pa_format_info *f_sink, *f_in;
+ uint32_t i, j;
+
+ pa_assert(s);
+
+ if (!in_formats || pa_idxset_isempty(in_formats))
+ goto done;
+
+ sink_formats = pa_sink_get_formats(s);
+
+ PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
+ PA_IDXSET_FOREACH(f_in, in_formats, j) {
+ if (pa_format_info_is_compatible(f_sink, f_in))
+ pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
+ }
+ }
+
+done:
+ if (sink_formats)
+ pa_idxset_free(sink_formats, (pa_free_cb_t) pa_format_info_free);
+
+ return out_formats;
+}
+
+/* Called from the main thread. */
+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
+ pa_cvolume old_volume;
+ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+ pa_assert(s);
+ pa_assert(volume);
+
+ old_volume = s->reference_volume;
+
+ if (pa_cvolume_equal(volume, &old_volume))
+ return;
+
+ s->reference_volume = *volume;
+ pa_log_debug("The reference volume of sink %s changed from %s to %s.", s->name,
+ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
+ s->flags & PA_SINK_DECIBEL_VOLUME),
+ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
+ s->flags & PA_SINK_DECIBEL_VOLUME));
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}