help us debug caps ref issues
[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   if (caps && caps->refcount == 0) {
350     g_warning ("Warning: refcount of caps %s is 0", label);
351     return;
352   }
353   while (caps) {
354     GST_DEBUG (GST_CAT_CAPS, "caps: %p %s %s (%sfixed) (refcount %d) %s", 
355                caps, caps->name, gst_caps_get_mime (caps), 
356                GST_CAPS_IS_FIXED (caps) ? "" : "NOT ", caps->refcount,
357                GST_CAPS_IS_FLOATING (caps) ? "FLOATING" : "");
358
359     if (caps->properties) {
360       gst_props_debug (caps->properties);
361     }
362     else {
363       GST_DEBUG (GST_CAT_CAPS, "no properties");
364     }
365
366     caps = caps->next;
367   }
368   GST_DEBUG_LEAVE ("caps debug");
369 }
370
371 /**
372  * gst_caps_unref:
373  * @caps: the caps to unref
374  *
375  * Decrease the refcount of this caps structure, 
376  * destroying it when the refcount is 0
377  *
378  * Returns: caps or NULL if the refcount reached 0
379  */
380 GstCaps*
381 gst_caps_unref (GstCaps *caps)
382 {
383   gboolean zero;
384
385   if (caps == NULL)
386     return NULL;
387
388   g_return_val_if_fail (caps->refcount > 0, NULL);
389
390   GST_DEBUG (GST_CAT_CAPS, "unref %p (%d->%d) %d", 
391              caps, caps->refcount, caps->refcount-1, GST_CAPS_FLAGS (caps));
392
393   caps->refcount--;
394   zero = (caps->refcount == 0);
395
396   if (zero) {
397     gst_caps_destroy (caps);
398     caps = NULL;
399   }
400   return caps;
401 }
402
403 /**
404  * gst_caps_ref:
405  * @caps: the caps to ref
406  *
407  * Increase the refcount of this caps structure
408  *
409  * Returns: the caps with the refcount incremented
410  */
411 GstCaps*
412 gst_caps_ref (GstCaps *caps)
413 {
414   if (caps == NULL)
415     return NULL;
416
417   g_return_val_if_fail (caps->refcount > 0, NULL);
418
419   GST_DEBUG (GST_CAT_CAPS, "ref %p (%d->%d) %d", 
420              caps, caps->refcount, caps->refcount+1, GST_CAPS_FLAGS (caps));
421
422   caps->refcount++;
423
424   return caps;
425 }
426
427 /**
428  * gst_caps_sink:
429  * @caps: the caps to take ownership of
430  *
431  * Take ownership of a GstCaps
432  */
433 void
434 gst_caps_sink (GstCaps *caps)
435 {
436   if (caps == NULL)
437     return;
438
439   if (GST_CAPS_IS_FLOATING (caps)) {
440     GST_DEBUG (GST_CAT_CAPS, "sink %p", caps);
441
442     GST_CAPS_FLAG_UNSET (caps, GST_CAPS_FLOATING);
443     gst_caps_unref (caps);
444   }
445 }
446
447 /**
448  * gst_caps_copy_1:
449  * @caps: the caps to copy
450  *
451  * Copies the caps, not copying any chained caps.
452  *
453  * Returns: a floating copy of the GstCaps structure.
454  */
455 GstCaps*
456 gst_caps_copy_1 (GstCaps *caps)
457 {
458   GstCaps *newcaps;
459   
460   if (!caps)
461     return NULL;
462
463   newcaps = gst_caps_new_id (
464                   caps->name,
465                   caps->id,
466                   gst_props_copy (caps->properties));
467
468   return newcaps;
469 }
470
471 /**
472  * gst_caps_copy:
473  * @caps: the caps to copy
474  *
475  * Copies the caps.
476  *
477  * Returns: a floating copy of the GstCaps structure.
478  */
479 GstCaps*
480 gst_caps_copy (GstCaps *caps)
481 {
482   GstCaps *new = NULL, *walk = NULL;
483
484   while (caps) {
485     GstCaps *newcaps;
486
487     newcaps = gst_caps_copy_1 (caps);
488
489     if (new == NULL) {
490       new = walk = newcaps;
491     }
492     else {
493       walk = walk->next = newcaps;
494     }
495     caps = caps->next;
496   }
497
498   return new;
499 }
500
501 /**
502  * gst_caps_copy_on_write:
503  * @caps: the caps to copy
504  *
505  * Copies the caps if the refcount is greater than 1
506  *
507  * Returns: a pointer to a GstCaps strcuture that can
508  * be safely written to.
509  */
510 GstCaps*
511 gst_caps_copy_on_write (GstCaps *caps)
512 {
513   GstCaps *new = caps;
514   gboolean needcopy;
515
516   g_return_val_if_fail (caps != NULL, NULL);
517
518   needcopy = (caps->refcount > 1);
519
520   if (needcopy) {
521     new = gst_caps_copy (caps);
522     gst_caps_unref (caps);
523   }
524
525   return new;
526 }
527
528 /**
529  * gst_caps_get_name:
530  * @caps: the caps to get the name from
531  *
532  * Get the name of a GstCaps structure.
533  *
534  * Returns: the name of the caps
535  */
536 const gchar*
537 gst_caps_get_name (GstCaps *caps)
538 {
539   g_return_val_if_fail (caps != NULL, NULL);
540
541   return (const gchar *)caps->name;
542 }
543
544 /**
545  * gst_caps_set_name:
546  * @caps: the caps to set the name to
547  * @name: the name to set
548  *
549  * Set the name of a caps.
550  */
551 void
552 gst_caps_set_name (GstCaps *caps, const gchar *name)
553 {
554   g_return_if_fail (caps != NULL);
555
556   g_free (caps->name);
557   caps->name = g_strdup (name);
558 }
559
560 /**
561  * gst_caps_get_mime:
562  * @caps: the caps to get the mime type from
563  *
564  * Get the mime type of the caps as a string.
565  *
566  * Returns: the mime type of the caps
567  */
568 const gchar*
569 gst_caps_get_mime (GstCaps *caps)
570 {
571   GstType *type;
572
573   g_return_val_if_fail (caps != NULL, NULL);
574
575   type = gst_type_find_by_id (caps->id);
576
577   if (type)
578     return type->mime;
579   else
580     return "unknown/unknown";
581 }
582
583 /**
584  * gst_caps_set_mime:
585  * @caps: the caps to set the mime type to
586  * @mime: the mime type to attach to the caps
587  *
588  * Set the mime type of the caps as a string.
589  */
590 void
591 gst_caps_set_mime (GstCaps *caps, const gchar *mime)
592 {
593   g_return_if_fail (caps != NULL);
594   g_return_if_fail (mime != NULL);
595
596   caps->id = get_type_for_mime (mime);
597 }
598
599 /**
600  * gst_caps_get_type_id:
601  * @caps: the caps to get the type id from
602  *
603  * Get the type id of the caps.
604  *
605  * Returns: the type id of the caps
606  */
607 guint16
608 gst_caps_get_type_id (GstCaps *caps)
609 {
610   g_return_val_if_fail (caps != NULL, 0);
611
612   return caps->id;
613 }
614
615 /**
616  * gst_caps_set_type_id:
617  * @caps: the caps to set the type id to
618  * @type_id: the type id to set
619  *
620  * Set the type id of the caps.
621  */
622 void
623 gst_caps_set_type_id (GstCaps *caps, guint16 type_id)
624 {
625   g_return_if_fail (caps != NULL);
626
627   caps->id = type_id;
628 }
629
630 /**
631  * gst_caps_set_props:
632  * @caps: the caps to attach the properties to
633  * @props: the properties to attach
634  *
635  * Set the properties to the given caps.
636  *
637  * Returns: the new caps structure
638  */
639 GstCaps*
640 gst_caps_set_props (GstCaps *caps, GstProps *props)
641 {
642   g_return_val_if_fail (caps != NULL, caps);
643
644   gst_props_replace_sink (&caps->properties, props);
645
646   if (props && !GST_PROPS_IS_FIXED (props))
647     GST_CAPS_FLAG_UNSET (caps, GST_CAPS_FIXED);
648   else
649     GST_CAPS_FLAG_SET (caps, GST_CAPS_FIXED);
650
651   return caps;
652 }
653
654 /**
655  * gst_caps_get_props:
656  * @caps: the caps to get the properties from
657  *
658  * Get the properties of the given caps.
659  *
660  * Returns: the properties of the caps
661  */
662 GstProps*
663 gst_caps_get_props (GstCaps *caps)
664 {
665   g_return_val_if_fail (caps != NULL, NULL);
666
667   return caps->properties;
668 }
669
670 /**
671  * gst_caps_next:
672  * @caps: the caps to query
673  *
674  * Get the next caps of this chained caps.
675  *
676  * Returns: the next caps or NULL if the chain ended.
677  */
678 GstCaps*
679 gst_caps_next (GstCaps *caps)
680 {
681   if (caps == NULL)
682     return NULL;
683
684   return caps->next;
685 }
686
687 /**
688  * gst_caps_chain:
689  * @caps: a capabilty
690  * @...: more capabilities
691  *
692  * chains the given capabilities
693  *
694  * Returns: the new capability
695  */
696 GstCaps*
697 gst_caps_chain (GstCaps *caps, ...)
698 {
699   GstCaps *orig = caps;
700   va_list var_args;
701
702   va_start (var_args, caps);
703
704   while (caps) {
705     GstCaps *toadd;
706     
707     toadd = va_arg (var_args, GstCaps*);
708     gst_caps_append (caps, toadd);
709     
710     caps = toadd;
711   }
712   va_end (var_args);
713   
714   return orig;
715 }
716
717 /**
718  * gst_caps_append:
719  * @caps: a capabilty
720  * @capstoadd: the capability to append
721  *
722  * Appends a capability to the existing capability.
723  *
724  * Returns: the new capability
725  */
726 GstCaps*
727 gst_caps_append (GstCaps *caps, GstCaps *capstoadd)
728 {
729   GstCaps *orig = caps;
730   
731   if (caps == NULL || caps == capstoadd)
732     return capstoadd;
733   
734   while (caps->next) {
735     caps = caps->next;
736   }
737   gst_caps_replace_sink (&caps->next, capstoadd);
738
739   return orig;
740 }
741
742 /**
743  * gst_caps_prepend:
744  * @caps: a capabilty
745  * @capstoadd: a capabilty to prepend
746  *
747  * prepend the capability to the list of capabilities
748  *
749  * Returns: the new capability
750  */
751 GstCaps*
752 gst_caps_prepend (GstCaps *caps, GstCaps *capstoadd)
753 {
754   GstCaps *orig = capstoadd;
755   
756   if (capstoadd == NULL)
757     return caps;
758
759   g_return_val_if_fail (caps != capstoadd, caps);
760
761   while (capstoadd->next) {
762     capstoadd = capstoadd->next;
763   }
764   gst_caps_replace_sink (&capstoadd->next, caps);
765
766   return orig;
767 }
768
769 /**
770  * gst_caps_get_by_name:
771  * @caps: a capabilty
772  * @name: the name of the capability to get
773  *
774  * Get the capability with the given name from this
775  * chain of capabilities.
776  *
777  * Returns: the first capability in the chain with the 
778  * given name
779  */
780 GstCaps*
781 gst_caps_get_by_name (GstCaps *caps, const gchar *name)
782 {
783   g_return_val_if_fail (caps != NULL, NULL);
784   g_return_val_if_fail (name != NULL, NULL);
785    
786   while (caps) {
787     if (!strcmp (caps->name, name)) 
788       return caps;
789     caps = caps->next;
790   }
791
792   return NULL;
793 }
794                                                                                                                    
795 static gboolean
796 gst_caps_check_compatibility_func (GstCaps *fromcaps, GstCaps *tocaps)
797 {
798   if (fromcaps->id != tocaps->id) {
799     GST_DEBUG (GST_CAT_CAPS,"mime types differ (%s to %s)",
800                gst_type_find_by_id (fromcaps->id)->mime, 
801                gst_type_find_by_id (tocaps->id)->mime);
802     return FALSE;
803   }
804
805   if (tocaps->properties) {
806     if (fromcaps->properties) {
807       return gst_props_check_compatibility (fromcaps->properties, tocaps->properties);
808     }
809     else {
810       GST_DEBUG (GST_CAT_CAPS,"no source caps");
811       return FALSE;
812     }
813   }
814   else {
815     /* assume it accepts everything */
816     GST_DEBUG (GST_CAT_CAPS,"no caps");
817     return TRUE;
818   }
819 }
820
821 /**
822  * gst_caps_is_always_compatible:
823  * @fromcaps: a #GstCaps capability to check compatibility of.
824  * @tocaps: the #GstCaps capability to check compatibility with.
825  *
826  * Checks if a link is always possible from fromcaps to tocaps, for all
827  * possible capabilities.
828  *
829  * Returns: TRUE if compatible under all circumstances, FALSE otherwise.
830  */
831 gboolean
832 gst_caps_is_always_compatible (GstCaps *fromcaps, GstCaps *tocaps)
833 {
834   if (fromcaps == NULL) {
835     if (tocaps == NULL) {
836       /* if both are NULL, they can always link.  Think filesrc ! filesink */
837       GST_DEBUG (GST_CAT_CAPS, "both caps NULL, compatible");
838       return TRUE;
839     }
840     else {
841       /* if source caps are NULL, it could be sending anything, so the
842        * destination can't know if it can accept this.  Think filesrc ! mad */
843       GST_DEBUG (GST_CAT_CAPS, "source caps NULL, not guaranteed compatible");
844       return FALSE;
845     }
846   }
847   else {
848     if (tocaps == NULL) {
849       /* if the dest caps are NULL, the element can accept anything, always,
850        * so they're compatible by definition.  Think mad ! filesink */
851       GST_DEBUG (GST_CAT_CAPS,"destination caps NULL");
852       return TRUE;
853     }
854   }
855
856   while (fromcaps) {
857     GstCaps *destcaps = tocaps;
858     /* assume caps is incompatible */
859     gboolean compat = FALSE;
860
861     while (destcaps && !compat) {
862       if (gst_caps_check_compatibility_func (fromcaps, destcaps)) {
863         compat = TRUE;
864       }
865       destcaps =  destcaps->next;
866     }
867     if (!compat)
868       return FALSE;
869
870     fromcaps =  fromcaps->next;
871   }
872   return TRUE;
873 }
874
875 static GstCaps*
876 gst_caps_intersect_func (GstCaps *caps1, GstCaps *caps2)
877 {
878   GstCaps *result = NULL;
879   GstProps *props;
880
881   if (caps1->id != caps2->id) {
882     GST_DEBUG (GST_CAT_CAPS, "mime types differ (%s to %s)",
883                gst_type_find_by_id (caps1->id)->mime, 
884                gst_type_find_by_id (caps2->id)->mime);
885     return NULL;
886   }
887
888   if (caps1->properties == NULL) {
889     return gst_caps_ref (caps2);
890   }
891   if (caps2->properties == NULL) {
892     return gst_caps_ref (caps1);
893   }
894   
895   props = gst_props_intersect (caps1->properties, caps2->properties);
896   if (props) {
897     result = gst_caps_new_id ("intersect", caps1->id, props);
898     gst_caps_ref (result);
899     gst_caps_sink (result);
900   }
901
902   return result;
903 }
904
905 /**
906  * gst_caps_intersect:
907  * @caps1: a capability
908  * @caps2: a capability
909  *
910  * Make the intersection between two caps.
911  *
912  * Returns: The intersection of the two caps or NULL if the intersection
913  * is empty. unref the caps after use.
914  */
915 GstCaps*
916 gst_caps_intersect (GstCaps *caps1, GstCaps *caps2)
917 {
918   GstCaps *result = NULL, *walk = NULL;
919
920   /* printing the name is not useful here since caps can be chained */
921   GST_DEBUG (GST_CAT_CAPS, "intersecting caps %p and %p", caps1, caps2);
922                   
923   if (caps1 == NULL) {
924     GST_DEBUG (GST_CAT_CAPS, "first caps is NULL, return other caps");
925     return gst_caps_ref (caps2);
926   }
927   if (caps2 == NULL) {
928     GST_DEBUG (GST_CAT_CAPS, "second caps is NULL, return other caps");
929     return gst_caps_ref (caps1);
930   }
931
932   /* same caps */
933   if (caps1 == caps2) {
934     return gst_caps_ref (caps1);
935   }
936
937   while (caps1) {
938     GstCaps *othercaps = caps2;
939
940     while (othercaps) {
941       GstCaps *intersection;
942       
943       intersection = gst_caps_intersect_func (caps1, othercaps);
944
945       if (intersection) {
946         if (!result) {
947           walk = result = intersection;
948         }
949         else {
950           walk = walk->next = intersection;
951         }
952       }
953       othercaps = othercaps->next;
954     }
955     caps1 = caps1->next;
956   }
957
958   return result;
959 }
960
961 GstCaps*
962 gst_caps_union (GstCaps *caps1, GstCaps *caps2)
963 {
964   GstCaps *result = NULL;
965
966   /* printing the name is not useful here since caps can be chained */
967   GST_DEBUG (GST_CAT_CAPS, "making union of caps %p and %p", caps1, caps2);
968                   
969   if (caps1 == NULL) {
970     GST_DEBUG (GST_CAT_CAPS, "first caps is NULL, return other caps");
971     return gst_caps_ref (caps2);
972   }
973   if (caps2 == NULL) {
974     GST_DEBUG (GST_CAT_CAPS, "second caps is NULL, return other caps");
975     return gst_caps_ref (caps1);
976   }
977
978   return result;
979 }
980
981 /**
982  * gst_caps_normalize:
983  * @caps: a capabilty
984  *
985  * Make the normalisation of the caps. This will return a new caps
986  * that is equivalent to the input caps with the exception that all
987  * lists are unrolled. This function is useful when you want to iterate
988  * the caps. unref the caps after use.
989  *
990  * Returns: The normalisation of the caps. Unref after usage.
991  */
992 GstCaps*
993 gst_caps_normalize (GstCaps *caps)
994 {
995   GstCaps *result = NULL, *walk;
996
997   if (caps == NULL)
998     return caps;
999
1000   GST_DEBUG (GST_CAT_CAPS, "normalizing caps %p ", caps);
1001
1002   walk = caps;
1003
1004   while (caps) {
1005     GList *proplist;
1006
1007     proplist = gst_props_normalize (caps->properties);
1008     while (proplist) {
1009       GstProps *props = (GstProps *) proplist->data;
1010       GstCaps *newcaps = gst_caps_new_id (caps->name, caps->id, props);
1011
1012       gst_caps_ref (newcaps);
1013       gst_caps_sink (newcaps);
1014
1015       if (result == NULL)
1016         walk = result = newcaps;
1017       else {
1018         walk = walk->next = newcaps;
1019       }
1020       proplist = g_list_next (proplist);  
1021     }
1022     caps = caps->next;
1023   }
1024   return result;
1025 }
1026
1027 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
1028 /**
1029  * gst_caps_save_thyself:
1030  * @caps: a capabilty to save
1031  * @parent: the parent XML node pointer
1032  *
1033  * Save the capability into an XML representation.
1034  *
1035  * Returns: a new XML node pointer
1036  */
1037 xmlNodePtr
1038 gst_caps_save_thyself (GstCaps *caps, xmlNodePtr parent)
1039 {
1040   xmlNodePtr subtree;
1041   xmlNodePtr subsubtree;
1042
1043   while (caps) {
1044     subtree = xmlNewChild (parent, NULL, "capscomp", NULL);
1045
1046     xmlNewChild (subtree, NULL, "name", caps->name);
1047     xmlNewChild (subtree, NULL, "type", gst_type_find_by_id (caps->id)->mime);
1048     if (caps->properties) {
1049       subsubtree = xmlNewChild (subtree, NULL, "properties", NULL);
1050
1051       gst_props_save_thyself (caps->properties, subsubtree);
1052     }
1053
1054     caps = caps->next;
1055   }
1056
1057   return parent;
1058 }
1059
1060 /**
1061  * gst_caps_load_thyself:
1062  * @parent: the parent XML node pointer
1063  *
1064  * Load a new caps from the XML representation.
1065  *
1066  * Returns: a new capability
1067  */
1068 GstCaps*
1069 gst_caps_load_thyself (xmlNodePtr parent)
1070 {
1071   GstCaps *result = NULL;
1072   xmlNodePtr field = parent->xmlChildrenNode;
1073
1074   while (field) {
1075     if (!strcmp (field->name, "capscomp")) {
1076       xmlNodePtr subfield = field->xmlChildrenNode;
1077       GstCaps *caps;
1078       gchar *content;
1079       GstCapsFlags fixed = GST_CAPS_FIXED;
1080
1081       caps = gst_mem_chunk_alloc0 (_gst_caps_chunk);
1082 #ifndef GST_DISABLE_TRACE
1083       gst_alloc_trace_new (_gst_caps_trace, caps);
1084 #endif
1085
1086       caps->refcount = 1;
1087       GST_CAPS_FLAG_SET (caps, GST_CAPS_FLOATING);
1088       caps->next = NULL;
1089         
1090       while (subfield) {
1091         if (!strcmp (subfield->name, "name")) {
1092           caps->name = xmlNodeGetContent (subfield);
1093         }
1094         if (!strcmp (subfield->name, "type")) {
1095           content = xmlNodeGetContent (subfield);
1096           caps->id = get_type_for_mime (content);
1097           g_free (content);
1098         }
1099         else if (!strcmp (subfield->name, "properties")) {
1100           GstProps *props = gst_props_load_thyself (subfield);
1101
1102           gst_props_ref (props);
1103           gst_props_sink (props);
1104           caps->properties = props;
1105
1106           fixed &= (GST_PROPS_IS_FIXED (caps->properties) ? GST_CAPS_FIXED : 0 );
1107         }
1108         
1109         subfield = subfield->next;
1110       }
1111       GST_CAPS_FLAG_SET (caps, fixed);
1112
1113       result = gst_caps_append (result, caps);
1114     }
1115     field = field->next;
1116   }
1117
1118   return result;
1119 }
1120
1121 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */