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