New plugin for low-latency video capture on Windows (#519935).
[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  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "ksvideohelpers.h"
22
23 #include <uuids.h>
24 #include "kshelpers.h"
25
26 GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
27 #define GST_CAT_DEFAULT gst_ks_debug
28
29 static const GUID MEDIASUBTYPE_FOURCC =
30     { 0x0 /* FourCC here */ , 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00,
31         0x38, 0x9B, 0x71} };
32
33 extern const GUID MEDIASUBTYPE_I420 =
34     { 0x30323449, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
35         0x71} };
36
37 static GstStructure *
38 ks_video_format_to_structure (GUID subtype_guid, GUID format_guid)
39 {
40   GstStructure *structure = NULL;
41
42   if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_MJPG) || IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_TVMJ) ||     /* FIXME: NOT tested */
43       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_WAKE) ||        /* FIXME: NOT tested */
44       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_CFCC) ||        /* FIXME: NOT tested */
45       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_IJPG)) {        /* FIXME: NOT tested */
46     structure = gst_structure_new ("image/jpeg", NULL);
47   } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB555) ||       /* FIXME: NOT tested */
48       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB565) ||      /* FIXME: NOT tested */
49       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB24) || IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB32) ||   /* FIXME: NOT tested */
50       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB1555) ||    /* FIXME: NOT tested */
51       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB32) ||      /* FIXME: NOT tested */
52       IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB4444)) {    /* FIXME: NOT tested */
53     guint depth = 0, bpp = 0;
54     gint endianness = 0;
55     guint32 r_mask = 0, b_mask = 0, g_mask = 0;
56
57     if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB555)) {
58       bpp = 16;
59       depth = 15;
60       endianness = G_BIG_ENDIAN;
61       r_mask = 0x7c00;
62       g_mask = 0x03e0;
63       b_mask = 0x001f;
64     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB565)) {
65       bpp = depth = 16;
66       endianness = G_BIG_ENDIAN;
67       r_mask = 0xf800;
68       g_mask = 0x07e0;
69       b_mask = 0x001f;
70     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB24)) {
71       bpp = depth = 24;
72       endianness = G_BIG_ENDIAN;
73       r_mask = 0x0000ff;
74       g_mask = 0x00ff00;
75       b_mask = 0xff0000;
76     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB32)) {
77       bpp = 32;
78       depth = 24;
79       endianness = G_BIG_ENDIAN;
80       r_mask = 0x000000ff;
81       g_mask = 0x0000ff00;
82       b_mask = 0x00ff0000;
83       /* FIXME: check
84        *r_mask = 0xff000000;
85        *g_mask = 0x00ff0000;
86        *b_mask = 0x0000ff00;
87        */
88     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB1555)) {
89       bpp = 16;
90       depth = 15;
91       endianness = G_BIG_ENDIAN;
92       r_mask = 0x7c00;
93       g_mask = 0x03e0;
94       b_mask = 0x001f;
95     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB32)) {
96       bpp = depth = 32;
97       endianness = G_BIG_ENDIAN;
98       r_mask = 0x000000ff;
99       g_mask = 0x0000ff00;
100       b_mask = 0x00ff0000;
101       /* FIXME: check
102        *r_mask = 0xff000000;
103        *g_mask = 0x00ff0000;
104        *b_mask = 0x0000ff00;
105        */
106     } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB4444)) {
107       bpp = 16;
108       depth = 12;
109       endianness = G_BIG_ENDIAN;
110       r_mask = 0x0f00;
111       g_mask = 0x00f0;
112       b_mask = 0x000f;
113       //r_mask = 0x000f;
114       //g_mask = 0x00f0;
115       //b_mask = 0x0f00;
116     } else {
117       g_assert_not_reached ();
118     }
119
120     structure = gst_structure_new ("video/x-raw-rgb",
121         "bpp", G_TYPE_INT, bpp,
122         "depth", G_TYPE_INT, depth,
123         "red_mask", G_TYPE_INT, r_mask,
124         "green_mask", G_TYPE_INT, g_mask,
125         "blue_mask", G_TYPE_INT, b_mask,
126         "endianness", G_TYPE_INT, endianness, NULL);
127   } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_dvsd)) {
128     if (IsEqualGUID (&format_guid, &FORMAT_DvInfo)) {
129       structure = gst_structure_new ("video/x-dv",
130           "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
131     } else if (IsEqualGUID (&format_guid, &FORMAT_VideoInfo)) {
132       structure = gst_structure_new ("video/x-dv",
133           "systemstream", G_TYPE_BOOLEAN, FALSE,
134           "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('d', 'v', 's', 'd'),
135           NULL);
136     }
137   } else if (memcmp (&subtype_guid.Data2, &MEDIASUBTYPE_FOURCC.Data2,
138           sizeof (subtype_guid) - sizeof (subtype_guid.Data1)) == 0) {
139     guint8 *p = (guint8 *) & subtype_guid.Data1;
140
141     structure = gst_structure_new ("video/x-raw-yuv",
142         "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC (p[0], p[1], p[2], p[3]),
143         NULL);
144   }
145
146   if (!structure) {
147     GST_DEBUG ("Unknown DirectShow Video GUID %08x-%04x-%04x-%04x-%08x%04x",
148         subtype_guid.Data1, subtype_guid.Data2, subtype_guid.Data3,
149         *(WORD *) subtype_guid.Data4, *(DWORD *) & subtype_guid.Data4[2],
150         *(WORD *) & subtype_guid.Data4[6]);
151   }
152
153   return structure;
154 }
155
156 static gboolean
157 ks_video_append_video_stream_cfg_fields (GstStructure * structure,
158     const KS_VIDEO_STREAM_CONFIG_CAPS * vscc)
159 {
160   g_return_val_if_fail (structure, FALSE);
161   g_return_val_if_fail (vscc, FALSE);
162
163   /* width */
164   if (vscc->MinOutputSize.cx == vscc->MaxOutputSize.cx) {
165     gst_structure_set (structure,
166         "width", G_TYPE_INT, vscc->MaxOutputSize.cx, NULL);
167   } else {
168     gst_structure_set (structure,
169         "width", GST_TYPE_INT_RANGE,
170         vscc->MinOutputSize.cx, vscc->MaxOutputSize.cx, NULL);
171   }
172
173   /* height */
174   if (vscc->MinOutputSize.cy == vscc->MaxOutputSize.cy) {
175     gst_structure_set (structure,
176         "height", G_TYPE_INT, vscc->MaxOutputSize.cy, NULL);
177   } else {
178     gst_structure_set (structure,
179         "height", GST_TYPE_INT_RANGE,
180         vscc->MinOutputSize.cy, vscc->MaxOutputSize.cy, NULL);
181   }
182
183   /* framerate */
184   if (vscc->MinFrameInterval == vscc->MaxFrameInterval) {
185     gst_structure_set (structure,
186         "framerate", GST_TYPE_FRACTION,
187         (gint) (10000000 / vscc->MaxFrameInterval), 1, NULL);
188   } else {
189     gst_structure_set (structure,
190         "framerate", GST_TYPE_FRACTION_RANGE,
191         (gint) (10000000 / vscc->MaxFrameInterval), 1,
192         (gint) (10000000 / vscc->MinFrameInterval), 1, NULL);
193   }
194
195   return TRUE;
196 }
197
198 KsVideoMediaType *
199 ks_video_media_type_dup (KsVideoMediaType * media_type)
200 {
201   KsVideoMediaType *result = g_new (KsVideoMediaType, 1);
202
203   memcpy (result, media_type, sizeof (KsVideoMediaType));
204
205   result->range = g_malloc (media_type->range->FormatSize);
206   memcpy ((gpointer) result->range, media_type->range,
207       media_type->range->FormatSize);
208
209   result->format = g_malloc (media_type->format_size);
210   memcpy (result->format, media_type->format, media_type->format_size);
211
212   result->translated_caps = gst_caps_ref (media_type->translated_caps);
213
214   return result;
215 }
216
217 void
218 ks_video_media_type_free (KsVideoMediaType * media_type)
219 {
220   if (media_type == NULL)
221     return;
222
223   g_free (media_type->format);
224
225   if (media_type->translated_caps != NULL)
226     gst_caps_unref (media_type->translated_caps);
227
228   g_free (media_type);
229 }
230
231 static GList *
232 ks_video_media_type_list_remove_duplicates (GList * media_types)
233 {
234   GList *master, *duplicates;
235
236   do {
237     GList *entry;
238
239     master = duplicates = NULL;
240
241     /* Find the first set of duplicates and their master */
242     for (entry = media_types; entry != NULL && duplicates == NULL;
243         entry = entry->next) {
244       KsVideoMediaType *mt = entry->data;
245       GList *other_entry;
246
247       for (other_entry = media_types; other_entry != NULL;
248           other_entry = other_entry->next) {
249         KsVideoMediaType *other_mt = other_entry->data;
250
251         if (other_mt == mt)
252           continue;
253
254         if (gst_caps_is_equal (mt->translated_caps, other_mt->translated_caps))
255           duplicates = g_list_prepend (duplicates, other_mt);
256       }
257
258       if (duplicates != NULL)
259         master = entry;
260     }
261
262     if (duplicates != NULL) {
263       KsVideoMediaType *selected_mt = master->data;
264
265       /*
266        * Pick a FORMAT_VideoInfo2 if present, if not we just stay with the
267        * first entry
268        */
269       for (entry = duplicates; entry != NULL; entry = entry->next) {
270         KsVideoMediaType *mt = entry->data;
271
272         if (IsEqualGUID (&mt->range->Specifier, &FORMAT_VideoInfo2)) {
273           ks_video_media_type_free (selected_mt);
274           selected_mt = mt;
275         } else {
276           ks_video_media_type_free (mt);
277         }
278
279         /* Remove the dupe from the main list */
280         media_types = g_list_remove (media_types, mt);
281       }
282
283       /* Update master node with the selected MediaType */
284       master->data = selected_mt;
285
286       g_list_free (duplicates);
287     }
288   }
289   while (master != NULL);
290
291   return media_types;
292 }
293
294 GList *
295 ks_video_probe_filter_for_caps (HANDLE filter_handle)
296 {
297   GList *ret = NULL;
298   gulong pin_count;
299   guint pin_id;
300
301   if (!ks_filter_get_pin_property (filter_handle, 0, KSPROPSETID_Pin,
302           KSPROPERTY_PIN_CTYPES, &pin_count, sizeof (pin_count)))
303     goto beach;
304
305   GST_DEBUG ("pin_count = %d", pin_count);
306
307   for (pin_id = 0; pin_id < pin_count; pin_id++) {
308     KSPIN_COMMUNICATION pin_comm;
309     KSPIN_DATAFLOW pin_flow;
310     GUID pin_cat;
311
312     if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
313             KSPROPERTY_PIN_COMMUNICATION, &pin_comm, sizeof (pin_comm)))
314       continue;
315
316     if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
317             KSPROPERTY_PIN_DATAFLOW, &pin_flow, sizeof (pin_flow)))
318       continue;
319
320     if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
321             KSPROPERTY_PIN_CATEGORY, &pin_cat, sizeof (pin_cat)))
322       continue;
323
324     GST_DEBUG ("pin[%d]: pin_comm=%d, pin_flow=%d", pin_id, pin_comm, pin_flow);
325
326     if (pin_flow == KSPIN_DATAFLOW_OUT &&
327         memcmp (&pin_cat, &PINNAME_CAPTURE, sizeof (GUID)) == 0) {
328       KSMULTIPLE_ITEM *items;
329
330       if (ks_filter_get_pin_property_multi (filter_handle, pin_id,
331               KSPROPSETID_Pin, KSPROPERTY_PIN_DATARANGES, &items)) {
332         KSDATARANGE *range = (KSDATARANGE *) (items + 1);
333         guint i;
334
335         for (i = 0; i < items->Count; i++) {
336           if (IsEqualGUID (&range->MajorFormat, &KSDATAFORMAT_TYPE_VIDEO)) {
337             KsVideoMediaType *entry;
338             gpointer src_vscc, src_format;
339             GstStructure *media_structure;
340
341             entry = g_new0 (KsVideoMediaType, 1);
342             entry->pin_id = pin_id;
343
344             entry->range = g_malloc (range->FormatSize);
345             memcpy ((gpointer) entry->range, range, range->FormatSize);
346
347             if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
348               KS_DATARANGE_VIDEO *vr = (KS_DATARANGE_VIDEO *) entry->range;
349
350               src_vscc = &vr->ConfigCaps;
351               src_format = &vr->VideoInfoHeader;
352
353               entry->format_size = sizeof (vr->VideoInfoHeader);
354               entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage;
355             } else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
356               KS_DATARANGE_VIDEO2 *vr = (KS_DATARANGE_VIDEO2 *) entry->range;
357
358               src_vscc = &vr->ConfigCaps;
359               src_format = &vr->VideoInfoHeader;
360
361               entry->format_size = sizeof (vr->VideoInfoHeader);
362               entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage;
363             } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
364               /* Untested and probably wrong... */
365               KS_DATARANGE_MPEG1_VIDEO *vr =
366                   (KS_DATARANGE_MPEG1_VIDEO *) entry->range;
367
368               src_vscc = &vr->ConfigCaps;
369               src_format = &vr->VideoInfoHeader;
370
371               entry->format_size = sizeof (vr->VideoInfoHeader);
372               entry->sample_size =
373                   vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage;
374             } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
375               /* Untested and probably wrong... */
376               KS_DATARANGE_MPEG2_VIDEO *vr =
377                   (KS_DATARANGE_MPEG2_VIDEO *) entry->range;
378
379               src_vscc = &vr->ConfigCaps;
380               src_format = &vr->VideoInfoHeader;
381
382               entry->format_size = sizeof (vr->VideoInfoHeader);
383               entry->sample_size =
384                   vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage;
385             } else
386               g_assert_not_reached ();
387
388             g_assert (entry->sample_size != 0);
389
390             memcpy ((gpointer) & entry->vscc, src_vscc, sizeof (entry->vscc));
391
392             entry->format = g_malloc (entry->format_size);
393             memcpy (entry->format, src_format, entry->format_size);
394
395             media_structure =
396                 ks_video_format_to_structure (range->SubFormat,
397                 range->MajorFormat);
398
399             if (media_structure == NULL) {
400               g_warning ("ks_video_format_to_structure returned NULL");
401               ks_video_media_type_free (entry);
402               entry = NULL;
403             } else if (ks_video_append_video_stream_cfg_fields (media_structure,
404                     &entry->vscc)) {
405               entry->translated_caps = gst_caps_new_empty ();
406               gst_caps_append_structure (entry->translated_caps,
407                   media_structure);
408             } else {
409               gst_structure_free (media_structure);
410               ks_video_media_type_free (entry);
411               entry = NULL;
412             }
413
414             if (entry != NULL)
415               ret = g_list_prepend (ret, entry);
416           }
417
418           /* REVISIT: Each KSDATARANGE should start on a 64-bit boundary */
419           range = (KSDATARANGE *) (((guchar *) range) + range->FormatSize);
420         }
421       }
422     }
423   }
424
425   if (ret != NULL) {
426     ret = g_list_reverse (ret);
427     ret = ks_video_media_type_list_remove_duplicates (ret);
428   }
429
430 beach:
431   return ret;
432 }
433
434 KSPIN_CONNECT *
435 ks_video_create_pin_conn_from_media_type (KsVideoMediaType * media_type)
436 {
437   KSPIN_CONNECT *conn = NULL;
438   KSDATAFORMAT *format = NULL;
439   guint8 *vih;
440
441   conn = g_malloc0 (sizeof (KSPIN_CONNECT) + sizeof (KSDATAFORMAT) +
442       media_type->format_size);
443
444   conn->Interface.Set = KSINTERFACESETID_Standard;
445   conn->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
446   conn->Interface.Flags = 0;
447
448   conn->Medium.Set = KSMEDIUMSETID_Standard;
449   conn->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
450   conn->Medium.Flags = 0;
451
452   conn->PinId = media_type->pin_id;
453   conn->PinToHandle = NULL;
454   conn->Priority.PriorityClass = KSPRIORITY_NORMAL;
455   conn->Priority.PrioritySubClass = 1;
456
457   format = (KSDATAFORMAT *) (conn + 1);
458   memcpy (format, media_type->range, sizeof (KSDATAFORMAT));
459   format->FormatSize = sizeof (KSDATAFORMAT) + media_type->format_size;
460
461   vih = (guint8 *) (format + 1);
462   memcpy (vih, media_type->format, media_type->format_size);
463
464   return conn;
465 }
466
467 gboolean
468 ks_video_fixate_media_type (const KSDATARANGE * range,
469     guint8 * format, gint width, gint height, gint fps_n, gint fps_d)
470 {
471   DWORD dwRate = (width * height * fps_n) / fps_d;
472
473   g_return_val_if_fail (format != NULL, FALSE);
474
475   if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
476     KS_VIDEOINFOHEADER *vih = (KS_VIDEOINFOHEADER *) format;
477
478     vih->AvgTimePerFrame = gst_util_uint64_scale_int (10000000, fps_d, fps_n);
479     vih->dwBitRate = dwRate * vih->bmiHeader.biBitCount;
480
481     g_assert (vih->bmiHeader.biWidth == width);
482     g_assert (vih->bmiHeader.biHeight == height);
483   } else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
484     KS_VIDEOINFOHEADER2 *vih = (KS_VIDEOINFOHEADER2 *) format;
485
486     vih->AvgTimePerFrame = gst_util_uint64_scale_int (10000000, fps_d, fps_n);
487     vih->dwBitRate = dwRate * vih->bmiHeader.biBitCount;
488
489     g_assert (vih->bmiHeader.biWidth == width);
490     g_assert (vih->bmiHeader.biHeight == height);
491   } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
492     KS_MPEG1VIDEOINFO *vih = (KS_MPEG1VIDEOINFO *) format;
493
494     vih->hdr.AvgTimePerFrame =
495         gst_util_uint64_scale_int (10000000, fps_d, fps_n);
496     vih->hdr.dwBitRate = dwRate * vih->hdr.bmiHeader.biBitCount;
497
498     /* FIXME: set height and width? */
499     g_assert (vih->hdr.bmiHeader.biWidth == width);
500     g_assert (vih->hdr.bmiHeader.biHeight == height);
501   } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
502     KS_MPEGVIDEOINFO2 *vih = (KS_MPEGVIDEOINFO2 *) format;
503
504     vih->hdr.AvgTimePerFrame =
505         gst_util_uint64_scale_int (10000000, fps_d, fps_n);
506     vih->hdr.dwBitRate = dwRate * vih->hdr.bmiHeader.biBitCount;
507
508     /* FIXME: set height and width? */
509     g_assert (vih->hdr.bmiHeader.biWidth == width);
510     g_assert (vih->hdr.bmiHeader.biHeight == height);
511   } else {
512     return FALSE;
513   }
514
515   return TRUE;
516 }
517
518 static GstStructure *
519 ks_video_append_var_video_fields (GstStructure * structure)
520 {
521   if (structure) {
522     gst_structure_set (structure,
523         "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
524         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
525         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
526   }
527
528   return structure;
529 }
530
531 GstCaps *
532 ks_video_get_all_caps (void)
533 {
534   static GstCaps *caps = NULL;
535
536   if (caps == NULL) {
537     GstStructure *structure;
538     caps = gst_caps_new_empty ();
539
540     /* from Windows SDK 6.0 uuids.h */
541     /* RGB formats */
542     structure =
543         ks_video_append_var_video_fields (ks_video_format_to_structure
544         (MEDIASUBTYPE_RGB555, FORMAT_VideoInfo));
545     gst_caps_append_structure (caps, structure);
546
547     structure =
548         ks_video_append_var_video_fields (ks_video_format_to_structure
549         (MEDIASUBTYPE_RGB565, FORMAT_VideoInfo));
550     gst_caps_append_structure (caps, structure);
551
552     structure =
553         ks_video_append_var_video_fields (ks_video_format_to_structure
554         (MEDIASUBTYPE_RGB24, FORMAT_VideoInfo));
555     gst_caps_append_structure (caps, structure);
556
557     structure =
558         ks_video_append_var_video_fields (ks_video_format_to_structure
559         (MEDIASUBTYPE_RGB32, FORMAT_VideoInfo));
560     gst_caps_append_structure (caps, structure);
561
562     /* YUV formats */
563     structure =
564         ks_video_append_var_video_fields (gst_structure_new ("video/x-raw-yuv",
565             NULL));
566     gst_caps_append_structure (caps, structure);
567
568     /* Other formats */
569     structure =
570         ks_video_append_var_video_fields (ks_video_format_to_structure
571         (MEDIASUBTYPE_MJPG, FORMAT_VideoInfo));
572     gst_caps_append_structure (caps, structure);
573
574     structure =
575         ks_video_append_var_video_fields (ks_video_format_to_structure
576         (MEDIASUBTYPE_dvsd, FORMAT_VideoInfo));
577     gst_caps_append_structure (caps, structure);
578
579     structure =                 /* no variable video fields (width, height, framerate) */
580         ks_video_format_to_structure (MEDIASUBTYPE_dvsd, FORMAT_DvInfo);
581     gst_caps_append_structure (caps, structure);
582   }
583
584   return caps;
585 }