pipewire/pinos/gst/gstpinosformat.c
Wim Taymans db16de85bb examples: add video upload example
Add an example of a node that makes a video available.
Improve buffer reuse in stream.
Add more video formats
2017-04-18 18:05:45 +02:00

834 lines
23 KiB
C

/* GStreamer
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/audio/audio.h>
#include <spa/lib/mapper.h>
#include <spa/include/spa/format-builder.h>
#include <spa/include/spa/video/format-utils.h>
#include <spa/include/spa/audio/format-utils.h>
#include "gstpinosformat.h"
typedef struct {
const char *name;
uint32_t *media_type;
uint32_t *media_subtype;
} MediaType;
static struct {
SpaTypeMap *map;
uint32_t format;
SpaTypeMediaType media_type;
SpaTypeMediaSubtype media_subtype;
SpaTypeMediaSubtypeVideo media_subtype_video;
SpaTypeMediaSubtypeAudio media_subtype_audio;
SpaTypeFormatVideo format_video;
SpaTypeFormatAudio format_audio;
SpaTypeVideoFormat video_format;
SpaTypeAudioFormat audio_format;
} type = { NULL, };
static void
ensure_types (void)
{
SpaTypeMap *map = type.map = spa_type_map_get_default ();
type.format = spa_type_map_get_id (map, SPA_TYPE__Format);
spa_type_media_type_map (map, &type.media_type);
spa_type_media_subtype_map (map, &type.media_subtype);
spa_type_media_subtype_video_map (map, &type.media_subtype_video);
spa_type_media_subtype_audio_map (map, &type.media_subtype_audio);
spa_type_format_video_map (map, &type.format_video);
spa_type_format_audio_map (map, &type.format_audio);
spa_type_video_format_map (map, &type.video_format);
spa_type_audio_format_map (map, &type.audio_format);
}
static const MediaType media_type_map[] = {
{ "video/x-raw", &type.media_type.video, &type.media_subtype.raw },
{ "audio/x-raw", &type.media_type.audio, &type.media_subtype.raw },
{ "image/jpeg", &type.media_type.video, &type.media_subtype_video.mjpg },
{ "video/x-h264", &type.media_type.video, &type.media_subtype_video.h264 },
{ NULL, }
};
static const uint32_t *video_format_map[] = {
&type.video_format.UNKNOWN,
&type.video_format.ENCODED,
&type.video_format.I420,
&type.video_format.YV12,
&type.video_format.YUY2,
&type.video_format.UYVY,
&type.video_format.AYUV,
&type.video_format.RGBx,
&type.video_format.BGRx,
&type.video_format.xRGB,
&type.video_format.xBGR,
&type.video_format.RGBA,
&type.video_format.BGRA,
&type.video_format.ARGB,
&type.video_format.ABGR,
&type.video_format.RGB,
&type.video_format.BGR,
&type.video_format.Y41B,
&type.video_format.Y42B,
&type.video_format.YVYU,
&type.video_format.Y444,
&type.video_format.v210,
&type.video_format.v216,
&type.video_format.NV12,
&type.video_format.NV21,
&type.video_format.GRAY8,
&type.video_format.GRAY16_BE,
&type.video_format.GRAY16_LE,
&type.video_format.v308,
&type.video_format.RGB16,
&type.video_format.BGR16,
&type.video_format.RGB15,
&type.video_format.BGR15,
&type.video_format.UYVP,
&type.video_format.A420,
&type.video_format.RGB8P,
&type.video_format.YUV9,
&type.video_format.YVU9,
&type.video_format.IYU1,
&type.video_format.ARGB64,
&type.video_format.AYUV64,
&type.video_format.r210,
&type.video_format.I420_10BE,
&type.video_format.I420_10LE,
&type.video_format.I422_10BE,
&type.video_format.I422_10LE,
&type.video_format.Y444_10BE,
&type.video_format.Y444_10LE,
&type.video_format.GBR,
&type.video_format.GBR_10BE,
&type.video_format.GBR_10LE,
&type.video_format.NV16,
&type.video_format.NV24,
&type.video_format.NV12_64Z32,
&type.video_format.A420_10BE,
&type.video_format.A420_10LE,
&type.video_format.A422_10BE,
&type.video_format.A422_10LE,
&type.video_format.A444_10BE,
&type.video_format.A444_10LE,
&type.video_format.NV61,
&type.video_format.P010_10BE,
&type.video_format.P010_10LE,
&type.video_format.IYU2,
&type.video_format.VYUY,
&type.video_format.GBRA,
&type.video_format.GBRA_10BE,
&type.video_format.GBRA_10LE,
&type.video_format.GBR_12BE,
&type.video_format.GBR_12LE,
&type.video_format.GBRA_12BE,
&type.video_format.GBRA_12LE,
&type.video_format.I420_12BE,
&type.video_format.I420_12LE,
&type.video_format.I422_12BE,
&type.video_format.I422_12LE,
&type.video_format.Y444_12BE,
&type.video_format.Y444_12LE,
};
#if __BYTE_ORDER == __BIG_ENDIAN
#define _FORMAT_LE(fmt) &type.audio_format. fmt ## _OE
#define _FORMAT_BE(fmt) &type.audio_format. fmt
#elif __BYTE_ORDER == __LITTLE_ENDIAN
#define _FORMAT_LE(fmt) &type.audio_format. fmt
#define _FORMAT_BE(fmt) &type.audio_format. fmt ## _OE
#endif
static const uint32_t *audio_format_map[] = {
&type.audio_format.UNKNOWN,
&type.audio_format.ENCODED,
&type.audio_format.S8,
&type.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 {
SpaPODBuilder b;
const MediaType *type;
const GstCapsFeatures *cf;
const GstStructure *cs;
} ConvertData;
static const MediaType *
find_media_types (const char *name)
{
int i;
for (i = 0; media_type_map[i].name; i++) {
if (!strcmp (media_type_map[i].name, name))
return &media_type_map[i];
}
return NULL;
}
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 < 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 < 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, SpaFraction *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 < 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, SpaRectangle *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 (wt == GST_TYPE_LIST && ht == GST_TYPE_LIST) {
GArray *wa = g_value_peek_pointer (width);
GArray *ha = g_value_peek_pointer (height);
if (idx < wa->len + 1)
w = &g_array_index (wa, GValue, SPA_MAX (idx-1, 0));
if (idx < 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 const uint32_t
get_range_type (const GValue *val)
{
GType type = G_VALUE_TYPE (val);
if (type == GST_TYPE_LIST)
return SPA_POD_PROP_RANGE_ENUM;
if (type == GST_TYPE_DOUBLE_RANGE || type == GST_TYPE_FRACTION_RANGE)
return SPA_POD_PROP_RANGE_MIN_MAX;
if (type == GST_TYPE_INT_RANGE) {
if (gst_value_get_int_range_step (val) == 1)
return SPA_POD_PROP_RANGE_MIN_MAX;
else
return SPA_POD_PROP_RANGE_STEP;
}
if (type == GST_TYPE_INT64_RANGE) {
if (gst_value_get_int64_range_step (val) == 1)
return SPA_POD_PROP_RANGE_MIN_MAX;
else
return SPA_POD_PROP_RANGE_STEP;
}
return SPA_POD_PROP_RANGE_NONE;
}
static const 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_POD_PROP_RANGE_STEP || r2 == SPA_POD_PROP_RANGE_STEP)
return SPA_POD_PROP_RANGE_STEP;
if (r1 == SPA_POD_PROP_RANGE_MIN_MAX || r2 == SPA_POD_PROP_RANGE_MIN_MAX)
return SPA_POD_PROP_RANGE_MIN_MAX;
return SPA_POD_PROP_RANGE_MIN_MAX;
}
static gboolean
handle_video_fields (ConvertData *d)
{
SpaPODFrame f;
const GValue *value, *value2;
int i;
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_push_prop (&d->b, &f,
type.format_video.format,
get_range_type (value));
idx = gst_video_format_from_string (v);
if (idx < SPA_N_ELEMENTS (video_format_map))
spa_pod_builder_id (&d->b, *video_format_map[idx]);
}
if (i > 1)
SPA_POD_BUILDER_DEREF (&d->b, f.ref, SpaPODProp)->body.flags |= SPA_POD_PROP_FLAG_UNSET;
spa_pod_builder_pop (&d->b, &f);
}
value = gst_structure_get_value (d->cs, "width");
value2 = gst_structure_get_value (d->cs, "height");
if (value || value2) {
SpaRectangle v;
for (i = 0; get_nth_rectangle (value, value2, i, &v); i++) {
if (i == 0)
spa_pod_builder_push_prop (&d->b, &f,
type.format_video.size,
get_range_type2 (value, value2));
spa_pod_builder_rectangle (&d->b, v.width, v.height);
}
if (i > 1)
SPA_POD_BUILDER_DEREF (&d->b, f.ref, SpaPODProp)->body.flags |= SPA_POD_PROP_FLAG_UNSET;
spa_pod_builder_pop (&d->b, &f);
}
value = gst_structure_get_value (d->cs, "framerate");
if (value) {
SpaFraction v;
for (i = 0; get_nth_fraction (value, i, &v); i++) {
if (i == 0)
spa_pod_builder_push_prop (&d->b, &f,
type.format_video.framerate,
get_range_type (value));
spa_pod_builder_fraction (&d->b, v.num, v.denom);
}
if (i > 1)
SPA_POD_BUILDER_DEREF (&d->b, f.ref, SpaPODProp)->body.flags |= SPA_POD_PROP_FLAG_UNSET;
spa_pod_builder_pop (&d->b, &f);
}
return TRUE;
}
static gboolean
handle_audio_fields (ConvertData *d)
{
SpaPODFrame f;
const GValue *value;
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_push_prop (&d->b, &f,
type.format_audio.format,
get_range_type (value));
idx = gst_audio_format_from_string (v);
if (idx < SPA_N_ELEMENTS (audio_format_map))
spa_pod_builder_id (&d->b, *audio_format_map[idx]);
}
if (i > 1)
SPA_POD_BUILDER_DEREF (&d->b, f.ref, SpaPODProp)->body.flags |= SPA_POD_PROP_FLAG_UNSET;
spa_pod_builder_pop (&d->b, &f);
}
value = gst_structure_get_value (d->cs, "layout");
if (value) {
const char *v;
for (i = 0; (v = get_nth_string (value, i)); i++) {
SpaAudioLayout layout;
if (!strcmp (v, "interleaved"))
layout = SPA_AUDIO_LAYOUT_INTERLEAVED;
else if (!strcmp (v, "non-interleaved"))
layout = SPA_AUDIO_LAYOUT_NON_INTERLEAVED;
else
break;
if (i == 0)
spa_pod_builder_push_prop (&d->b, &f,
type.format_audio.layout,
get_range_type (value));
spa_pod_builder_int (&d->b, layout);
}
if (i > 1)
SPA_POD_BUILDER_DEREF (&d->b, f.ref, SpaPODProp)->body.flags |= SPA_POD_PROP_FLAG_UNSET;
spa_pod_builder_pop (&d->b, &f);
}
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_push_prop (&d->b, &f,
type.format_audio.rate,
get_range_type (value));
spa_pod_builder_int (&d->b, v);
}
if (i > 1)
SPA_POD_BUILDER_DEREF (&d->b, f.ref, SpaPODProp)->body.flags |= SPA_POD_PROP_FLAG_UNSET;
spa_pod_builder_pop (&d->b, &f);
}
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_push_prop (&d->b, &f,
type.format_audio.channels,
get_range_type (value));
spa_pod_builder_int (&d->b, v);
}
if (i > 1)
SPA_POD_BUILDER_DEREF (&d->b, f.ref, SpaPODProp)->body.flags |= SPA_POD_PROP_FLAG_UNSET;
spa_pod_builder_pop (&d->b, &f);
}
return TRUE;
}
static uint32_t
write_pod (SpaPODBuilder *b, uint32_t ref, const void *data, uint32_t size)
{
if (ref == -1)
ref = b->offset;
if (b->size <= b->offset) {
b->size = SPA_ROUND_UP_N (b->offset + size, 512);
b->data = realloc (b->data, b->size);
}
memcpy (b->data + ref, data, size);
return ref;
}
static SpaFormat *
convert_1 (GstCapsFeatures *cf, GstStructure *cs)
{
ConvertData d;
SpaPODFrame f;
spa_zero (d);
d.cf = cf;
d.cs = cs;
if (!(d.type = find_media_types (gst_structure_get_name (cs))))
return NULL;
d.b.write = write_pod;
spa_pod_builder_push_format (&d.b, &f, type.format,
*d.type->media_type,
*d.type->media_subtype);
if (*d.type->media_type == type.media_type.video)
handle_video_fields (&d);
else if (*d.type->media_type == type.media_type.audio)
handle_audio_fields (&d);
spa_pod_builder_pop (&d.b, &f);
return SPA_MEMBER (d.b.data, 0, SpaFormat);
}
SpaFormat *
gst_caps_to_format (GstCaps *caps, guint index)
{
GstCapsFeatures *f;
GstStructure *s;
SpaFormat *res;
g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
g_return_val_if_fail (gst_caps_is_fixed (caps), NULL);
ensure_types();
f = gst_caps_get_features (caps, index);
s = gst_caps_get_structure (caps, index);
res = convert_1 (f, s);
return res;
}
static gboolean
foreach_func (GstCapsFeatures *features,
GstStructure *structure,
GPtrArray *array)
{
SpaFormat *fmt;
if ((fmt = convert_1 (features, structure)))
g_ptr_array_insert (array, -1, fmt);
return TRUE;
}
GPtrArray *
gst_caps_to_format_all (GstCaps *caps)
{
GPtrArray *res;
ensure_types();
res = g_ptr_array_new_full (gst_caps_get_size (caps), (GDestroyNotify)g_free);
gst_caps_foreach (caps, (GstCapsForeachFunc) foreach_func, res);
return res;
}
static void
handle_id_prop (SpaPODProp *prop, const char *key, GstCaps *res)
{
const char * str;
uint32_t *id = SPA_POD_CONTENTS (SpaPODProp, prop);
uint32_t i, n_items = SPA_POD_PROP_N_VALUES (prop);
uint32_t flags;
flags = prop->body.flags;
if (!(flags & SPA_POD_PROP_FLAG_UNSET))
flags &= ~SPA_POD_PROP_RANGE_MASK;
switch (flags) {
case SPA_POD_PROP_RANGE_NONE:
if (!(str = spa_type_map_get_type (type.map, id[0])))
return;
gst_caps_set_simple (res, key, G_TYPE_STRING, rindex (str, ':') + 1, NULL);
break;
case SPA_POD_PROP_RANGE_ENUM:
{
GValue list = { 0 }, v = { 0 };
g_value_init (&list, GST_TYPE_LIST);
for (i = 1; i < n_items; i++) {
if (!(str = spa_type_map_get_type (type.map, id[i])))
continue;
g_value_init (&v, G_TYPE_STRING);
g_value_set_string (&v, rindex (str, ':') + 1);
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 (SpaPODProp *prop, const char *key, GstCaps *res)
{
uint32_t *val = SPA_POD_CONTENTS (SpaPODProp, prop);
uint32_t i, n_items = SPA_POD_PROP_N_VALUES (prop);
uint32_t flags;
flags = prop->body.flags;
if (!(flags & SPA_POD_PROP_FLAG_UNSET))
flags &= ~SPA_POD_PROP_RANGE_MASK;
switch (flags) {
case SPA_POD_PROP_RANGE_NONE:
gst_caps_set_simple (res, key, G_TYPE_INT, val[0], NULL);
break;
case SPA_POD_PROP_RANGE_MIN_MAX:
case SPA_POD_PROP_RANGE_STEP:
{
if (n_items < 3)
return;
gst_caps_set_simple (res, key, GST_TYPE_INT_RANGE, val[1], val[2], NULL);
break;
}
case SPA_POD_PROP_RANGE_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, val[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 (SpaPODProp *prop, const char *width, const char *height, GstCaps *res)
{
SpaRectangle *rect = SPA_POD_CONTENTS (SpaPODProp, prop);
uint32_t i, n_items = SPA_POD_PROP_N_VALUES (prop);
uint32_t flags;
flags = prop->body.flags;
if (!(flags & SPA_POD_PROP_FLAG_UNSET))
flags &= ~SPA_POD_PROP_RANGE_MASK;
switch (flags) {
case SPA_POD_PROP_RANGE_NONE:
gst_caps_set_simple (res, width, G_TYPE_INT, rect[0].width,
height, G_TYPE_INT, rect[0].height, NULL);
break;
case SPA_POD_PROP_RANGE_MIN_MAX:
case SPA_POD_PROP_RANGE_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_POD_PROP_RANGE_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 (SpaPODProp *prop, const char *key, GstCaps *res)
{
SpaFraction *fract = SPA_POD_CONTENTS (SpaPODProp, prop);
uint32_t i, n_items = SPA_POD_PROP_N_VALUES (prop);
uint32_t flags;
flags = prop->body.flags;
if (!(flags & SPA_POD_PROP_FLAG_UNSET))
flags &= ~SPA_POD_PROP_RANGE_MASK;
switch (flags) {
case SPA_POD_PROP_RANGE_NONE:
gst_caps_set_simple (res, key, GST_TYPE_FRACTION, fract[0].num, fract[0].denom, NULL);
break;
case SPA_POD_PROP_RANGE_MIN_MAX:
case SPA_POD_PROP_RANGE_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_POD_PROP_RANGE_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 SpaFormat *format)
{
GstCaps *res = NULL;
uint32_t media_type, media_subtype;
SpaPODProp *prop;
ensure_types();
media_type = SPA_FORMAT_MEDIA_TYPE (format);
media_subtype = SPA_FORMAT_MEDIA_SUBTYPE (format);
if (media_type == type.media_type.video) {
if (media_subtype == type.media_subtype.raw) {
res = gst_caps_new_empty_simple ("video/x-raw");
if ((prop = spa_format_find_prop (format, type.format_video.format))) {
handle_id_prop (prop, "format", res);
}
}
else if (media_subtype == type.media_subtype_video.mjpg) {
res = gst_caps_new_empty_simple ("image/jpeg");
}
else if (media_subtype == type.media_subtype_video.h264) {
res = gst_caps_new_simple ("video/x-h264",
"stream-format", G_TYPE_STRING, "byte-stream",
"alignment", G_TYPE_STRING, "au",
NULL);
}
if ((prop = spa_format_find_prop (format, type.format_video.size))) {
handle_rect_prop (prop, "width", "height", res);
}
if ((prop = spa_format_find_prop (format, type.format_video.framerate))) {
handle_fraction_prop (prop, "framerate", res);
}
} else if (media_type == type.media_type.audio) {
if (media_subtype == type.media_subtype.raw) {
res = gst_caps_new_simple ("audio/x-raw",
"layout", G_TYPE_STRING, "interleaved",
NULL);
if ((prop = spa_format_find_prop (format, type.format_audio.format))) {
handle_id_prop (prop, "format", res);
}
if ((prop = spa_format_find_prop (format, type.format_audio.rate))) {
handle_int_prop (prop, "rate", res);
}
if ((prop = spa_format_find_prop (format, type.format_audio.channels))) {
handle_int_prop (prop, "channels", res);
}
}
else if (media_subtype == type.media_subtype_audio.aac) {
}
}
return res;
}