gststructure.c 87 KB
Newer Older
David Schleef's avatar
David Schleef committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* GStreamer
 * Copyright (C) 2003 David A. Schleef <ds@schleef.org>
 *
 * gststructure.c: lists of { GQuark, GValue } tuples
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
21

Stefan Kost's avatar
Stefan Kost committed
22
23
24
/**
 * SECTION:gststructure
 * @short_description: Generic structure containing fields of names and values
25
 * @see_also: #GstCaps, #GstMessage, #GstEvent, #GstQuery
Stefan Kost's avatar
Stefan Kost committed
26
 *
27
28
 * A #GstStructure is a collection of key/value pairs. The keys are expressed
 * as GQuarks and the values can be of any GType.
29
 *
30
31
 * In addition to the key/value pairs, a #GstStructure also has a name. The name
 * starts with a letter and can be folled by letters, numbers and any of "/-_.:".
32
 *
33
34
35
36
37
38
 * #GstStructure is used by various GStreamer subsystems to store information
 * in a flexible and extensible way. A #GstStructure does not have a refcount
 * because it usually is part of a higher level object such as #GstCaps. It
 * provides a means to enforce mutability using the refcount of the parent
 * with the gst_structure_set_parent_refcount() method.
 *
Wim Taymans's avatar
Wim Taymans committed
39
 * A #GstStructure can be created with gst_structure_new_empty() or
40
41
 * gst_structure_new(), which both take a name and an optional set of
 * key/value pairs along with the types of the values.
42
 *
43
44
 * Field values can be changed with gst_structure_set_value() or
 * gst_structure_set().
45
 *
46
47
 * Field values can be retrieved with gst_structure_get_value() or the more
 * convenient gst_structure_get_*() functions.
48
 *
49
50
 * Fields can be removed with gst_structure_remove_field() or
 * gst_structure_remove_fields().
51
 *
52
53
54
55
 * Strings in structures must be ASCII or UTF-8 encoded. Other encodings are
 * not allowed. Strings must not be empty either, but may be NULL.
 *
 * Last reviewed on 2009-06-08 (0.10.23)
Stefan Kost's avatar
Stefan Kost committed
56
 */
David Schleef's avatar
David Schleef committed
57
58
59
60
61
62
63

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

64
#include "gst_private.h"
65
#include "gstquark.h"
David Schleef's avatar
David Schleef committed
66
67
68
#include <gst/gst.h>
#include <gobject/gvaluecollector.h>

David Schleef's avatar
David Schleef committed
69
typedef struct _GstStructureField GstStructureField;
70

71
72
struct _GstStructureField
{
David Schleef's avatar
David Schleef committed
73
74
75
76
  GQuark name;
  GValue value;
};

Wim Taymans's avatar
Wim Taymans committed
77
78
79
80
81
82
83
84
85
86
87
88
89
typedef struct
{
  GstStructure s;

  /* owned by parent structure, NULL if no parent */
  gint *parent_refcount;

  GArray *fields;
} GstStructureImpl;

#define GST_STRUCTURE_REFCOUNT(s) (((GstStructureImpl*)(s))->parent_refcount)
#define GST_STRUCTURE_FIELDS(s) (((GstStructureImpl*)(s))->fields)

David Schleef's avatar
David Schleef committed
90
#define GST_STRUCTURE_FIELD(structure, index) \
Wim Taymans's avatar
Wim Taymans committed
91
    &g_array_index(GST_STRUCTURE_FIELDS(structure), GstStructureField, (index))
David Schleef's avatar
David Schleef committed
92

93
#define IS_MUTABLE(structure) \
Wim Taymans's avatar
Wim Taymans committed
94
95
    (!GST_STRUCTURE_REFCOUNT(structure) || \
     g_atomic_int_get (GST_STRUCTURE_REFCOUNT(structure)) == 1)
96

97
98
99
#define IS_TAGLIST(structure) \
    (structure->name == GST_QUARK (TAGLIST))

100
101
102
103
104
105
106
107
108
109
110
111
112
static void gst_structure_set_field (GstStructure * structure,
    GstStructureField * field);
static GstStructureField *gst_structure_get_field (const GstStructure *
    structure, const gchar * fieldname);
static GstStructureField *gst_structure_id_get_field (const GstStructure *
    structure, GQuark field);
static void gst_structure_transform_to_string (const GValue * src_value,
    GValue * dest_value);
static GstStructure *gst_structure_copy_conditional (const GstStructure *
    structure);
static gboolean gst_structure_parse_value (gchar * str, gchar ** after,
    GValue * value, GType default_type);
static gboolean gst_structure_parse_simple_string (gchar * s, gchar ** end);
David Schleef's avatar
David Schleef committed
113

Wim Taymans's avatar
Wim Taymans committed
114
GType _gst_structure_type = 0;
115

Johan Dahlin's avatar
Johan Dahlin committed
116
117
118
119

G_DEFINE_BOXED_TYPE (GstStructure, gst_structure,
    gst_structure_copy_conditional, gst_structure_free);

Wim Taymans's avatar
Wim Taymans committed
120
void
121
_priv_gst_structure_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
122
{
Johan Dahlin's avatar
Johan Dahlin committed
123
  _gst_structure_type = gst_structure_get_type ();
124

Wim Taymans's avatar
Wim Taymans committed
125
126
  g_value_register_transform_func (_gst_structure_type, G_TYPE_STRING,
      gst_structure_transform_to_string);
David Schleef's avatar
David Schleef committed
127
128
}

129
static GstStructure *
Wim Taymans's avatar
Wim Taymans committed
130
gst_structure_new_id_empty_with_size (GQuark quark, guint prealloc)
131
{
Wim Taymans's avatar
Wim Taymans committed
132
  GstStructureImpl *structure;
133

Wim Taymans's avatar
Wim Taymans committed
134
135
136
137
138
  structure = g_slice_new (GstStructureImpl);
  ((GstStructure *) structure)->type = _gst_structure_type;
  ((GstStructure *) structure)->name = quark;
  GST_STRUCTURE_REFCOUNT (structure) = NULL;
  GST_STRUCTURE_FIELDS (structure) =
139
      g_array_sized_new (FALSE, FALSE, sizeof (GstStructureField), prealloc);
140

Wim Taymans's avatar
Wim Taymans committed
141
142
  GST_TRACE ("created structure %p", structure);

Wim Taymans's avatar
Wim Taymans committed
143
  return GST_STRUCTURE_CAST (structure);
144
145
}

Benjamin Otte's avatar
Benjamin Otte committed
146
/**
Wim Taymans's avatar
Wim Taymans committed
147
 * gst_structure_new_id_empty:
148
 * @quark: name of new structure
Benjamin Otte's avatar
Benjamin Otte committed
149
 *
150
 * Creates a new, empty #GstStructure with the given name as a GQuark.
Benjamin Otte's avatar
Benjamin Otte committed
151
 *
152
153
154
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new, empty #GstStructure
Benjamin Otte's avatar
Benjamin Otte committed
155
 */
156
GstStructure *
Wim Taymans's avatar
Wim Taymans committed
157
gst_structure_new_id_empty (GQuark quark)
Benjamin Otte's avatar
Benjamin Otte committed
158
{
159
  g_return_val_if_fail (quark != 0, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
160

Wim Taymans's avatar
Wim Taymans committed
161
  return gst_structure_new_id_empty_with_size (quark, 0);
Benjamin Otte's avatar
Benjamin Otte committed
162
163
}

164
#ifndef G_DISABLE_CHECKS
165
166
167
168
169
170
171
static gboolean
gst_structure_validate_name (const gchar * name)
{
  const gchar *s;

  g_return_val_if_fail (name != NULL, FALSE);

Wim Taymans's avatar
Wim Taymans committed
172
  if (G_UNLIKELY (!g_ascii_isalpha (*name))) {
173
174
175
176
177
178
179
    GST_WARNING ("Invalid character '%c' at offset 0 in structure name: %s",
        *name, name);
    return FALSE;
  }

  /* FIXME: test name string more */
  s = &name[1];
180
  while (*s && (g_ascii_isalnum (*s) || strchr ("/-_.:+", *s) != NULL))
181
    s++;
182
  if (G_UNLIKELY (*s != '\0')) {
183
184
    GST_WARNING ("Invalid character '%c' at offset %" G_GUINTPTR_FORMAT " in"
        " structure name: %s", *s, ((guintptr) s - (guintptr) name), name);
185
186
187
    return FALSE;
  }

188
189
190
191
192
193
194
195
  if (strncmp (name, "video/x-raw-", 12) == 0) {
    g_warning ("0.10-style raw video caps are being created. Should be "
        "video/x-raw,format=(string).. now.");
  } else if (strncmp (name, "audio/x-raw-", 12) == 0) {
    g_warning ("0.10-style raw audio caps are being created. Should be "
        "audio/x-raw,format=(string).. now.");
  }

196
197
  return TRUE;
}
198
#endif
199

David Schleef's avatar
David Schleef committed
200
/**
Wim Taymans's avatar
Wim Taymans committed
201
 * gst_structure_new_empty:
David Schleef's avatar
David Schleef committed
202
203
 * @name: name of new structure
 *
204
205
206
 * Creates a new, empty #GstStructure with the given @name.
 *
 * See gst_structure_set_name() for constraints on the @name parameter.
David Schleef's avatar
David Schleef committed
207
 *
208
209
210
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new, empty #GstStructure
David Schleef's avatar
David Schleef committed
211
 */
212
GstStructure *
Wim Taymans's avatar
Wim Taymans committed
213
gst_structure_new_empty (const gchar * name)
David Schleef's avatar
David Schleef committed
214
{
215
  g_return_val_if_fail (gst_structure_validate_name (name), NULL);
David Schleef's avatar
David Schleef committed
216

Wim Taymans's avatar
Wim Taymans committed
217
  return gst_structure_new_id_empty_with_size (g_quark_from_string (name), 0);
David Schleef's avatar
David Schleef committed
218
219
220
221
222
223
224
225
226
227
228
229
230
}

/**
 * gst_structure_new:
 * @name: name of new structure
 * @firstfield: name of first field to set
 * @...: additional arguments
 *
 * Creates a new #GstStructure with the given name.  Parses the
 * list of variable arguments and sets fields to the values listed.
 * Variable arguments should be passed as field name, field type,
 * and value.  Last variable argument should be NULL.
 *
231
232
233
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new #GstStructure
David Schleef's avatar
David Schleef committed
234
 */
235
236
GstStructure *
gst_structure_new (const gchar * name, const gchar * firstfield, ...)
David Schleef's avatar
David Schleef committed
237
238
239
240
{
  GstStructure *structure;
  va_list varargs;

241
242
243
  va_start (varargs, firstfield);
  structure = gst_structure_new_valist (name, firstfield, varargs);
  va_end (varargs);
David Schleef's avatar
David Schleef committed
244
245
246
247
248
249
250
251

  return structure;
}

/**
 * gst_structure_new_valist:
 * @name: name of new structure
 * @firstfield: name of first field to set
252
 * @varargs: variable argument list
David Schleef's avatar
David Schleef committed
253
 *
254
 * Creates a new #GstStructure with the given @name.  Structure fields
David Schleef's avatar
David Schleef committed
255
 * are set according to the varargs in a manner similar to
256
257
258
 * gst_structure_new().
 *
 * See gst_structure_set_name() for constraints on the @name parameter.
David Schleef's avatar
David Schleef committed
259
 *
260
261
262
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new #GstStructure
David Schleef's avatar
David Schleef committed
263
 */
264
GstStructure *
265
266
gst_structure_new_valist (const gchar * name,
    const gchar * firstfield, va_list varargs)
David Schleef's avatar
David Schleef committed
267
268
269
{
  GstStructure *structure;

Wim Taymans's avatar
Wim Taymans committed
270
  structure = gst_structure_new_empty (name);
271
272
273

  if (structure)
    gst_structure_set_valist (structure, firstfield, varargs);
David Schleef's avatar
David Schleef committed
274
275
276
277

  return structure;
}

278
279
280
/**
 * gst_structure_set_parent_refcount:
 * @structure: a #GstStructure
281
 * @refcount: (in): a pointer to the parent's refcount
282
283
284
 *
 * Sets the parent_refcount field of #GstStructure. This field is used to
 * determine whether a structure is mutable or not. This function should only be
285
 * called by code implementing parent objects of #GstStructure, as described in
286
 * the MT Refcounting section of the design documents.
Wim Taymans's avatar
Wim Taymans committed
287
288
 *
 * Returns: %TRUE if the parent refcount could be set.
289
 */
Wim Taymans's avatar
Wim Taymans committed
290
gboolean
291
gst_structure_set_parent_refcount (GstStructure * structure, gint * refcount)
292
{
Wim Taymans's avatar
Wim Taymans committed
293
  g_return_val_if_fail (structure != NULL, FALSE);
294
295
296

  /* if we have a parent_refcount already, we can only clear
   * if with a NULL refcount */
Wim Taymans's avatar
Wim Taymans committed
297
298
299
300
301
302
303
304
305
306
307
  if (GST_STRUCTURE_REFCOUNT (structure)) {
    if (refcount != NULL) {
      g_return_val_if_fail (refcount == NULL, FALSE);
      return FALSE;
    }
  } else {
    if (refcount == NULL) {
      g_return_val_if_fail (refcount != NULL, FALSE);
      return FALSE;
    }
  }
308

Wim Taymans's avatar
Wim Taymans committed
309
310
311
  GST_STRUCTURE_REFCOUNT (structure) = refcount;

  return TRUE;
312
313
}

David Schleef's avatar
David Schleef committed
314
/**
315
 * gst_structure_copy:
David Schleef's avatar
David Schleef committed
316
317
318
319
 * @structure: a #GstStructure to duplicate
 *
 * Duplicates a #GstStructure and all its fields and values.
 *
320
321
322
 * Free-function: gst_structure_free
 *
 * Returns: (transfer none): a new #GstStructure.
David Schleef's avatar
David Schleef committed
323
 */
324
GstStructure *
325
gst_structure_copy (const GstStructure * structure)
David Schleef's avatar
David Schleef committed
326
327
328
{
  GstStructure *new_structure;
  GstStructureField *field;
329
  guint i, len;
David Schleef's avatar
David Schleef committed
330

331
  g_return_val_if_fail (structure != NULL, NULL);
David Schleef's avatar
David Schleef committed
332

Wim Taymans's avatar
Wim Taymans committed
333
  len = GST_STRUCTURE_FIELDS (structure)->len;
Wim Taymans's avatar
Wim Taymans committed
334
  new_structure = gst_structure_new_id_empty_with_size (structure->name, len);
335

336
  for (i = 0; i < len; i++) {
David Schleef's avatar
David Schleef committed
337
338
    GstStructureField new_field = { 0 };

339
    field = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
340
341

    new_field.name = field->name;
342
    gst_value_init_and_copy (&new_field.value, &field->value);
Wim Taymans's avatar
Wim Taymans committed
343
    g_array_append_val (GST_STRUCTURE_FIELDS (new_structure), new_field);
David Schleef's avatar
David Schleef committed
344
  }
Wim Taymans's avatar
Wim Taymans committed
345
346
  GST_CAT_TRACE (GST_CAT_PERFORMANCE, "doing copy %p -> %p",
      structure, new_structure);
David Schleef's avatar
David Schleef committed
347

348
  return new_structure;
David Schleef's avatar
David Schleef committed
349
350
351
}

/**
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
352
 * gst_structure_free:
353
 * @structure: (in) (transfer full): the #GstStructure to free
David Schleef's avatar
David Schleef committed
354
 *
355
 * Frees a #GstStructure and all its fields and values. The structure must not
356
 * have a parent when this function is called.
David Schleef's avatar
David Schleef committed
357
 */
358
void
359
gst_structure_free (GstStructure * structure)
David Schleef's avatar
David Schleef committed
360
361
{
  GstStructureField *field;
362
  guint i, len;
David Schleef's avatar
David Schleef committed
363

364
  g_return_if_fail (structure != NULL);
Wim Taymans's avatar
Wim Taymans committed
365
  g_return_if_fail (GST_STRUCTURE_REFCOUNT (structure) == NULL);
David Schleef's avatar
David Schleef committed
366

Wim Taymans's avatar
Wim Taymans committed
367
  len = GST_STRUCTURE_FIELDS (structure)->len;
368
  for (i = 0; i < len; i++) {
369
    field = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
370

371
372
    if (G_IS_VALUE (&field->value)) {
      g_value_unset (&field->value);
David Schleef's avatar
David Schleef committed
373
374
    }
  }
Wim Taymans's avatar
Wim Taymans committed
375
  g_array_free (GST_STRUCTURE_FIELDS (structure), TRUE);
David Schleef's avatar
David Schleef committed
376
#ifdef USE_POISONING
377
  memset (structure, 0xff, sizeof (GstStructure));
David Schleef's avatar
David Schleef committed
378
#endif
Wim Taymans's avatar
Wim Taymans committed
379
380
  GST_TRACE ("free structure %p", structure);

Wim Taymans's avatar
Wim Taymans committed
381
  g_slice_free1 (sizeof (GstStructureImpl), structure);
David Schleef's avatar
David Schleef committed
382
383
384
385
386
387
}

/**
 * gst_structure_get_name:
 * @structure: a #GstStructure
 *
388
 * Get the name of @structure as a string.
David Schleef's avatar
David Schleef committed
389
390
391
 *
 * Returns: the name of the structure.
 */
392
const gchar *
393
gst_structure_get_name (const GstStructure * structure)
David Schleef's avatar
David Schleef committed
394
{
395
  g_return_val_if_fail (structure != NULL, NULL);
David Schleef's avatar
David Schleef committed
396

397
  return g_quark_to_string (structure->name);
David Schleef's avatar
David Schleef committed
398
399
}

400
/**
401
402
403
404
 * gst_structure_has_name:
 * @structure: a #GstStructure
 * @name: structure name to check for
 *
Wim Taymans's avatar
Wim Taymans committed
405
406
 * Checks if the structure has the given name
 *
407
408
409
410
411
412
413
414
415
416
 * Returns: TRUE if @name matches the name of the structure.
 */
gboolean
gst_structure_has_name (const GstStructure * structure, const gchar * name)
{
  const gchar *structure_name;

  g_return_val_if_fail (structure != NULL, FALSE);
  g_return_val_if_fail (name != NULL, FALSE);

417
418
419
  /* getting the string is cheap and comparing short strings is too
   * should be faster than getting the quark for name and comparing the quarks
   */
420
421
422
423
424
425
426
  structure_name = g_quark_to_string (structure->name);

  return (structure_name && strcmp (structure_name, name) == 0);
}

/**
 * gst_structure_get_name_id:
427
428
 * @structure: a #GstStructure
 *
429
 * Get the name of @structure as a GQuark.
430
431
432
433
434
435
436
437
438
439
440
 *
 * Returns: the quark representing the name of the structure.
 */
GQuark
gst_structure_get_name_id (const GstStructure * structure)
{
  g_return_val_if_fail (structure != NULL, 0);

  return structure->name;
}

David Schleef's avatar
David Schleef committed
441
442
443
444
445
/**
 * gst_structure_set_name:
 * @structure: a #GstStructure
 * @name: the new name of the structure
 *
446
 * Sets the name of the structure to the given @name.  The string
447
448
 * provided is copied before being used. It must not be empty, start with a
 * letter and can be followed by letters, numbers and any of "/-_.:".
David Schleef's avatar
David Schleef committed
449
 */
450
void
451
gst_structure_set_name (GstStructure * structure, const gchar * name)
David Schleef's avatar
David Schleef committed
452
{
453
  g_return_if_fail (structure != NULL);
454
  g_return_if_fail (IS_MUTABLE (structure));
455
  g_return_if_fail (gst_structure_validate_name (name));
David Schleef's avatar
David Schleef committed
456

457
  structure->name = g_quark_from_string (name);
David Schleef's avatar
David Schleef committed
458
459
}

460
461
462
463
464
465
466
467
468
469
470
471
static inline void
gst_structure_id_set_value_internal (GstStructure * structure, GQuark field,
    const GValue * value)
{
  GstStructureField gsfield = { 0, {0,} };

  gsfield.name = field;
  gst_value_init_and_copy (&gsfield.value, value);

  gst_structure_set_field (structure, &gsfield);
}

David Schleef's avatar
David Schleef committed
472
473
474
/**
 * gst_structure_id_set_value:
 * @structure: a #GstStructure
475
 * @field: a #GQuark representing a field
David Schleef's avatar
David Schleef committed
476
477
 * @value: the new value of the field
 *
478
 * Sets the field with the given GQuark @field to @value.  If the field
David Schleef's avatar
David Schleef committed
479
 * does not exist, it is created.  If the field exists, the previous
480
 * value is replaced and freed.
David Schleef's avatar
David Schleef committed
481
 */
482
void
483
484
gst_structure_id_set_value (GstStructure * structure,
    GQuark field, const GValue * value)
David Schleef's avatar
David Schleef committed
485
486
{

487
488
  g_return_if_fail (structure != NULL);
  g_return_if_fail (G_IS_VALUE (value));
489
  g_return_if_fail (IS_MUTABLE (structure));
David Schleef's avatar
David Schleef committed
490

491
  gst_structure_id_set_value_internal (structure, field, value);
David Schleef's avatar
David Schleef committed
492
493
494
495
496
}

/**
 * gst_structure_set_value:
 * @structure: a #GstStructure
497
 * @fieldname: the name of the field to set
David Schleef's avatar
David Schleef committed
498
499
 * @value: the new value of the field
 *
500
 * Sets the field with the given name @field to @value.  If the field
David Schleef's avatar
David Schleef committed
501
 * does not exist, it is created.  If the field exists, the previous
502
 * value is replaced and freed.
David Schleef's avatar
David Schleef committed
503
 */
504
void
505
506
gst_structure_set_value (GstStructure * structure,
    const gchar * fieldname, const GValue * value)
David Schleef's avatar
David Schleef committed
507
{
508
509
510
  g_return_if_fail (structure != NULL);
  g_return_if_fail (fieldname != NULL);
  g_return_if_fail (G_IS_VALUE (value));
511
  g_return_if_fail (IS_MUTABLE (structure));
David Schleef's avatar
David Schleef committed
512

513
514
  gst_structure_id_set_value_internal (structure,
      g_quark_from_string (fieldname), value);
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
}

static inline void
gst_structure_id_take_value_internal (GstStructure * structure, GQuark field,
    GValue * value)
{
  GstStructureField gsfield = { 0, {0,} };

  gsfield.name = field;
  gsfield.value = *value;

  gst_structure_set_field (structure, &gsfield);

  /* we took ownership */
#ifdef USE_POISONING
  memset (value, 0, sizeof (GValue));
#else
  value->g_type = G_TYPE_INVALID;
#endif
}

/**
 * gst_structure_id_take_value:
 * @structure: a #GstStructure
 * @field: a #GQuark representing a field
 * @value: (transfer full): the new value of the field
 *
 * Sets the field with the given GQuark @field to @value.  If the field
 * does not exist, it is created.  If the field exists, the previous
 * value is replaced and freed.
 *
 * Since: 0.10.31
 */
void
gst_structure_id_take_value (GstStructure * structure, GQuark field,
    GValue * value)
{
  g_return_if_fail (structure != NULL);
  g_return_if_fail (G_IS_VALUE (value));
  g_return_if_fail (IS_MUTABLE (structure));

  gst_structure_id_take_value_internal (structure, field, value);
}

/**
 * gst_structure_take_value:
 * @structure: a #GstStructure
 * @fieldname: the name of the field to set
 * @value: (transfer full): the new value of the field
 *
 * Sets the field with the given name @field to @value.  If the field
 * does not exist, it is created.  If the field exists, the previous
 * value is replaced and freed. The function will take ownership of @value.
 *
 * Since: 0.10.31
 */
void
gst_structure_take_value (GstStructure * structure, const gchar * fieldname,
    GValue * value)
{
  g_return_if_fail (structure != NULL);
  g_return_if_fail (fieldname != NULL);
  g_return_if_fail (G_IS_VALUE (value));
  g_return_if_fail (IS_MUTABLE (structure));

  gst_structure_id_take_value_internal (structure,
      g_quark_from_string (fieldname), value);
David Schleef's avatar
David Schleef committed
582
583
}

584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
static void
gst_structure_set_valist_internal (GstStructure * structure,
    const gchar * fieldname, va_list varargs)
{
  gchar *err = NULL;
  GType type;

  while (fieldname) {
    GstStructureField field = { 0 };

    field.name = g_quark_from_string (fieldname);

    type = va_arg (varargs, GType);

    G_VALUE_COLLECT_INIT (&field.value, type, varargs, 0, &err);
    if (G_UNLIKELY (err)) {
      g_critical ("%s", err);
      return;
    }
    gst_structure_set_field (structure, &field);

    fieldname = va_arg (varargs, gchar *);
  }
}

David Schleef's avatar
David Schleef committed
609
610
611
/**
 * gst_structure_set:
 * @structure: a #GstStructure
612
 * @fieldname: the name of the field to set
David Schleef's avatar
David Schleef committed
613
614
615
616
 * @...: variable arguments
 *
 * Parses the variable arguments and sets fields accordingly.
 * Variable arguments should be in the form field name, field type
617
 * (as a GType), value(s).  The last variable argument should be NULL.
David Schleef's avatar
David Schleef committed
618
 */
619
void
620
gst_structure_set (GstStructure * structure, const gchar * field, ...)
David Schleef's avatar
David Schleef committed
621
622
623
{
  va_list varargs;

624
  g_return_if_fail (structure != NULL);
625
  g_return_if_fail (IS_MUTABLE (structure) || field == NULL);
David Schleef's avatar
David Schleef committed
626

627
  va_start (varargs, field);
628
  gst_structure_set_valist_internal (structure, field, varargs);
629
  va_end (varargs);
David Schleef's avatar
David Schleef committed
630
631
632
}

/**
David Schleef's avatar
David Schleef committed
633
 * gst_structure_set_valist:
David Schleef's avatar
David Schleef committed
634
 * @structure: a #GstStructure
635
 * @fieldname: the name of the field to set
David Schleef's avatar
David Schleef committed
636
637
 * @varargs: variable arguments
 *
638
 * va_list form of gst_structure_set().
David Schleef's avatar
David Schleef committed
639
 */
640
void
641
642
gst_structure_set_valist (GstStructure * structure,
    const gchar * fieldname, va_list varargs)
David Schleef's avatar
David Schleef committed
643
{
644
  g_return_if_fail (structure != NULL);
645
  g_return_if_fail (IS_MUTABLE (structure));
David Schleef's avatar
David Schleef committed
646

647
648
649
650
651
652
653
654
655
656
  gst_structure_set_valist_internal (structure, fieldname, varargs);
}

static void
gst_structure_id_set_valist_internal (GstStructure * structure,
    GQuark fieldname, va_list varargs)
{
  gchar *err = NULL;
  GType type;

657
  while (fieldname) {
David Schleef's avatar
David Schleef committed
658
659
    GstStructureField field = { 0 };

660
    field.name = fieldname;
David Schleef's avatar
David Schleef committed
661

662
    type = va_arg (varargs, GType);
663

664
#ifndef G_VALUE_COLLECT_INIT
665
666
    g_value_init (&field.value, type);
    G_VALUE_COLLECT (&field.value, varargs, 0, &err);
667
668
#else
    G_VALUE_COLLECT_INIT (&field.value, type, varargs, 0, &err);
669
#endif
670
    if (G_UNLIKELY (err)) {
671
672
      g_critical ("%s", err);
      return;
David Schleef's avatar
David Schleef committed
673
    }
674
    gst_structure_set_field (structure, &field);
David Schleef's avatar
David Schleef committed
675

676
    fieldname = va_arg (varargs, GQuark);
David Schleef's avatar
David Schleef committed
677
678
679
  }
}

680
681
682
683
684
685
686
687
688
689
690
/**
 * gst_structure_id_set:
 * @structure: a #GstStructure
 * @fieldname: the GQuark for the name of the field to set
 * @...: variable arguments
 *
 * Identical to gst_structure_set, except that field names are
 * passed using the GQuark for the field name. This allows more efficient
 * setting of the structure if the caller already knows the associated
 * quark values.
 * The last variable argument must be NULL.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
691
692
 *
 * Since: 0.10.10
693
694
695
696
697
698
699
700
701
 */
void
gst_structure_id_set (GstStructure * structure, GQuark field, ...)
{
  va_list varargs;

  g_return_if_fail (structure != NULL);

  va_start (varargs, field);
702
  gst_structure_id_set_valist_internal (structure, field, varargs);
703
704
705
706
707
708
709
710
711
712
  va_end (varargs);
}

/**
 * gst_structure_id_set_valist:
 * @structure: a #GstStructure
 * @fieldname: the name of the field to set
 * @varargs: variable arguments
 *
 * va_list form of gst_structure_id_set().
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
713
714
 *
 * Since: 0.10.10
715
716
717
718
719
720
721
722
 */
void
gst_structure_id_set_valist (GstStructure * structure,
    GQuark fieldname, va_list varargs)
{
  g_return_if_fail (structure != NULL);
  g_return_if_fail (IS_MUTABLE (structure));

723
  gst_structure_id_set_valist_internal (structure, fieldname, varargs);
724
725
}

726
/**
Wim Taymans's avatar
Wim Taymans committed
727
 * gst_structure_new_id:
728
729
730
731
732
733
734
 * @name_quark: name of new structure
 * @field_quark: the GQuark for the name of the field to set
 * @...: variable arguments
 *
 * Creates a new #GstStructure with the given name as a GQuark, followed by
 * fieldname quark, GType, argument(s) "triplets" in the same format as
 * gst_structure_id_set(). Basically a convenience wrapper around
Wim Taymans's avatar
Wim Taymans committed
735
 * gst_structure_new_id_empty() and gst_structure_id_set().
736
737
738
 *
 * The last variable argument must be NULL (or 0).
 *
739
740
741
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new #GstStructure
742
743
744
745
 *
 * Since: 0.10.24
 */
GstStructure *
Wim Taymans's avatar
Wim Taymans committed
746
gst_structure_new_id (GQuark name_quark, GQuark field_quark, ...)
747
748
749
750
751
752
753
{
  GstStructure *s;
  va_list varargs;

  g_return_val_if_fail (name_quark != 0, NULL);
  g_return_val_if_fail (field_quark != 0, NULL);

Wim Taymans's avatar
Wim Taymans committed
754
  s = gst_structure_new_id_empty (name_quark);
755
756

  va_start (varargs, field_quark);
757
  gst_structure_id_set_valist_internal (s, field_quark, varargs);
758
759
760
761
762
  va_end (varargs);

  return s;
}

763
764
765
766
767
768
#if GST_VERSION_NANO == 1
#define GIT_G_WARNING g_warning
#else
#define GIT_G_WARNING GST_WARNING
#endif

769
770
771
/* If the structure currently contains a field with the same name, it is
 * replaced with the provided field. Otherwise, the field is added to the
 * structure. The field's value is not deeply copied.
David Schleef's avatar
David Schleef committed
772
 */
773
static void
774
gst_structure_set_field (GstStructure * structure, GstStructureField * field)
David Schleef's avatar
David Schleef committed
775
776
{
  GstStructureField *f;
Wim Taymans's avatar
Wim Taymans committed
777
  guint i, len = GST_STRUCTURE_FIELDS (structure)->len;
David Schleef's avatar
David Schleef committed
778

779
780
781
782
783
784
  if (G_UNLIKELY (G_VALUE_HOLDS_STRING (&field->value))) {
    const gchar *s;

    s = g_value_get_string (&field->value);
    /* only check for NULL strings in taglists, as they are allowed in message
     * structs, e.g. error message debug strings */
785
786
    if (G_UNLIKELY (IS_TAGLIST (structure) && (s == NULL || *s == '\0'))) {
      if (s == NULL) {
787
        GIT_G_WARNING ("Trying to set NULL string on field '%s' on taglist. "
788
789
790
791
792
            "Please file a bug.", g_quark_to_string (field->name));
        g_value_unset (&field->value);
        return;
      } else {
        /* empty strings never make sense */
793
        GIT_G_WARNING ("Trying to set empty string on taglist field '%s'. "
794
795
796
797
            "Please file a bug.", g_quark_to_string (field->name));
        g_value_unset (&field->value);
        return;
      }
798
799
800
801
802
    } else if (G_UNLIKELY (s != NULL && !g_utf8_validate (s, -1, NULL))) {
      g_warning ("Trying to set string on %s field '%s', but string is not "
          "valid UTF-8. Please file a bug.",
          IS_TAGLIST (structure) ? "taglist" : "structure",
          g_quark_to_string (field->name));
803
      g_value_unset (&field->value);
804
805
      return;
    }
806
  } else if (G_UNLIKELY (G_VALUE_HOLDS (&field->value, G_TYPE_DATE))) {
807
808
    const GDate *d;

809
    d = g_value_get_boxed (&field->value);
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
    /* only check for NULL GDates in taglists, as they might make sense
     * in other, generic structs */
    if (G_UNLIKELY ((IS_TAGLIST (structure) && d == NULL))) {
      GIT_G_WARNING ("Trying to set NULL GDate on field '%s' on taglist. "
          "Please file a bug.", g_quark_to_string (field->name));
      g_value_unset (&field->value);
      return;
    } else if (G_UNLIKELY (d != NULL && !g_date_valid (d))) {
      g_warning
          ("Trying to set invalid GDate on %s field '%s'. Please file a bug.",
          IS_TAGLIST (structure) ? "taglist" : "structure",
          g_quark_to_string (field->name));
      g_value_unset (&field->value);
      return;
    }
825
826
  }

827
  for (i = 0; i < len; i++) {
828
    f = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
829

830
    if (G_UNLIKELY (f->name == field->name)) {
831
832
      g_value_unset (&f->value);
      memcpy (f, field, sizeof (GstStructureField));
David Schleef's avatar
David Schleef committed
833
834
835
836
      return;
    }
  }

Wim Taymans's avatar
Wim Taymans committed
837
  g_array_append_val (GST_STRUCTURE_FIELDS (structure), *field);
David Schleef's avatar
David Schleef committed
838
839
}

840
/* If there is no field with the given ID, NULL is returned.
David Schleef's avatar
David Schleef committed
841
 */
842
static GstStructureField *
843
gst_structure_id_get_field (const GstStructure * structure, GQuark field_id)
David Schleef's avatar
David Schleef committed
844
845
{
  GstStructureField *field;
846
  guint i, len;
David Schleef's avatar
David Schleef committed
847

Wim Taymans's avatar
Wim Taymans committed
848
  len = GST_STRUCTURE_FIELDS (structure)->len;
David Schleef's avatar
David Schleef committed
849

850
  for (i = 0; i < len; i++) {
851
    field = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
852

853
    if (G_UNLIKELY (field->name == field_id))
854
      return field;
David Schleef's avatar
David Schleef committed
855
856
857
858
859
  }

  return NULL;
}

860
/* If there is no field with the given ID, NULL is returned.
David Schleef's avatar
David Schleef committed
861
 */
David Schleef's avatar
David Schleef committed
862
static GstStructureField *
863
864
gst_structure_get_field (const GstStructure * structure,
    const gchar * fieldname)
David Schleef's avatar
David Schleef committed
865
{
866
867
  g_return_val_if_fail (structure != NULL, NULL);
  g_return_val_if_fail (fieldname != NULL, NULL);
David Schleef's avatar
David Schleef committed
868

869
870
  return gst_structure_id_get_field (structure,
      g_quark_from_string (fieldname));
David Schleef's avatar
David Schleef committed
871
872
873
}

/**
874
 * gst_structure_get_value:
David Schleef's avatar
David Schleef committed
875
876
877
 * @structure: a #GstStructure
 * @fieldname: the name of the field to get
 *
878
 * Get the value of the field with name @fieldname.
David Schleef's avatar
David Schleef committed
879
880
881
882
 *
 * Returns: the #GValue corresponding to the field with the given name.
 */
const GValue *
883
884
gst_structure_get_value (const GstStructure * structure,
    const gchar * fieldname)
David Schleef's avatar
David Schleef committed
885
886
887
{
  GstStructureField *field;

888
889
  g_return_val_if_fail (structure != NULL, NULL);
  g_return_val_if_fail (fieldname != NULL, NULL);
David Schleef's avatar
David Schleef committed
890

891
892
893
  field = gst_structure_get_field (structure, fieldname);
  if (field == NULL)
    return NULL;
David Schleef's avatar
David Schleef committed
894
895
896
897

  return &field->value;
}

David Schleef's avatar
David Schleef committed
898
899
900
/**
 * gst_structure_id_get_value:
 * @structure: a #GstStructure
901
 * @field: the #GQuark of the field to get
David Schleef's avatar
David Schleef committed
902
 *
903
 * Get the value of the field with GQuark @field.
David Schleef's avatar
David Schleef committed
904
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
905
 * Returns: the #GValue corresponding to the field with the given name
David Schleef's avatar
David Schleef committed
906
907
908
 *          identifier.
 */
const GValue *
909
gst_structure_id_get_value (const GstStructure * structure, GQuark field)
David Schleef's avatar
David Schleef committed
910
{
911
  GstStructureField *gsfield;
David Schleef's avatar
David Schleef committed
912

913
  g_return_val_if_fail (structure != NULL, NULL);
David Schleef's avatar
David Schleef committed
914

915
916
917
  gsfield = gst_structure_id_get_field (structure, field);
  if (gsfield == NULL)
    return NULL;
David Schleef's avatar
David Schleef committed
918

919
  return &gsfield->value;
David Schleef's avatar
David Schleef committed
920
921
}

David Schleef's avatar
David Schleef committed
922
923
924
925
926
927
928
929
930
/**
 * gst_structure_remove_field:
 * @structure: a #GstStructure
 * @fieldname: the name of the field to remove
 *
 * Removes the field with the given name.  If the field with the given
 * name does not exist, the structure is unchanged.
 */
void
931
gst_structure_remove_field (GstStructure * structure, const gchar * fieldname)
David Schleef's avatar
David Schleef committed
932
933
934
{
  GstStructureField *field;
  GQuark id;
935
  guint i, len;
David Schleef's avatar
David Schleef committed
936

937
938
  g_return_if_fail (structure != NULL);
  g_return_if_fail (fieldname != NULL);
939
  g_return_if_fail (IS_MUTABLE (structure));
David Schleef's avatar
David Schleef committed
940

941
  id = g_quark_from_string (fieldname);
Wim Taymans's avatar
Wim Taymans committed
942
  len = GST_STRUCTURE_FIELDS (structure)->len;
David Schleef's avatar
David Schleef committed
943

944
  for (i = 0; i < len; i++) {
945
    field = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
946

947
948
    if (field->name == id) {
      if (G_IS_VALUE (&field->value)) {
949
        g_value_unset (&field->value);
David Schleef's avatar
David Schleef committed
950
      }
Wim Taymans's avatar
Wim Taymans committed
951
952
      GST_STRUCTURE_FIELDS (structure) =
          g_array_remove_index (GST_STRUCTURE_FIELDS (structure), i);
David Schleef's avatar
David Schleef committed
953
954
955
956
957
      return;
    }
  }
}

958
959
960
961
962
963
/**
 * gst_structure_remove_fields:
 * @structure: a #GstStructure
 * @fieldname: the name of the field to remove
 * @...: NULL-terminated list of more fieldnames to remove
 *
964
 * Removes the fields with the given names. If a field does not exist, the
965
966
 * argument is ignored.
 */
967
968
969
void
gst_structure_remove_fields (GstStructure * structure,
    const gchar * fieldname, ...)
970
971
972
973
974
{
  va_list varargs;

  g_return_if_fail (structure != NULL);
  g_return_if_fail (fieldname != NULL);
975
  /* mutability checked in remove_field */
976

977
  va_start (varargs, fieldname);
978
  gst_structure_remove_fields_valist (structure, fieldname, varargs);
979
  va_end (varargs);
980
981
982
983
984
985
986
987
}

/**
 * gst_structure_remove_fields_valist:
 * @structure: a #GstStructure
 * @fieldname: the name of the field to remove
 * @varargs: NULL-terminated list of more fieldnames to remove
 *
988
 * va_list form of gst_structure_remove_fields().
989
 */
990
991
992
void
gst_structure_remove_fields_valist (GstStructure * structure,
    const gchar * fieldname, va_list varargs)
993
994
{
  gchar *field = (gchar *) fieldname;
995

996
997
  g_return_if_fail (structure != NULL);
  g_return_if_fail (fieldname != NULL);
998
  /* mutability checked in remove_field */
999
1000
1001

  while (field) {
    gst_structure_remove_field (structure, field);
1002
    field = va_arg (varargs, char *);
1003
1004
1005
  }
}

Benjamin Otte's avatar
Benjamin Otte committed
1006
1007
1008
1009
/**
 * gst_structure_remove_all_fields:
 * @structure: a #GstStructure
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1010
 * Removes all fields in a GstStructure.
Benjamin Otte's avatar
Benjamin Otte committed
1011
1012
 */
void
1013
gst_structure_remove_all_fields (GstStructure * structure)
Benjamin Otte's avatar
Benjamin Otte committed
1014
1015
1016
1017
{
  GstStructureField *field;
  int i;

1018
  g_return_if_fail (structure != NULL);
1019
  g_return_if_fail (IS_MUTABLE (structure));
Benjamin Otte's avatar
Benjamin Otte committed
1020

Wim Taymans's avatar
Wim Taymans committed
1021
  for (i = GST_STRUCTURE_FIELDS (structure)->len - 1; i >= 0; i--) {
1022
    field = GST_STRUCTURE_FIELD (structure, i);
Benjamin Otte's avatar
Benjamin Otte committed
1023
1024

    if (G_IS_VALUE (&field->value)) {
1025
      g_value_unset (&field->value);
Benjamin Otte's avatar
Benjamin Otte committed
1026
    }
Wim Taymans's avatar
Wim Taymans committed
1027
1028
    GST_STRUCTURE_FIELDS (structure) =
        g_array_remove_index (GST_STRUCTURE_FIELDS (structure), i);
Benjamin Otte's avatar
Benjamin Otte committed
1029
1030
1031
  }
}

David Schleef's avatar
David Schleef committed
1032
1033
1034
1035
1036
1037
/**
 * gst_structure_get_field_type:
 * @structure: a #GstStructure
 * @fieldname: the name of the field
 *
 * Finds the field with the given name, and returns the type of the
1038
 * value it contains.  If the field is not found, G_TYPE_INVALID is
David Schleef's avatar
David Schleef committed
1039
1040
1041
1042
1043
 * returned.
 *
 * Returns: the #GValue of the field
 */
GType
1044
1045
gst_structure_get_field_type (const GstStructure * structure,
    const gchar * fieldname)
David Schleef's avatar
David Schleef committed
1046
1047
1048
{
  GstStructureField *field;

1049
1050
  g_return_val_if_fail (structure != NULL, G_TYPE_INVALID);
  g_return_val_if_fail (fieldname != NULL, G_TYPE_INVALID);
David Schleef's avatar
David Schleef committed
1051

1052
1053
1054