format: Extend properties to handle lists/ranges
authorArun Raghavan <arun.raghavan@collabora.co.uk>
Wed, 13 Apr 2011 08:35:18 +0000 (14:05 +0530)
committerArun Raghavan <arun.raghavan@collabora.co.uk>
Sun, 15 May 2011 04:39:35 +0000 (10:09 +0530)
This replaces the simple string used by pa_format_info's proplist with a
JSON string (accessed via new API only). This allows us to express lists
and ranges more cleanly, and embed type information for future
extensibility.

We use json-c for JSON parsing. This is a lightweight depdency (32 KB on
my system) and avoids the hassle of having to reinvent a JSON parser.

Also included is a test which verifies functionality and is
valgrind-clean.

configure.ac
src/Makefile.am
src/map-file
src/pulse/format.c
src/pulse/format.h
src/pulse/internal.h
src/tests/format-test.c [new file with mode: 0644]

index 9edae0e..9d388ef 100644 (file)
@@ -603,6 +603,12 @@ fi
 
 AC_CHECK_HEADERS_ONCE([valgrind/memcheck.h])
 
 
 AC_CHECK_HEADERS_ONCE([valgrind/memcheck.h])
 
+#### json parsing ####
+
+PKG_CHECK_MODULES(LIBJSON, [ json >= 0.9 ])
+AC_SUBST(LIBJSON_CFLAGS)
+AC_SUBST(LIBJSON_LIBS)
+
 #### Sound file ####
 
 PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
 #### Sound file ####
 
 PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
index a68cdc2..7a4a32d 100644 (file)
@@ -252,6 +252,7 @@ TESTS = \
                channelmap-test \
                thread-mainloop-test \
                utf8-test \
                channelmap-test \
                thread-mainloop-test \
                utf8-test \
+               format-test \
                get-binary-name-test \
                ipacl-test \
                hook-list-test \
                get-binary-name-test \
                ipacl-test \
                hook-list-test \
@@ -285,6 +286,7 @@ TESTS_BINARIES = \
                channelmap-test \
                thread-mainloop-test \
                utf8-test \
                channelmap-test \
                thread-mainloop-test \
                utf8-test \
+               format-test \
                get-binary-name-test \
                ipacl-test \
                hook-list-test \
                get-binary-name-test \
                ipacl-test \
                hook-list-test \
@@ -370,6 +372,11 @@ utf8_test_CFLAGS = $(AM_CFLAGS)
 utf8_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
 utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
 utf8_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
 utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
+format_test_SOURCES = tests/format-test.c
+format_test_CFLAGS = $(AM_CFLAGS)
+format_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+format_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
 get_binary_name_test_SOURCES = tests/get-binary-name-test.c
 get_binary_name_test_CFLAGS = $(AM_CFLAGS)
 get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
 get_binary_name_test_SOURCES = tests/get-binary-name-test.c
 get_binary_name_test_CFLAGS = $(AM_CFLAGS)
 get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
@@ -652,9 +659,9 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
                pulsecore/sndfile-util.c pulsecore/sndfile-util.h \
                pulsecore/socket.h
 
                pulsecore/sndfile-util.c pulsecore/sndfile-util.h \
                pulsecore/socket.h
 
-libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS)
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(LIBJSON_CFLAGS)
 libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
 libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
-libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS)
+libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LIBJSON_LIBS)
 
 if HAVE_X11
 libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h
 
 if HAVE_X11
 libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h
index 8d68340..a12f30d 100644 (file)
@@ -166,6 +166,11 @@ pa_format_info_free;
 pa_format_info_is_compatible;
 pa_format_info_is_pcm;
 pa_format_info_new;
 pa_format_info_is_compatible;
 pa_format_info_is_pcm;
 pa_format_info_new;
+pa_format_info_set_prop_int;
+pa_format_info_set_prop_int_array;
+pa_format_info_set_prop_int_range;
+pa_format_info_set_prop_string;
+pa_format_info_set_prop_string_array;
 pa_format_info_snprint;
 pa_format_info_valid;
 pa_frame_size;
 pa_format_info_snprint;
 pa_format_info_valid;
 pa_frame_size;
index 84df76b..1cb804e 100644 (file)
@@ -25,6 +25,8 @@
 #include <config.h>
 #endif
 
 #include <config.h>
 #endif
 
+#include <json.h>
+
 #include <pulse/internal.h>
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
 #include <pulse/internal.h>
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
 
 #include "format.h"
 
 
 #include "format.h"
 
+#define PA_JSON_MIN_KEY "min"
+#define PA_JSON_MAX_KEY "max"
+
+static int pa_format_info_prop_compatible(const char *one, const char *two);
+
 const char *pa_encoding_to_string(pa_encoding_t e) {
     static const char* const table[]= {
         [PA_ENCODING_PCM] = "pcm",
 const char *pa_encoding_to_string(pa_encoding_t e) {
     static const char* const table[]= {
         [PA_ENCODING_PCM] = "pcm",
@@ -131,7 +138,7 @@ int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second)
         value_one = pa_proplist_gets(first->plist, key);
         value_two = pa_proplist_gets(second->plist, key);
 
         value_one = pa_proplist_gets(first->plist, key);
         value_two = pa_proplist_gets(second->plist, key);
 
-        if (!value_two || !pa_streq(value_one, value_two))
+        if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
             return FALSE;
     }
 
             return FALSE;
     }
 
@@ -148,13 +155,13 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m
     f = pa_format_info_new();
     f->encoding = PA_ENCODING_PCM;
 
     f = pa_format_info_new();
     f->encoding = PA_ENCODING_PCM;
 
-    pa_proplist_sets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format));
-    pa_proplist_setf(f->plist, PA_PROP_FORMAT_RATE, "%u", (unsigned int) ss->rate);
-    pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNELS, "%u", (unsigned int) ss->channels);
+    pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format));
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, ss->rate);
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, ss->channels);
 
     if (map) {
         pa_channel_map_snprint(cm, sizeof(cm), map);
 
     if (map) {
         pa_channel_map_snprint(cm, sizeof(cm), map);
-        pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNEL_MAP, "%s", cm);
+        pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
     }
 
     return f;
     }
 
     return f;
@@ -162,36 +169,51 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m
 
 /* For PCM streams */
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
 
 /* For PCM streams */
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
-    const char *sf, *r, *ch;
-    uint32_t channels;
+    char *sf = NULL, *m = NULL;
+    int rate, channels;
+    pa_bool_t ret = FALSE;
 
     pa_assert(f);
     pa_assert(ss);
     pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
 
 
     pa_assert(f);
     pa_assert(ss);
     pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
 
-    pa_return_val_if_fail(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT), FALSE);
-    pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE);
-    pa_return_val_if_fail(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS), FALSE);
+    if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf))
+        goto out;
+    if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate))
+        goto out;
+    if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels))
+        goto out;
 
 
-    pa_return_val_if_fail((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID, FALSE);
-    pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE);
-    pa_return_val_if_fail(pa_atou(ch, &channels) == 0, FALSE);
+    if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID)
+        goto out;
+
+    ss->rate = (uint32_t) rate;
     ss->channels = (uint8_t) channels;
 
     if (map) {
     ss->channels = (uint8_t) channels;
 
     if (map) {
-        const char *m = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNEL_MAP);
         pa_channel_map_init(map);
 
         pa_channel_map_init(map);
 
-        if (m)
-            pa_return_val_if_fail(pa_channel_map_parse(map, m) != NULL, FALSE);
+        if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m))
+            goto out;
+
+        if (m && pa_channel_map_parse(map, m) == NULL)
+            goto out;
     }
 
     }
 
-    return TRUE;
+    ret = TRUE;
+
+out:
+    if (sf)
+        pa_xfree(sf);
+    if (m)
+        pa_xfree(m);
+
+    return ret;
 }
 
 /* For compressed streams */
 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
 }
 
 /* For compressed streams */
 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
-    const char *r;
+    int rate;
 
     pa_assert(f);
     pa_assert(ss);
 
     pa_assert(f);
     pa_assert(ss);
@@ -200,11 +222,219 @@ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *
     ss->format = PA_SAMPLE_S16LE;
     ss->channels = 2;
 
     ss->format = PA_SAMPLE_S16LE;
     ss->channels = 2;
 
-    pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE);
-    pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE);
+    pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate), FALSE);
+    ss->rate = (uint32_t) rate;
 
     if (f->encoding == PA_ENCODING_EAC3_IEC61937)
         ss->rate *= 4;
 
     return TRUE;
 }
 
     if (f->encoding == PA_ENCODING_EAC3_IEC61937)
         ss->rate *= 4;
 
     return TRUE;
 }
+
+pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
+    const char *str;
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(v);
+
+    pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
+    o = json_tokener_parse(str);
+    pa_return_val_if_fail(!is_error(o), FALSE);
+    if (json_object_get_type(o) != json_type_int) {
+        json_object_put(o);
+        return FALSE;
+    }
+
+    *v = json_object_get_int(o);
+    json_object_put(o);
+
+    return TRUE;
+}
+
+pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) {
+    const char *str = NULL;
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(v);
+
+    pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
+    o = json_tokener_parse(str);
+    pa_return_val_if_fail(!is_error(o), FALSE);
+    if (json_object_get_type(o) != json_type_string) {
+        json_object_put(o);
+        return FALSE;
+    }
+
+    *v = pa_xstrdup(json_object_get_string(o));
+    json_object_put(o);
+
+    return TRUE;
+}
+
+void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_int(value);
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
+    json_object *o;
+    int i;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_array();
+
+    for (i = 0; i < n_values; i++)
+        json_object_array_add(o, json_object_new_int(values[i]));
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_object();
+
+    json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min));
+    json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max));
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_string(value);
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
+    json_object *o;
+    int i;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_array();
+
+    for (i = 0; i < n_values; i++)
+        json_object_array_add(o, json_object_new_string(values[i]));
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+static pa_bool_t pa_json_is_fixed_type(json_object *o)
+{
+    switch(json_object_get_type(o)) {
+        case json_type_object:
+        case json_type_array:
+            return FALSE;
+
+        default:
+            return TRUE;
+    }
+}
+
+static int pa_json_value_equal(json_object *o1, json_object *o2) {
+    return (json_object_get_type(o1) == json_object_get_type(o2)) &&
+        pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2));
+}
+
+static int pa_format_info_prop_compatible(const char *one, const char *two) {
+    json_object *o1 = NULL, *o2 = NULL;
+    int i, ret = 0;
+
+    o1 = json_tokener_parse(one);
+    if (is_error(o1))
+        goto out;
+
+    o2 = json_tokener_parse(two);
+    if (is_error(o2))
+        goto out;
+
+    /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
+    pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE);
+
+    if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
+        ret = pa_json_value_equal(o1, o2);
+        goto out;
+    }
+
+    if (pa_json_is_fixed_type(o1)) {
+        json_object *tmp = o2;
+        o2 = o1;
+        o1 = tmp;
+    }
+
+    /* o2 is now a fixed type, and o1 is not */
+
+    if (json_object_get_type(o1) == json_type_array) {
+        for (i = 0; i < json_object_array_length(o1); i++) {
+            if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) {
+                ret = 1;
+                break;
+            }
+        }
+    } else if (json_object_get_type(o1) == json_type_object) {
+        /* o1 should be a range type */
+        int min, max, v;
+        json_object *o_min = NULL, *o_max = NULL;
+
+        if (json_object_get_type(o2) != json_type_int) {
+            /* We don't support non-integer ranges */
+            goto out;
+        }
+
+        o_min = json_object_object_get(o1, PA_JSON_MIN_KEY);
+        if (!o_min || json_object_get_type(o_min) != json_type_int)
+            goto out;
+
+        o_max = json_object_object_get(o1, PA_JSON_MAX_KEY);
+        if (!o_max || json_object_get_type(o_max) != json_type_int)
+            goto out;
+
+        v = json_object_get_int(o2);
+        min = json_object_get_int(o_min);
+        max = json_object_get_int(o_max);
+
+        ret = v >= min && v <= max;
+    } else {
+        pa_log_warn("Got a format type that we don't support");
+    }
+
+out:
+    if (o1)
+        json_object_put(o1);
+    if (o2)
+        json_object_put(o2);
+
+    return ret;
+}
index b0efe50..0498e68 100644 (file)
@@ -102,6 +102,17 @@ int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
 /** Return a human-readable string representing the given format. \since 1.0 */
 char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f);
 
 /** Return a human-readable string representing the given format. \since 1.0 */
 char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f);
 
+/** Sets an integer property on the given format info */
+void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value);
+/** Sets a property with a list of integer values on the given format info */
+void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values);
+/** Sets a property which can have any value in a given integer range on the given format info */
+void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max);
+/** Sets a string property on the given format info */
+void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value);
+/** Sets a property with a list of string values on the given format info */
+void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values);
+
 PA_C_DECL_END
 
 #endif
 PA_C_DECL_END
 
 #endif
index a659576..40f6804 100644 (file)
@@ -300,6 +300,8 @@ void pa_format_info_free2(pa_format_info *f, void *userdata);
 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);
 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);
+pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v);
+pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v);
 
 pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m);
 
 
 pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m);
 
diff --git a/src/tests/format-test.c b/src/tests/format-test.c
new file mode 100644 (file)
index 0000000..888db8c
--- /dev/null
@@ -0,0 +1,106 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <pulsecore/macro.h>
+#include <pulse/format.h>
+
+#define INIT(f) f = pa_format_info_new()
+#define DEINIT(f) pa_format_info_free(f);
+#define REINIT(f) { DEINIT(f); INIT(f); }
+
+int main(int argc, char *argv[]) {
+    pa_format_info *f1 = NULL, *f2 = NULL;
+    int rates1[] = { 32000, 44100, 48000 };
+    const char *strings[] = { "thing1", "thing2", "thing3" };
+
+    /* 1. Simple fixed format int check */
+    INIT(f1); INIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f1, PA_PROP_FORMAT_RATE, 32000);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+
+    /* 2. Check int array membership - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int_array(f1, PA_PROP_FORMAT_RATE, rates1, PA_ELEMENTSOF(rates1));
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    pa_assert(pa_format_info_is_compatible(f1, f2));
+    pa_assert(pa_format_info_is_compatible(f2, f1));
+
+    /* 3. Check int array memebership - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+    pa_assert(!pa_format_info_is_compatible(f2, f1));
+
+    /* 4. Check int range - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int_range(f1, PA_PROP_FORMAT_RATE, 32000, 48000);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    pa_assert(pa_format_info_is_compatible(f1, f2));
+    pa_assert(pa_format_info_is_compatible(f2, f1));
+
+    /* 5. Check int range - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+    pa_assert(!pa_format_info_is_compatible(f2, f1));
+
+    /* 6. Simple fixed format string check */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f1, "format.test_string", "thing1");
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "notthing1");
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+
+    /* 7. Check string array membership - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string_array(f1, "format.test_string", strings, PA_ELEMENTSOF(strings));
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "thing3");
+    pa_assert(pa_format_info_is_compatible(f1, f2));
+    pa_assert(pa_format_info_is_compatible(f2, f1));
+
+    /* 8. Check string array memebership - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "thing5");
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+    pa_assert(!pa_format_info_is_compatible(f2, f1));
+
+    DEINIT(f1);
+    DEINIT(f2);
+
+    return 0;
+}