- Removed unused locking from the cothreads
[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 #include "gstlog.h"
29
30 static GMemChunk *_gst_caps_chunk;
31 static GMutex *_gst_caps_chunk_lock;
32
33 GType _gst_caps_type;
34
35 void
36 _gst_caps_initialize (void)
37 {
38   _gst_caps_chunk = g_mem_chunk_new ("GstCaps",
39                   sizeof (GstCaps), sizeof (GstCaps) * 256,
40                   G_ALLOC_AND_FREE);
41   _gst_caps_chunk_lock = g_mutex_new ();
42
43   _gst_caps_type = g_boxed_type_register_static ("GstCaps",
44                                        (GBoxedCopyFunc) gst_caps_ref,
45                                        (GBoxedFreeFunc) gst_caps_unref);
46
47 }
48
49 static guint16
50 get_type_for_mime (const gchar *mime)
51 {
52   guint16 typeid;
53
54   typeid = gst_type_find_by_mime (mime);
55   if (typeid == 0) {
56      GstTypeDefinition definition;
57      GstTypeFactory *factory;
58
59      definition.name = "capstype";
60      definition.mime = g_strdup (mime);
61      definition.exts = NULL;
62      definition.typefindfunc = NULL;
63
64      factory = gst_type_factory_new (&definition);
65
66      typeid = gst_type_register (factory);
67   }
68   return typeid;
69 }
70
71 /**
72  * gst_caps_new:
73  * @name: the name of this capability
74  * @mime: the mime type to attach to the capability
75  * @props: the properties to add to this capability
76  *
77  * Create a new capability with the given mime typei and properties.
78  *
79  * Returns: a new capability
80  */
81 GstCaps*
82 gst_caps_new (const gchar *name, const gchar *mime, GstProps *props)
83 {
84   g_return_val_if_fail (mime != NULL, NULL);
85
86   return gst_caps_new_id (name, get_type_for_mime (mime), props);
87 }
88
89 /**
90  * gst_caps_new_id:
91  * @name: the name of this capability
92  * @id: the id of the mime type 
93  * @props: the properties to add to this capability
94  *
95  * Create a new capability with the given mime typeid and properties.
96  *
97  * Returns: a new capability
98  */
99 GstCaps*
100 gst_caps_new_id (const gchar *name, const guint16 id, GstProps *props)
101 {
102   GstCaps *caps;
103
104   g_mutex_lock (_gst_caps_chunk_lock);
105   caps = g_mem_chunk_alloc (_gst_caps_chunk);
106   g_mutex_unlock (_gst_caps_chunk_lock);
107
108   caps->name = g_strdup (name);
109   caps->id = id;
110   caps->properties = props;
111   caps->next = NULL;
112   caps->refcount = 1;
113   if (props)
114     caps->fixed = props->fixed;
115   else
116     caps->fixed = TRUE;
117
118   return caps;
119 }
120
121 /**
122  * gst_caps_destroy:
123  * @caps: the caps to destroy
124  *
125  * Frees the memory used by this caps structure and all
126  * the chained caps and properties.
127  */
128 void
129 gst_caps_destroy (GstCaps *caps)
130 {
131   GstCaps *next;
132
133   if (caps == NULL)
134     return;
135   
136   next = caps->next;
137
138   gst_props_unref (caps->properties);
139   g_free (caps->name);
140   g_mutex_lock (_gst_caps_chunk_lock);
141   g_mem_chunk_free (_gst_caps_chunk, caps);
142   g_mutex_unlock (_gst_caps_chunk_lock);
143
144   if (next) 
145     gst_caps_unref (next);
146 }
147
148 /**
149  * gst_caps_debug:
150  * @caps: the caps to print out
151  * @label: a label to put on the printout, or NULL
152  *
153  * Print out the contents of the caps structure. Useful for debugging.
154  */
155 void
156 gst_caps_debug (GstCaps *caps, const gchar *label)
157 {
158   GST_DEBUG_ENTER ("caps debug: %s", label);
159   while (caps) {
160     GST_DEBUG (GST_CAT_CAPS, "caps: %p %s %s (%sfixed)", caps, caps->name, gst_caps_get_mime (caps), 
161                caps->fixed ? "" : "NOT ");
162
163     if (caps->properties) {
164       gst_props_debug (caps->properties);
165     }
166     else {
167       GST_DEBUG (GST_CAT_CAPS, "no properties");
168     }
169
170     caps = caps->next;
171   }
172   GST_DEBUG_LEAVE ("caps debug");
173 }
174
175 /**
176  * gst_caps_unref:
177  * @caps: the caps to unref
178  *
179  * Decrease the refcount of this caps structure, 
180  * destroying it when the refcount is 0
181  *
182  * Returns: caps or NULL if the refcount reached 0
183  */
184 GstCaps*
185 gst_caps_unref (GstCaps *caps)
186 {
187   gboolean zero;
188   GstCaps **next;
189
190   if (caps == NULL)
191     return NULL;
192
193   g_return_val_if_fail (caps->refcount > 0, NULL);
194
195   caps->refcount--;
196   zero = (caps->refcount == 0);
197   next = &caps->next;
198
199   if (*next)
200     *next = gst_caps_unref (*next);
201
202   if (zero) {
203     gst_caps_destroy (caps);
204     caps = NULL;
205   }
206   return caps;
207 }
208
209 /**
210  * gst_caps_ref:
211  * @caps: the caps to ref
212  *
213  * Increase the refcount of this caps structure
214  *
215  * Returns: the caps with the refcount incremented
216  */
217 GstCaps*
218 gst_caps_ref (GstCaps *caps)
219 {
220   g_return_val_if_fail (caps != NULL, NULL);
221
222   caps->refcount++;
223
224   return caps;
225 }
226
227 /**
228  * gst_caps_copy_1:
229  * @caps: the caps to copy
230  *
231  * Copies the caps, not copying any chained caps.
232  *
233  * Returns: a copy of the GstCaps structure.
234  */
235 GstCaps*
236 gst_caps_copy_1 (GstCaps *caps)
237 {
238   GstCaps *newcaps;
239   
240   if (!caps)
241     return NULL;
242
243   newcaps = gst_caps_new_id (
244                   caps->name,
245                   caps->id,
246                   gst_props_copy (caps->properties));
247
248   return newcaps;
249 }
250
251 /**
252  * gst_caps_copy:
253  * @caps: the caps to copy
254  *
255  * Copies the caps.
256  *
257  * Returns: a copy of the GstCaps structure.
258  */
259 GstCaps*
260 gst_caps_copy (GstCaps *caps)
261 {
262   GstCaps *new = NULL, *walk = NULL;
263
264   while (caps) {
265     GstCaps *newcaps;
266
267     newcaps = gst_caps_copy_1 (caps);
268
269     if (new == NULL) {
270       new = walk = newcaps;
271     }
272     else {
273       walk = walk->next = newcaps;
274     }
275     caps = caps->next;
276   }
277
278   return new;
279 }
280
281 /**
282  * gst_caps_copy_on_write:
283  * @caps: the caps to copy
284  *
285  * Copies the caps if the refcount is greater than 1
286  *
287  * Returns: a pointer to a GstCaps strcuture that can
288  * be safely written to
289  */
290 GstCaps*
291 gst_caps_copy_on_write (GstCaps *caps)
292 {
293   GstCaps *new = caps;
294   gboolean needcopy;
295
296   g_return_val_if_fail (caps != NULL, NULL);
297
298   needcopy = (caps->refcount > 1);
299
300   if (needcopy) {
301     new = gst_caps_copy (caps);
302     gst_caps_unref (caps);
303   }
304
305   return new;
306 }
307
308 /**
309  * gst_caps_get_name:
310  * @caps: the caps to get the name from
311  *
312  * Get the name of a GstCaps structure.
313  *
314  * Returns: the name of the caps
315  */
316 const gchar*
317 gst_caps_get_name (GstCaps *caps)
318 {
319   g_return_val_if_fail (caps != NULL, NULL);
320
321   return (const gchar *)caps->name;
322 }
323
324 /**
325  * gst_caps_set_name:
326  * @caps: the caps to set the name to
327  * @name: the name to set
328  *
329  * Set the name of a caps.
330  */
331 void
332 gst_caps_set_name (GstCaps *caps, const gchar *name)
333 {
334   g_return_if_fail (caps != NULL);
335
336   if (caps->name)
337     g_free (caps->name);
338
339   caps->name = g_strdup (name);
340 }
341
342 /**
343  * gst_caps_get_mime:
344  * @caps: the caps to get the mime type from
345  *
346  * Get the mime type of the caps as a string.
347  *
348  * Returns: the mime type of the caps
349  */
350 const gchar*
351 gst_caps_get_mime (GstCaps *caps)
352 {
353   GstType *type;
354
355   g_return_val_if_fail (caps != NULL, NULL);
356
357   type = gst_type_find_by_id (caps->id);
358
359   if (type)
360     return type->mime;
361   else
362     return "unknown/unknown";
363 }
364
365 /**
366  * gst_caps_set_mime:
367  * @caps: the caps to set the mime type to
368  * @mime: the mime type to attach to the caps
369  *
370  * Set the mime type of the caps as a string.
371  */
372 void
373 gst_caps_set_mime (GstCaps *caps, const gchar *mime)
374 {
375   g_return_if_fail (caps != NULL);
376   g_return_if_fail (mime != NULL);
377
378   caps->id = get_type_for_mime (mime);
379 }
380
381 /**
382  * gst_caps_get_type_id:
383  * @caps: the caps to get the type id from
384  *
385  * Get the type id of the caps.
386  *
387  * Returns: the type id of the caps
388  */
389 guint16
390 gst_caps_get_type_id (GstCaps *caps)
391 {
392   g_return_val_if_fail (caps != NULL, 0);
393
394   return caps->id;
395 }
396
397 /**
398  * gst_caps_set_type_id:
399  * @caps: the caps to set the type id to
400  * @type_id: the type id to set
401  *
402  * Set the type id of the caps.
403  */
404 void
405 gst_caps_set_type_id (GstCaps *caps, guint16 type_id)
406 {
407   g_return_if_fail (caps != NULL);
408
409   caps->id = type_id;
410 }
411
412 /**
413  * gst_caps_set_props:
414  * @caps: the caps to attach the properties to
415  * @props: the properties to attach
416  *
417  * Set the properties to the given caps.
418  *
419  * Returns: the new caps structure
420  */
421 GstCaps*
422 gst_caps_set_props (GstCaps *caps, GstProps *props)
423 {
424   g_return_val_if_fail (caps != NULL, caps);
425   g_return_val_if_fail (props != NULL, caps);
426   g_return_val_if_fail (caps->properties == NULL, caps);
427
428   caps->properties = props;
429
430   return caps;
431 }
432
433 /**
434  * gst_caps_get_props:
435  * @caps: the caps to get the properties from
436  *
437  * Get the properties of the given caps.
438  *
439  * Returns: the properties of the caps
440  */
441 GstProps*
442 gst_caps_get_props (GstCaps *caps)
443 {
444   g_return_val_if_fail (caps != NULL, NULL);
445
446   return caps->properties;
447 }
448
449 /**
450  * gst_caps_chain:
451  * @caps: a capabilty
452  * @...: more capabilities
453  *
454  * chains the given capabilities
455  *
456  * Returns: the new capability
457  */
458 GstCaps*
459 gst_caps_chain (GstCaps *caps, ...)
460 {
461   GstCaps *orig = caps;
462   va_list var_args;
463
464   va_start (var_args, caps);
465
466   while (caps) {
467     GstCaps *toadd;
468     
469     toadd = va_arg (var_args, GstCaps*);
470     gst_caps_append (caps, toadd);
471     
472     caps = toadd;
473   }
474   va_end (var_args);
475   
476   return orig;
477 }
478
479 /**
480  * gst_caps_append:
481  * @caps: a capabilty
482  * @capstoadd: the capability to append
483  *
484  * Appends a capability to the existing capability.
485  *
486  * Returns: the new capability
487  */
488 GstCaps*
489 gst_caps_append (GstCaps *caps, GstCaps *capstoadd)
490 {
491   GstCaps *orig = caps;
492   
493   if (caps == NULL || caps == capstoadd)
494     return capstoadd;
495   
496   while (caps->next) {
497     caps = caps->next;
498   }
499   caps->next = capstoadd;
500
501   return orig;
502 }
503
504 /**
505  * gst_caps_prepend:
506  * @caps: a capabilty
507  * @capstoadd: a capabilty to prepend
508  *
509  * prepend the capability to the list of capabilities
510  *
511  * Returns: the new capability
512  */
513 GstCaps*
514 gst_caps_prepend (GstCaps *caps, GstCaps *capstoadd)
515 {
516   GstCaps *orig = capstoadd;
517   
518   if (capstoadd == NULL)
519     return caps;
520
521   g_return_val_if_fail (caps != capstoadd, caps);
522
523   while (capstoadd->next) {
524     capstoadd = capstoadd->next;
525   }
526   capstoadd->next = caps;
527
528   return orig;
529 }
530
531 /**
532  * gst_caps_get_by_name:
533  * @caps: a capabilty
534  * @name: the name of the capability to get
535  *
536  * Get the capability with the given name from this
537  * chain of capabilities.
538  *
539  * Returns: the first capability in the chain with the 
540  * given name
541  */
542 GstCaps*
543 gst_caps_get_by_name (GstCaps *caps, const gchar *name)
544 {
545   g_return_val_if_fail (caps != NULL, NULL);
546   g_return_val_if_fail (name != NULL, NULL);
547    
548   while (caps) {
549     if (!strcmp (caps->name, name)) 
550       return caps;
551     caps = caps->next;
552   }
553
554   return NULL;
555 }
556                                                                                                                    
557 static gboolean
558 gst_caps_check_compatibility_func (GstCaps *fromcaps, GstCaps *tocaps)
559 {
560   if (fromcaps->id != tocaps->id) {
561     GST_DEBUG (GST_CAT_CAPS,"mime types differ (%s to %s)",
562                gst_type_find_by_id (fromcaps->id)->mime, 
563                gst_type_find_by_id (tocaps->id)->mime);
564     return FALSE;
565   }
566
567   if (tocaps->properties) {
568     if (fromcaps->properties) {
569       return gst_props_check_compatibility (fromcaps->properties, tocaps->properties);
570     }
571     else {
572       GST_DEBUG (GST_CAT_CAPS,"no source caps");
573       return FALSE;
574     }
575   }
576   else {
577     /* assume it accepts everything */
578     GST_DEBUG (GST_CAT_CAPS,"no caps");
579     return TRUE;
580   }
581 }
582
583 /**
584  * gst_caps_check_compatibility:
585  * @fromcaps: a capabilty
586  * @tocaps: a capabilty
587  *
588  * Checks whether two capabilities are compatible.
589  *
590  * Returns: TRUE if compatible, FALSE otherwise
591  */
592 gboolean
593 gst_caps_check_compatibility (GstCaps *fromcaps, GstCaps *tocaps)
594 {
595   if (fromcaps == NULL) {
596     if (tocaps == NULL) {
597       GST_DEBUG (GST_CAT_CAPS,"no caps");
598       return TRUE;
599     }
600     else {
601       GST_DEBUG (GST_CAT_CAPS,"no source but destination caps");
602       return FALSE;
603     }
604   }
605   else {
606     if (tocaps == NULL) {
607       GST_DEBUG (GST_CAT_CAPS,"source caps and no destination caps");
608       return TRUE;
609     }
610   }
611
612   while (fromcaps) {
613     GstCaps *destcaps = tocaps;
614
615     while (destcaps) {
616       if (gst_caps_check_compatibility_func (fromcaps, destcaps))
617         return TRUE;
618
619       destcaps =  destcaps->next;
620     }
621     fromcaps =  fromcaps->next;
622   }
623   return FALSE;
624 }
625
626 static GstCaps*
627 gst_caps_intersect_func (GstCaps *caps1, GstCaps *caps2)
628 {
629   GstCaps *result = NULL;
630   GstProps *props;
631
632   if (caps1->id != caps2->id) {
633     GST_DEBUG (GST_CAT_CAPS,"mime types differ (%s to %s)",
634                gst_type_find_by_id (caps1->id)->mime, 
635                gst_type_find_by_id (caps2->id)->mime);
636     return NULL;
637   }
638
639   if (caps1->properties == NULL) {
640     return gst_caps_ref (caps2);
641   }
642   if (caps2->properties == NULL) {
643     return gst_caps_ref (caps1);
644   }
645   
646   props = gst_props_intersect (caps1->properties, caps2->properties);
647   if (props) {
648     result = gst_caps_new_id ("intersect", caps1->id, props);
649   }
650
651   return result;
652 }
653
654 /**
655  * gst_caps_intersect:
656  * @caps1: a capabilty
657  * @caps2: a capabilty
658  *
659  * Make the intersection between two caps.
660  *
661  * Returns: The intersection of the two caps or NULL if the intersection
662  * is empty.
663  */
664 GstCaps*
665 gst_caps_intersect (GstCaps *caps1, GstCaps *caps2)
666 {
667   GstCaps *result = NULL, *walk = NULL;
668
669   if (caps1 == NULL) {
670     GST_DEBUG (GST_CAT_CAPS, "first caps is NULL, return other caps");
671     return gst_caps_copy (caps2);
672   }
673   if (caps2 == NULL) {
674     GST_DEBUG (GST_CAT_CAPS, "second caps is NULL, return other caps");
675     return gst_caps_copy (caps1);
676   }
677
678   while (caps1) {
679     GstCaps *othercaps = caps2;
680
681     while (othercaps) {
682       GstCaps *intersection;
683       
684       intersection = gst_caps_intersect_func (caps1, othercaps);
685
686       if (intersection) {
687         if (!result) {
688           walk = result = intersection;
689         }
690         else {
691           walk = walk->next = intersection;
692         }
693       }
694       othercaps = othercaps->next;
695     }
696     caps1 =  caps1->next;
697   }
698
699   return result;
700 }
701
702 /**
703  * gst_caps_normalize:
704  * @caps: a capabilty
705  *
706  * Make the normalisation of the caps. This will return a new caps
707  * that is equivalent to the input caps with the exception that all
708  * lists are unrolled. This function is useful when you want to iterate
709  * the caps.
710  *
711  * Returns: The normalisation of the caps.
712  */
713 GstCaps*
714 gst_caps_normalize (GstCaps *caps)
715 {
716   GstCaps *result = NULL, *walk = caps;
717
718   if (caps == NULL)
719     return caps;
720
721   while (caps) {
722     GList *proplist;
723
724     proplist = gst_props_normalize (caps->properties);
725     if (proplist && g_list_next (proplist) == NULL) {
726       if (result == NULL)
727         walk = result = caps;
728       else {
729         walk = walk->next = caps;
730       }
731       goto next;
732     }
733
734     while (proplist) {
735       GstProps *props = (GstProps *) proplist->data;
736       GstCaps *newcaps = gst_caps_new_id (caps->name, caps->id, props);
737
738       if (result == NULL)
739         walk = result = newcaps;
740       else {
741         walk = walk->next = newcaps;
742       }
743       proplist = g_list_next (proplist);  
744     }
745 next:
746     caps = caps->next;
747   }
748   return result;
749 }
750
751 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
752 /**
753  * gst_caps_save_thyself:
754  * @caps: a capabilty to save
755  * @parent: the parent XML node pointer
756  *
757  * Save the capability into an XML representation.
758  *
759  * Returns: a new XML node pointer
760  */
761 xmlNodePtr
762 gst_caps_save_thyself (GstCaps *caps, xmlNodePtr parent)
763 {
764   xmlNodePtr subtree;
765   xmlNodePtr subsubtree;
766
767   while (caps) {
768     subtree = xmlNewChild (parent, NULL, "capscomp", NULL);
769
770     xmlNewChild (subtree, NULL, "name", caps->name);
771     xmlNewChild (subtree, NULL, "type", gst_type_find_by_id (caps->id)->mime);
772     if (caps->properties) {
773       subsubtree = xmlNewChild (subtree, NULL, "properties", NULL);
774
775       gst_props_save_thyself (caps->properties, subsubtree);
776     }
777
778     caps = caps->next;
779   }
780
781   return parent;
782 }
783
784 /**
785  * gst_caps_load_thyself:
786  * @parent: the parent XML node pointer
787  *
788  * Load a new caps from the XML representation.
789  *
790  * Returns: a new capability
791  */
792 GstCaps*
793 gst_caps_load_thyself (xmlNodePtr parent)
794 {
795   GstCaps *result = NULL;
796   xmlNodePtr field = parent->xmlChildrenNode;
797
798   while (field) {
799     if (!strcmp (field->name, "capscomp")) {
800       xmlNodePtr subfield = field->xmlChildrenNode;
801       GstCaps *caps;
802       gchar *content;
803
804       g_mutex_lock (_gst_caps_chunk_lock);
805       caps = g_mem_chunk_alloc0 (_gst_caps_chunk);
806       g_mutex_unlock (_gst_caps_chunk_lock);
807
808       caps->refcount = 1;
809       caps->next = NULL;
810       caps->fixed = TRUE;
811         
812       while (subfield) {
813         if (!strcmp (subfield->name, "name")) {
814           caps->name = xmlNodeGetContent (subfield);
815         }
816         if (!strcmp (subfield->name, "type")) {
817           content = xmlNodeGetContent (subfield);
818           caps->id = get_type_for_mime (content);
819           g_free (content);
820         }
821         else if (!strcmp (subfield->name, "properties")) {
822           caps->properties = gst_props_load_thyself (subfield);
823         }
824         
825         subfield = subfield->next;
826       }
827       result = gst_caps_append (result, caps);
828     }
829     field = field->next;
830   }
831
832   return result;
833 }
834
835 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */