documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / sys / 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   }
460
461   return GST_ELEMENT_CLASS(gst_dshowvideosrc_parent_class)->change_state(element, transition);
462 }
463
464 static gboolean
465 gst_dshowvideosrc_start (GstBaseSrc * bsrc)
466 {
467   HRESULT hres = S_FALSE;
468   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
469   DshowDeviceEntry *device_entry;
470   IMoniker *moniker = NULL;
471
472   device_entry = gst_dshow_select_device (&CLSID_VideoInputDeviceCategory,
473       src->device, src->device_name, src->device_index);
474   if (device_entry == NULL) {
475     GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to find device"), (NULL));
476     return FALSE;
477   }
478
479   g_free (src->device);
480   g_free (src->device_name);
481   src->device = g_strdup (device_entry->device);
482   src->device_name = g_strdup (device_entry->device_name);
483   src->device_index = device_entry->device_index;
484   moniker = device_entry->moniker;
485   device_entry->moniker = NULL;
486   gst_dshow_device_entry_free (device_entry);
487
488   src->video_cap_filter = gst_dshow_create_capture_filter (moniker);
489   moniker->Release ();
490   if (src->video_cap_filter == NULL) {
491     GST_ELEMENT_ERROR (src, RESOURCE, FAILED,
492         ("Failed to create capture filter for device"), (NULL));
493     return FALSE;
494   }
495
496   src->caps = gst_dshowvideosrc_getcaps_from_capture_filter (
497       src->video_cap_filter, (GList**)&src->pins_mediatypes);
498   if (gst_caps_is_empty (src->caps)) {
499     GST_ELEMENT_ERROR (src, RESOURCE, FAILED,
500         ("Failed to get any caps from device"), (NULL));
501     return FALSE;
502   }
503
504   /*
505   The filter graph now is created via the IGraphBuilder Interface   
506   Code added to build upstream filters, needed for USB Analog TV Tuners / DVD Maker, based on AMCap code.
507   by Fabrice Costa <fabricio.costa@moldeointeractive.com.ar>
508   */
509
510   hres =  CoCreateInstance(CLSID_FilterGraph, NULL,
511     CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (LPVOID *) & src->graph_builder );
512   if (hres != S_OK || !src->graph_builder ) {
513     GST_ERROR
514         ("Can't create an instance of the dshow graph builder (error=0x%x)",
515         hres);
516     goto error;
517   } else {
518         /*graph builder is derived from IFilterGraph so we can assign it to the old src->filter_graph*/
519         src->filter_graph = (IFilterGraph*) src->graph_builder;
520   }
521   
522   /*adding capture graph builder to correctly create upstream filters, Analog TV, TV Tuner */
523
524   hres = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
525         CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, 
526         (LPVOID *) & src->capture_builder);
527   if ( hres != S_OK || !src->capture_builder ) {        
528     GST_ERROR
529         ("Can't create an instance of the dshow capture graph builder manager (error=0x%x)",
530         hres);
531         goto error;
532   } else {      
533         src->capture_builder->SetFiltergraph(src->graph_builder);
534   }
535
536   hres = src->filter_graph->QueryInterface (IID_IMediaFilter,
537       (LPVOID *) & src->media_filter);
538   if (hres != S_OK || !src->media_filter) {
539     GST_ERROR
540         ("Can't get IMediacontrol interface from the graph manager (error=0x%x)",
541         hres);
542     goto error;
543   }
544
545   src->dshow_fakesink = new CDshowFakeSink;
546   src->dshow_fakesink->AddRef ();
547
548   hres = src->filter_graph->AddFilter (src->video_cap_filter, L"capture");
549   if (hres != S_OK) {
550     GST_ERROR ("Can't add video capture filter to the graph (error=0x%x)",
551         hres);
552     goto error;
553   }
554
555   /* Finding interfaces really creates the upstream filters */
556
557   hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
558                                       &MEDIATYPE_Interleaved, src->video_cap_filter, 
559                                       IID_IAMVideoCompression, (LPVOID *)&src->pVC);
560   
561   if(hres != S_OK)
562   {
563         hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
564                                           &MEDIATYPE_Video, src->video_cap_filter, 
565                                           IID_IAMVideoCompression, (LPVOID *)&src->pVC);
566   }
567   
568   hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
569                                       &MEDIATYPE_Interleaved,
570                                       src->video_cap_filter, IID_IAMStreamConfig, (LPVOID *)&src->pVSC);
571   if(hres != S_OK)
572   {
573           hres = src->capture_builder->FindInterface(&PIN_CATEGORY_CAPTURE,
574                                                                                         &MEDIATYPE_Video, src->video_cap_filter,
575                                                                                         IID_IAMStreamConfig, (LPVOID *)&src->pVSC);
576           if (hres != S_OK) {
577                   /* this means we can't set frame rate (non-DV only) */
578                   GST_ERROR ("Error %x: Cannot find VCapture:IAMStreamConfig",  hres);
579                         goto error;
580           }
581   }
582
583   hres = src->filter_graph->AddFilter (src->dshow_fakesink, L"sink");
584   if (hres != S_OK) {
585     GST_ERROR ("Can't add our fakesink filter to the graph (error=0x%x)", hres);
586     goto error;
587   }
588
589   return TRUE;
590
591 error:
592   GST_ELEMENT_ERROR (src, RESOURCE, FAILED,
593      ("Failed to build filter graph"), (NULL));
594
595   if (src->dshow_fakesink) {
596     src->dshow_fakesink->Release ();
597     src->dshow_fakesink = NULL;
598   }
599
600   if (src->media_filter) {
601     src->media_filter->Release ();
602     src->media_filter = NULL;
603   }
604   if (src->graph_builder) {
605     src->graph_builder->Release ();
606     src->graph_builder = NULL;
607   }
608   if (src->capture_builder) {
609     src->capture_builder->Release ();
610     src->capture_builder = NULL;
611   }
612   if (src->pVC) {
613     src->pVC->Release ();
614     src->pVC = NULL;
615   }
616   if (src->pVSC) {
617     src->pVSC->Release ();
618     src->pVSC = NULL;
619   }
620
621   return FALSE;
622 }
623
624 static gboolean
625 gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
626 {
627   HRESULT hres;
628   IPin *input_pin = NULL;
629   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
630   GstStructure *s = gst_caps_get_structure (caps, 0);
631   GstCaps *current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc));
632
633   if (current_caps) {
634     if (gst_caps_is_equal (caps, current_caps)) {
635       gst_caps_unref (current_caps);
636       return TRUE;
637     }
638     gst_caps_unref (current_caps);
639   }
640
641   /* Same remark as in gstdshowaudiosrc. */
642   gboolean was_running = src->is_running;
643   if (was_running) {
644     HRESULT hres = src->media_filter->Stop ();
645     if (hres != S_OK) {
646       GST_ERROR ("Can't STOP the directshow capture graph (error=0x%x)", hres);
647       return FALSE;
648     }
649     src->is_running = FALSE;
650   }
651
652   /* search the negotiated caps in our caps list to get its index and the corresponding mediatype */
653   if (gst_caps_is_subset (caps, src->caps)) {
654     guint i = 0;
655     gint res = -1;
656
657     for (; i < gst_caps_get_size (src->caps) && res == -1; i++) {
658       GstCaps *capstmp = gst_caps_copy_nth (src->caps, i);
659
660       if (gst_caps_is_subset (caps, capstmp)) {
661         res = i;
662       }
663       gst_caps_unref (capstmp);
664     }
665
666     if (res != -1 && src->pins_mediatypes) {
667       /* get the corresponding media type and build the dshow graph */
668       GList *type_pin_mediatype = g_list_nth (src->pins_mediatypes, res);
669
670       if (type_pin_mediatype) {
671         GstCapturePinMediaType *pin_mediatype =
672             (GstCapturePinMediaType *) type_pin_mediatype->data;
673         gchar *caps_string = NULL;
674         gchar *src_caps_string = NULL;
675
676         GST_DEBUG_OBJECT (src, "Default: %dx%d@%d", pin_mediatype->defaultWidth, pin_mediatype->defaultHeight, pin_mediatype->defaultFPS);
677
678         /* retrieve the desired video size */
679         VIDEOINFOHEADER *video_info = NULL;
680         gint width = 0;
681         gint height = 0;
682         gint numerator = 0;
683         gint denominator = 0;
684         gst_structure_get_int (s, "width", &width);
685         gst_structure_get_int (s, "height", &height);
686         gst_structure_get_fraction (s, "framerate", &numerator, &denominator);
687
688         /* check if the desired video size is valid about granularity  */
689         /* This check will be removed when GST_TYPE_INT_RANGE_STEP exits */
690         /* See remarks in gst_dshow_new_video_caps function */
691         if (pin_mediatype->granularityWidth != 0
692             && width % pin_mediatype->granularityWidth != 0)
693           g_warning ("your desired video size is not valid : %d mod %d !=0\n",
694               width, pin_mediatype->granularityWidth);
695         if (pin_mediatype->granularityHeight != 0
696             && height % pin_mediatype->granularityHeight != 0)
697           g_warning ("your desired video size is not valid : %d mod %d !=0\n",
698               height, pin_mediatype->granularityHeight);
699
700         /* update mediatype */
701         video_info = (VIDEOINFOHEADER *) pin_mediatype->mediatype->pbFormat;
702         video_info->bmiHeader.biWidth = width;
703         video_info->bmiHeader.biHeight = height;
704         video_info->AvgTimePerFrame =
705             (LONGLONG) (10000000 * denominator / (double) numerator);
706         video_info->bmiHeader.biSizeImage = DIBSIZE (video_info->bmiHeader);
707         pin_mediatype->mediatype->lSampleSize = DIBSIZE (video_info->bmiHeader);
708
709         src->dshow_fakesink->gst_set_media_type (pin_mediatype->mediatype);
710         src->dshow_fakesink->gst_set_buffer_callback (
711             (push_buffer_func) gst_dshowvideosrc_push_buffer, src);
712
713         gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT,
714             &input_pin);
715         if (!input_pin) {
716           GST_ERROR ("Can't get input pin from our dshow fakesink");
717           goto error;
718         }
719
720         if (gst_dshow_is_pin_connected (pin_mediatype->capture_pin)) {
721           GST_DEBUG_OBJECT (src,
722               "capture_pin already connected, disconnecting");
723           src->filter_graph->Disconnect (pin_mediatype->capture_pin);
724         }
725
726         if (gst_dshow_is_pin_connected (input_pin)) {
727           GST_DEBUG_OBJECT (src, "input_pin already connected, disconnecting");
728           src->filter_graph->Disconnect (input_pin);
729         }
730
731         hres = src->pVSC->SetFormat(pin_mediatype->mediatype);
732         if (FAILED (hres)) {
733           GST_ERROR ("Failed to set capture pin format (error=0x%x)", hres);
734           goto error;
735         }
736
737         hres = src->filter_graph->ConnectDirect (pin_mediatype->capture_pin,
738             input_pin, pin_mediatype->mediatype);
739         input_pin->Release ();
740
741         if (hres != S_OK) {
742           GST_ERROR
743               ("Can't connect capture filter with fakesink filter (error=0x%x)",
744               hres);
745           goto error;
746         }
747
748         /* save width and height negotiated */
749         gst_structure_get_int (s, "width", &src->width);
750         gst_structure_get_int (s, "height", &src->height);
751
752         GstVideoInfo info;
753         gst_video_info_from_caps(&info, caps);
754         switch (GST_VIDEO_INFO_FORMAT(&info)) {
755           case GST_VIDEO_FORMAT_RGB:
756           case GST_VIDEO_FORMAT_BGR:
757             src->is_rgb = TRUE;
758             break;
759         default:
760           src->is_rgb = FALSE;
761           break;
762         }
763       }
764     }
765   }
766
767   if (was_running) {
768     HRESULT hres = src->media_filter->Run (0);
769     if (hres != S_OK) {
770       GST_ERROR ("Can't RUN the directshow capture graph (error=0x%x)", hres);
771       return FALSE;
772     }
773     src->is_running = TRUE;
774   }
775
776   return TRUE;
777
778 error:
779   return FALSE;
780 }
781
782 static gboolean
783 gst_dshowvideosrc_stop (GstBaseSrc * bsrc)
784 {
785   IPin *input_pin = NULL, *output_pin = NULL;
786   HRESULT hres = S_FALSE;
787   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
788
789   if (!src->filter_graph)
790     return TRUE;
791
792   /* disconnect filters */
793   gst_dshow_get_pin_from_filter (src->video_cap_filter, PINDIR_OUTPUT,
794       &output_pin);
795   if (output_pin) {
796     hres = src->filter_graph->Disconnect (output_pin);
797     output_pin->Release ();
798   }
799
800   gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT, &input_pin);
801   if (input_pin) {
802     hres = src->filter_graph->Disconnect (input_pin);
803     input_pin->Release ();
804   }
805
806   /* remove filters from the graph */
807   src->filter_graph->RemoveFilter (src->video_cap_filter);
808   src->filter_graph->RemoveFilter (src->dshow_fakesink);
809
810   /* release our gstreamer dshow sink */
811   src->dshow_fakesink->Release ();
812   src->dshow_fakesink = NULL;
813
814   /* release media filter interface */
815   src->media_filter->Release ();
816   src->media_filter = NULL;
817
818   /* release any upstream filter */
819   if (src->pVC) {
820       src->pVC->Release ();
821       src->pVC = NULL;
822   }
823
824   if (src->pVSC) {
825       src->pVSC->Release ();
826       src->pVSC = NULL;
827   }
828
829 /* release the graph builder */
830   if (src->graph_builder) {
831     src->graph_builder->Release ();
832     src->graph_builder = NULL;
833     src->filter_graph = NULL;
834   }
835
836 /* release the capture builder */
837   if (src->capture_builder) {
838     src->capture_builder->Release ();
839     src->capture_builder = NULL;
840   }
841
842   /* reset caps */
843   if (src->caps) {
844     gst_caps_unref (src->caps);
845     src->caps = NULL;
846   }
847
848   /* reset device id */
849   if (src->device) {
850     g_free (src->device);
851     src->device = NULL;
852   }
853
854   if (src->video_cap_filter) {
855     src->video_cap_filter->Release ();
856     src->video_cap_filter = NULL;
857   }
858
859   return TRUE;
860 }
861
862 static gboolean
863 gst_dshowvideosrc_unlock (GstBaseSrc * bsrc)
864 {
865   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
866
867   g_mutex_lock (&src->buffer_mutex);
868   src->stop_requested = TRUE;
869   g_cond_signal (&src->buffer_cond);
870   g_mutex_unlock (&src->buffer_mutex);
871
872   return TRUE;
873 }
874
875 static gboolean
876 gst_dshowvideosrc_unlock_stop (GstBaseSrc * bsrc)
877 {
878   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
879
880   src->stop_requested = FALSE;
881
882   return TRUE;
883 }
884
885 static GstFlowReturn
886 gst_dshowvideosrc_create (GstPushSrc * psrc, GstBuffer ** buf)
887 {
888   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (psrc);
889
890   g_mutex_lock (&src->buffer_mutex);
891   while (src->buffer == NULL && !src->stop_requested)
892     g_cond_wait (&src->buffer_cond, &src->buffer_mutex);
893   *buf = src->buffer;
894   src->buffer = NULL;
895   g_mutex_unlock (&src->buffer_mutex);
896
897   if (src->stop_requested) {
898     if (*buf != NULL) {
899       gst_buffer_unref (*buf);
900       *buf = NULL;
901     }
902     return GST_FLOW_FLUSHING;
903   }
904
905   GST_DEBUG ("dshowvideosrc_create => pts %" GST_TIME_FORMAT " duration %"
906       GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*buf)),
907       GST_TIME_ARGS (GST_BUFFER_DURATION (*buf)));
908
909   return GST_FLOW_OK;
910 }
911
912 GstCaps *
913 gst_dshowvideosrc_getcaps_from_streamcaps (IPin * pin, GList ** pins_mediatypes)
914 {
915   GstCaps *caps = NULL;
916   HRESULT hres = S_OK;
917   int icount = 0;
918   int isize = 0;
919   int i = 0;
920   IAMStreamConfig *streamcaps = NULL;
921
922   hres = pin->QueryInterface (IID_IAMStreamConfig, (LPVOID *) & streamcaps);
923   if (FAILED (hres)) {
924     GST_ERROR ("Failed to retrieve IAMStreamConfig (error=0x%x)", hres);
925     return NULL;
926   }
927
928   streamcaps->GetNumberOfCapabilities (&icount, &isize);
929
930   if (isize != sizeof (VIDEO_STREAM_CONFIG_CAPS)) {
931     streamcaps->Release ();
932     return NULL;
933   }
934
935   caps = gst_caps_new_empty ();
936
937   for (i = 0; i < icount; i++) {
938
939     GstCapturePinMediaType *pin_mediatype =
940       gst_dshow_new_pin_mediatype_from_streamcaps (pin, i, streamcaps);
941
942     if (pin_mediatype) {
943
944       GstCaps *mediacaps = NULL;
945       GstVideoFormat video_format = 
946         gst_dshow_guid_to_gst_video_format (pin_mediatype->mediatype);
947
948       if (video_format != GST_VIDEO_FORMAT_UNKNOWN) {
949         mediacaps = gst_dshow_new_video_caps (video_format, NULL,
950             pin_mediatype);
951
952       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
953               MEDIASUBTYPE_dvsd, FORMAT_VideoInfo)) {
954         mediacaps =
955             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_UNKNOWN,
956             "video/x-dv, systemstream=FALSE", pin_mediatype);
957
958       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
959               MEDIASUBTYPE_dvsd, FORMAT_DvInfo)) {
960         mediacaps =
961             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_UNKNOWN,
962             "video/x-dv, systemstream=TRUE", pin_mediatype);
963
964         pin_mediatype->granularityWidth = 0;
965         pin_mediatype->granularityHeight = 0;
966
967       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
968               MEDIASUBTYPE_MJPG, FORMAT_VideoInfo)) {
969         mediacaps =
970             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_ENCODED,
971             "image/jpeg", pin_mediatype);
972
973       } else if (gst_dshow_check_mediatype (pin_mediatype->mediatype,
974               MEDIASUBTYPE_H264, FORMAT_VideoInfo)) {
975         mediacaps =
976             gst_dshow_new_video_caps (GST_VIDEO_FORMAT_ENCODED,
977             "video/x-h264", pin_mediatype);
978       }
979
980       if (mediacaps) {
981         if (pins_mediatypes != NULL) {
982           *pins_mediatypes = g_list_append (*pins_mediatypes, pin_mediatype);
983         }
984         gst_caps_append (caps, mediacaps);
985       } else {
986         /* failed to convert dshow caps */
987         gst_dshow_free_pin_mediatype (pin_mediatype);
988       }
989     }
990   }
991
992   streamcaps->Release ();
993
994   if (caps && gst_caps_is_empty (caps)) {
995     gst_caps_unref (caps);
996     caps = NULL;
997   }
998
999   return caps;
1000 }
1001
1002 GstCaps *
1003 gst_dshowvideosrc_getcaps_from_enum_mediatypes (IPin * pin, GList ** pins_mediatypes)
1004 {
1005   GstCaps *caps = NULL;
1006   IEnumMediaTypes *enum_mediatypes = NULL;
1007   HRESULT hres = S_OK;
1008   GstCapturePinMediaType *pin_mediatype = NULL;
1009
1010   hres = pin->EnumMediaTypes (&enum_mediatypes);
1011   if (FAILED (hres)) {
1012     GST_ERROR ("Failed to retrieve IEnumMediaTypes (error=0x%x)", hres);
1013     return NULL;
1014   }
1015
1016   caps = gst_caps_new_empty ();
1017
1018   while ((pin_mediatype = gst_dshow_new_pin_mediatype_from_enum_mediatypes (pin, enum_mediatypes)) != NULL) {
1019
1020     GstCaps *mediacaps = NULL;
1021     GstVideoFormat video_format = gst_dshow_guid_to_gst_video_format (pin_mediatype->mediatype);
1022
1023         if (video_format != GST_VIDEO_FORMAT_UNKNOWN) {
1024                 GstVideoInfo info;
1025
1026                 gst_video_info_init(&info);
1027                 gst_video_info_set_format(&info, video_format, pin_mediatype->defaultWidth, pin_mediatype->defaultHeight);
1028                 info.fps_n = pin_mediatype->defaultFPS;
1029                 info.fps_d = 1;
1030                 info.par_n = 1;
1031                 info.par_d = 1;
1032                 info.interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; /* XXX is this correct ? */
1033                 mediacaps = gst_video_info_to_caps(&info);
1034         }
1035
1036     if (mediacaps) {
1037       if (pins_mediatypes != NULL) {
1038         *pins_mediatypes = g_list_append (*pins_mediatypes, pin_mediatype);
1039       }
1040       gst_caps_append (caps, mediacaps);
1041     } else {
1042       /* failed to convert dshow caps */
1043       gst_dshow_free_pin_mediatype (pin_mediatype);
1044     }
1045   }
1046
1047   enum_mediatypes->Release ();
1048
1049   if (caps && gst_caps_is_empty (caps)) {
1050     gst_caps_unref (caps);
1051     caps = NULL;
1052   }
1053
1054   return caps;
1055 }
1056
1057 static gboolean
1058 gst_dshowvideosrc_push_buffer (guint8 * buffer, guint size, gpointer src_object,
1059     GstClockTime duration)
1060 {
1061   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (src_object);
1062   GstBuffer *buf = NULL;
1063   IPin *pPin = NULL;
1064   HRESULT hres = S_FALSE;
1065   AM_MEDIA_TYPE *pMediaType = NULL;
1066   GstMapInfo info;
1067
1068   if (!buffer || size == 0 || !src) {
1069     return FALSE;
1070   }
1071
1072   /* create a new buffer assign to it the clock time as timestamp */
1073   buf = gst_buffer_new_and_alloc (size);
1074
1075   gst_buffer_set_size(buf, size);
1076
1077   GstClock *clock = gst_element_get_clock (GST_ELEMENT (src));
1078   GST_BUFFER_TIMESTAMP (buf) =
1079     GST_CLOCK_DIFF (gst_element_get_base_time (GST_ELEMENT (src)), gst_clock_get_time (clock));
1080   gst_object_unref (clock);
1081
1082   GST_BUFFER_DURATION (buf) = duration;
1083
1084   if (!gst_buffer_map(buf, &info, GST_MAP_WRITE)) {
1085           gst_buffer_unref(buf);
1086           GST_ERROR("Failed to map buffer");
1087           return FALSE;
1088   }
1089
1090   if (src->is_rgb) {
1091     /* FOR RGB directshow decoder will return bottom-up BITMAP
1092      * There is probably a way to get top-bottom video frames from
1093      * the decoder...
1094      */
1095     gint line = 0;
1096     gint stride = size / src->height;
1097
1098     for (; line < src->height; line++) {
1099       memcpy (info.data + (line * stride),
1100           buffer + (size - ((line + 1) * (stride))), stride);
1101     }
1102   } else {
1103     memcpy (info.data, buffer, size);
1104   }
1105
1106   gst_buffer_unmap(buf, &info);
1107
1108   GST_DEBUG ("push_buffer => pts %" GST_TIME_FORMAT "duration %"
1109       GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
1110       GST_TIME_ARGS (duration));
1111
1112   g_mutex_lock (&src->buffer_mutex);
1113   if (src->buffer != NULL)
1114     gst_buffer_unref (src->buffer);
1115   src->buffer = buf;
1116   g_cond_signal (&src->buffer_cond);
1117   g_mutex_unlock (&src->buffer_mutex);
1118
1119   return TRUE;
1120 }