Implemented real Caps checking.
[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     default:
61       g_print("gstcaps: unknown caps id found\n");
62       g_free (entry);
63       entry = NULL;
64       break;
65   }
66
67   *skipped = i;
68
69   return entry;
70 }
71
72
73 static gint 
74 caps_compare_func (gconstpointer a,
75                    gconstpointer b) 
76 {
77   GstCapsEntry *entry1 = (GstCapsEntry *)a;
78   GstCapsEntry *entry2 = (GstCapsEntry *)b;
79
80   return (entry1->propid - entry2->propid);
81 }
82
83 /**
84  * gst_caps_register:
85  * @factory: the factory to register
86  *
87  * Register the factory. 
88  *
89  * Returns: The registered capability
90  */
91 GstCaps *
92 gst_caps_register (GstCapsFactory factory)
93 {
94   GstCapsFactoryEntry tag;
95   gint i = 0;
96   guint16 typeid;
97   GstCaps *caps;
98   gint skipped;
99   
100   g_return_val_if_fail (factory != NULL, NULL);
101
102   tag = factory[i++];
103
104   g_return_val_if_fail (tag != NULL, NULL);
105   
106   typeid = gst_type_find_by_mime ((gchar *)tag);
107   if (typeid == 0) {
108      GstTypeFactory *factory = g_new0 (GstTypeFactory, 1);
109
110      factory->mime = g_strdup ((gchar *)tag);
111      factory->exts = NULL;
112      factory->typefindfunc = NULL;
113
114      typeid = gst_type_register (factory);
115   }
116
117   caps = g_new0 (GstCaps, 1);
118   g_return_val_if_fail (caps != NULL, NULL);
119
120   caps->id = typeid;
121   caps->properties = NULL;
122
123   tag = factory[i++];
124   
125   while (tag) {
126     GQuark quark;
127     GstCapsEntry *entry;
128     
129     quark = g_quark_from_string ((gchar *)tag);
130
131     tag = factory[i];
132     switch (GPOINTER_TO_INT (tag)) {
133       case GST_CAPS_LIST_ID: 
134       {
135         GstCapsEntry *list_entry;
136
137         entry = g_new0 (GstCapsEntry, 1);
138         entry->propid = quark;
139         entry->capstype = GST_CAPS_LIST_ID_NUM;
140         entry->data.list_data.entries = NULL;
141
142         i++; // skip list tag
143         tag = factory[i];
144         while (tag) {
145           list_entry = gst_caps_create_entry (&factory[i], &skipped);
146           list_entry->propid = quark;
147           i += skipped;
148           tag = factory[i];
149           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, list_entry);
150         }
151         entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
152         i++; //skip NULL (list end)
153         break;
154       }
155       default:
156       {
157         entry = gst_caps_create_entry (&factory[i], &skipped);
158         entry->propid = quark;
159         i += skipped;
160         break;
161       }
162     }
163     caps->properties = g_slist_insert_sorted (caps->properties, entry, caps_compare_func);
164      
165     tag = factory[i++];
166   }
167
168   return caps;
169 }
170
171 static void
172 gst_caps_dump_entry_func (GstCapsEntry *entry)
173 {
174   switch (entry->capstype) {
175     case GST_CAPS_INT_ID_NUM: 
176       g_print("gstcaps:    int %d\n", entry->data.int_data);
177       break;
178     case GST_CAPS_INT_RANGE_ID_NUM: 
179       g_print("gstcaps:    int range %d %d\n", 
180                       entry->data.int_range_data.min,
181                       entry->data.int_range_data.max);
182       break;
183     case GST_CAPS_FOURCC_ID_NUM: 
184       g_print("gstcaps:    fourcc 0x%08x (%4.4s)\n", entry->data.fourcc_data, &entry->data.fourcc_data);
185       break;
186     case GST_CAPS_BOOL_ID_NUM: 
187       g_print("gstcaps:    boolean %d\n", entry->data.bool_data);
188       break;
189     default:
190       g_print("gstcaps:    **illegal entry**\n");
191       break;
192   }
193 }
194
195 static void
196 gst_caps_dump_list_func (gpointer entry,
197                          gpointer list_entry)
198 {
199   gst_caps_dump_entry_func ((GstCapsEntry *)entry);
200 }
201
202 static void
203 gst_caps_dump_func (gpointer data,
204                     gpointer user_data)
205 {
206   GstCapsEntry *entry;
207
208   entry = (GstCapsEntry *)data;
209
210   g_print("gstcaps:  property type \"%s\"\n", g_quark_to_string (entry->propid));
211
212   switch (entry->capstype) {
213     case GST_CAPS_LIST_ID_NUM: 
214     {
215       g_print("gstcaps:   list type (\n");
216       g_list_foreach (entry->data.list_data.entries, gst_caps_dump_list_func, entry);
217       g_print("gstcaps:   )\n");
218       break;
219     }
220     default:
221       gst_caps_dump_entry_func (entry);
222       break;
223   }
224 }
225
226 /**
227  * gst_caps_dump:
228  * @caps: the capability to dump
229  *
230  * Dumps the contents of the capabilty one the console
231  */
232 void
233 gst_caps_dump (GstCaps *caps)
234 {
235   g_return_if_fail (caps != NULL);
236
237   g_print("gstcaps: {\ngstcaps:  mime type \"%d\"\n", caps->id);
238
239   g_slist_foreach (caps->properties, gst_caps_dump_func, caps);
240   g_print("gstcaps: }\n");
241 }
242         
243 /* entry2 is always a list, entry1 never is */
244 static gboolean
245 gst_caps_entry_check_list_compatibility (GstCapsEntry *entry1, GstCapsEntry *entry2)
246 {
247   GList *entrylist = entry2->data.list_data.entries;
248   gboolean found = FALSE;
249
250   while (entrylist && !found) {
251     GstCapsEntry *entry = (GstCapsEntry *) entrylist->data;
252
253     found |= gst_caps_entry_check_compatibility (entry1, entry);
254
255     entrylist = g_list_next (entrylist);
256   }
257
258   return found;
259 }
260
261 static gboolean
262 gst_caps_entry_check_compatibility (GstCapsEntry *entry1, GstCapsEntry *entry2)
263 {
264   DEBUG ("compare: %s %s\n", g_quark_to_string (entry1->propid),
265                              g_quark_to_string (entry2->propid));
266   switch (entry1->capstype) {
267     case GST_CAPS_LIST_ID_NUM:
268     {
269       GList *entrylist = entry1->data.list_data.entries;
270       gboolean valid = TRUE;    // innocent until proven guilty
271
272       while (entrylist && valid) {
273         GstCapsEntry *entry = (GstCapsEntry *) entrylist->data;
274
275         valid &= gst_caps_entry_check_compatibility (entry, entry2);
276         
277         entrylist = g_list_next (entrylist);
278       }
279       
280       return valid;
281     }
282     case GST_CAPS_INT_RANGE_ID_NUM:
283       switch (entry2->capstype) {
284         // a - b   <--->   a - c
285         case GST_CAPS_INT_RANGE_ID_NUM:
286           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
287                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
288         case GST_CAPS_LIST_ID_NUM:
289           return gst_caps_entry_check_list_compatibility (entry1, entry2);
290         default:
291           return FALSE;
292       }
293       break;
294     case GST_CAPS_FOURCC_ID_NUM:
295       switch (entry2->capstype) {
296         // b   <--->   a
297         case GST_CAPS_FOURCC_ID_NUM:
298           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
299         // b   <--->   a,b,c
300         case GST_CAPS_LIST_ID_NUM:
301           return gst_caps_entry_check_list_compatibility (entry1, entry2);
302         default:
303           return FALSE;
304       }
305       break;
306     case GST_CAPS_INT_ID_NUM:
307       switch (entry2->capstype) {
308         // b   <--->   a - d
309         case GST_CAPS_INT_RANGE_ID_NUM:
310           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
311                   entry2->data.int_range_data.max >= entry1->data.int_data);
312         // b   <--->   a
313         case GST_CAPS_INT_ID_NUM:
314           return (entry2->data.int_data == entry1->data.int_data);
315         // b   <--->   a,b,c
316         case GST_CAPS_LIST_ID_NUM:
317           return gst_caps_entry_check_list_compatibility (entry1, entry2);
318         default:
319           return FALSE;
320       }
321       break;
322     case GST_CAPS_BOOL_ID_NUM:
323       switch (entry2->capstype) {
324         // t   <--->   t
325         case GST_CAPS_BOOL_ID_NUM:
326           return (entry2->data.bool_data == entry1->data.bool_data);
327         case GST_CAPS_LIST_ID_NUM:
328           return gst_caps_entry_check_list_compatibility (entry1, entry2);
329         default:
330           return FALSE;
331       }
332     default:
333       break;
334   }
335
336   return FALSE;
337 }
338
339 /**
340  * gst_caps_check_compatibility:
341  * @fromcaps: a capabilty
342  * @tocaps: a capabilty
343  *
344  * Checks whether two capabilities are compatible
345  *
346  * Returns: true if compatible, false otherwise
347  */
348 gboolean
349 gst_caps_check_compatibility (GstCaps *fromcaps, GstCaps *tocaps)
350 {
351   GSList *sourcelist;
352   GSList *sinklist;
353   gint missing = 0;
354   gint more = 0;
355   gboolean compatible = TRUE;
356
357   g_return_val_if_fail (fromcaps != NULL, FALSE);
358   g_return_val_if_fail (tocaps != NULL, FALSE);
359         
360   if (fromcaps->id != tocaps->id)
361     return FALSE;
362
363   sourcelist = fromcaps->properties;
364   sinklist   = tocaps->properties;
365
366   while (sourcelist && sinklist && compatible) {
367     GstCapsEntry *entry1;
368     GstCapsEntry *entry2;
369
370     entry1 = (GstCapsEntry *)sourcelist->data;
371     entry2 = (GstCapsEntry *)sinklist->data;
372
373     while (entry1->propid < entry2->propid) {
374       DEBUG ("source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
375       more++;
376       sourcelist = g_slist_next (sourcelist);
377       if (sourcelist) entry1 = (GstCapsEntry *)sourcelist->data;
378       else goto end;
379     }
380     while (entry1->propid > entry2->propid) {
381       DEBUG ("source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
382       missing++;
383       sinklist = g_slist_next (sinklist);
384       if (sinklist) entry2 = (GstCapsEntry *)sinklist->data;
385       else goto end;
386     }
387
388     compatible &= gst_caps_entry_check_compatibility (entry1, entry2);
389
390     sourcelist = g_slist_next (sourcelist);
391     sinklist = g_slist_next (sinklist);
392   }
393 end:
394
395   if (missing)
396     return FALSE;
397
398   return compatible;
399 }
400
401 /**
402  * gst_caps_register_va:
403  * @factory: the factories to register
404  *
405  * Register the given factories. 
406  *
407  * Returns: A list of the registered factories
408  */
409 GList *
410 gst_caps_register_va (GstCapsFactory factory, ...)
411 {
412   va_list var_args;
413   GstCapsFactoryEntry *current_factory;
414
415   va_start (var_args, factory);
416
417   current_factory = (GstCapsFactoryEntry *) factory;
418   
419   while (current_factory) {
420     gst_caps_register (current_factory);
421     
422     current_factory = va_arg (var_args, GstCapsFactoryEntry *);
423   }
424   
425   va_end(var_args);
426
427   return NULL;
428 }