summaryrefslogtreecommitdiffstats
path: root/src/gst/gstpipewireformat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gst/gstpipewireformat.c')
-rw-r--r--src/gst/gstpipewireformat.c981
1 files changed, 981 insertions, 0 deletions
diff --git a/src/gst/gstpipewireformat.c b/src/gst/gstpipewireformat.c
new file mode 100644
index 0000000..006c499
--- /dev/null
+++ b/src/gst/gstpipewireformat.c
@@ -0,0 +1,981 @@
+/* GStreamer
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include <gst/gst.h>
+#include <gst/allocators/gstdmabuf.h>
+#include <gst/video/video.h>
+#include <gst/audio/audio.h>
+
+#include <spa/utils/string.h>
+#include <spa/utils/type.h>
+#include <spa/param/video/format-utils.h>
+#include <spa/param/audio/format-utils.h>
+#include <spa/pod/builder.h>
+
+#include "gstpipewireformat.h"
+
+#ifndef DRM_FORMAT_MOD_INVALID
+#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
+#endif
+
+#ifndef DRM_FORMAT_MOD_LINEAR
+#define DRM_FORMAT_MOD_LINEAR 0
+#endif
+
+struct media_type {
+ const char *name;
+ uint32_t media_type;
+ uint32_t media_subtype;
+};
+
+static const struct media_type media_type_map[] = {
+ { "video/x-raw", SPA_MEDIA_TYPE_video, SPA_MEDIA_SUBTYPE_raw },
+ { "audio/x-raw", SPA_MEDIA_TYPE_audio, SPA_MEDIA_SUBTYPE_raw },
+ { "image/jpeg", SPA_MEDIA_TYPE_video, SPA_MEDIA_SUBTYPE_mjpg },
+ { "video/x-jpeg", SPA_MEDIA_TYPE_video, SPA_MEDIA_SUBTYPE_mjpg },
+ { "video/x-h264", SPA_MEDIA_TYPE_video, SPA_MEDIA_SUBTYPE_h264 },
+ { "audio/x-mulaw", SPA_MEDIA_TYPE_audio, SPA_MEDIA_SUBTYPE_raw },
+ { "audio/x-alaw", SPA_MEDIA_TYPE_audio, SPA_MEDIA_SUBTYPE_raw },
+ { "audio/mpeg", SPA_MEDIA_TYPE_audio, SPA_MEDIA_SUBTYPE_mp3 },
+ { "audio/x-flac", SPA_MEDIA_TYPE_audio, SPA_MEDIA_SUBTYPE_flac },
+ { NULL, }
+};
+
+static const uint32_t video_format_map[] = {
+ SPA_VIDEO_FORMAT_UNKNOWN,
+ SPA_VIDEO_FORMAT_ENCODED,
+ SPA_VIDEO_FORMAT_I420,
+ SPA_VIDEO_FORMAT_YV12,
+ SPA_VIDEO_FORMAT_YUY2,
+ SPA_VIDEO_FORMAT_UYVY,
+ SPA_VIDEO_FORMAT_AYUV,
+ SPA_VIDEO_FORMAT_RGBx,
+ SPA_VIDEO_FORMAT_BGRx,
+ SPA_VIDEO_FORMAT_xRGB,
+ SPA_VIDEO_FORMAT_xBGR,
+ SPA_VIDEO_FORMAT_RGBA,
+ SPA_VIDEO_FORMAT_BGRA,
+ SPA_VIDEO_FORMAT_ARGB,
+ SPA_VIDEO_FORMAT_ABGR,
+ SPA_VIDEO_FORMAT_RGB,
+ SPA_VIDEO_FORMAT_BGR,
+ SPA_VIDEO_FORMAT_Y41B,
+ SPA_VIDEO_FORMAT_Y42B,
+ SPA_VIDEO_FORMAT_YVYU,
+ SPA_VIDEO_FORMAT_Y444,
+ SPA_VIDEO_FORMAT_v210,
+ SPA_VIDEO_FORMAT_v216,
+ SPA_VIDEO_FORMAT_NV12,
+ SPA_VIDEO_FORMAT_NV21,
+ SPA_VIDEO_FORMAT_GRAY8,
+ SPA_VIDEO_FORMAT_GRAY16_BE,
+ SPA_VIDEO_FORMAT_GRAY16_LE,
+ SPA_VIDEO_FORMAT_v308,
+ SPA_VIDEO_FORMAT_RGB16,
+ SPA_VIDEO_FORMAT_BGR16,
+ SPA_VIDEO_FORMAT_RGB15,
+ SPA_VIDEO_FORMAT_BGR15,
+ SPA_VIDEO_FORMAT_UYVP,
+ SPA_VIDEO_FORMAT_A420,
+ SPA_VIDEO_FORMAT_RGB8P,
+ SPA_VIDEO_FORMAT_YUV9,
+ SPA_VIDEO_FORMAT_YVU9,
+ SPA_VIDEO_FORMAT_IYU1,
+ SPA_VIDEO_FORMAT_ARGB64,
+ SPA_VIDEO_FORMAT_AYUV64,
+ SPA_VIDEO_FORMAT_r210,
+ SPA_VIDEO_FORMAT_I420_10BE,
+ SPA_VIDEO_FORMAT_I420_10LE,
+ SPA_VIDEO_FORMAT_I422_10BE,
+ SPA_VIDEO_FORMAT_I422_10LE,
+ SPA_VIDEO_FORMAT_Y444_10BE,
+ SPA_VIDEO_FORMAT_Y444_10LE,
+ SPA_VIDEO_FORMAT_GBR,
+ SPA_VIDEO_FORMAT_GBR_10BE,
+ SPA_VIDEO_FORMAT_GBR_10LE,
+ SPA_VIDEO_FORMAT_NV16,
+ SPA_VIDEO_FORMAT_NV24,
+ SPA_VIDEO_FORMAT_NV12_64Z32,
+ SPA_VIDEO_FORMAT_A420_10BE,
+ SPA_VIDEO_FORMAT_A420_10LE,
+ SPA_VIDEO_FORMAT_A422_10BE,
+ SPA_VIDEO_FORMAT_A422_10LE,
+ SPA_VIDEO_FORMAT_A444_10BE,
+ SPA_VIDEO_FORMAT_A444_10LE,
+ SPA_VIDEO_FORMAT_NV61,
+ SPA_VIDEO_FORMAT_P010_10BE,
+ SPA_VIDEO_FORMAT_P010_10LE,
+ SPA_VIDEO_FORMAT_IYU2,
+ SPA_VIDEO_FORMAT_VYUY,
+ SPA_VIDEO_FORMAT_GBRA,
+ SPA_VIDEO_FORMAT_GBRA_10BE,
+ SPA_VIDEO_FORMAT_GBRA_10LE,
+ SPA_VIDEO_FORMAT_GBR_12BE,
+ SPA_VIDEO_FORMAT_GBR_12LE,
+ SPA_VIDEO_FORMAT_GBRA_12BE,
+ SPA_VIDEO_FORMAT_GBRA_12LE,
+ SPA_VIDEO_FORMAT_I420_12BE,
+ SPA_VIDEO_FORMAT_I420_12LE,
+ SPA_VIDEO_FORMAT_I422_12BE,
+ SPA_VIDEO_FORMAT_I422_12LE,
+ SPA_VIDEO_FORMAT_Y444_12BE,
+ SPA_VIDEO_FORMAT_Y444_12LE,
+};
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define _FORMAT_LE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## _OE
+#define _FORMAT_BE(fmt) SPA_AUDIO_FORMAT_ ## fmt
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define _FORMAT_LE(fmt) SPA_AUDIO_FORMAT_ ## fmt
+#define _FORMAT_BE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## _OE
+#endif
+
+static const uint32_t audio_format_map[] = {
+ SPA_AUDIO_FORMAT_UNKNOWN,
+ SPA_AUDIO_FORMAT_ENCODED,
+ SPA_AUDIO_FORMAT_S8,
+ SPA_AUDIO_FORMAT_U8,
+ _FORMAT_LE (S16),
+ _FORMAT_BE (S16),
+ _FORMAT_LE (U16),
+ _FORMAT_BE (U16),
+ _FORMAT_LE (S24_32),
+ _FORMAT_BE (S24_32),
+ _FORMAT_LE (U24_32),
+ _FORMAT_BE (U24_32),
+ _FORMAT_LE (S32),
+ _FORMAT_BE (S32),
+ _FORMAT_LE (U32),
+ _FORMAT_BE (U32),
+ _FORMAT_LE (S24),
+ _FORMAT_BE (S24),
+ _FORMAT_LE (U24),
+ _FORMAT_BE (U24),
+ _FORMAT_LE (S20),
+ _FORMAT_BE (S20),
+ _FORMAT_LE (U20),
+ _FORMAT_BE (U20),
+ _FORMAT_LE (S18),
+ _FORMAT_BE (S18),
+ _FORMAT_LE (U18),
+ _FORMAT_BE (U18),
+ _FORMAT_LE (F32),
+ _FORMAT_BE (F32),
+ _FORMAT_LE (F64),
+ _FORMAT_BE (F64),
+};
+
+typedef struct {
+ struct spa_pod_builder b;
+ const struct media_type *type;
+ uint32_t id;
+ const GstCapsFeatures *cf;
+ const GstStructure *cs;
+ GPtrArray *array;
+} ConvertData;
+
+static const struct media_type *
+find_media_types (const char *name)
+{
+ int i;
+ for (i = 0; media_type_map[i].name; i++) {
+ if (spa_streq(media_type_map[i].name, name))
+ return &media_type_map[i];
+ }
+ return NULL;
+}
+
+static int find_index(const uint32_t *items, int n_items, uint32_t id)
+{
+ int i;
+ for (i = 0; i < n_items; i++)
+ if (items[i] == id)
+ return i;
+ return -1;
+}
+
+static const char *
+get_nth_string (const GValue *val, int idx)
+{
+ const GValue *v = NULL;
+ GType type = G_VALUE_TYPE (val);
+
+ if (type == G_TYPE_STRING && idx == 0)
+ v = val;
+ else if (type == GST_TYPE_LIST) {
+ GArray *array = g_value_peek_pointer (val);
+ if (idx < (int)(array->len + 1)) {
+ v = &g_array_index (array, GValue, SPA_MAX (idx - 1, 0));
+ }
+ }
+ if (v)
+ return g_value_get_string (v);
+
+ return NULL;
+}
+
+static bool
+get_nth_int (const GValue *val, int idx, int *res)
+{
+ const GValue *v = NULL;
+ GType type = G_VALUE_TYPE (val);
+
+ if (type == G_TYPE_INT && idx == 0) {
+ v = val;
+ } else if (type == GST_TYPE_INT_RANGE) {
+ if (idx == 0 || idx == 1) {
+ *res = gst_value_get_int_range_min (val);
+ return true;
+ } else if (idx == 2) {
+ *res = gst_value_get_int_range_max (val);
+ return true;
+ }
+ } else if (type == GST_TYPE_LIST) {
+ GArray *array = g_value_peek_pointer (val);
+ if (idx < (int)(array->len + 1)) {
+ v = &g_array_index (array, GValue, SPA_MAX (idx - 1, 0));
+ }
+ }
+ if (v) {
+ *res = g_value_get_int (v);
+ return true;
+ }
+ return false;
+}
+
+static gboolean
+get_nth_fraction (const GValue *val, int idx, struct spa_fraction *f)
+{
+ const GValue *v = NULL;
+ GType type = G_VALUE_TYPE (val);
+
+ if (type == GST_TYPE_FRACTION && idx == 0) {
+ v = val;
+ } else if (type == GST_TYPE_FRACTION_RANGE) {
+ if (idx == 0 || idx == 1) {
+ v = gst_value_get_fraction_range_min (val);
+ } else if (idx == 2) {
+ v = gst_value_get_fraction_range_max (val);
+ }
+ } else if (type == GST_TYPE_LIST) {
+ GArray *array = g_value_peek_pointer (val);
+ if (idx < (int)(array->len + 1)) {
+ v = &g_array_index (array, GValue, SPA_MAX (idx-1, 0));
+ }
+ }
+ if (v) {
+ f->num = gst_value_get_fraction_numerator (v);
+ f->denom = gst_value_get_fraction_denominator (v);
+ return true;
+ }
+ return false;
+}
+
+static gboolean
+get_nth_rectangle (const GValue *width, const GValue *height, int idx, struct spa_rectangle *r)
+{
+ const GValue *w = NULL, *h = NULL;
+ GType wt = G_VALUE_TYPE (width);
+ GType ht = G_VALUE_TYPE (height);
+
+ if (wt == G_TYPE_INT && ht == G_TYPE_INT && idx == 0) {
+ w = width;
+ h = height;
+ } else if (wt == GST_TYPE_INT_RANGE && ht == GST_TYPE_INT_RANGE) {
+ if (idx == 0 || idx == 1) {
+ r->width = gst_value_get_int_range_min (width);
+ r->height = gst_value_get_int_range_min (height);
+ return true;
+ } else if (idx == 2) {
+ r->width = gst_value_get_int_range_max (width);
+ r->height = gst_value_get_int_range_max (height);
+ return true;
+ } else if (idx == 3) {
+ r->width = gst_value_get_int_range_step (width);
+ r->height = gst_value_get_int_range_step (height);
+ if (r->width > 1 || r->height > 1)
+ return true;
+ else
+ return false;
+ }
+ } else if (wt == GST_TYPE_LIST && ht == GST_TYPE_LIST) {
+ GArray *wa = g_value_peek_pointer (width);
+ GArray *ha = g_value_peek_pointer (height);
+ if (idx < (int)(wa->len + 1))
+ w = &g_array_index (wa, GValue, SPA_MAX (idx-1, 0));
+ if (idx < (int)(ha->len + 1))
+ h = &g_array_index (ha, GValue, SPA_MAX (idx-1, 0));
+ }
+ if (w && h) {
+ r->width = g_value_get_int (w);
+ r->height = g_value_get_int (h);
+ return true;
+ }
+ return false;
+}
+
+static uint32_t
+get_range_type (const GValue *val)
+{
+ GType type = G_VALUE_TYPE (val);
+
+ if (type == GST_TYPE_LIST)
+ return SPA_CHOICE_Enum;
+ if (type == GST_TYPE_DOUBLE_RANGE || type == GST_TYPE_FRACTION_RANGE)
+ return SPA_CHOICE_Range;
+ if (type == GST_TYPE_INT_RANGE) {
+ if (gst_value_get_int_range_step (val) == 1)
+ return SPA_CHOICE_Range;
+ else
+ return SPA_CHOICE_Step;
+ }
+ if (type == GST_TYPE_INT64_RANGE) {
+ if (gst_value_get_int64_range_step (val) == 1)
+ return SPA_CHOICE_Range;
+ else
+ return SPA_CHOICE_Step;
+ }
+ return SPA_CHOICE_None;
+}
+
+static uint32_t
+get_range_type2 (const GValue *v1, const GValue *v2)
+{
+ uint32_t r1, r2;
+
+ r1 = get_range_type (v1);
+ r2 = get_range_type (v2);
+
+ if (r1 == r2)
+ return r1;
+ if (r1 == SPA_CHOICE_Step || r2 == SPA_CHOICE_Step)
+ return SPA_CHOICE_Step;
+ if (r1 == SPA_CHOICE_Range || r2 == SPA_CHOICE_Range)
+ return SPA_CHOICE_Range;
+ return SPA_CHOICE_Range;
+}
+
+static gboolean
+handle_video_fields (ConvertData *d)
+{
+ const GValue *value, *value2;
+ int i;
+ struct spa_pod_choice *choice;
+ struct spa_pod_frame f;
+
+ value = gst_structure_get_value (d->cs, "format");
+ if (value) {
+ const char *v;
+ int idx;
+ for (i = 0; (v = get_nth_string (value, i)); i++) {
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_format, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
+ }
+
+ idx = gst_video_format_from_string (v);
+ if (idx != GST_VIDEO_FORMAT_UNKNOWN && idx < (int)SPA_N_ELEMENTS (video_format_map))
+ spa_pod_builder_id (&d->b, video_format_map[idx]);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1)
+ choice->body.type = SPA_CHOICE_None;
+ }
+ }
+ value = gst_structure_get_value (d->cs, "width");
+ value2 = gst_structure_get_value (d->cs, "height");
+ if (value && value2) {
+ struct spa_rectangle v;
+ for (i = 0; get_nth_rectangle (value, value2, i, &v); i++) {
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_size, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type2 (value, value2), 0);
+ }
+
+ spa_pod_builder_rectangle (&d->b, v.width, v.height);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1)
+ choice->body.type = SPA_CHOICE_None;
+ }
+ }
+
+ value = gst_structure_get_value (d->cs, "framerate");
+ if (value) {
+ struct spa_fraction v;
+ for (i = 0; get_nth_fraction (value, i, &v); i++) {
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_framerate, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
+ }
+
+ spa_pod_builder_fraction (&d->b, v.num, v.denom);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1)
+ choice->body.type = SPA_CHOICE_None;
+ }
+ }
+
+ value = gst_structure_get_value (d->cs, "max-framerate");
+ if (value) {
+ struct spa_fraction v;
+ for (i = 0; get_nth_fraction (value, i, &v); i++) {
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_maxFramerate, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
+ }
+
+ spa_pod_builder_fraction (&d->b, v.num, v.denom);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1)
+ choice->body.type = SPA_CHOICE_None;
+ }
+ }
+ return TRUE;
+}
+
+static void
+set_default_channels (struct spa_pod_builder *b, uint32_t channels)
+{
+ uint32_t position[SPA_AUDIO_MAX_CHANNELS] = {0};
+ gboolean ok = TRUE;
+
+ switch (channels) {
+ case 8:
+ position[6] = SPA_AUDIO_CHANNEL_SL;
+ position[7] = SPA_AUDIO_CHANNEL_SR;
+ SPA_FALLTHROUGH
+ case 6:
+ position[5] = SPA_AUDIO_CHANNEL_LFE;
+ SPA_FALLTHROUGH
+ case 5:
+ position[4] = SPA_AUDIO_CHANNEL_FC;
+ SPA_FALLTHROUGH
+ case 4:
+ position[2] = SPA_AUDIO_CHANNEL_RL;
+ position[3] = SPA_AUDIO_CHANNEL_RR;
+ SPA_FALLTHROUGH
+ case 2:
+ position[0] = SPA_AUDIO_CHANNEL_FL;
+ position[1] = SPA_AUDIO_CHANNEL_FR;
+ break;
+ case 1:
+ position[0] = SPA_AUDIO_CHANNEL_MONO;
+ break;
+ default:
+ ok = FALSE;
+ break;
+ }
+
+ if (ok)
+ spa_pod_builder_add (b, SPA_FORMAT_AUDIO_position,
+ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, channels, position), 0);
+}
+
+static gboolean
+handle_audio_fields (ConvertData *d)
+{
+ const GValue *value;
+ struct spa_pod_choice *choice;
+ struct spa_pod_frame f;
+ int i = 0;
+
+ value = gst_structure_get_value (d->cs, "format");
+ if (value) {
+ const char *v;
+ int idx;
+ for (i = 0; (v = get_nth_string (value, i)); i++) {
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
+ }
+
+ idx = gst_audio_format_from_string (v);
+ if (idx < (int)SPA_N_ELEMENTS (audio_format_map))
+ spa_pod_builder_id (&d->b, audio_format_map[idx]);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1)
+ choice->body.type = SPA_CHOICE_None;
+ }
+ } else if (strcmp(d->type->name, "audio/x-mulaw") == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
+ spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ULAW);
+ } else if (strcmp(d->type->name, "audio/x-alaw") == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
+ spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ALAW);
+ } else if (strcmp(d->type->name, "audio/mpeg") == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
+ spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ENCODED);
+ } else if (strcmp(d->type->name, "audio/x-flac") == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
+ spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ENCODED);
+ }
+
+#if 0
+ value = gst_structure_get_value (d->cs, "layout");
+ if (value) {
+ const char *v;
+ for (i = 0; (v = get_nth_string (value, i)); i++) {
+ enum spa_audio_layout layout;
+
+ if (spa_streq(v, "interleaved"))
+ layout = SPA_AUDIO_LAYOUT_INTERLEAVED;
+ else if (spa_streq(v, "non-interleaved"))
+ layout = SPA_AUDIO_LAYOUT_NON_INTERLEAVED;
+ else
+ break;
+
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_layout, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
+ }
+
+ spa_pod_builder_id (&d->b, layout);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1)
+ choice->body.type = SPA_CHOICE_None;
+ }
+ }
+#endif
+ value = gst_structure_get_value (d->cs, "rate");
+ if (value) {
+ int v;
+ for (i = 0; get_nth_int (value, i, &v); i++) {
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_rate, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
+ }
+
+ spa_pod_builder_int (&d->b, v);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1)
+ choice->body.type = SPA_CHOICE_None;
+ }
+ }
+ value = gst_structure_get_value (d->cs, "channels");
+ if (value) {
+ int v;
+ for (i = 0; get_nth_int (value, i, &v); i++) {
+ if (i == 0) {
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_channels, 0);
+ spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
+ }
+
+ spa_pod_builder_int (&d->b, v);
+ }
+ if (i > 0) {
+ choice = spa_pod_builder_pop(&d->b, &f);
+ if (i == 1) {
+ choice->body.type = SPA_CHOICE_None;
+ set_default_channels (&d->b, v);
+ }
+ }
+ }
+ return TRUE;
+}
+
+static int
+builder_overflow (void *event_data, uint32_t size)
+{
+ struct spa_pod_builder *b = event_data;
+ b->size = SPA_ROUND_UP_N (size, 512);
+ b->data = realloc (b->data, b->size);
+ if (b->data == NULL)
+ return -errno;
+ return 0;
+}
+
+static const struct spa_pod_builder_callbacks builder_callbacks = {
+ SPA_VERSION_POD_BUILDER_CALLBACKS,
+ .overflow = builder_overflow
+};
+
+static struct spa_pod *
+convert_1 (ConvertData *d)
+{
+ struct spa_pod_frame f;
+
+ if (!(d->type = find_media_types (gst_structure_get_name (d->cs))))
+ return NULL;
+
+ spa_pod_builder_set_callbacks(&d->b, &builder_callbacks, &d->b);
+
+ spa_pod_builder_push_object (&d->b, &f, SPA_TYPE_OBJECT_Format, d->id);
+
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_mediaType, 0);
+ spa_pod_builder_id(&d->b, d->type->media_type);
+
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_mediaSubtype, 0);
+ spa_pod_builder_id(&d->b, d->type->media_subtype);
+
+ if (d->cf && gst_caps_features_contains (d->cf, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
+ struct spa_pod_frame f2;
+
+ spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_modifier,
+ (SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE));
+ spa_pod_builder_push_choice (&d->b, &f2, SPA_CHOICE_Enum, 0);
+ spa_pod_builder_long (&d->b, DRM_FORMAT_MOD_INVALID);
+ spa_pod_builder_long (&d->b, DRM_FORMAT_MOD_INVALID);
+ spa_pod_builder_long (&d->b, DRM_FORMAT_MOD_LINEAR);
+ spa_pod_builder_pop (&d->b, &f2);
+ }
+
+ if (d->type->media_type == SPA_MEDIA_TYPE_video)
+ handle_video_fields (d);
+ else if (d->type->media_type == SPA_MEDIA_TYPE_audio)
+ handle_audio_fields (d);
+
+ spa_pod_builder_pop (&d->b, &f);
+
+ return SPA_PTROFF (d->b.data, 0, struct spa_pod);
+}
+
+struct spa_pod *
+gst_caps_to_format (GstCaps *caps, guint index, uint32_t id)
+{
+ ConvertData d;
+ struct spa_pod *res;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
+ g_return_val_if_fail (gst_caps_is_fixed (caps), NULL);
+
+ spa_zero (d);
+ d.cf = gst_caps_get_features (caps, index);
+ d.cs = gst_caps_get_structure (caps, index);
+ d.id = id;
+
+ res = convert_1 (&d);
+
+ return res;
+}
+
+static gboolean
+foreach_func (GstCapsFeatures *features,
+ GstStructure *structure,
+ ConvertData *d)
+{
+ struct spa_pod *fmt;
+ int idx;
+
+ spa_zero(d->b);
+ d->cf = features;
+ d->cs = structure;
+
+ if (d->cf && gst_caps_features_contains (d->cf, GST_CAPS_FEATURE_MEMORY_DMABUF))
+ idx = 0;
+ else
+ idx = -1;
+
+ if ((fmt = convert_1 (d)))
+ g_ptr_array_insert (d->array, idx, fmt);
+
+ return TRUE;
+}
+
+
+GPtrArray *
+gst_caps_to_format_all (GstCaps *caps, uint32_t id)
+{
+ ConvertData d;
+
+ spa_zero (d);
+ d.id = id;
+ d.array = g_ptr_array_new_full (gst_caps_get_size (caps), (GDestroyNotify)g_free);
+
+ gst_caps_foreach (caps, (GstCapsForeachFunc) foreach_func, &d);
+
+ return d.array;
+}
+
+typedef const char *(*id_to_string_func)(uint32_t id);
+
+static const char *video_id_to_string(uint32_t id)
+{
+ int idx;
+ if ((idx = find_index(video_format_map, SPA_N_ELEMENTS(video_format_map), id)) == -1)
+ return NULL;
+ return gst_video_format_to_string(idx);
+}
+
+static const char *audio_id_to_string(uint32_t id)
+{
+ int idx;
+ if ((idx = find_index(audio_format_map, SPA_N_ELEMENTS(audio_format_map), id)) == -1)
+ return NULL;
+ return gst_audio_format_to_string(idx);
+}
+
+static void
+handle_id_prop (const struct spa_pod_prop *prop, const char *key, id_to_string_func func, GstCaps *res)
+{
+ const char * str;
+ struct spa_pod *val;
+ uint32_t *id;
+ uint32_t i, n_items, choice;
+
+ val = spa_pod_get_values(&prop->value, &n_items, &choice);
+ if (val->type != SPA_TYPE_Id)
+ return;
+
+ id = SPA_POD_BODY(val);
+
+ switch (choice) {
+ case SPA_CHOICE_None:
+ if (!(str = func(id[0])))
+ return;
+ gst_caps_set_simple (res, key, G_TYPE_STRING, str, NULL);
+ break;
+ case SPA_CHOICE_Enum:
+ {
+ GValue list = { 0 }, v = { 0 };
+
+ g_value_init (&list, GST_TYPE_LIST);
+ for (i = 1; i < n_items; i++) {
+ if (!(str = func(id[i])))
+ continue;
+
+ g_value_init (&v, G_TYPE_STRING);
+ g_value_set_string (&v, str);
+ gst_value_list_append_and_take_value (&list, &v);
+ }
+ gst_caps_set_value (res, key, &list);
+ g_value_unset (&list);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void
+handle_int_prop (const struct spa_pod_prop *prop, const char *key, GstCaps *res)
+{
+ struct spa_pod *val;
+ uint32_t *ints;
+ uint32_t i, n_items, choice;
+
+ val = spa_pod_get_values(&prop->value, &n_items, &choice);
+ if (val->type != SPA_TYPE_Int)
+ return;
+
+ ints = SPA_POD_BODY(val);
+
+ switch (choice) {
+ case SPA_CHOICE_None:
+ gst_caps_set_simple (res, key, G_TYPE_INT, ints[0], NULL);
+ break;
+ case SPA_CHOICE_Range:
+ case SPA_CHOICE_Step:
+ {
+ if (n_items < 3)
+ return;
+ gst_caps_set_simple (res, key, GST_TYPE_INT_RANGE, ints[1], ints[2], NULL);
+ break;
+ }
+ case SPA_CHOICE_Enum:
+ {
+ GValue list = { 0 }, v = { 0 };
+
+ g_value_init (&list, GST_TYPE_LIST);
+ for (i = 1; i < n_items; i++) {
+ g_value_init (&v, G_TYPE_INT);
+ g_value_set_int (&v, ints[i]);
+ gst_value_list_append_and_take_value (&list, &v);
+ }
+ gst_caps_set_value (res, key, &list);
+ g_value_unset (&list);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void
+handle_rect_prop (const struct spa_pod_prop *prop, const char *width, const char *height, GstCaps *res)
+{
+ struct spa_pod *val;
+ struct spa_rectangle *rect;
+ uint32_t i, n_items, choice;
+
+ val = spa_pod_get_values(&prop->value, &n_items, &choice);
+ if (val->type != SPA_TYPE_Rectangle)
+ return;
+
+ rect = SPA_POD_BODY(val);
+
+ switch (choice) {
+ case SPA_CHOICE_None:
+ gst_caps_set_simple (res, width, G_TYPE_INT, rect[0].width,
+ height, G_TYPE_INT, rect[0].height, NULL);
+ break;
+ case SPA_CHOICE_Range:
+ case SPA_CHOICE_Step:
+ {
+ if (n_items < 3)
+ return;
+ gst_caps_set_simple (res, width, GST_TYPE_INT_RANGE, rect[1].width, rect[2].width,
+ height, GST_TYPE_INT_RANGE, rect[1].height, rect[2].height, NULL);
+ break;
+ }
+ case SPA_CHOICE_Enum:
+ {
+ GValue l1 = { 0 }, l2 = { 0 }, v1 = { 0 }, v2 = { 0 };
+
+ g_value_init (&l1, GST_TYPE_LIST);
+ g_value_init (&l2, GST_TYPE_LIST);
+ for (i = 1; i < n_items; i++) {
+ g_value_init (&v1, G_TYPE_INT);
+ g_value_set_int (&v1, rect[i].width);
+ gst_value_list_append_and_take_value (&l1, &v1);
+
+ g_value_init (&v2, G_TYPE_INT);
+ g_value_set_int (&v2, rect[i].height);
+ gst_value_list_append_and_take_value (&l2, &v2);
+ }
+ gst_caps_set_value (res, width, &l1);
+ gst_caps_set_value (res, height, &l2);
+ g_value_unset (&l1);
+ g_value_unset (&l2);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void
+handle_fraction_prop (const struct spa_pod_prop *prop, const char *key, GstCaps *res)
+{
+ struct spa_pod *val;
+ struct spa_fraction *fract;
+ uint32_t i, n_items, choice;
+
+ val = spa_pod_get_values(&prop->value, &n_items, &choice);
+ if (val->type != SPA_TYPE_Fraction)
+ return;
+
+ fract = SPA_POD_BODY(val);
+
+ switch (choice) {
+ case SPA_CHOICE_None:
+ gst_caps_set_simple (res, key, GST_TYPE_FRACTION, fract[0].num, fract[0].denom, NULL);
+ break;
+ case SPA_CHOICE_Range:
+ case SPA_CHOICE_Step:
+ {
+ if (n_items < 3)
+ return;
+ gst_caps_set_simple (res, key, GST_TYPE_FRACTION_RANGE, fract[1].num, fract[1].denom,
+ fract[2].num, fract[2].denom, NULL);
+ break;
+ }
+ case SPA_CHOICE_Enum:
+ {
+ GValue l1 = { 0 }, v1 = { 0 };
+
+ g_value_init (&l1, GST_TYPE_LIST);
+ for (i = 1; i < n_items; i++) {
+ g_value_init (&v1, GST_TYPE_FRACTION);
+ gst_value_set_fraction (&v1, fract[i].num, fract[i].denom);
+ gst_value_list_append_and_take_value (&l1, &v1);
+ }
+ gst_caps_set_value (res, key, &l1);
+ g_value_unset (&l1);
+ break;
+ }
+ default:
+ break;
+ }
+}
+GstCaps *
+gst_caps_from_format (const struct spa_pod *format)
+{
+ GstCaps *res = NULL;
+ uint32_t media_type, media_subtype;
+ const struct spa_pod_prop *prop = NULL;
+ const struct spa_pod_object *obj = (const struct spa_pod_object *) format;
+
+ if (spa_format_parse(format, &media_type, &media_subtype) < 0)
+ return res;
+
+ if (media_type == SPA_MEDIA_TYPE_video) {
+ if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
+ res = gst_caps_new_empty_simple ("video/x-raw");
+ if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_VIDEO_format))) {
+ handle_id_prop (prop, "format", video_id_to_string, res);
+ }
+ }
+ else if (media_subtype == SPA_MEDIA_SUBTYPE_mjpg) {
+ res = gst_caps_new_empty_simple ("image/jpeg");
+ }
+ else if (media_subtype == SPA_MEDIA_SUBTYPE_h264) {
+ res = gst_caps_new_simple ("video/x-h264",
+ "stream-format", G_TYPE_STRING, "byte-stream",
+ "alignment", G_TYPE_STRING, "au",
+ NULL);
+ } else {
+ return NULL;
+ }
+ if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_VIDEO_size))) {
+ handle_rect_prop (prop, "width", "height", res);
+ }
+ if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_VIDEO_framerate))) {
+ handle_fraction_prop (prop, "framerate", res);
+ }
+ if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_VIDEO_maxFramerate))) {
+ handle_fraction_prop (prop, "max-framerate", res);
+ }
+ } else if (media_type == SPA_MEDIA_TYPE_audio) {
+ if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
+ res = gst_caps_new_simple ("audio/x-raw",
+ "layout", G_TYPE_STRING, "interleaved",
+ NULL);
+ if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_AUDIO_format))) {
+ handle_id_prop (prop, "format", audio_id_to_string, res);
+ }
+ if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_AUDIO_rate))) {
+ handle_int_prop (prop, "rate", res);
+ }
+ if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_AUDIO_channels))) {
+ handle_int_prop (prop, "channels", res);
+ }
+ }
+ }
+ return res;
+}