A rather large patch:
[platform/upstream/gstreamer.git] / gst / gstcaps.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstcaps.c: Element capabilities subsystem
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 //#define GST_DEBUG_ENABLED
24 #include "gst_private.h"
25
26 #include "gstcaps.h"
27 #include "gsttype.h"
28
29 #include "gstpropsprivate.h"
30
31 static GMemChunk *_gst_caps_chunk;
32 static GMutex *_gst_caps_chunk_lock;
33
34 void
35 _gst_caps_initialize (void)
36 {
37   _gst_caps_chunk = g_mem_chunk_new ("GstCaps",
38                   sizeof (GstCaps), sizeof (GstCaps) * 256,
39                   G_ALLOC_AND_FREE);
40   _gst_caps_chunk_lock = g_mutex_new ();
41 }
42
43 static guint16
44 get_type_for_mime (const gchar *mime)
45 {
46   guint16 typeid;
47
48   typeid = gst_type_find_by_mime (mime);
49   if (typeid == 0) {
50      GstTypeFactory factory; // = g_new0 (GstTypeFactory, 1);
51
52      factory.mime = g_strdup (mime);
53      factory.exts = NULL;
54      factory.typefindfunc = NULL;
55
56      typeid = gst_type_register (&factory);
57   }
58   return typeid;
59 }
60
61 /**
62  * gst_caps_new:
63  * @name: the name of this capability
64  * @mime: the mime type to attach to the capability
65  * @props: the properties to add to this capability
66  *
67  * Create a new capability with the given mime typei and properties.
68  *
69  * Returns: a new capability
70  */
71 GstCaps*
72 gst_caps_new (const gchar *name, const gchar *mime, GstProps *props)
73 {
74   GstCaps *caps;
75
76   g_return_val_if_fail (mime != NULL, NULL);
77
78   g_mutex_lock (_gst_caps_chunk_lock);
79   caps = g_mem_chunk_alloc (_gst_caps_chunk);
80   g_mutex_unlock (_gst_caps_chunk_lock);
81
82   caps->name = g_strdup (name);
83   caps->id = get_type_for_mime (mime);
84   caps->properties = props;
85   caps->next = NULL;
86   caps->refcount = 1;
87   caps->lock = g_mutex_new ();
88
89   return caps;
90 }
91
92 /**
93  * gst_caps_destroy:
94  * @caps: the caps to destroy
95  *
96  * Frees the memory used by this caps structure and all
97  * the chained caps and properties.
98  */
99 void
100 gst_caps_destroy (GstCaps *caps)
101 {
102   GstCaps *next;
103
104   g_return_if_fail (caps != NULL);
105
106   GST_CAPS_LOCK (caps);
107   next = caps->next;
108   g_free (caps->name);
109   g_free (caps);
110   GST_CAPS_UNLOCK (caps);
111
112   if (next) 
113     gst_caps_unref (next);
114 }
115
116 /**
117  * gst_caps_unref:
118  * @caps: the caps to unref
119  *
120  * Decrease the refcount of this caps structure, 
121  * destroying it when the refcount is 0
122  */
123 void
124 gst_caps_unref (GstCaps *caps)
125 {
126   gboolean zero;
127   GstCaps *next;
128
129   g_return_if_fail (caps != NULL);
130
131   GST_CAPS_LOCK (caps);
132   caps->refcount--;
133   zero = (caps->refcount == 0);
134   next = caps->next;
135   GST_CAPS_UNLOCK (caps);
136
137   if (next)
138     gst_caps_unref (next);
139
140   if (zero)
141     gst_caps_destroy (caps);
142 }
143
144 /**
145  * gst_caps_ref:
146  * @caps: the caps to ref
147  *
148  * Increase the refcount of this caps structure
149  */
150 void
151 gst_caps_ref (GstCaps *caps)
152 {
153   g_return_if_fail (caps != NULL);
154
155   GST_CAPS_LOCK (caps);
156   caps->refcount++;
157   GST_CAPS_UNLOCK (caps);
158 }
159
160 /**
161  * gst_caps_copy:
162  * @caps: the caps to copy
163  *
164  * Copies the caps.
165  *
166  * Returns: a copy of the GstCaps structure.
167  */
168 GstCaps*
169 gst_caps_copy (GstCaps *caps)
170 {
171   GstCaps *new = caps;;
172
173   g_return_val_if_fail (caps != NULL, NULL);
174
175   GST_CAPS_LOCK (caps);
176   new = gst_caps_new (
177                   caps->name,
178                   (gst_type_find_by_id (caps->id))->mime,
179                   gst_props_copy (caps->properties));
180   GST_CAPS_UNLOCK (caps);
181
182   return new;
183 }
184
185 /**
186  * gst_caps_copy_on_write:
187  * @caps: the caps to copy
188  *
189  * Copies the caps if the refcount is greater than 1
190  *
191  * Returns: a pointer to a GstCaps strcuture that can
192  * be safely written to
193  */
194 GstCaps*
195 gst_caps_copy_on_write (GstCaps *caps)
196 {
197   GstCaps *new = caps;
198   gboolean needcopy;
199
200   g_return_val_if_fail (caps != NULL, NULL);
201
202   GST_CAPS_LOCK (caps);
203   needcopy = (caps->refcount > 1);
204   GST_CAPS_UNLOCK (caps);
205
206   if (needcopy) {
207     new = gst_caps_copy (caps);
208     gst_caps_unref (caps);
209   }
210
211   return new;
212 }
213
214 /**
215  * gst_caps_get_name:
216  * @caps: the caps to get the name from
217  *
218  * Get the name of a GstCaps structure.
219  *
220  * Returns: the name of the caps
221  */
222 const gchar*
223 gst_caps_get_name (GstCaps *caps)
224 {
225   g_return_val_if_fail (caps != NULL, NULL);
226
227   return (const gchar *)caps->name;
228 }
229
230 /**
231  * gst_caps_set_name:
232  * @caps: the caps to set the name to
233  * @name: the name to set
234  *
235  * Set the name of a caps.
236  */
237 void
238 gst_caps_set_name (GstCaps *caps, const gchar *name)
239 {
240   g_return_if_fail (caps != NULL);
241
242   if (caps->name)
243     g_free (caps->name);
244
245   caps->name = g_strdup (name);
246 }
247
248 /**
249  * gst_caps_get_mime:
250  * @caps: the caps to get the mime type from
251  *
252  * Get the mime type of the caps as a string.
253  *
254  * Returns: the mime type of the caps
255  */
256 const gchar*
257 gst_caps_get_mime (GstCaps *caps)
258 {
259   GstType *type;
260
261   g_return_val_if_fail (caps != NULL, NULL);
262
263   type = gst_type_find_by_id (caps->id);
264
265   if (type)
266     return type->mime;
267   else
268     return "unknown/unknown";
269 }
270
271 /**
272  * gst_caps_set_mime:
273  * @caps: the caps to set the mime type to
274  * @mime: the mime type to attach to the caps
275  *
276  * Set the mime type of the caps as a string.
277  */
278 void
279 gst_caps_set_mime (GstCaps *caps, const gchar *mime)
280 {
281   g_return_if_fail (caps != NULL);
282   g_return_if_fail (mime != NULL);
283
284   caps->id = get_type_for_mime (mime);
285 }
286
287 /**
288  * gst_caps_get_type_id:
289  * @caps: the caps to get the type id from
290  *
291  * Get the type id of the caps.
292  *
293  * Returns: the type id of the caps
294  */
295 guint16
296 gst_caps_get_type_id (GstCaps *caps)
297 {
298   g_return_val_if_fail (caps != NULL, 0);
299
300   return caps->id;
301 }
302
303 /**
304  * gst_caps_set_type_id:
305  * @caps: the caps to set the type id to
306  * @type_id: the type id to set
307  *
308  * Set the type id of the caps.
309  */
310 void
311 gst_caps_set_type_id (GstCaps *caps, guint16 type_id)
312 {
313   g_return_if_fail (caps != NULL);
314
315   caps->id = type_id;
316 }
317
318 /**
319  * gst_caps_set_props:
320  * @caps: the caps to attach the properties to
321  * @props: the properties to attach
322  *
323  * Set the properties to the given caps.
324  *
325  * Returns: the new caps structure
326  */
327 GstCaps*
328 gst_caps_set_props (GstCaps *caps, GstProps *props)
329 {
330   g_return_val_if_fail (caps != NULL, caps);
331   g_return_val_if_fail (props != NULL, caps);
332   g_return_val_if_fail (caps->properties == NULL, caps);
333
334   caps->properties = props;
335
336   return caps;
337 }
338
339 /**
340  * gst_caps_get_props:
341  * @caps: the caps to get the properties from
342  *
343  * Get the properties of the given caps.
344  *
345  * Returns: the properties of the caps
346  */
347 GstProps*
348 gst_caps_get_props (GstCaps *caps)
349 {
350   g_return_val_if_fail (caps != NULL, NULL);
351
352   return caps->properties;
353 }
354
355 /**
356  * gst_caps_append:
357  * @caps: a capabilty
358  * @capstoadd: the capability to append
359  *
360  * Appends a capability to the existing capability.
361  *
362  * Returns: the new capability
363  */
364 GstCaps*
365 gst_caps_append (GstCaps *caps, GstCaps *capstoadd)
366 {
367   GstCaps *orig = caps;
368   
369   g_return_val_if_fail (caps != capstoadd, caps);
370
371   if (caps == NULL)
372     return capstoadd;
373   
374   while (caps->next) {
375     caps = caps->next;
376   }
377   caps->next = capstoadd;
378
379   return orig;
380 }
381
382 /**
383  * gst_caps_prepend:
384  * @caps: a capabilty
385  * @capstoadd: a capabilty to prepend
386  *
387  * prepend the capability to the list of capabilities
388  *
389  * Returns: the new capability
390  */
391 GstCaps*
392 gst_caps_prepend (GstCaps *caps, GstCaps *capstoadd)
393 {
394   GstCaps *orig = capstoadd;
395   
396   g_return_val_if_fail (caps != capstoadd, caps);
397
398   if (capstoadd == NULL)
399     return caps;
400
401   while (capstoadd->next) {
402     capstoadd = capstoadd->next;
403   }
404   capstoadd->next = caps;
405
406   return orig;
407 }
408
409 /**
410  * gst_caps_get_by_name:
411  * @caps: a capabilty
412  * @name: the name of the capability to get
413  *
414  * Get the capability with the given name from this
415  * chain of capabilities.
416  *
417  * Returns: the first capability in the chain with the 
418  * given name
419  */
420 GstCaps*
421 gst_caps_get_by_name (GstCaps *caps, const gchar *name)
422 {
423   g_return_val_if_fail (caps != NULL, NULL);
424   g_return_val_if_fail (name != NULL, NULL);
425    
426   while (caps) {
427     if (!strcmp (caps->name, name)) 
428       return caps;
429     caps = caps->next;
430   }
431
432   return NULL;
433 }
434                                                                                                                    
435 static gboolean
436 gst_caps_check_compatibility_func (GstCaps *fromcaps, GstCaps *tocaps)
437 {
438   if (fromcaps->id != tocaps->id) {
439     GST_DEBUG (0,"gstcaps: mime types differ (%s to %s)\n",
440                gst_type_find_by_id (fromcaps->id)->mime, 
441                gst_type_find_by_id (tocaps->id)->mime);
442     return FALSE;
443   }
444
445   if (tocaps->properties) {
446     if (fromcaps->properties) {
447       return gst_props_check_compatibility (fromcaps->properties, tocaps->properties);
448     }
449     else {
450       GST_DEBUG (0,"gstcaps: no source caps\n");
451       return FALSE;
452     }
453   }
454   else {
455     // assume it accepts everything
456     GST_DEBUG (0,"gstcaps: no caps\n");
457     return TRUE;
458   }
459 }
460
461 /**
462  * gst_caps_check_compatibility:
463  * @fromcaps: a capabilty
464  * @tocaps: a capabilty
465  *
466  * Checks whether two capabilities are compatible.
467  *
468  * Returns: TRUE if compatible, FALSE otherwise
469  */
470 gboolean
471 gst_caps_check_compatibility (GstCaps *fromcaps, GstCaps *tocaps)
472 {
473   if (fromcaps == NULL) {
474     if (tocaps == NULL) {
475       GST_DEBUG (0,"gstcaps: no caps\n");
476       return TRUE;
477     }
478     else {
479       GST_DEBUG (0,"gstcaps: no src but destination caps\n");
480       return FALSE;
481     }
482   }
483   else {
484     if (tocaps == NULL) {
485       GST_DEBUG (0,"gstcaps: src caps and no dest caps\n");
486       return TRUE;
487     }
488   }
489
490   while (fromcaps) {
491     GstCaps *destcaps = tocaps;
492
493     while (destcaps) {
494       if (gst_caps_check_compatibility_func (fromcaps, destcaps))
495         return TRUE;
496
497       destcaps =  destcaps->next;
498     }
499     fromcaps =  fromcaps->next;
500   }
501   return FALSE;
502 }
503
504 /**
505  * gst_caps_save_thyself:
506  * @caps: a capabilty to save
507  * @parent: the parent XML node pointer
508  *
509  * Save the capability into an XML representation.
510  *
511  * Returns: a new XML node pointer
512  */
513 xmlNodePtr
514 gst_caps_save_thyself (GstCaps *caps, xmlNodePtr parent)
515 {
516   xmlNodePtr subtree;
517   xmlNodePtr subsubtree;
518
519   while (caps) {
520     subtree = xmlNewChild (parent, NULL, "capscomp", NULL);
521
522     xmlNewChild (subtree, NULL, "name", caps->name);
523     xmlNewChild (subtree, NULL, "type", gst_type_find_by_id (caps->id)->mime);
524     if (caps->properties) {
525       subsubtree = xmlNewChild (subtree, NULL, "properties", NULL);
526
527       gst_props_save_thyself (caps->properties, subsubtree);
528     }
529
530     caps = caps->next;
531   }
532
533   return parent;
534 }
535
536 /**
537  * gst_caps_load_thyself:
538  * @parent: the parent XML node pointer
539  *
540  * Load a new caps from the XML representation.
541  *
542  * Returns: a new capability
543  */
544 GstCaps*
545 gst_caps_load_thyself (xmlNodePtr parent)
546 {
547   GstCaps *result = NULL;
548   xmlNodePtr field = parent->xmlChildrenNode;
549
550   while (field) {
551     if (!strcmp (field->name, "capscomp")) {
552       xmlNodePtr subfield = field->xmlChildrenNode;
553       GstCaps *caps;
554       gchar *content;
555
556       g_mutex_lock (_gst_caps_chunk_lock);
557       caps = g_mem_chunk_alloc0 (_gst_caps_chunk);
558       g_mutex_unlock (_gst_caps_chunk_lock);
559
560       caps->refcount = 1;
561       caps->lock = g_mutex_new ();
562       caps->next = NULL;
563         
564       while (subfield) {
565         if (!strcmp (subfield->name, "name")) {
566           caps->name = xmlNodeGetContent (subfield);
567         }
568         if (!strcmp (subfield->name, "type")) {
569           content = xmlNodeGetContent (subfield);
570           caps->id = get_type_for_mime (content);
571           g_free (content);
572         }
573         else if (!strcmp (subfield->name, "properties")) {
574           caps->properties = gst_props_load_thyself (subfield);
575         }
576         
577         subfield = subfield->next;
578       }
579       result = gst_caps_append (result, caps);
580     }
581     field = field->next;
582   }
583
584   return result;
585 }
586
587
588