2a06b6cfc27c566f36471e9527fd8673094f123c
[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 #include "gst_private.h"
24
25 #include "gstcaps.h"
26 #include "gsttype.h"
27 #include "gstmemchunk.h"
28 #include "gstlog.h"
29
30 #ifndef GST_DISABLE_TRACE
31 /* #define GST_WITH_ALLOC_TRACE */
32 #include "gsttrace.h"
33
34 static GstAllocTrace *_gst_caps_trace;
35 #endif
36
37 static GstMemChunk *_gst_caps_chunk;
38
39 GType _gst_caps_type;
40
41 extern GstProps *       __gst_props_from_string_func            (gchar *s, gchar **end, gboolean caps);
42 extern gboolean         __gst_props_parse_string                (gchar *r, gchar **end, gchar **next);
43
44 /* transform functions */
45 static void             gst_caps_transform_to_string            (const GValue *src_value, GValue *dest_value);
46
47 static void             gst_caps_destroy                        (GstCaps *caps);
48
49
50 static void
51 gst_caps_transform_to_string (const GValue *src_value, GValue *dest_value)
52 {
53   GstCaps *caps = g_value_peek_pointer (src_value);
54   dest_value->data[0].v_pointer = gst_caps_to_string (caps);
55 }
56 /**
57  * gst_caps_to_string:
58  * caps: the caps to convert to a string
59  *
60  * Converts a #GstCaps into a readable format. This is mainly intended for
61  * debugging purposes. You have to free the string using g_free.
62  * A string converted with #gst_caps_to_string can always be converted back to
63  * its caps representation using #gst_caps_from_string.
64  *
65  * Returns: A newly allocated string
66  */
67 gchar *
68 gst_caps_to_string (GstCaps *caps)
69 {
70   gchar *ret;
71   GString *result;
72
73   g_return_val_if_fail (caps != NULL, NULL);
74
75   result = g_string_new ("");
76
77   while (caps) {
78     gchar *props;
79     GValue value = { 0, }; /* the important thing is that value.type = 0 */
80     
81     g_string_append_printf (result, "\"%s\"", gst_caps_get_mime (caps));
82
83     if (caps->properties) {
84       g_value_init (&value, GST_TYPE_PROPS);
85       g_value_set_boxed  (&value, caps->properties);
86       props = g_strdup_value_contents (&value);
87
88       g_value_unset (&value);
89       g_string_append (result, ", ");
90       g_string_append (result, props);
91       g_free (props);
92     }
93
94     caps = caps->next;
95     if (caps)
96       g_string_append (result, "; ");
97   }
98   ret = result->str;
99   g_string_free (result, FALSE);
100   return ret;
101 }
102
103 static GstCaps *
104 gst_caps_from_string_func (gchar *r)
105 {
106   gchar *mime, *w;
107   GstCaps *caps, *append;
108   GstProps *props = NULL;
109
110   mime = r;
111   if (!__gst_props_parse_string (r, &w, &r)) goto error;
112     
113   if (*r == '\0') goto found;
114   if (*r++ != ',') goto error;
115   while (g_ascii_isspace (*r)) r++;
116     
117   props = __gst_props_from_string_func (r, &r, TRUE);
118   if (!props) goto error;
119
120 found:
121   *w = '\0';
122   if (*mime == '\0') {
123     gst_props_unref (props);
124     goto error;
125   }  
126   caps = gst_caps_new ("parsed caps", mime, props);
127   if (*r == '\0')
128     return caps;
129   
130   while (g_ascii_isspace (*r)) r++;
131   if (*r == ';') {
132     r++;
133     while (g_ascii_isspace (*r)) r++;
134     append = gst_caps_from_string_func (r);
135     if (!append) {
136       gst_caps_unref (caps);
137       goto error;
138     }
139     gst_caps_append (caps, append);
140   }
141
142   return caps;
143
144 error:
145   return NULL;
146 }
147 /**
148  * gst_caps_from_string:
149  * str: the str to convert into caps
150  *
151  * Tries to convert a string into a #GstCaps. This is mainly intended for
152  * debugging purposes. The returned caps are floating.
153  *
154  * Returns: A floating caps or NULL if the string couldn't be converted
155  */
156 GstCaps *
157 gst_caps_from_string (gchar *str)
158 {
159   gchar *s;
160   GstCaps *caps;
161   g_return_val_if_fail (str != NULL, NULL);  
162  
163   s = g_strdup (str);
164   caps = gst_caps_from_string_func (s);
165   g_free (s);
166
167   return caps;
168 }
169 void
170 _gst_caps_initialize (void)
171 {
172   _gst_caps_chunk = gst_mem_chunk_new ("GstCaps",
173                   sizeof (GstCaps), sizeof (GstCaps) * 256,
174                   G_ALLOC_AND_FREE);
175
176   _gst_caps_type = g_boxed_type_register_static ("GstCaps",
177                                        (GBoxedCopyFunc) gst_caps_ref,
178                                        (GBoxedFreeFunc) gst_caps_unref);
179
180   g_value_register_transform_func (_gst_caps_type, G_TYPE_STRING,
181                                    gst_caps_transform_to_string);
182
183 #ifndef GST_DISABLE_TRACE
184   _gst_caps_trace = gst_alloc_trace_register (GST_CAPS_TRACE_NAME);
185 #endif
186 }
187
188 GType
189 gst_caps_get_type (void)
190 {
191   return _gst_caps_type;
192 }
193
194 static guint16
195 get_type_for_mime (const gchar *mime)
196 {
197   guint16 typeid;
198
199   typeid = gst_type_find_by_mime (mime);
200   if (typeid == 0) {
201      GstTypeDefinition definition;
202      GstTypeFactory *factory;
203
204      definition.name = "capstype";
205      definition.mime = g_strdup (mime);
206      definition.exts = NULL;
207      definition.typefindfunc = NULL;
208
209      factory = gst_type_factory_new (&definition);
210
211      typeid = gst_type_register (factory);
212   }
213   return typeid;
214 }
215
216 /**
217  * gst_caps_new:
218  * @name: the name of this capability
219  * @mime: the mime type to attach to the capability
220  * @props: the properties to add to this capability
221  *
222  * Create a new capability with the given mime type and properties.
223  *
224  * Returns: a new capability
225  */
226 GstCaps*
227 gst_caps_new (const gchar *name, const gchar *mime, GstProps *props)
228 {
229   g_return_val_if_fail (mime != NULL, NULL);
230
231   return gst_caps_new_id (name, get_type_for_mime (mime), props);
232 }
233
234 /**
235  * gst_caps_new_id:
236  * @name: the name of this capability
237  * @id: the id of the mime type 
238  * @props: the properties to add to this capability
239  *
240  * Create a new capability with the given mime typeid and properties.
241  *
242  * Returns: a new capability
243  */
244 GstCaps*
245 gst_caps_new_id (const gchar *name, const guint16 id, GstProps *props)
246 {
247   GstCaps *caps;
248
249   caps = gst_mem_chunk_alloc (_gst_caps_chunk);
250 #ifndef GST_DISABLE_TRACE
251   gst_alloc_trace_new (_gst_caps_trace, caps);
252 #endif
253
254   GST_DEBUG (GST_CAT_CAPS, "new %p", caps);
255
256   gst_props_ref (props);
257   gst_props_sink (props);
258
259   caps->name = g_strdup (name);
260   caps->id = id;
261   caps->properties = props;
262   caps->next = NULL;
263   caps->refcount = 1;
264   GST_CAPS_FLAG_SET (caps, GST_CAPS_FLOATING);
265
266   if (props && !GST_PROPS_IS_FIXED (props))
267     GST_CAPS_FLAG_UNSET (caps, GST_CAPS_FIXED);
268   else
269     GST_CAPS_FLAG_SET (caps, GST_CAPS_FIXED);
270
271   return caps;
272 }
273
274 /**
275  * gst_caps_replace:
276  * @oldcaps: the caps to take replace
277  * @newcaps: the caps to take replace 
278  *
279  * Replace the pointer to the caps, doing proper
280  * refcounting.
281  */
282 void
283 gst_caps_replace (GstCaps **oldcaps, GstCaps *newcaps)
284 {
285   if (*oldcaps != newcaps) {
286     if (newcaps)  gst_caps_ref   (newcaps);
287     if (*oldcaps) gst_caps_unref (*oldcaps);
288
289     *oldcaps = newcaps;
290   }
291 }
292
293 /**
294  * gst_caps_replace_sink:
295  * @oldcaps: the caps to take replace
296  * @newcaps: the caps to take replace 
297  *
298  * Replace the pointer to the caps and take ownership.
299  */
300 void
301 gst_caps_replace_sink (GstCaps **oldcaps, GstCaps *newcaps)
302 {
303   gst_caps_replace (oldcaps, newcaps);
304   gst_caps_sink (newcaps);
305 }
306
307 /**
308  * gst_caps_destroy:
309  * @caps: the caps to destroy
310  *
311  * Frees the memory used by this caps structure and all
312  * the chained caps and properties.
313  */
314 static void
315 gst_caps_destroy (GstCaps *caps)
316 {
317   GstCaps *next;
318
319   if (caps == NULL)
320     return;
321
322   next = caps->next;
323
324   GST_DEBUG (GST_CAT_CAPS, "destroy %p", caps);
325
326   gst_props_unref (caps->properties);
327   g_free (caps->name);
328
329 #ifndef GST_DISABLE_TRACE
330   gst_alloc_trace_free (_gst_caps_trace, caps);
331 #endif
332   gst_mem_chunk_free (_gst_caps_chunk, caps);
333
334   if (next) 
335     gst_caps_unref (next);
336 }
337
338 /**
339  * gst_caps_debug:
340  * @caps: the caps to print out
341  * @label: a label to put on the printout, or NULL
342  *
343  * Print out the contents of the caps structure. Useful for debugging.
344  */
345 void
346 gst_caps_debug (GstCaps *caps, const gchar *label)
347 {
348   GST_DEBUG_ENTER ("caps debug: %s", label);
349   while (caps) {
350     GST_DEBUG (GST_CAT_CAPS, "caps: %p %s %s (%sfixed) (refcount %d) %s", 
351                caps, caps->name, gst_caps_get_mime (caps), 
352                GST_CAPS_IS_FIXED (caps) ? "" : "NOT ", caps->refcount,
353                GST_CAPS_IS_FLOATING (caps) ? "FLOATING" : "");
354
355     if (caps->properties) {
356       gst_props_debug (caps->properties);
357     }
358     else {
359       GST_DEBUG (GST_CAT_CAPS, "no properties");
360     }
361
362     caps = caps->next;
363   }
364   GST_DEBUG_LEAVE ("caps debug");
365 }
366
367 /**
368  * gst_caps_unref:
369  * @caps: the caps to unref
370  *
371  * Decrease the refcount of this caps structure, 
372  * destroying it when the refcount is 0
373  *
374  * Returns: caps or NULL if the refcount reached 0
375  */
376 GstCaps*
377 gst_caps_unref (GstCaps *caps)
378 {
379   gboolean zero;
380
381   if (caps == NULL)
382     return NULL;
383
384   g_return_val_if_fail (caps->refcount > 0, NULL);
385
386   GST_DEBUG (GST_CAT_CAPS, "unref %p (%d->%d) %d", 
387              caps, caps->refcount, caps->refcount-1, GST_CAPS_FLAGS (caps));
388
389   caps->refcount--;
390   zero = (caps->refcount == 0);
391
392   if (zero) {
393     gst_caps_destroy (caps);
394     caps = NULL;
395   }
396   return caps;
397 }
398
399 /**
400  * gst_caps_ref:
401  * @caps: the caps to ref
402  *
403  * Increase the refcount of this caps structure
404  *
405  * Returns: the caps with the refcount incremented
406  */
407 GstCaps*
408 gst_caps_ref (GstCaps *caps)
409 {
410   if (caps == NULL)
411     return NULL;
412
413   g_return_val_if_fail (caps->refcount > 0, NULL);
414
415   GST_DEBUG (GST_CAT_CAPS, "ref %p (%d->%d) %d", 
416              caps, caps->refcount, caps->refcount+1, GST_CAPS_FLAGS (caps));
417
418   caps->refcount++;
419
420   return caps;
421 }
422
423 /**
424  * gst_caps_sink:
425  * @caps: the caps to take ownership of
426  *
427  * Take ownership of a GstCaps
428  */
429 void
430 gst_caps_sink (GstCaps *caps)
431 {
432   if (caps == NULL)
433     return;
434
435   if (GST_CAPS_IS_FLOATING (caps)) {
436     GST_DEBUG (GST_CAT_CAPS, "sink %p", caps);
437
438     GST_CAPS_FLAG_UNSET (caps, GST_CAPS_FLOATING);
439     gst_caps_unref (caps);
440   }
441 }
442
443 /**
444  * gst_caps_copy_1:
445  * @caps: the caps to copy
446  *
447  * Copies the caps, not copying any chained caps.
448  *
449  * Returns: a floating copy of the GstCaps structure.
450  */
451 GstCaps*
452 gst_caps_copy_1 (GstCaps *caps)
453 {
454   GstCaps *newcaps;
455   
456   if (!caps)
457     return NULL;
458
459   newcaps = gst_caps_new_id (
460                   caps->name,
461                   caps->id,
462                   gst_props_copy (caps->properties));
463
464   return newcaps;
465 }
466
467 /**
468  * gst_caps_copy:
469  * @caps: the caps to copy
470  *
471  * Copies the caps.
472  *
473  * Returns: a floating copy of the GstCaps structure.
474  */
475 GstCaps*
476 gst_caps_copy (GstCaps *caps)
477 {
478   GstCaps *new = NULL, *walk = NULL;
479
480   while (caps) {
481     GstCaps *newcaps;
482
483     newcaps = gst_caps_copy_1 (caps);
484
485     if (new == NULL) {
486       new = walk = newcaps;
487     }
488     else {
489       walk = walk->next = newcaps;
490     }
491     caps = caps->next;
492   }
493
494   return new;
495 }
496
497 /**
498  * gst_caps_copy_on_write:
499  * @caps: the caps to copy
500  *
501  * Copies the caps if the refcount is greater than 1
502  *
503  * Returns: a pointer to a GstCaps strcuture that can
504  * be safely written to.
505  */
506 GstCaps*
507 gst_caps_copy_on_write (GstCaps *caps)
508 {
509   GstCaps *new = caps;
510   gboolean needcopy;
511
512   g_return_val_if_fail (caps != NULL, NULL);
513
514   needcopy = (caps->refcount > 1);
515
516   if (needcopy) {
517     new = gst_caps_copy (caps);
518     gst_caps_unref (caps);
519   }
520
521   return new;
522 }
523
524 /**
525  * gst_caps_get_name:
526  * @caps: the caps to get the name from
527  *
528  * Get the name of a GstCaps structure.
529  *
530  * Returns: the name of the caps
531  */
532 const gchar*
533 gst_caps_get_name (GstCaps *caps)
534 {
535   g_return_val_if_fail (caps != NULL, NULL);
536
537   return (const gchar *)caps->name;
538 }
539
540 /**
541  * gst_caps_set_name:
542  * @caps: the caps to set the name to
543  * @name: the name to set
544  *
545  * Set the name of a caps.
546  */
547 void
548 gst_caps_set_name (GstCaps *caps, const gchar *name)
549 {
550   g_return_if_fail (caps != NULL);
551
552   g_free (caps->name);
553   caps->name = g_strdup (name);
554 }
555
556 /**
557  * gst_caps_get_mime:
558  * @caps: the caps to get the mime type from
559  *
560  * Get the mime type of the caps as a string.
561  *
562  * Returns: the mime type of the caps
563  */
564 const gchar*
565 gst_caps_get_mime (GstCaps *caps)
566 {
567   GstType *type;
568
569   g_return_val_if_fail (caps != NULL, NULL);
570
571   type = gst_type_find_by_id (caps->id);
572
573   if (type)
574     return type->mime;
575   else
576     return "unknown/unknown";
577 }
578
579 /**
580  * gst_caps_set_mime:
581  * @caps: the caps to set the mime type to
582  * @mime: the mime type to attach to the caps
583  *
584  * Set the mime type of the caps as a string.
585  */
586 void
587 gst_caps_set_mime (GstCaps *caps, const gchar *mime)
588 {
589   g_return_if_fail (caps != NULL);
590   g_return_if_fail (mime != NULL);
591
592   caps->id = get_type_for_mime (mime);
593 }
594
595 /**
596  * gst_caps_get_type_id:
597  * @caps: the caps to get the type id from
598  *
599  * Get the type id of the caps.
600  *
601  * Returns: the type id of the caps
602  */
603 guint16
604 gst_caps_get_type_id (GstCaps *caps)
605 {
606   g_return_val_if_fail (caps != NULL, 0);
607
608   return caps->id;
609 }
610
611 /**
612  * gst_caps_set_type_id:
613  * @caps: the caps to set the type id to
614  * @type_id: the type id to set
615  *
616  * Set the type id of the caps.
617  */
618 void
619 gst_caps_set_type_id (GstCaps *caps, guint16 type_id)
620 {
621   g_return_if_fail (caps != NULL);
622
623   caps->id = type_id;
624 }
625
626 /**
627  * gst_caps_set_props:
628  * @caps: the caps to attach the properties to
629  * @props: the properties to attach
630  *
631  * Set the properties to the given caps.
632  *
633  * Returns: the new caps structure
634  */
635 GstCaps*
636 gst_caps_set_props (GstCaps *caps, GstProps *props)
637 {
638   g_return_val_if_fail (caps != NULL, caps);
639
640   gst_props_replace_sink (&caps->properties, props);
641
642   if (props && !GST_PROPS_IS_FIXED (props))
643     GST_CAPS_FLAG_UNSET (caps, GST_CAPS_FIXED);
644   else
645     GST_CAPS_FLAG_SET (caps, GST_CAPS_FIXED);
646
647   return caps;
648 }
649
650 /**
651  * gst_caps_get_props:
652  * @caps: the caps to get the properties from
653  *
654  * Get the properties of the given caps.
655  *
656  * Returns: the properties of the caps
657  */
658 GstProps*
659 gst_caps_get_props (GstCaps *caps)
660 {
661   g_return_val_if_fail (caps != NULL, NULL);
662
663   return caps->properties;
664 }
665
666 /**
667  * gst_caps_next:
668  * @caps: the caps to query
669  *
670  * Get the next caps of this chained caps.
671  *
672  * Returns: the next caps or NULL if the chain ended.
673  */
674 GstCaps*
675 gst_caps_next (GstCaps *caps)
676 {
677   if (caps == NULL)
678     return NULL;
679
680   return caps->next;
681 }
682
683 /**
684  * gst_caps_chain:
685  * @caps: a capabilty
686  * @...: more capabilities
687  *
688  * chains the given capabilities
689  *
690  * Returns: the new capability
691  */
692 GstCaps*
693 gst_caps_chain (GstCaps *caps, ...)
694 {
695   GstCaps *orig = caps;
696   va_list var_args;
697
698   va_start (var_args, caps);
699
700   while (caps) {
701     GstCaps *toadd;
702     
703     toadd = va_arg (var_args, GstCaps*);
704     gst_caps_append (caps, toadd);
705     
706     caps = toadd;
707   }
708   va_end (var_args);
709   
710   return orig;
711 }
712
713 /**
714  * gst_caps_append:
715  * @caps: a capabilty
716  * @capstoadd: the capability to append
717  *
718  * Appends a capability to the existing capability.
719  *
720  * Returns: the new capability
721  */
722 GstCaps*
723 gst_caps_append (GstCaps *caps, GstCaps *capstoadd)
724 {
725   GstCaps *orig = caps;
726   
727   if (caps == NULL || caps == capstoadd)
728     return capstoadd;
729   
730   while (caps->next) {
731     caps = caps->next;
732   }
733   gst_caps_replace_sink (&caps->next, capstoadd);
734
735   return orig;
736 }
737
738 /**
739  * gst_caps_prepend:
740  * @caps: a capabilty
741  * @capstoadd: a capabilty to prepend
742  *
743  * prepend the capability to the list of capabilities
744  *
745  * Returns: the new capability
746  */
747 GstCaps*
748 gst_caps_prepend (GstCaps *caps, GstCaps *capstoadd)
749 {
750   GstCaps *orig = capstoadd;
751   
752   if (capstoadd == NULL)
753     return caps;
754
755   g_return_val_if_fail (caps != capstoadd, caps);
756
757   while (capstoadd->next) {
758     capstoadd = capstoadd->next;
759   }
760   gst_caps_replace_sink (&capstoadd->next, caps);
761
762   return orig;
763 }
764
765 /**
766  * gst_caps_get_by_name:
767  * @caps: a capabilty
768  * @name: the name of the capability to get
769  *
770  * Get the capability with the given name from this
771  * chain of capabilities.
772  *
773  * Returns: the first capability in the chain with the 
774  * given name
775  */
776 GstCaps*
777 gst_caps_get_by_name (GstCaps *caps, const gchar *name)
778 {
779   g_return_val_if_fail (caps != NULL, NULL);
780   g_return_val_if_fail (name != NULL, NULL);
781    
782   while (caps) {
783     if (!strcmp (caps->name, name)) 
784       return caps;
785     caps = caps->next;
786   }
787
788   return NULL;
789 }
790                                                                                                                    
791 static gboolean
792 gst_caps_check_compatibility_func (GstCaps *fromcaps, GstCaps *tocaps)
793 {
794   if (fromcaps->id != tocaps->id) {
795     GST_DEBUG (GST_CAT_CAPS,"mime types differ (%s to %s)",
796                gst_type_find_by_id (fromcaps->id)->mime, 
797                gst_type_find_by_id (tocaps->id)->mime);
798     return FALSE;
799   }
800
801   if (tocaps->properties) {
802     if (fromcaps->properties) {
803       return gst_props_check_compatibility (fromcaps->properties, tocaps->properties);
804     }
805     else {
806       GST_DEBUG (GST_CAT_CAPS,"no source caps");
807       return FALSE;
808     }
809   }
810   else {
811     /* assume it accepts everything */
812     GST_DEBUG (GST_CAT_CAPS,"no caps");
813     return TRUE;
814   }
815 }
816
817 /**
818  * gst_caps_is_always_compatible:
819  * @fromcaps: a #GstCaps capability to check compatibility of.
820  * @tocaps: the #GstCaps capability to check compatibility with.
821  *
822  * Checks if a link is always possible from fromcaps to tocaps, for all
823  * possible capabilities.
824  *
825  * Returns: TRUE if compatible under all circumstances, FALSE otherwise.
826  */
827 gboolean
828 gst_caps_is_always_compatible (GstCaps *fromcaps, GstCaps *tocaps)
829 {
830   if (fromcaps == NULL) {
831     if (tocaps == NULL) {
832       /* if both are NULL, they can always link.  Think filesrc ! filesink */
833       GST_DEBUG (GST_CAT_CAPS, "both caps NULL, compatible");
834       return TRUE;
835     }
836     else {
837       /* if source caps are NULL, it could be sending anything, so the
838        * destination can't know if it can accept this.  Think filesrc ! mad */
839       GST_DEBUG (GST_CAT_CAPS, "source caps NULL, not guaranteed compatible");
840       return FALSE;
841     }
842   }
843   else {
844     if (tocaps == NULL) {
845       /* if the dest caps are NULL, the element can accept anything, always,
846        * so they're compatible by definition.  Think mad ! filesink */
847       GST_DEBUG (GST_CAT_CAPS,"destination caps NULL");
848       return TRUE;
849     }
850   }
851
852   while (fromcaps) {
853     GstCaps *destcaps = tocaps;
854     /* assume caps is incompatible */
855     gboolean compat = FALSE;
856
857     while (destcaps && !compat) {
858       if (gst_caps_check_compatibility_func (fromcaps, destcaps)) {
859         compat = TRUE;
860       }
861       destcaps =  destcaps->next;
862     }
863     if (!compat)
864       return FALSE;
865
866     fromcaps =  fromcaps->next;
867   }
868   return TRUE;
869 }
870
871 static GstCaps*
872 gst_caps_intersect_func (GstCaps *caps1, GstCaps *caps2)
873 {
874   GstCaps *result = NULL;
875   GstProps *props;
876
877   if (caps1->id != caps2->id) {
878     GST_DEBUG (GST_CAT_CAPS, "mime types differ (%s to %s)",
879                gst_type_find_by_id (caps1->id)->mime, 
880                gst_type_find_by_id (caps2->id)->mime);
881     return NULL;
882   }
883
884   if (caps1->properties == NULL) {
885     return gst_caps_ref (caps2);
886   }
887   if (caps2->properties == NULL) {
888     return gst_caps_ref (caps1);
889   }
890   
891   props = gst_props_intersect (caps1->properties, caps2->properties);
892   if (props) {
893     result = gst_caps_new_id ("intersect", caps1->id, props);
894     gst_caps_ref (result);
895     gst_caps_sink (result);
896   }
897
898   return result;
899 }
900
901 /**
902  * gst_caps_intersect:
903  * @caps1: a capability
904  * @caps2: a capability
905  *
906  * Make the intersection between two caps.
907  *
908  * Returns: The intersection of the two caps or NULL if the intersection
909  * is empty. unref the caps after use.
910  */
911 GstCaps*
912 gst_caps_intersect (GstCaps *caps1, GstCaps *caps2)
913 {
914   GstCaps *result = NULL, *walk = NULL;
915
916   /* printing the name is not useful here since caps can be chained */
917   GST_DEBUG (GST_CAT_CAPS, "intersecting caps %p and %p", caps1, caps2);
918                   
919   if (caps1 == NULL) {
920     GST_DEBUG (GST_CAT_CAPS, "first caps is NULL, return other caps");
921     return gst_caps_ref (caps2);
922   }
923   if (caps2 == NULL) {
924     GST_DEBUG (GST_CAT_CAPS, "second caps is NULL, return other caps");
925     return gst_caps_ref (caps1);
926   }
927
928   /* same caps */
929   if (caps1 == caps2) {
930     return gst_caps_ref (caps1);
931   }
932
933   while (caps1) {
934     GstCaps *othercaps = caps2;
935
936     while (othercaps) {
937       GstCaps *intersection;
938       
939       intersection = gst_caps_intersect_func (caps1, othercaps);
940
941       if (intersection) {
942         if (!result) {
943           walk = result = intersection;
944         }
945         else {
946           walk = walk->next = intersection;
947         }
948       }
949       othercaps = othercaps->next;
950     }
951     caps1 = caps1->next;
952   }
953
954   return result;
955 }
956
957 GstCaps*
958 gst_caps_union (GstCaps *caps1, GstCaps *caps2)
959 {
960   GstCaps *result = NULL;
961
962   /* printing the name is not useful here since caps can be chained */
963   GST_DEBUG (GST_CAT_CAPS, "making union of caps %p and %p", caps1, caps2);
964                   
965   if (caps1 == NULL) {
966     GST_DEBUG (GST_CAT_CAPS, "first caps is NULL, return other caps");
967     return gst_caps_ref (caps2);
968   }
969   if (caps2 == NULL) {
970     GST_DEBUG (GST_CAT_CAPS, "second caps is NULL, return other caps");
971     return gst_caps_ref (caps1);
972   }
973
974   return result;
975 }
976
977 /**
978  * gst_caps_normalize:
979  * @caps: a capabilty
980  *
981  * Make the normalisation of the caps. This will return a new caps
982  * that is equivalent to the input caps with the exception that all
983  * lists are unrolled. This function is useful when you want to iterate
984  * the caps. unref the caps after use.
985  *
986  * Returns: The normalisation of the caps. Unref after usage.
987  */
988 GstCaps*
989 gst_caps_normalize (GstCaps *caps)
990 {
991   GstCaps *result = NULL, *walk;
992
993   if (caps == NULL)
994     return caps;
995
996   GST_DEBUG (GST_CAT_CAPS, "normalizing caps %p ", caps);
997
998   walk = caps;
999
1000   while (caps) {
1001     GList *proplist;
1002
1003     proplist = gst_props_normalize (caps->properties);
1004     while (proplist) {
1005       GstProps *props = (GstProps *) proplist->data;
1006       GstCaps *newcaps = gst_caps_new_id (caps->name, caps->id, props);
1007
1008       gst_caps_ref (newcaps);
1009       gst_caps_sink (newcaps);
1010
1011       if (result == NULL)
1012         walk = result = newcaps;
1013       else {
1014         walk = walk->next = newcaps;
1015       }
1016       proplist = g_list_next (proplist);  
1017     }
1018     caps = caps->next;
1019   }
1020   return result;
1021 }
1022
1023 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
1024 /**
1025  * gst_caps_save_thyself:
1026  * @caps: a capabilty to save
1027  * @parent: the parent XML node pointer
1028  *
1029  * Save the capability into an XML representation.
1030  *
1031  * Returns: a new XML node pointer
1032  */
1033 xmlNodePtr
1034 gst_caps_save_thyself (GstCaps *caps, xmlNodePtr parent)
1035 {
1036   xmlNodePtr subtree;
1037   xmlNodePtr subsubtree;
1038
1039   while (caps) {
1040     subtree = xmlNewChild (parent, NULL, "capscomp", NULL);
1041
1042     xmlNewChild (subtree, NULL, "name", caps->name);
1043     xmlNewChild (subtree, NULL, "type", gst_type_find_by_id (caps->id)->mime);
1044     if (caps->properties) {
1045       subsubtree = xmlNewChild (subtree, NULL, "properties", NULL);
1046
1047       gst_props_save_thyself (caps->properties, subsubtree);
1048     }
1049
1050     caps = caps->next;
1051   }
1052
1053   return parent;
1054 }
1055
1056 /**
1057  * gst_caps_load_thyself:
1058  * @parent: the parent XML node pointer
1059  *
1060  * Load a new caps from the XML representation.
1061  *
1062  * Returns: a new capability
1063  */
1064 GstCaps*
1065 gst_caps_load_thyself (xmlNodePtr parent)
1066 {
1067   GstCaps *result = NULL;
1068   xmlNodePtr field = parent->xmlChildrenNode;
1069
1070   while (field) {
1071     if (!strcmp (field->name, "capscomp")) {
1072       xmlNodePtr subfield = field->xmlChildrenNode;
1073       GstCaps *caps;
1074       gchar *content;
1075       GstCapsFlags fixed = GST_CAPS_FIXED;
1076
1077       caps = gst_mem_chunk_alloc0 (_gst_caps_chunk);
1078 #ifndef GST_DISABLE_TRACE
1079       gst_alloc_trace_new (_gst_caps_trace, caps);
1080 #endif
1081
1082       caps->refcount = 1;
1083       GST_CAPS_FLAG_SET (caps, GST_CAPS_FLOATING);
1084       caps->next = NULL;
1085         
1086       while (subfield) {
1087         if (!strcmp (subfield->name, "name")) {
1088           caps->name = xmlNodeGetContent (subfield);
1089         }
1090         if (!strcmp (subfield->name, "type")) {
1091           content = xmlNodeGetContent (subfield);
1092           caps->id = get_type_for_mime (content);
1093           g_free (content);
1094         }
1095         else if (!strcmp (subfield->name, "properties")) {
1096           GstProps *props = gst_props_load_thyself (subfield);
1097
1098           gst_props_ref (props);
1099           gst_props_sink (props);
1100           caps->properties = props;
1101
1102           fixed &= (GST_PROPS_IS_FIXED (caps->properties) ? GST_CAPS_FIXED : 0 );
1103         }
1104         
1105         subfield = subfield->next;
1106       }
1107       GST_CAPS_FLAG_SET (caps, fixed);
1108
1109       result = gst_caps_append (result, caps);
1110     }
1111     field = field->next;
1112   }
1113
1114   return result;
1115 }
1116
1117 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */