Added pulregion handling.
[platform/upstream/gstreamer.git] / gst / gstcaps.c
1 /* Gnome-Streamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 //#define DEBUG_ENABLED
21
22 #include <stdarg.h>
23 #include <gst/gstcapsprivate.h>
24
25 static gboolean         gst_caps_entry_check_compatibility      (GstCapsEntry *entry1, GstCapsEntry *entry2);
26         
27
28 void 
29 _gst_caps_initialize (void) 
30 {
31 }
32
33 static GstCapsEntry *
34 gst_caps_create_entry (GstCapsFactory factory, gint *skipped)
35 {
36   GstCapsFactoryEntry tag;
37   GstCapsEntry *entry;
38   guint i=0;
39
40   entry = g_new0 (GstCapsEntry, 1);
41
42   tag = factory[i++];
43   switch (GPOINTER_TO_INT (tag)) {
44     case GST_CAPS_INT_ID:
45       entry->capstype = GST_CAPS_INT_ID_NUM;
46       entry->data.int_data = GPOINTER_TO_INT (factory[i++]);
47       break;
48     case GST_CAPS_INT_RANGE_ID:
49       entry->capstype = GST_CAPS_INT_RANGE_ID_NUM;
50       entry->data.int_range_data.min = GPOINTER_TO_INT (factory[i++]);
51       entry->data.int_range_data.max = GPOINTER_TO_INT (factory[i++]);
52       break;
53     case GST_CAPS_FOURCC_ID:
54       entry->capstype = GST_CAPS_FOURCC_ID_NUM;
55       entry->data.fourcc_data = GPOINTER_TO_INT (factory[i++]);
56       break;
57     case GST_CAPS_LIST_ID:
58       g_print("gstcaps: list not allowed in list\n");
59       break;
60     case GST_CAPS_BOOL_ID:
61       entry->capstype = GST_CAPS_BOOL_ID_NUM;
62       entry->data.bool_data = GPOINTER_TO_INT (factory[i++]);
63       break;
64     default:
65       g_print("gstcaps: unknown caps id found\n");
66       g_free (entry);
67       entry = NULL;
68       break;
69   }
70
71   *skipped = i;
72
73   return entry;
74 }
75
76
77 static gint 
78 caps_compare_func (gconstpointer a,
79                    gconstpointer b) 
80 {
81   GstCapsEntry *entry1 = (GstCapsEntry *)a;
82   GstCapsEntry *entry2 = (GstCapsEntry *)b;
83
84   return (entry1->propid - entry2->propid);
85 }
86
87 /**
88  * gst_caps_register:
89  * @factory: the factory to register
90  *
91  * Register the factory. 
92  *
93  * Returns: The registered capability
94  */
95 GstCaps *
96 gst_caps_register (GstCapsFactory factory)
97 {
98   GstCapsFactoryEntry tag;
99   gint i = 0;
100   guint16 typeid;
101   GstCaps *caps;
102   gint skipped;
103   
104   g_return_val_if_fail (factory != NULL, NULL);
105
106   tag = factory[i++];
107
108   g_return_val_if_fail (tag != NULL, NULL);
109   
110   typeid = gst_type_find_by_mime ((gchar *)tag);
111   if (typeid == 0) {
112      GstTypeFactory *factory = g_new0 (GstTypeFactory, 1);
113
114      factory->mime = g_strdup ((gchar *)tag);
115      factory->exts = NULL;
116      factory->typefindfunc = NULL;
117
118      typeid = gst_type_register (factory);
119   }
120
121   caps = g_new0 (GstCaps, 1);
122   g_return_val_if_fail (caps != NULL, NULL);
123
124   caps->id = typeid;
125   caps->properties = NULL;
126
127   tag = factory[i++];
128   
129   while (tag) {
130     GQuark quark;
131     GstCapsEntry *entry;
132     
133     quark = g_quark_from_string ((gchar *)tag);
134
135     tag = factory[i];
136     switch (GPOINTER_TO_INT (tag)) {
137       case GST_CAPS_LIST_ID: 
138       {
139         GstCapsEntry *list_entry;
140
141         entry = g_new0 (GstCapsEntry, 1);
142         entry->propid = quark;
143         entry->capstype = GST_CAPS_LIST_ID_NUM;
144         entry->data.list_data.entries = NULL;
145
146         i++; // skip list tag
147         tag = factory[i];
148         while (tag) {
149           list_entry = gst_caps_create_entry (&factory[i], &skipped);
150           list_entry->propid = quark;
151           i += skipped;
152           tag = factory[i];
153           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, list_entry);
154         }
155         entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
156         i++; //skip NULL (list end)
157         break;
158       }
159       default:
160       {
161         entry = gst_caps_create_entry (&factory[i], &skipped);
162         entry->propid = quark;
163         i += skipped;
164         break;
165       }
166     }
167     caps->properties = g_slist_insert_sorted (caps->properties, entry, caps_compare_func);
168      
169     tag = factory[i++];
170   }
171
172   return caps;
173 }
174
175 static void
176 gst_caps_dump_entry_func (GstCapsEntry *entry)
177 {
178   switch (entry->capstype) {
179     case GST_CAPS_INT_ID_NUM: 
180       g_print("gstcaps:    int %d\n", entry->data.int_data);
181       break;
182     case GST_CAPS_INT_RANGE_ID_NUM: 
183       g_print("gstcaps:    int range %d %d\n", 
184                       entry->data.int_range_data.min,
185                       entry->data.int_range_data.max);
186       break;
187     case GST_CAPS_FOURCC_ID_NUM: 
188       g_print("gstcaps:    fourcc 0x%08x (%4.4s)\n", entry->data.fourcc_data, (gchar *)&entry->data.fourcc_data);
189       break;
190     case GST_CAPS_BOOL_ID_NUM: 
191       g_print("gstcaps:    boolean %d\n", entry->data.bool_data);
192       break;
193     default:
194       g_print("gstcaps:    **illegal entry**\n");
195       break;
196   }
197 }
198
199 static void
200 gst_caps_dump_list_func (gpointer entry,
201                          gpointer list_entry)
202 {
203   gst_caps_dump_entry_func ((GstCapsEntry *)entry);
204 }
205
206 static void
207 gst_caps_dump_func (gpointer data,
208                     gpointer user_data)
209 {
210   GstCapsEntry *entry;
211
212   entry = (GstCapsEntry *)data;
213
214   g_print("gstcaps:  property type \"%s\"\n", g_quark_to_string (entry->propid));
215
216   switch (entry->capstype) {
217     case GST_CAPS_LIST_ID_NUM: 
218     {
219       g_print("gstcaps:   list type (\n");
220       g_list_foreach (entry->data.list_data.entries, gst_caps_dump_list_func, entry);
221       g_print("gstcaps:   )\n");
222       break;
223     }
224     default:
225       gst_caps_dump_entry_func (entry);
226       break;
227   }
228 }
229
230 /**
231  * gst_caps_dump:
232  * @caps: the capability to dump
233  *
234  * Dumps the contents of the capabilty one the console
235  */
236 void
237 gst_caps_dump (GstCaps *caps)
238 {
239   g_return_if_fail (caps != NULL);
240
241   g_print("gstcaps: {\ngstcaps:  mime type \"%d\"\n", caps->id);
242
243   g_slist_foreach (caps->properties, gst_caps_dump_func, caps);
244   g_print("gstcaps: }\n");
245 }
246         
247 /* entry2 is always a list, entry1 never is */
248 static gboolean
249 gst_caps_entry_check_list_compatibility (GstCapsEntry *entry1, GstCapsEntry *entry2)
250 {
251   GList *entrylist = entry2->data.list_data.entries;
252   gboolean found = FALSE;
253
254   while (entrylist && !found) {
255     GstCapsEntry *entry = (GstCapsEntry *) entrylist->data;
256
257     found |= gst_caps_entry_check_compatibility (entry1, entry);
258
259     entrylist = g_list_next (entrylist);
260   }
261
262   return found;
263 }
264
265 static gboolean
266 gst_caps_entry_check_compatibility (GstCapsEntry *entry1, GstCapsEntry *entry2)
267 {
268   DEBUG ("compare: %s %s\n", g_quark_to_string (entry1->propid),
269                              g_quark_to_string (entry2->propid));
270   switch (entry1->capstype) {
271     case GST_CAPS_LIST_ID_NUM:
272     {
273       GList *entrylist = entry1->data.list_data.entries;
274       gboolean valid = TRUE;    // innocent until proven guilty
275
276       while (entrylist && valid) {
277         GstCapsEntry *entry = (GstCapsEntry *) entrylist->data;
278
279         valid &= gst_caps_entry_check_compatibility (entry, entry2);
280         
281         entrylist = g_list_next (entrylist);
282       }
283       
284       return valid;
285     }
286     case GST_CAPS_INT_RANGE_ID_NUM:
287       switch (entry2->capstype) {
288         // a - b   <--->   a - c
289         case GST_CAPS_INT_RANGE_ID_NUM:
290           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
291                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
292         case GST_CAPS_LIST_ID_NUM:
293           return gst_caps_entry_check_list_compatibility (entry1, entry2);
294         default:
295           return FALSE;
296       }
297       break;
298     case GST_CAPS_FOURCC_ID_NUM:
299       switch (entry2->capstype) {
300         // b   <--->   a
301         case GST_CAPS_FOURCC_ID_NUM:
302           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
303         // b   <--->   a,b,c
304         case GST_CAPS_LIST_ID_NUM:
305           return gst_caps_entry_check_list_compatibility (entry1, entry2);
306         default:
307           return FALSE;
308       }
309       break;
310     case GST_CAPS_INT_ID_NUM:
311       switch (entry2->capstype) {
312         // b   <--->   a - d
313         case GST_CAPS_INT_RANGE_ID_NUM:
314           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
315                   entry2->data.int_range_data.max >= entry1->data.int_data);
316         // b   <--->   a
317         case GST_CAPS_INT_ID_NUM:
318           return (entry2->data.int_data == entry1->data.int_data);
319         // b   <--->   a,b,c
320         case GST_CAPS_LIST_ID_NUM:
321           return gst_caps_entry_check_list_compatibility (entry1, entry2);
322         default:
323           return FALSE;
324       }
325       break;
326     case GST_CAPS_BOOL_ID_NUM:
327       switch (entry2->capstype) {
328         // t   <--->   t
329         case GST_CAPS_BOOL_ID_NUM:
330           return (entry2->data.bool_data == entry1->data.bool_data);
331         case GST_CAPS_LIST_ID_NUM:
332           return gst_caps_entry_check_list_compatibility (entry1, entry2);
333         default:
334           return FALSE;
335       }
336     default:
337       break;
338   }
339
340   return FALSE;
341 }
342
343 /**
344  * gst_caps_check_compatibility:
345  * @fromcaps: a capabilty
346  * @tocaps: a capabilty
347  *
348  * Checks whether two capabilities are compatible
349  *
350  * Returns: true if compatible, false otherwise
351  */
352 gboolean
353 gst_caps_check_compatibility (GstCaps *fromcaps, GstCaps *tocaps)
354 {
355   GSList *sourcelist;
356   GSList *sinklist;
357   gint missing = 0;
358   gint more = 0;
359   gboolean compatible = TRUE;
360
361   g_return_val_if_fail (fromcaps != NULL, FALSE);
362   g_return_val_if_fail (tocaps != NULL, FALSE);
363         
364   if (fromcaps->id != tocaps->id)
365     return FALSE;
366
367   sourcelist = fromcaps->properties;
368   sinklist   = tocaps->properties;
369
370   while (sourcelist && sinklist && compatible) {
371     GstCapsEntry *entry1;
372     GstCapsEntry *entry2;
373
374     entry1 = (GstCapsEntry *)sourcelist->data;
375     entry2 = (GstCapsEntry *)sinklist->data;
376
377     while (entry1->propid < entry2->propid) {
378       DEBUG ("source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
379       more++;
380       sourcelist = g_slist_next (sourcelist);
381       if (sourcelist) entry1 = (GstCapsEntry *)sourcelist->data;
382       else goto end;
383     }
384     while (entry1->propid > entry2->propid) {
385       DEBUG ("source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
386       missing++;
387       sinklist = g_slist_next (sinklist);
388       if (sinklist) entry2 = (GstCapsEntry *)sinklist->data;
389       else goto end;
390     }
391
392     compatible &= gst_caps_entry_check_compatibility (entry1, entry2);
393
394     sourcelist = g_slist_next (sourcelist);
395     sinklist = g_slist_next (sinklist);
396   }
397 end:
398
399   if (missing)
400     return FALSE;
401
402   return compatible;
403 }
404
405 /**
406  * gst_caps_register_va:
407  * @factory: the factories to register
408  *
409  * Register the given factories. 
410  *
411  * Returns: A list of the registered factories
412  */
413 GList *
414 gst_caps_register_va (GstCapsFactory factory, ...)
415 {
416   va_list var_args;
417   GstCapsFactoryEntry *current_factory;
418
419   va_start (var_args, factory);
420
421   current_factory = (GstCapsFactoryEntry *) factory;
422   
423   while (current_factory) {
424     gst_caps_register (current_factory);
425     
426     current_factory = va_arg (var_args, GstCapsFactoryEntry *);
427   }
428   
429   va_end(var_args);
430
431   return NULL;
432 }