winks: fix framerate fraction range mapping
[platform/upstream/gstreamer.git] / sys / winks / ksvideohelpers.c
1 /*
2  * Copyright (C) 2007 Haakon Sporsheim <hakon.sporsheim@tandberg.com>
3  *               2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
4  *               2009 Knut Inge Hvidsten <knut.inge.hvidsten@tandberg.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "ksvideohelpers.h"
23
24 #include <math.h>
25 #include <uuids.h>
26 #include "kshelpers.h"
27
28 GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
29 #define GST_CAT_DEFAULT gst_ks_debug
30
31 static const GUID MEDIASUBTYPE_FOURCC =
32     { 0x0 /* FourCC here */ , 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00,
33     0x38, 0x9B, 0x71}
34 };
35
36 extern const GUID MEDIASUBTYPE_I420 =
37     { 0x30323449, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
38     0x71}
39 };
40
41 typedef struct _KsVideoDeviceEntry KsVideoDeviceEntry;
42
43 struct _KsVideoDeviceEntry
44 {
45   KsDeviceEntry *device;
46   gint priority;
47 };
48
49 static void
50 ks_video_device_entry_decide_priority (KsVideoDeviceEntry * videodevice)
51 {
52   HANDLE filter_handle;
53
54   videodevice->priority = 0;
55
56   filter_handle = CreateFile (videodevice->device->path,
57       GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
58       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
59   if (ks_is_valid_handle (filter_handle)) {
60     GUID *propsets = NULL;
61     gulong propsets_len;
62
63     if (ks_object_get_supported_property_sets (filter_handle, &propsets,
64             &propsets_len)) {
65       gulong i;
66
67       for (i = 0; i < propsets_len; i++) {
68         if (memcmp (&propsets[i], &PROPSETID_VIDCAP_CAMERACONTROL,
69                 sizeof (GUID)) == 0) {
70           videodevice->priority++;
71           break;
72         }
73       }
74
75       g_free (propsets);
76     }
77   }
78
79   CloseHandle (filter_handle);
80 }
81
82 static gint
83 ks_video_device_entry_compare (gconstpointer a, gconstpointer b)
84 {
85   const KsVideoDeviceEntry *videodevice_a = a;
86   const KsVideoDeviceEntry *videodevice_b = b;
87
88   if (videodevice_a->priority > videodevice_b->priority)
89     return -1;
90   else if (videodevice_a->priority == videodevice_b->priority)
91     return 0;
92   else
93     return 1;
94 }
95
96 GList *
97 ks_video_device_list_sort_cameras_first (GList * devices)
98 {
99   GList *videodevices = NULL, *walk;
100   guint i;
101
102   for (walk = devices; walk != NULL; walk = walk->next) {
103     KsDeviceEntry *device = walk->data;
104     KsVideoDeviceEntry *videodevice;
105
106     videodevice = g_new (KsVideoDeviceEntry, 1);
107     videodevice->device = device;
108     ks_video_device_entry_decide_priority (videodevice);
109
110     videodevices = g_list_append (videodevices, videodevice);
111   }
112
113   videodevices = g_list_sort (videodevices, ks_video_device_entry_compare);
114
115   g_list_free (devices);
116   devices = NULL;
117
118   for (walk = videodevices, i = 0; walk != NULL; walk = walk->next, i++) {
119     KsVideoDeviceEntry *videodevice = walk->data;
120
121     videodevice->device->index = i;
122     devices = g_list_append (devices, videodevice->device);
123
124     g_free (videodevice);
125   }
126
127   g_list_free (videodevices);
128
129   return devices;
130 }
131
132 static GstStructure *
133 ks_video_format_to_structure (GUID subtype_guid, GUID format_guid)
134 {
135   GstStructure *structure = NULL;
136
137   if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_MJPG) || IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_TVMJ) ||     /* FIXME: NOT tested */
138       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_WAKE) ||        /* FIXME: NOT tested */
139       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_CFCC) ||        /* FIXME: NOT tested */
140       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_IJPG)) {        /* FIXME: NOT tested */
141     structure = gst_structure_new ("image/jpeg", NULL);
142   } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB555) ||       /* FIXME: NOT tested */
143       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB565) ||      /* FIXME: NOT tested */
144       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB24) || IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB32) ||   /* FIXME: NOT tested */
145       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB1555) ||    /* FIXME: NOT tested */
146       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB32) ||      /* FIXME: NOT tested */
147       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB4444)) {    /* FIXME: NOT tested */
148     guint depth = 0, bpp = 0;
149     gint endianness = 0;
150     guint32 r_mask = 0, b_mask = 0, g_mask = 0;
151
152     if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB555)) {
153       bpp = 16;
154       depth = 15;
155       endianness = G_BIG_ENDIAN;
156       r_mask = 0x7c00;
157       g_mask = 0x03e0;
158       b_mask = 0x001f;
159     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB565)) {
160       bpp = depth = 16;
161       endianness = G_BIG_ENDIAN;
162       r_mask = 0xf800;
163       g_mask = 0x07e0;
164       b_mask = 0x001f;
165     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB24)) {
166       bpp = depth = 24;
167       endianness = G_BIG_ENDIAN;
168       r_mask = 0x0000ff;
169       g_mask = 0x00ff00;
170       b_mask = 0xff0000;
171     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB32)) {
172       bpp = 32;
173       depth = 24;
174       endianness = G_BIG_ENDIAN;
175       r_mask = 0x000000ff;
176       g_mask = 0x0000ff00;
177       b_mask = 0x00ff0000;
178       /* FIXME: check
179        *r_mask = 0xff000000;
180        *g_mask = 0x00ff0000;
181        *b_mask = 0x0000ff00;
182        */
183     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB1555)) {
184       bpp = 16;
185       depth = 15;
186       endianness = G_BIG_ENDIAN;
187       r_mask = 0x7c00;
188       g_mask = 0x03e0;
189       b_mask = 0x001f;
190     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB32)) {
191       bpp = depth = 32;
192       endianness = G_BIG_ENDIAN;
193       r_mask = 0x000000ff;
194       g_mask = 0x0000ff00;
195       b_mask = 0x00ff0000;
196       /* FIXME: check
197        *r_mask = 0xff000000;
198        *g_mask = 0x00ff0000;
199        *b_mask = 0x0000ff00;
200        */
201     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB4444)) {
202       bpp = 16;
203       depth = 12;
204       endianness = G_BIG_ENDIAN;
205       r_mask = 0x0f00;
206       g_mask = 0x00f0;
207       b_mask = 0x000f;
208       //r_mask = 0x000f;
209       //g_mask = 0x00f0;
210       //b_mask = 0x0f00;
211     } else {
212       g_assert_not_reached ();
213     }
214
215     structure = gst_structure_new ("video/x-raw-rgb",
216         "bpp", G_TYPE_INT, bpp,
217         "depth", G_TYPE_INT, depth,
218         "red_mask", G_TYPE_INT, r_mask,
219         "green_mask", G_TYPE_INT, g_mask,
220         "blue_mask", G_TYPE_INT, b_mask,
221         "endianness", G_TYPE_INT, endianness, NULL);
222   } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_dvsd)) {
223     if (IsEqualGUID (&format_guid, &FORMAT_DvInfo)) {
224       structure = gst_structure_new ("video/x-dv",
225           "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
226     } else if (IsEqualGUID (&format_guid, &FORMAT_VideoInfo)) {
227       structure = gst_structure_new ("video/x-dv",
228           "systemstream", G_TYPE_BOOLEAN, FALSE,
229           "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('d', 'v', 's', 'd'),
230           NULL);
231     }
232   } else if (memcmp (&subtype_guid.Data2, &MEDIASUBTYPE_FOURCC.Data2,
233           sizeof (subtype_guid) - sizeof (subtype_guid.Data1)) == 0) {
234     guint8 *p = (guint8 *) & subtype_guid.Data1;
235
236     structure = gst_structure_new ("video/x-raw-yuv",
237         "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC (p[0], p[1], p[2], p[3]),
238         NULL);
239   }
240
241   if (!structure) {
242     GST_DEBUG ("Unknown DirectShow Video GUID %08x-%04x-%04x-%04x-%08x%04x",
243         subtype_guid.Data1, subtype_guid.Data2, subtype_guid.Data3,
244         *(WORD *) subtype_guid.Data4, *(DWORD *) & subtype_guid.Data4[2],
245         *(WORD *) & subtype_guid.Data4[6]);
246   }
247
248   return structure;
249 }
250
251 static void
252 guess_aspect (gint width, gint height, gint * par_width, gint * par_height)
253 {
254   /*
255    * As we dont have access to the actual pixel aspect, we will try to do a
256    * best-effort guess. The guess is based on most sensors being either 4/3
257    * or 16/9, and most pixel aspects being close to 1/1.
258    */
259   if ((width == 768) && (height == 448)) {      /* special case for w448p */
260     *par_width = 28;
261     *par_height = 27;
262   } else {
263     if (((float) width / (float) height) < 1.2778) {
264       *par_width = 12;
265       *par_height = 11;
266     } else {
267       *par_width = 1;
268       *par_height = 1;
269     }
270   }
271 }
272
273 /* NOTE: would probably be better to use a continued fractions approach here */
274 static void
275 compress_fraction (gint64 in_num, gint64 in_den, gint64 * out_num,
276     gint64 * out_den)
277 {
278   gdouble on, od, orig;
279   guint denominators[] = { 1, 2, 3, 5, 7 }, i;
280   const gdouble max_loss = 0.1;
281
282   on = in_num;
283   od = in_den;
284   orig = on / od;
285
286   for (i = 0; i < G_N_ELEMENTS (denominators); i++) {
287     gint64 cur_n, cur_d;
288     gdouble cur, loss;
289
290     cur_n = floor ((on / (od / (gdouble) denominators[i])) + 0.5);
291     cur_d = denominators[i];
292     cur = (gdouble) cur_n / (gdouble) cur_d;
293     loss = fabs (cur - orig);
294
295     if (loss <= max_loss) {
296       *out_num = cur_n;
297       *out_den = cur_d;
298
299       return;
300     }
301   }
302
303   *out_num = in_num;
304   *out_den = in_den;
305 }
306
307 static gboolean
308 ks_video_append_video_stream_cfg_fields (GstStructure * structure,
309     const KS_VIDEO_STREAM_CONFIG_CAPS * vscc)
310 {
311   GValue val = { 0, };
312   gint64 min_n, min_d;
313   gint64 max_n, max_d;
314
315   g_return_val_if_fail (structure, FALSE);
316   g_return_val_if_fail (vscc, FALSE);
317
318   /* width */
319   if (vscc->MinOutputSize.cx == vscc->MaxOutputSize.cx) {
320     gst_structure_set (structure,
321         "width", G_TYPE_INT, vscc->MaxOutputSize.cx, NULL);
322   } else {
323     gst_structure_set (structure,
324         "width", GST_TYPE_INT_RANGE,
325         vscc->MinOutputSize.cx, vscc->MaxOutputSize.cx, NULL);
326   }
327
328   /* height */
329   if (vscc->MinOutputSize.cy == vscc->MaxOutputSize.cy) {
330     gst_structure_set (structure,
331         "height", G_TYPE_INT, vscc->MaxOutputSize.cy, NULL);
332   } else {
333     gst_structure_set (structure,
334         "height", GST_TYPE_INT_RANGE,
335         vscc->MinOutputSize.cy, vscc->MaxOutputSize.cy, NULL);
336   }
337
338   /* framerate */
339   compress_fraction (NANOSECONDS, vscc->MinFrameInterval, &min_n, &min_d);
340   compress_fraction (NANOSECONDS, vscc->MaxFrameInterval, &max_n, &max_d);
341
342   if (min_n == max_n && min_d == max_d) {
343     g_value_init (&val, GST_TYPE_FRACTION);
344     gst_value_set_fraction (&val, max_n, max_d);
345   } else {
346     g_value_init (&val, GST_TYPE_FRACTION_RANGE);
347     gst_value_set_fraction_range_full (&val, max_n, max_d, min_n, min_d);
348   }
349
350   gst_structure_set_value (structure, "framerate", &val);
351   g_value_unset (&val);
352
353   {
354     gint par_width, par_height;
355
356     guess_aspect (vscc->MaxOutputSize.cx, vscc->MaxOutputSize.cy,
357         &par_width, &par_height);
358
359     gst_structure_set (structure,
360         "pixel-aspect-ratio", GST_TYPE_FRACTION, par_width, par_height, NULL);
361   }
362
363   return TRUE;
364 }
365
366 KsVideoMediaType *
367 ks_video_media_type_dup (KsVideoMediaType * media_type)
368 {
369   KsVideoMediaType *result = g_new (KsVideoMediaType, 1);
370
371   memcpy (result, media_type, sizeof (KsVideoMediaType));
372
373   result->range = g_malloc (media_type->range->FormatSize);
374   memcpy ((gpointer) result->range, media_type->range,
375       media_type->range->FormatSize);
376
377   result->format = g_malloc (media_type->format_size);
378   memcpy (result->format, media_type->format, media_type->format_size);
379
380   result->translated_caps = gst_caps_ref (media_type->translated_caps);
381
382   return result;
383 }
384
385 void
386 ks_video_media_type_free (KsVideoMediaType * media_type)
387 {
388   if (media_type == NULL)
389     return;
390
391   g_free ((gpointer) media_type->range);
392
393   g_free (media_type->format);
394
395   if (media_type->translated_caps != NULL)
396     gst_caps_unref (media_type->translated_caps);
397
398   g_free (media_type);
399 }
400
401 static GList *
402 ks_video_media_type_list_remove_duplicates (GList * media_types)
403 {
404   GList *master, *duplicates;
405
406   do {
407     GList *entry;
408
409     master = duplicates = NULL;
410
411     /* Find the first set of duplicates and their master */
412     for (entry = media_types; entry != NULL && duplicates == NULL;
413         entry = entry->next) {
414       KsVideoMediaType *mt = entry->data;
415       GList *other_entry;
416
417       for (other_entry = media_types; other_entry != NULL;
418           other_entry = other_entry->next) {
419         KsVideoMediaType *other_mt = other_entry->data;
420
421         if (other_mt == mt)
422           continue;
423
424         if (gst_caps_is_equal (mt->translated_caps, other_mt->translated_caps))
425           duplicates = g_list_prepend (duplicates, other_mt);
426       }
427
428       if (duplicates != NULL)
429         master = entry;
430     }
431
432     if (duplicates != NULL) {
433       KsVideoMediaType *selected_mt = master->data;
434
435       /*
436        * Pick a FORMAT_VideoInfo2 if present, if not we just stay with the
437        * first entry
438        */
439       for (entry = duplicates; entry != NULL; entry = entry->next) {
440         KsVideoMediaType *mt = entry->data;
441
442         if (IsEqualGUID (&mt->range->Specifier, &FORMAT_VideoInfo2)) {
443           ks_video_media_type_free (selected_mt);
444           selected_mt = mt;
445         } else {
446           ks_video_media_type_free (mt);
447         }
448
449         /* Remove the dupe from the main list */
450         media_types = g_list_remove (media_types, mt);
451       }
452
453       /* Update master node with the selected MediaType */
454       master->data = selected_mt;
455
456       g_list_free (duplicates);
457     }
458   }
459   while (master != NULL);
460
461   return media_types;
462 }
463
464 GList *
465 ks_video_probe_filter_for_caps (HANDLE filter_handle)
466 {
467   GList *ret = NULL;
468   gulong pin_count;
469   guint pin_id;
470
471   if (!ks_filter_get_pin_property (filter_handle, 0, KSPROPSETID_Pin,
472           KSPROPERTY_PIN_CTYPES, &pin_count, sizeof (pin_count), NULL))
473     goto beach;
474
475   GST_DEBUG ("pin_count = %d", pin_count);
476
477   for (pin_id = 0; pin_id < pin_count; pin_id++) {
478     KSPIN_COMMUNICATION pin_comm;
479     KSPIN_DATAFLOW pin_flow;
480     GUID pin_cat;
481
482     if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
483             KSPROPERTY_PIN_COMMUNICATION, &pin_comm, sizeof (pin_comm), NULL))
484       continue;
485
486     if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
487             KSPROPERTY_PIN_DATAFLOW, &pin_flow, sizeof (pin_flow), NULL))
488       continue;
489
490     if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
491             KSPROPERTY_PIN_CATEGORY, &pin_cat, sizeof (pin_cat), NULL))
492       continue;
493
494     GST_DEBUG ("pin[%u]: pin_comm=%d, pin_flow=%d", pin_id, pin_comm, pin_flow);
495
496     if (pin_flow == KSPIN_DATAFLOW_OUT &&
497         memcmp (&pin_cat, &PINNAME_CAPTURE, sizeof (GUID)) == 0) {
498       KSMULTIPLE_ITEM *items;
499
500       if (ks_filter_get_pin_property_multi (filter_handle, pin_id,
501               KSPROPSETID_Pin, KSPROPERTY_PIN_DATARANGES, &items, NULL)) {
502         KSDATARANGE *range = (KSDATARANGE *) (items + 1);
503         guint i;
504
505         for (i = 0; i < items->Count; i++) {
506           if (IsEqualGUID (&range->MajorFormat, &KSDATAFORMAT_TYPE_VIDEO)) {
507             KsVideoMediaType *entry;
508             gpointer src_vscc, src_format;
509             GstStructure *media_structure;
510
511             entry = g_new0 (KsVideoMediaType, 1);
512             entry->pin_id = pin_id;
513
514             entry->range = g_malloc (range->FormatSize);
515             memcpy ((gpointer) entry->range, range, range->FormatSize);
516
517             if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
518               KS_DATARANGE_VIDEO *vr = (KS_DATARANGE_VIDEO *) entry->range;
519
520               src_vscc = &vr->ConfigCaps;
521               src_format = &vr->VideoInfoHeader;
522
523               entry->format_size = sizeof (vr->VideoInfoHeader);
524               entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage;
525             } else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
526               KS_DATARANGE_VIDEO2 *vr = (KS_DATARANGE_VIDEO2 *) entry->range;
527
528               src_vscc = &vr->ConfigCaps;
529               src_format = &vr->VideoInfoHeader;
530
531               entry->format_size = sizeof (vr->VideoInfoHeader);
532               entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage;
533             } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
534               /* Untested and probably wrong... */
535               KS_DATARANGE_MPEG1_VIDEO *vr =
536                   (KS_DATARANGE_MPEG1_VIDEO *) entry->range;
537
538               src_vscc = &vr->ConfigCaps;
539               src_format = &vr->VideoInfoHeader;
540
541               entry->format_size = sizeof (vr->VideoInfoHeader);
542               entry->sample_size =
543                   vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage;
544             } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
545               /* Untested and probably wrong... */
546               KS_DATARANGE_MPEG2_VIDEO *vr =
547                   (KS_DATARANGE_MPEG2_VIDEO *) entry->range;
548
549               src_vscc = &vr->ConfigCaps;
550               src_format = &vr->VideoInfoHeader;
551
552               entry->format_size = sizeof (vr->VideoInfoHeader);
553               entry->sample_size =
554                   vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage;
555             } else {
556               gchar *guid_str;
557
558               guid_str = ks_guid_to_string (&range->Specifier);
559               GST_DEBUG ("pin[%u]: ignoring unknown specifier GUID %s",
560                   pin_id, guid_str);
561               g_free (guid_str);
562
563               ks_video_media_type_free (entry);
564               entry = NULL;
565             }
566
567             if (entry != NULL) {
568               g_assert (entry->sample_size != 0);
569
570               memcpy ((gpointer) & entry->vscc, src_vscc, sizeof (entry->vscc));
571
572               entry->format = g_malloc (entry->format_size);
573               memcpy (entry->format, src_format, entry->format_size);
574
575               media_structure =
576                   ks_video_format_to_structure (range->SubFormat,
577                   range->MajorFormat);
578
579               if (media_structure == NULL) {
580                 g_warning ("ks_video_format_to_structure returned NULL");
581                 ks_video_media_type_free (entry);
582                 entry = NULL;
583               } else if (ks_video_append_video_stream_cfg_fields
584                   (media_structure, &entry->vscc)) {
585                 entry->translated_caps = gst_caps_new_empty ();
586                 gst_caps_append_structure (entry->translated_caps,
587                     media_structure);
588               } else {
589                 gst_structure_free (media_structure);
590                 ks_video_media_type_free (entry);
591                 entry = NULL;
592               }
593
594               if (entry != NULL)
595                 ret = g_list_prepend (ret, entry);
596             }
597           }
598
599           /* REVISIT: Each KSDATARANGE should start on a 64-bit boundary */
600           range = (KSDATARANGE *) (((guchar *) range) + range->FormatSize);
601         }
602
603         g_free (items);
604       }
605     }
606   }
607
608   if (ret != NULL) {
609     ret = g_list_reverse (ret);
610     ret = ks_video_media_type_list_remove_duplicates (ret);
611   }
612
613 beach:
614   return ret;
615 }
616
617 KSPIN_CONNECT *
618 ks_video_create_pin_conn_from_media_type (KsVideoMediaType * media_type)
619 {
620   KSPIN_CONNECT *conn = NULL;
621   KSDATAFORMAT *format = NULL;
622   guint8 *vih;
623
624   conn = g_malloc0 (sizeof (KSPIN_CONNECT) + sizeof (KSDATAFORMAT) +
625       media_type->format_size);
626
627   conn->Interface.Set = KSINTERFACESETID_Standard;
628   conn->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
629   conn->Interface.Flags = 0;
630
631   conn->Medium.Set = KSMEDIUMSETID_Standard;
632   conn->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
633   conn->Medium.Flags = 0;
634
635   conn->PinId = media_type->pin_id;
636   conn->PinToHandle = NULL;
637   conn->Priority.PriorityClass = KSPRIORITY_NORMAL;
638   conn->Priority.PrioritySubClass = KSPRIORITY_NORMAL;
639
640   format = (KSDATAFORMAT *) (conn + 1);
641   memcpy (format, media_type->range, sizeof (KSDATAFORMAT));
642   format->FormatSize = sizeof (KSDATAFORMAT) + media_type->format_size;
643
644   vih = (guint8 *) (format + 1);
645   memcpy (vih, media_type->format, media_type->format_size);
646
647   return conn;
648 }
649
650 gboolean
651 ks_video_fixate_media_type (const KSDATARANGE * range,
652     guint8 * format, gint width, gint height, gint fps_n, gint fps_d)
653 {
654   KS_DATARANGE_VIDEO *vr;
655   KS_VIDEOINFOHEADER *vih;
656   KS_BITMAPINFOHEADER *bih;
657   DWORD dwRate;
658
659   g_return_val_if_fail (format != NULL, FALSE);
660
661   if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
662     bih = &((KS_VIDEOINFOHEADER *) format)->bmiHeader;
663   } else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
664     bih = &((KS_VIDEOINFOHEADER2 *) format)->bmiHeader;
665   } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
666     bih = &((KS_MPEG1VIDEOINFO *) format)->hdr.bmiHeader;
667   } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
668     bih = &((KS_MPEGVIDEOINFO2 *) format)->hdr.bmiHeader;
669   } else {
670     return FALSE;
671   }
672
673   /* These formats' structures share the most basic stuff */
674   vr = (KS_DATARANGE_VIDEO *) range;
675   vih = (KS_VIDEOINFOHEADER *) format;
676
677   /* FIXME: Need to figure out how to properly handle ranges */
678   if (bih->biWidth != width || bih->biHeight != height)
679     return FALSE;
680
681   /* Framerate, clamped because of fraction conversion rounding errors */
682   vih->AvgTimePerFrame =
683       gst_util_uint64_scale_int_round (NANOSECONDS, fps_d, fps_n);
684   vih->AvgTimePerFrame =
685       MAX (vih->AvgTimePerFrame, vr->ConfigCaps.MinFrameInterval);
686   vih->AvgTimePerFrame =
687       MIN (vih->AvgTimePerFrame, vr->ConfigCaps.MaxFrameInterval);
688
689   /* Bitrate, clamped for the same reason as framerate */
690   dwRate = (width * height * fps_n) / fps_d;
691   vih->dwBitRate = dwRate * bih->biBitCount;
692   vih->dwBitRate = MAX (vih->dwBitRate, vr->ConfigCaps.MinBitsPerSecond);
693   vih->dwBitRate = MIN (vih->dwBitRate, vr->ConfigCaps.MaxBitsPerSecond);
694
695   return TRUE;
696 }
697
698 static GstStructure *
699 ks_video_append_var_video_fields (GstStructure * structure)
700 {
701   if (structure) {
702     gst_structure_set (structure,
703         "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
704         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
705         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
706   }
707
708   return structure;
709 }
710
711 GstCaps *
712 ks_video_get_all_caps (void)
713 {
714   static GstCaps *caps = NULL;
715
716   if (caps == NULL) {
717     GstStructure *structure;
718     caps = gst_caps_new_empty ();
719
720     /* from Windows SDK 6.0 uuids.h */
721     /* RGB formats */
722     structure =
723         ks_video_append_var_video_fields (ks_video_format_to_structure
724         (MEDIASUBTYPE_RGB555, FORMAT_VideoInfo));
725     gst_caps_append_structure (caps, structure);
726
727     structure =
728         ks_video_append_var_video_fields (ks_video_format_to_structure
729         (MEDIASUBTYPE_RGB565, FORMAT_VideoInfo));
730     gst_caps_append_structure (caps, structure);
731
732     structure =
733         ks_video_append_var_video_fields (ks_video_format_to_structure
734         (MEDIASUBTYPE_RGB24, FORMAT_VideoInfo));
735     gst_caps_append_structure (caps, structure);
736
737     structure =
738         ks_video_append_var_video_fields (ks_video_format_to_structure
739         (MEDIASUBTYPE_RGB32, FORMAT_VideoInfo));
740     gst_caps_append_structure (caps, structure);
741
742     /* YUV formats */
743     structure =
744         ks_video_append_var_video_fields (gst_structure_new ("video/x-raw-yuv",
745             NULL));
746     gst_caps_append_structure (caps, structure);
747
748     /* Other formats */
749     structure =
750         ks_video_append_var_video_fields (ks_video_format_to_structure
751         (MEDIASUBTYPE_MJPG, FORMAT_VideoInfo));
752     gst_caps_append_structure (caps, structure);
753
754     structure =
755         ks_video_append_var_video_fields (ks_video_format_to_structure
756         (MEDIASUBTYPE_dvsd, FORMAT_VideoInfo));
757     gst_caps_append_structure (caps, structure);
758
759     structure =                 /* no variable video fields (width, height, framerate) */
760         ks_video_format_to_structure (MEDIASUBTYPE_dvsd, FORMAT_DvInfo);
761     gst_caps_append_structure (caps, structure);
762   }
763
764   return caps;
765 }