dc0d5d6ef9b86edf04ef7b4519cc402c8afc3214
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / directshow / dshowsrcwrapper / gstdshowvideosrc.cpp
1 /* GStreamer
2  * Copyright (C)  2007 Sebastien Moutte <sebastien@moutte.net>
3  * Copyright (C)  2009 Julien Isorce <julien.isorce@gmail.com>
4  *
5  * gstdshowvideosrc.c:
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "gstdshowvideosrc.h"
28
29 #include <gst/video/video.h>
30
31 GST_DEBUG_CATEGORY_STATIC (dshowvideosrc_debug);
32 #define GST_CAT_DEFAULT dshowvideosrc_debug
33
34 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
35     GST_PAD_SRC,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS (
38         GST_VIDEO_CAPS_MAKE ("{ I420, BGR, YUY2, UYVY, BGRx, BGR16, BGR15, "
39                              "GRAY8 }") "; "
40
41         "video/x-dv, "
42         "format= (string) DVSD, "
43         "width = " GST_VIDEO_SIZE_RANGE ", "
44         "height = " GST_VIDEO_SIZE_RANGE ", "
45         "framerate = " GST_VIDEO_FPS_RANGE ", "
46         "systemstream = (boolean) { TRUE, FALSE }; "
47
48         "image/jpeg, "
49         "width = " GST_VIDEO_SIZE_RANGE ", "
50         "height = " GST_VIDEO_SIZE_RANGE ", "
51         "framerate = " GST_VIDEO_FPS_RANGE "; "
52
53         "video/x-h264, "
54         "width = " GST_VIDEO_SIZE_RANGE ", "
55         "height = " GST_VIDEO_SIZE_RANGE ", "
56         "framerate = " GST_VIDEO_FPS_RANGE
57         )
58     );
59
60 G_DEFINE_TYPE (GstDshowVideoSrc, gst_dshowvideosrc, GST_TYPE_PUSH_SRC)
61
62 enum
63 {
64   PROP_0,
65   PROP_DEVICE,
66   PROP_DEVICE_NAME,
67   PROP_DEVICE_INDEX
68 };
69
70 #define DEFAULT_PROP_DEVICE_INDEX 0
71
72
73 static void gst_dshowvideosrc_dispose (GObject * gobject);
74 static void gst_dshowvideosrc_set_property (GObject * object, guint prop_id,
75     const GValue * value, GParamSpec * pspec);
76 static void gst_dshowvideosrc_get_property (GObject * object, guint prop_id,
77     GValue * value, GParamSpec * pspec);
78 static GstStateChangeReturn gst_dshowvideosrc_change_state (GstElement *
79     element, GstStateChange transition);
80
81
82 static gboolean gst_dshowvideosrc_start (GstBaseSrc * bsrc);
83 static gboolean gst_dshowvideosrc_stop (GstBaseSrc * bsrc);
84 static gboolean gst_dshowvideosrc_unlock (GstBaseSrc * bsrc);
85 static gboolean gst_dshowvideosrc_unlock_stop (GstBaseSrc * bsrc);
86 static gboolean gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps);
87 static GstCaps *gst_dshowvideosrc_get_caps (GstBaseSrc * bsrc, GstCaps * filter);
88 static GstCaps *gst_dshowvideosrc_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
89 static GstFlowReturn gst_dshowvideosrc_create (GstPushSrc * psrc,
90     GstBuffer ** buf);
91
92 /*utils*/
93 GstCaps *gst_dshowvideosrc_getcaps_from_streamcaps (IPin * pin,
94     GList ** pins_mediatypes);
95 GstCaps *gst_dshowvideosrc_getcaps_from_enum_mediatypes (IPin * pin,
96     GList ** pins_mediatypes);
97
98 static gboolean gst_dshowvideosrc_push_buffer (guint8 * buffer, guint size,
99     gpointer src_object, GstClockTime duration);
100
101 static void
102 gst_dshowvideosrc_class_init (GstDshowVideoSrcClass * klass)
103 {
104   GObjectClass *gobject_class;
105   GstElementClass *gstelement_class;
106   GstBaseSrcClass *gstbasesrc_class;
107   GstPushSrcClass *gstpushsrc_class;
108
109   gobject_class = (GObjectClass *) klass;
110   gstelement_class = (GstElementClass *) klass;
111   gstbasesrc_class = (GstBaseSrcClass *) klass;
112   gstpushsrc_class = (GstPushSrcClass *) klass;
113
114   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_dispose);
115   gobject_class->set_property =
116       GST_DEBUG_FUNCPTR (gst_dshowvideosrc_set_property);
117   gobject_class->get_property =
118       GST_DEBUG_FUNCPTR (gst_dshowvideosrc_get_property);
119
120   gstelement_class->change_state =
121       GST_DEBUG_FUNCPTR (gst_dshowvideosrc_change_state);
122
123   gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_set_caps);
124   gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_get_caps);
125   gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_src_fixate);
126   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_start);
127   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_stop);
128   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_unlock);
129   gstbasesrc_class->unlock_stop =
130       GST_DEBUG_FUNCPTR (gst_dshowvideosrc_unlock_stop);
131
132   gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_create);
133
134   g_object_class_install_property
135       (gobject_class, PROP_DEVICE,
136       g_param_spec_string ("device", "Device",
137           "Directshow device path (@..classID/name)", NULL,
138           static_cast < GParamFlags > (G_PARAM_READWRITE)));
139
140   g_object_class_install_property
141       (gobject_class, PROP_DEVICE_NAME,
142       g_param_spec_string ("device-name", "Device name",
143           "Human-readable name of the video device", NULL,
144           static_cast < GParamFlags > (G_PARAM_READWRITE)));
145
146   g_object_class_install_property
147       (gobject_class, PROP_DEVICE_INDEX,
148       g_param_spec_int ("device-index", "Device index",
149           "Index of the enumerated video device", 0, G_MAXINT,
150           DEFAULT_PROP_DEVICE_INDEX,
151           static_cast < GParamFlags > (G_PARAM_READWRITE)));
152
153   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
154
155   gst_element_class_set_static_metadata (gstelement_class,
156       "DirectShow video capture source", "Source/Video",
157       "Receive data from a directshow video capture graph",
158       "Sebastien Moutte <sebastien@moutte.net>");
159
160   GST_DEBUG_CATEGORY_INIT (dshowvideosrc_debug, "dshowvideosrc", 0,
161       "Directshow video source");
162
163 }
164
165 static void
166 gst_dshowvideosrc_init (GstDshowVideoSrc * src)
167 {
168   src->device = NULL;
169   src->device_name = NULL;
170   src->device_index = DEFAULT_PROP_DEVICE_INDEX;
171   src->video_cap_filter = NULL;
172   src->dshow_fakesink = NULL;
173   src->media_filter = NULL;
174   src->filter_graph = NULL;
175   src->caps = NULL;
176   src->pins_mediatypes = NULL;
177   src->is_rgb = FALSE;
178   src->is_running = FALSE;
179
180   /*added for analog input*/
181   src->graph_builder = NULL;
182   src->capture_builder = NULL;
183   src->pVC = NULL;
184   src->pVSC = NULL;
185
186   g_cond_init(&src->buffer_cond);
187   g_mutex_init(&src->buffer_mutex);
188   src->buffer = NULL;
189   src->stop_requested = FALSE;
190
191   CoInitializeEx (NULL, COINIT_MULTITHREADED);
192
193   gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
194 }
195
196 static GstCaps *
197 gst_dshowvideosrc_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
198 {
199   /* If there is no desired video size, set default video size to device preferred video size */
200
201   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
202   GstStructure *structure = gst_caps_get_structure (caps, 0);
203   guint i = 0;
204   gint res = -1;
205
206   for (; i < gst_caps_get_size (src->caps) && res == -1; i++) {
207     GstCaps *capstmp = gst_caps_copy_nth (src->caps, i);
208
209     if (gst_caps_is_subset (caps, capstmp)) {
210       res = i;
211     }
212     gst_caps_unref (capstmp);
213   }
214
215   if (res != -1) {
216     GList *type_pin_mediatype = g_list_nth (src->pins_mediatypes, res);
217     if (type_pin_mediatype) {
218       GstCapturePinMediaType *pin_mediatype =
219           (GstCapturePinMediaType *) type_pin_mediatype->data;
220       gst_structure_fixate_field_nearest_int (structure, "width",
221           pin_mediatype->defaultWidth);
222       gst_structure_fixate_field_nearest_int (structure, "height",
223           pin_mediatype->defaultHeight);
224       gst_structure_fixate_field_nearest_fraction (structure, "framerate",
225           pin_mediatype->defaultFPS, 1);
226     }
227   }
228
229   caps = GST_BASE_SRC_CLASS (gst_dshowvideosrc_parent_class)->fixate(bsrc, caps);
230
231   return caps;
232 }
233
234 static void
235 gst_dshowvideosrc_dispose (GObject * gobject)
236 {
237   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (gobject);
238
239   if (src->device) {
240     g_free (src->device);
241     src->device = NULL;
242   }
243
244   if (src->device_name) {
245     g_free (src->device_name);
246     src->device_name = NULL;
247   }
248
249   if (src->caps) {
250     gst_caps_unref (src->caps);
251     src->caps = NULL;
252   }
253
254   if (src->pins_mediatypes) {
255     gst_dshow_free_pins_mediatypes (src->pins_mediatypes);
256     src->pins_mediatypes = NULL;
257   }
258
259   /* clean dshow */
260   if (src->video_cap_filter) {
261     src->video_cap_filter->Release ();
262     src->video_cap_filter = NULL;
263   }
264
265   g_cond_clear(&src->buffer_cond);
266   g_mutex_clear(&src->buffer_mutex);
267
268   CoUninitialize ();
269
270   G_OBJECT_CLASS (gst_dshowvideosrc_parent_class)->dispose (gobject);
271 }
272
273 static void
274 gst_dshowvideosrc_set_property (GObject * object, guint prop_id,
275     const GValue * value, GParamSpec * pspec)
276 {
277   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (object);
278
279   switch (prop_id) {
280     case PROP_DEVICE:
281     {
282       const gchar *device = g_value_get_string (value);
283       g_free (src->device);
284       src->device = NULL;
285       if (device && strlen (device) != 0) {
286         src->device = g_value_dup_string (value);
287       }
288       break;
289     }
290     case PROP_DEVICE_NAME:
291     {
292       const gchar *device_name = g_value_get_string (value);
293       g_free (src->device_name);
294       src->device_name = NULL;
295       if (device_name && strlen (device_name) != 0) {
296         src->device_name = g_value_dup_string (value);
297       }
298       break;
299     }
300     case PROP_DEVICE_INDEX:
301     {
302       src->device_index = g_value_get_int (value);
303       break;
304     }
305     default:
306       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307       break;
308   }
309 }
310
311 static void
312 gst_dshowvideosrc_get_property (GObject * object, guint prop_id,
313     GValue * value, GParamSpec * pspec)
314 {
315   GstDshowVideoSrc *src;
316
317   g_return_if_fail (GST_IS_DSHOWVIDEOSRC (object));
318   src = GST_DSHOWVIDEOSRC (object);
319
320   switch (prop_id) {
321     case PROP_DEVICE:
322       g_value_set_string (value, src->device);
323       break;
324     case PROP_DEVICE_NAME:
325       g_value_set_string (value, src->device_name);
326       break;
327     case PROP_DEVICE_INDEX:
328       g_value_set_int (value, src->device_index);
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332       break;
333   }
334 }
335
336 static GstCaps *
337 gst_dshowvideosrc_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
338 {
339   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (basesrc);
340   GstCaps *caps;
341
342   if (src->caps) {
343     caps = gst_caps_ref (src->caps);
344   } else {
345     caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src));
346   }
347
348   if (caps) {
349     GstCaps *filtcaps;
350
351     if (filter) {
352       filtcaps = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
353     } else {
354       filtcaps = gst_caps_ref (caps);
355     }
356     gst_caps_unref (caps);
357
358     return filtcaps;
359   }
360
361   return NULL;
362 }
363
364 GstCaps *
365 gst_dshowvideosrc_getcaps_from_capture_filter (IBaseFilter * filter,
366     GList ** pins_mediatypes)
367 {
368   IPin *capture_pin = NULL;
369   IEnumPins *enumpins = NULL;
370   HRESULT hres;
371   GstCaps *caps;
372
373   g_assert (filter);
374
375   caps = gst_caps_new_empty ();
376
377   /* get the capture pins supported types */
378   hres = filter->EnumPins (&enumpins);
379   if (SUCCEEDED (hres)) {
380     while (enumpins->Next (1, &capture_pin, NULL) == S_OK) {
381       IKsPropertySet *pKs = NULL;
382       hres =
383           capture_pin->QueryInterface (IID_IKsPropertySet, (LPVOID *) & pKs);
384       if (SUCCEEDED (hres) && pKs) {
385         DWORD cbReturned;
386         GUID pin_category;
387         RPC_STATUS rpcstatus;
388
389         hres =
390             pKs->Get (AMPROPSETID_Pin,
391             AMPROPERTY_PIN_CATEGORY, NULL, 0, &pin_category, sizeof (GUID),
392             &cbReturned);
393
394         /* we only want capture pins */
395         if (UuidCompare (&pin_category, (UUID *) & PIN_CATEGORY_CAPTURE,
396                 &rpcstatus) == 0) {
397           GstCaps *caps2;
398           caps2 = gst_dshowvideosrc_getcaps_from_streamcaps (capture_pin,
399               pins_mediatypes);
400           if (caps2) {
401             gst_caps_append (caps, caps2);
402           } else {
403             caps2 = gst_dshowvideosrc_getcaps_from_enum_mediatypes (
404                 capture_pin, pins_mediatypes);
405             if (caps2) {
406               gst_caps_append (caps, caps2);
407             }
408           }
409         }
410         pKs->Release ();
411       }
412       capture_pin->Release ();
413     }
414     enumpins->Release ();
415   }
416
417   GST_DEBUG ("Device supports these caps: %" GST_PTR_FORMAT, caps);
418
419   return caps;
420 }
421
422 static GstStateChangeReturn
423 gst_dshowvideosrc_change_state (GstElement * element, GstStateChange transition)
424 {
425   HRESULT hres = S_FALSE;
426   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (element);
427
428   switch (transition) {
429     case GST_STATE_CHANGE_NULL_TO_READY:
430       break;
431     case GST_STATE_CHANGE_READY_TO_PAUSED:
432       break;
433     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
434       if (src->media_filter) {
435         /* Setting this to TRUE because set_caps may be invoked before
436            Run() returns. */
437         src->is_running = TRUE;
438         hres = src->media_filter->Run (0);
439       }
440       if (hres != S_OK) {
441         GST_ERROR ("Can't RUN the directshow capture graph (error=0x%x)", hres);
442         src->is_running = FALSE;
443         return GST_STATE_CHANGE_FAILURE;
444       }
445       break;
446     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
447       if (src->media_filter)
448         hres = src->media_filter->Stop ();
449       if (hres != S_OK) {
450         GST_ERROR ("Can't STOP the directshow capture graph (error=%d)", hres);
451         return GST_STATE_CHANGE_FAILURE;
452       }
453       src->is_running = FALSE;
454       break;
455     case GST_STATE_CHANGE_PAUSED_TO_READY:
456       break;
457     case GST_STATE_CHANGE_READY_TO_NULL:
458       break;
459     default:
460       break;
461   }
462
463   return GST_ELEMENT_CLASS(gst_dshowvideosrc_parent_class)->change_state(element, transition);
464 }
465
466 static gboolean
467 gst_dshowvideosrc_start (GstBaseSrc * bsrc)
468 {
469   HRESULT hres = S_FALSE;
470   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
471   DshowDeviceEntry *device_entry;
472   IMoniker *moniker = NULL;
473
474   device_entry = gst_dshow_select_device (&CLSID_VideoInputDeviceCategory,
475       src->device, src->device_name, src->device_index);
476   if (device_entry == NULL) {
477     GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to find device"), (NULL));
478     return FALSE;
479   }
480
481   g_free (src->device);
482   g_free (src->device_name);
483   src->device = g_strdup (device_entry->device);
484   src->device_name = g_strdup (device_entry->device_name);
485   src->device_index = device_entry->device_index;
486   moniker = device_entry->moniker;
487   device_entry->moniker = NULL;
488   gst_dshow_device_entry_free (device_entry);
489
490   src->video_cap_filter = gst_dshow_create_capture_filter (moniker);
491   moniker->Release ();
492   if (src->video_cap_filter == NULL) {
493     GST_ELEMENT_ERROR (src, RESOURCE, FAILED,
494         ("Failed to create capture filter for device"), (NULL));
495     return FALSE;
496   }
497
498   src->caps = gst_dshowvideosrc_getcaps_from_capture_filter (
499       src->video_cap_filter, (GList**)&src->pins_mediatypes);
500   if (gst_caps_is_empty (src->caps)) {
501     GST_ELEMENT_ERROR (src, RESOURCE, FAILED,
502         ("Failed to get any caps from device"), (NULL));
503     return FALSE;
504   }
505
506   /*
507   The filter graph now is created via the IGraphBuilder Interface   
508   Code added to build upstream filters, needed for USB Analog TV Tuners / DVD Maker, based on AMCap code.
509   by Fabrice Costa <fabricio.costa@moldeointeractive.com.ar>
510   */
511
512   hres =  CoCreateInstance(CLSID_FilterGraph, NULL,
513     CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (LPVOID *) & src->graph_builder );
514   if (hres != S_OK || !src->graph_builder ) {
515     GST_ERROR
516         ("Can't create an instance of the dshow graph builder (error=0x%x)",
517         hres);
518     goto error;
519   } else {
520         /*graph builder is derived from IFilterGraph so we can assign it to the old src->filter_graph*/
521         src->filter_graph = (IFilterGraph*) src->graph_builder;
522   }
523   
524   /*adding capture graph builder to correctly create upstream filters, Analog TV, TV Tuner */
525
526   hres = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
527         CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, 
528         (LPVOID *) & src->capture_builder);
529   if ( hres != S_OK || !src->capture_builder ) {        
530     GST_ERROR
531         ("Can't create an instance of the dshow capture graph builder manager (error=0x%x)",
532         hres);
533         goto error;
534   } else {      
535         src->capture_builder->SetFiltergraph(src->graph_builder);
536   }
537
538   hres = src->filter_graph->QueryInterface (IID_IMediaFilter,
539       (LPVOID *) & src->media_filter);
540   if (hres != S_OK || !src->media_filter) {
541     GST_ERROR
542         ("Can't get IMediacontrol interface from the graph manager (error=0x%x)",
543         hres);
544     goto error;
545   }
546
547   src->dshow_fakesink = new CDshowFakeSink;
548   src->dshow_fakesink->AddRef ();
549
550   hres = src->filter_graph->AddFilter (src->video_cap_filter, L"capture");
551   if (hres != S_OK) {
552     GST_ERROR ("Can't add video capture filter to the graph (error=0x%x)",
553         hres);
554     goto error;
555   }
556
557   /* Finding interfaces really creates the upstream filters */
558
559   hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
560                                       &MEDIATYPE_Interleaved, src->video_cap_filter, 
561                                       IID_IAMVideoCompression, (LPVOID *)&src->pVC);
562   
563   if(hres != S_OK)
564   {
565         hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
566                                           &MEDIATYPE_Video, src->video_cap_filter, 
567                                           IID_IAMVideoCompression, (LPVOID *)&src->pVC);
568   }
569   
570   hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
571                                       &MEDIATYPE_Interleaved,
572                                       src->video_cap_filter, IID_IAMStreamConfig, (LPVOID *)&src->pVSC);
573   if(hres != S_OK)
574   {
575           hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
576                                                                                         &MEDIATYPE_Video, src->video_cap_filter,
577                                                                                         IID_IAMStreamConfig, (LPVOID *)&src->pVSC);
578           if (hres != S_OK) {
579                   /* this means we can't set frame rate (non-DV only) */
580                   GST_ERROR ("Error %x: Cannot find VCapture:IAMStreamConfig",  hres);
581                         goto error;
582           }
583   }
584
585   hres = src->filter_graph->AddFilter (src->dshow_fakesink, L"sink");
586   if (hres != S_OK) {
587     GST_ERROR ("Can't add our fakesink filter to the graph (error=0x%x)", hres);
588     goto error;
589   }
590
591   return TRUE;
592
593 error:
594   GST_ELEMENT_ERROR (src, RESOURCE, FAILED,
595      ("Failed to build filter graph"), (NULL));
596
597   if (src->dshow_fakesink) {
598     src->dshow_fakesink->Release ();
599     src->dshow_fakesink = NULL;
600   }
601
602   if (src->media_filter) {
603     src->media_filter->Release ();
604     src->media_filter = NULL;
605   }
606   if (src->graph_builder) {
607     src->graph_builder->Release ();
608     src->graph_builder = NULL;
609   }
610   if (src->capture_builder) {
611     src->capture_builder->Release ();
612     src->capture_builder = NULL;
613   }
614   if (src->pVC) {
615     src->pVC->Release ();
616     src->pVC = NULL;
617   }
618   if (src->pVSC) {
619     src->pVSC->Release ();
620     src->pVSC = NULL;
621   }
622
623   return FALSE;
624 }
625
626 static gboolean
627 gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
628 {
629   HRESULT hres;
630   IPin *input_pin = NULL;
631   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
632   GstStructure *s = gst_caps_get_structure (caps, 0);
633   GstCaps *current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc));
634
635   if (current_caps) {
636     if (gst_caps_is_equal (caps, current_caps)) {
637       gst_caps_unref (current_caps);
638       return TRUE;
639     }
640     gst_caps_unref (current_caps);
641   }
642
643   /* Same remark as in gstdshowaudiosrc. */
644   gboolean was_running = src->is_running;
645   if (was_running) {
646     HRESULT hres = src->media_filter->Stop ();
647     if (hres != S_OK) {
648       GST_ERROR ("Can't STOP the directshow capture graph (error=0x%x)", hres);
649       return FALSE;
650     }
651     src->is_running = FALSE;
652   }
653
654   /* search the negotiated caps in our caps list to get its index and the corresponding mediatype */
655   if (gst_caps_is_subset (caps, src->caps)) {
656     guint i = 0;
657     gint res = -1;
658
659     for (; i < gst_caps_get_size (src->caps) && res == -1; i++) {
660       GstCaps *capstmp = gst_caps_copy_nth (src->caps, i);
661
662       if (gst_caps_is_subset (caps, capstmp)) {
663         res = i;
664       }
665       gst_caps_unref (capstmp);
666     }
667
668     if (res != -1 && src->pins_mediatypes) {
669       /* get the corresponding media type and build the dshow graph */
670       GList *type_pin_mediatype = g_list_nth (src->pins_mediatypes, res);
671
672       if (type_pin_mediatype) {
673         GstCapturePinMediaType *pin_mediatype =
674             (GstCapturePinMediaType *) type_pin_mediatype->data;
675         gchar *caps_string = NULL;
676         gchar *src_caps_string = NULL;
677
678         GST_DEBUG_OBJECT (src, "Default: %dx%d@%d", pin_mediatype->defaultWidth, pin_mediatype->defaultHeight, pin_mediatype->defaultFPS);
679
680         /* retrieve the desired video size */
681         VIDEOINFOHEADER *video_info = NULL;
682         gint width = 0;
683         gint height = 0;
684         gint numerator = 0;
685         gint denominator = 0;
686         gst_structure_get_int (s, "width", &width);
687         gst_structure_get_int (s, "height", &height);
688         gst_structure_get_fraction (s, "framerate", &numerator, &denominator);
689
690         /* check if the desired video size is valid about granularity  */
691         /* This check will be removed when GST_TYPE_INT_RANGE_STEP exits */
692         /* See remarks in gst_dshow_new_video_caps function */
693         if (pin_mediatype->granularityWidth != 0
694             && width % pin_mediatype->granularityWidth != 0)
695           g_warning ("your desired video size is not valid : %d mod %d !=0\n",
696               width, pin_mediatype->granularityWidth);
697         if (pin_mediatype->granularityHeight != 0
698             && height % pin_mediatype->granularityHeight != 0)
699           g_warning ("your desired video size is not valid : %d mod %d !=0\n",
700               height, pin_mediatype->granularityHeight);
701
702         /* update mediatype */
703         video_info = (VIDEOINFOHEADER *) pin_mediatype->mediatype->pbFormat;
704         video_info->bmiHeader.biWidth = width;
705         video_info->bmiHeader.biHeight = height;
706         video_info->AvgTimePerFrame =
707             (LONGLONG) (10000000 * denominator / (double) numerator);
708         video_info->bmiHeader.biSizeImage = DIBSIZE (video_info->bmiHeader);
709         pin_mediatype->mediatype->lSampleSize = DIBSIZE (video_info->bmiHeader);
710
711         src->dshow_fakesink->gst_set_media_type (pin_mediatype->mediatype);
712         src->dshow_fakesink->gst_set_buffer_callback (
713             (push_buffer_func) gst_dshowvideosrc_push_buffer, src);
714
715         gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT,
716             &input_pin);
717         if (!input_pin) {
718           GST_ERROR ("Can't get input pin from our dshow fakesink");
719           goto error;
720         }
721
722         if (gst_dshow_is_pin_connected (pin_mediatype->capture_pin)) {
723           GST_DEBUG_OBJECT (src,
724               "capture_pin already connected, disconnecting");
725           src->filter_graph->Disconnect (pin_mediatype->capture_pin);
726         }
727
728         if (gst_dshow_is_pin_connected (input_pin)) {
729           GST_DEBUG_OBJECT (src, "input_pin already connected, disconnecting");
730           src->filter_graph->Disconnect (input_pin);
731         }
732
733         hres = src->pVSC->SetFormat(pin_mediatype->mediatype);
734         if (FAILED (hres)) {
735           GST_ERROR ("Failed to set capture pin format (error=0x%x)", hres);
736           goto error;
737         }
738
739         hres = src->filter_graph->ConnectDirect (pin_mediatype->capture_pin,
740             input_pin, pin_mediatype->mediatype);
741         input_pin->Release ();
742
743         if (hres != S_OK) {
744           GST_ERROR
745               ("Can't connect capture filter with fakesink filter (error=0x%x)",
746               hres);
747           goto error;
748         }
749
750         /* save width and height negotiated */
751         gst_structure_get_int (s, "width", &src->width);
752         gst_structure_get_int (s, "height", &src->height);
753
754         GstVideoInfo info;
755         gst_video_info_from_caps(&info, caps);
756         switch (GST_VIDEO_INFO_FORMAT(&info)) {
757           case GST_VIDEO_FORMAT_RGB:
758           case GST_VIDEO_FORMAT_BGR:
759             src->is_rgb = TRUE;
760             break;
761         default:
762           src->is_rgb = FALSE;
763           break;
764         }
765       }
766     }
767   }
768
769   if (was_running) {
770     HRESULT hres = src->media_filter->Run (0);
771     if (hres != S_OK) {
772       GST_ERROR ("Can't RUN the directshow capture graph (error=0x%x)", hres);
773       return FALSE;
774     }
775     src->is_running = TRUE;
776   }
777
778   return TRUE;
779
780 error:
781   return FALSE;
782 }
783
784 static gboolean
785 gst_dshowvideosrc_stop (GstBaseSrc * bsrc)
786 {
787   IPin *input_pin = NULL, *output_pin = NULL;
788   HRESULT hres = S_FALSE;
789   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
790
791   if (!src->filter_graph)
792     return TRUE;
793
794   /* disconnect filters */
795   gst_dshow_get_pin_from_filter (src->video_cap_filter, PINDIR_OUTPUT,
796       &output_pin);
797   if (output_pin) {
798     hres = src->filter_graph->Disconnect (output_pin);
799     output_pin->Release ();
800   }
801
802   gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT, &input_pin);
803   if (input_pin) {
804     hres = src->filter_graph->Disconnect (input_pin);
805     input_pin->Release ();
806   }
807
808   /* remove filters from the graph */
809   src->filter_graph->RemoveFilter (src->video_cap_filter);
810   src->filter_graph->RemoveFilter (src->dshow_fakesink);
811
812   /* release our gstreamer dshow sink */
813   src->dshow_fakesink->Release ();
814   src->dshow_fakesink = NULL;
815
816   /* release media filter interface */
817   src->media_filter->Release ();
818   src->media_filter = NULL;
819
820   /* release any upstream filter */
821   if (src->pVC) {
822       src->pVC->Release ();
823       src->pVC = NULL;
824   }
825
826   if (src->pVSC) {
827       src->pVSC->Release ();
828       src->pVSC = NULL;
829   }
830
831 /* release the graph builder */
832   if (src->graph_builder) {
833     src->graph_builder->Release ();
834     src->graph_builder = NULL;
835     src->filter_graph = NULL;
836   }
837
838 /* release the capture builder */
839   if (src->capture_builder) {
840     src->capture_builder->Release ();
841     src->capture_builder = NULL;
842   }
843
844   /* reset caps */
845   if (src->caps) {
846     gst_caps_unref (src->caps);
847     src->caps = NULL;
848   }
849
850   /* reset device id */
851   if (src->device) {
852     g_free (src->device);
853     src->device = NULL;
854   }
855
856   if (src->video_cap_filter) {
857     src->video_cap_filter->Release ();
858     src->video_cap_filter = NULL;
859   }
860
861   return TRUE;
862 }
863
864 static gboolean
865 gst_dshowvideosrc_unlock (GstBaseSrc * bsrc)
866 {
867   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
868
869   g_mutex_lock (&src->buffer_mutex);
870   src->stop_requested = TRUE;
871   g_cond_signal (&src->buffer_cond);
872   g_mutex_unlock (&src->buffer_mutex);
873
874   return TRUE;
875 }
876
877 static gboolean
878 gst_dshowvideosrc_unlock_stop (GstBaseSrc * bsrc)
879 {
880   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
881
882   src->stop_requested = FALSE;
883
884   return TRUE;
885 }
886
887 static GstFlowReturn
888 gst_dshowvideosrc_create (GstPushSrc * psrc, GstBuffer ** buf)
889 {
890   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (psrc);
891
892   g_mutex_lock (&src->buffer_mutex);
893   while (src->buffer == NULL && !src->stop_requested)
894     g_cond_wait (&src->buffer_cond, &src->buffer_mutex);
895   *buf = src->buffer;
896   src->buffer = NULL;
897   g_mutex_unlock (&src->buffer_mutex);
898
899   if (src->stop_requested) {
900     if (*buf != NULL) {
901       gst_buffer_unref (*buf);
902       *buf = NULL;
903     }
904     return GST_FLOW_FLUSHING;
905   }
906
907   GST_DEBUG ("dshowvideosrc_create => pts %" GST_TIME_FORMAT " duration %"
908       GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*buf)),
909       GST_TIME_ARGS (GST_BUFFER_DURATION (*buf)));
910
911   return GST_FLOW_OK;
912 }
913
914 GstCaps *
915 gst_dshowvideosrc_getcaps_from_streamcaps (IPin * pin, GList ** pins_mediatypes)
916 {
917   GstCaps *caps = NULL;
918   HRESULT hres = S_OK;
919   int icount = 0;
920   int isize = 0;
921   int i = 0;
922   IAMStreamConfig *streamcaps = NULL;
923
924   hres = pin->QueryInterface (IID_IAMStreamConfig, (LPVOID *) & streamcaps);
925   if (FAILED (hres)) {
926     GST_ERROR ("Failed to retrieve IAMStreamConfig (error=0x%x)", hres);
927     return NULL;
928   }
929
930   streamcaps->GetNumberOfCapabilities (&icount, &isize);
931
932   if (isize != sizeof (VIDEO_STREAM_CONFIG_CAPS)) {
933     streamcaps->Release ();
934     return NULL;
935   }
936
937   caps = gst_caps_new_empty ();
938
939   for (i = 0; i < icount; i++) {
940
941     GstCapturePinMediaType *pin_mediatype =
942       gst_dshow_new_pin_mediatype_from_streamcaps (pin, i, streamcaps);
943
944     if (pin_mediatype) {
945
946       GstCaps *mediacaps = NULL;
947       GstVideoFormat video_format = 
948         gst_dshow_guid_to_gst_video_format (pin_mediatype->mediatype);
949
950       if (video_format != GST_VIDEO_FORMAT_UNKNOWN) {
951         mediacaps = gst_dshow_new_video_caps (video_format, NULL,
952             pin_mediatype);
953
954       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
955               MEDIASUBTYPE_dvsd, FORMAT_VideoInfo)) {
956         mediacaps =
957             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_UNKNOWN,
958             "video/x-dv, systemstream=FALSE", pin_mediatype);
959
960       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
961               MEDIASUBTYPE_dvsd, FORMAT_DvInfo)) {
962         mediacaps =
963             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_UNKNOWN,
964             "video/x-dv, systemstream=TRUE", pin_mediatype);
965
966         pin_mediatype->granularityWidth = 0;
967         pin_mediatype->granularityHeight = 0;
968
969       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
970               MEDIASUBTYPE_MJPG, FORMAT_VideoInfo)) {
971         mediacaps =
972             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_ENCODED,
973             "image/jpeg", pin_mediatype);
974
975       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
976               MEDIASUBTYPE_H264, FORMAT_VideoInfo)) {
977         mediacaps =
978             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_ENCODED,
979             "video/x-h264", pin_mediatype);
980       }
981
982       if (mediacaps) {
983         if (pins_mediatypes != NULL) {
984           *pins_mediatypes = g_list_append (*pins_mediatypes, pin_mediatype);
985         }
986         gst_caps_append (caps, mediacaps);
987       } else {
988         /* failed to convert dshow caps */
989         gst_dshow_free_pin_mediatype (pin_mediatype);
990       }
991     }
992   }
993
994   streamcaps->Release ();
995
996   if (caps && gst_caps_is_empty (caps)) {
997     gst_caps_unref (caps);
998     caps = NULL;
999   }
1000
1001   return caps;
1002 }
1003
1004 GstCaps *
1005 gst_dshowvideosrc_getcaps_from_enum_mediatypes (IPin * pin, GList ** pins_mediatypes)
1006 {
1007   GstCaps *caps = NULL;
1008   IEnumMediaTypes *enum_mediatypes = NULL;
1009   HRESULT hres = S_OK;
1010   GstCapturePinMediaType *pin_mediatype = NULL;
1011
1012   hres = pin->EnumMediaTypes (&enum_mediatypes);
1013   if (FAILED (hres)) {
1014     GST_ERROR ("Failed to retrieve IEnumMediaTypes (error=0x%x)", hres);
1015     return NULL;
1016   }
1017
1018   caps = gst_caps_new_empty ();
1019
1020   while ((pin_mediatype = gst_dshow_new_pin_mediatype_from_enum_mediatypes (pin, enum_mediatypes)) != NULL) {
1021
1022     GstCaps *mediacaps = NULL;
1023     GstVideoFormat video_format = gst_dshow_guid_to_gst_video_format (pin_mediatype->mediatype);
1024
1025         if (video_format != GST_VIDEO_FORMAT_UNKNOWN) {
1026                 GstVideoInfo info;
1027
1028                 gst_video_info_init(&info);
1029                 gst_video_info_set_format(&info, video_format, pin_mediatype->defaultWidth, pin_mediatype->defaultHeight);
1030                 info.fps_n = pin_mediatype->defaultFPS;
1031                 info.fps_d = 1;
1032                 info.par_n = 1;
1033                 info.par_d = 1;
1034                 info.interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; /* XXX is this correct ? */
1035                 mediacaps = gst_video_info_to_caps(&info);
1036         }
1037
1038     if (mediacaps) {
1039       if (pins_mediatypes != NULL) {
1040         *pins_mediatypes = g_list_append (*pins_mediatypes, pin_mediatype);
1041       }
1042       gst_caps_append (caps, mediacaps);
1043     } else {
1044       /* failed to convert dshow caps */
1045       gst_dshow_free_pin_mediatype (pin_mediatype);
1046     }
1047   }
1048
1049   enum_mediatypes->Release ();
1050
1051   if (caps && gst_caps_is_empty (caps)) {
1052     gst_caps_unref (caps);
1053     caps = NULL;
1054   }
1055
1056   return caps;
1057 }
1058
1059 static gboolean
1060 gst_dshowvideosrc_push_buffer (guint8 * buffer, guint size, gpointer src_object,
1061     GstClockTime duration)
1062 {
1063   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (src_object);
1064   GstBuffer *buf = NULL;
1065   IPin *pPin = NULL;
1066   HRESULT hres = S_FALSE;
1067   AM_MEDIA_TYPE *pMediaType = NULL;
1068   GstMapInfo info;
1069
1070   if (!buffer || size == 0 || !src) {
1071     return FALSE;
1072   }
1073
1074   /* create a new buffer assign to it the clock time as timestamp */
1075   buf = gst_buffer_new_and_alloc (size);
1076
1077   gst_buffer_set_size(buf, size);
1078
1079   GstClock *clock = gst_element_get_clock (GST_ELEMENT (src));
1080   GST_BUFFER_TIMESTAMP (buf) =
1081     GST_CLOCK_DIFF (gst_element_get_base_time (GST_ELEMENT (src)), gst_clock_get_time (clock));
1082   gst_object_unref (clock);
1083
1084   GST_BUFFER_DURATION (buf) = duration;
1085
1086   if (!gst_buffer_map(buf, &info, GST_MAP_WRITE)) {
1087           gst_buffer_unref(buf);
1088           GST_ERROR("Failed to map buffer");
1089           return FALSE;
1090   }
1091
1092   if (src->is_rgb) {
1093     /* FOR RGB directshow decoder will return bottom-up BITMAP
1094      * There is probably a way to get top-bottom video frames from
1095      * the decoder...
1096      */
1097     gint line = 0;
1098     gint stride = size / src->height;
1099
1100     for (; line < src->height; line++) {
1101       memcpy (info.data + (line * stride),
1102           buffer + (size - ((line + 1) * (stride))), stride);
1103     }
1104   } else {
1105     memcpy (info.data, buffer, size);
1106   }
1107
1108   gst_buffer_unmap(buf, &info);
1109
1110   GST_DEBUG ("push_buffer => pts %" GST_TIME_FORMAT "duration %"
1111       GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
1112       GST_TIME_ARGS (duration));
1113
1114   g_mutex_lock (&src->buffer_mutex);
1115   if (src->buffer != NULL)
1116     gst_buffer_unref (src->buffer);
1117   src->buffer = buf;
1118   g_cond_signal (&src->buffer_cond);
1119   g_mutex_unlock (&src->buffer_mutex);
1120
1121   return TRUE;
1122 }