dshowvideosrc: can use other video sizes and framerates than the defaults
authorJulien Isorce <julien.isorce@gmail.com>
Mon, 10 Aug 2009 12:23:14 +0000 (14:23 +0200)
committerJulien Isorce <julien.isorce@gmail.com>
Mon, 10 Aug 2009 12:23:14 +0000 (14:23 +0200)
Even if the device could capture several video sizes at several framerates,
without this commit, it was only possible to use one video size and
one framerate: the default directshow values.

sys/dshowsrcwrapper/gstdshowvideosrc.cpp
sys/dshowsrcwrapper/gstdshowvideosrc.h

index 14654f604034f262a16c21f54b3dcb5aee1c3840..8801e248466e8113a477afd3f379be6d6ab25c0d 100755 (executable)
@@ -1,5 +1,6 @@
 /* GStreamer
  * Copyright (C)  2007 Sebastien Moutte <sebastien@moutte.net>
+ * Copyright (C)  2009 Julien Isorce <julien.isorce@gmail.com>
  *
  * gstdshowvideosrc.c: 
  *
@@ -104,6 +105,7 @@ static gboolean gst_dshowvideosrc_unlock (GstBaseSrc * bsrc);
 static gboolean gst_dshowvideosrc_unlock_stop (GstBaseSrc * bsrc);
 static gboolean gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps);
 static GstCaps *gst_dshowvideosrc_get_caps (GstBaseSrc * bsrc);
+static void gst_dshowvideosrc_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
 static GstFlowReturn gst_dshowvideosrc_create (GstPushSrc * psrc,
     GstBuffer ** buf);
 
@@ -170,6 +172,7 @@ gst_dshowvideosrc_class_init (GstDshowVideoSrcClass * klass)
 
   gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_get_caps);
   gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_set_caps);
+  gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_src_fixate);
   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_start);
   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_stop);
   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_dshowvideosrc_unlock);
@@ -205,6 +208,7 @@ gst_dshowvideosrc_init (GstDshowVideoSrc * src, GstDshowVideoSrcClass * klass)
   src->media_filter = NULL;
   src->filter_graph = NULL;
   src->caps = NULL;
+  src->video_defaults = NULL;
   src->pins_mediatypes = NULL;
   src->is_rgb = FALSE;
 
@@ -218,6 +222,36 @@ gst_dshowvideosrc_init (GstDshowVideoSrc * src, GstDshowVideoSrcClass * klass)
   gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
 }
 
+static void
+gst_dshowvideosrc_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
+{
+  /* If there is no desired video size, set default video size to device preffered video size */
+
+  GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc);
+  GstStructure *structure = gst_caps_get_structure (caps, 0);
+  guint i = 0;
+  gint res = -1;
+
+  for (; i < gst_caps_get_size (src->caps) && res == -1; i++) {
+    GstCaps *capstmp = gst_caps_copy_nth (src->caps, i);
+
+    if (gst_caps_is_subset (caps, capstmp)) {
+      res = i;
+    }
+    gst_caps_unref (capstmp);
+  }
+
+  if (res != -1) {
+    GList *type_video_default = g_list_nth (src->video_defaults, res);
+    if (type_video_default) {
+      GstCaptureVideoDefault *video_default = (GstCaptureVideoDefault *) type_video_default->data;
+      gst_structure_fixate_field_nearest_int (structure, "width", video_default->defaultWidth);
+      gst_structure_fixate_field_nearest_int (structure, "height", video_default->defaultHeight);
+      gst_structure_fixate_field_nearest_fraction (structure, "framerate", video_default->defaultFPS, 1);
+    }
+  }
+}
+
 static void
 gst_dshowvideosrc_dispose (GObject * gobject)
 {
@@ -238,6 +272,11 @@ gst_dshowvideosrc_dispose (GObject * gobject)
     src->caps = NULL;
   }
 
+  if (src->video_defaults) {
+    g_list_free (src->video_defaults);
+    src->video_defaults = NULL;
+  }
+
   if (src->pins_mediatypes) {
     gst_dshow_free_pins_mediatypes (src->pins_mediatypes);
     src->pins_mediatypes = NULL;
@@ -292,7 +331,6 @@ gst_dshowvideosrc_probe_probe_property (GstPropertyProbe * probe,
 
   switch (prop_id) {
     case PROP_DEVICE_NAME:
-      //gst_v4l_class_probe_devices (klass, FALSE);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
@@ -556,7 +594,6 @@ static GstStateChangeReturn
 gst_dshowvideosrc_change_state (GstElement * element, GstStateChange transition)
 {
   HRESULT hres = S_FALSE;
-  IAMVfwCaptureDialogs *dialog = NULL;
   GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (element);
 
   switch (transition) {
@@ -675,13 +712,51 @@ gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
 
     if (res != -1 && src->pins_mediatypes) {
       /* get the corresponding media type and build the dshow graph */
-      GstCapturePinMediaType *pin_mediatype = NULL;
-      gchar *caps_string = NULL;
       GList *type = g_list_nth (src->pins_mediatypes, res);
 
-      if (type) {
+      //will be removed when GST_TYPE_INT_RANGE_STEP exits
+      GList *type_video_default = g_list_nth (src->video_defaults, res);
+
+      if (type && type_video_default) {
+        //will be removed when GST_TYPE_INT_RANGE_STEP exits
+        GstCaptureVideoDefault *video_default = (GstCaptureVideoDefault *) type_video_default->data;
+        GstCapturePinMediaType *pin_mediatype = NULL;
+        gchar *caps_string = NULL;
+        gchar *src_caps_string = NULL;
+
+        /* retrieve the desired video size */
+        VIDEOINFOHEADER *video_info = NULL;
+        gint width = 0;
+        gint height = 0;
+        gint numerator = 0;
+        gint denominator = 0;
+        gst_structure_get_int (s, "width", &width);
+        gst_structure_get_int (s, "height", &height);
+        gst_structure_get_fraction (s, "framerate", &numerator, &denominator);
+
+        /* check if the desired video size is valid about granularity  */
+                   /* This check will be removed when GST_TYPE_INT_RANGE_STEP exits */
+        /* See remarks in gst_dshowvideosrc_getcaps_from_streamcaps function */
+        if (video_default->granularityWidth != 0 && width % video_default->granularityWidth != 0)
+          g_warning ("your desired video size is not valid : %d mod %d !=0\n", width, video_default->granularityWidth) ;
+        if (video_default->granularityHeight !=0 && height % video_default->granularityHeight != 0)
+          g_warning ("your desired video size is not valid : %d mod %d !=0\n", height, video_default->granularityHeight) ;
+
+        /* display all capabilities when using --gst-debug-level=3 */
+        src_caps_string = gst_caps_to_string (src->caps);
+        GST_CAT_LEVEL_LOG (dshowvideosrc_debug, GST_LEVEL_INFO, src, src_caps_string);
+        g_free (src_caps_string);
+
         pin_mediatype = (GstCapturePinMediaType *) type->data;
 
+        /* update mediatype */
+        video_info = (VIDEOINFOHEADER *) pin_mediatype->mediatype->pbFormat;
+        video_info->bmiHeader.biWidth = width;
+        video_info->bmiHeader.biHeight = height;
+        video_info->AvgTimePerFrame = (LONGLONG) (10000000 * denominator / (double)numerator);
+        video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
+        pin_mediatype->mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
+
         src->dshow_fakesink->gst_set_media_type (pin_mediatype->mediatype);
         src->dshow_fakesink->gst_set_buffer_callback(
           (push_buffer_func) gst_dshowvideosrc_push_buffer, src);
@@ -695,7 +770,7 @@ gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
         }
 
         hres = src->filter_graph->ConnectDirect(pin_mediatype->capture_pin, 
-          input_pin, NULL);
+          input_pin, pin_mediatype->mediatype);
         input_pin->Release();
 
         if (hres != S_OK) {
@@ -845,6 +920,7 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin,
 
   for (; i < icount; i++) {
     GstCapturePinMediaType *pin_mediatype = g_new0 (GstCapturePinMediaType, 1);
+    GstCaptureVideoDefault *video_default = g_new0 (GstCaptureVideoDefault, 1);
 
     pin->AddRef();
     pin_mediatype->capture_pin = pin;
@@ -857,6 +933,17 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin,
       if (!caps)
         caps = gst_caps_new_empty ();
 
+      /* some remarks: */
+      /* Hope GST_TYPE_INT_RANGE_STEP will exits in future gstreamer releases  */
+      /* because we could use :  */
+      /* "width", GST_TYPE_INT_RANGE_STEP, video_default->minWidth, video_default->maxWidth,  video_default->granularityWidth */
+      /* instead of : */
+      /* "width", GST_TYPE_INT_RANGE, video_default->minWidth, video_default->maxWidth */
+
+      /* For framerate we do not need a step (granularity) because  */
+      /* "The IAMStreamConfig::SetFormat method will set the frame rate to the closest  */
+      /* value that the filter supports" as it said in the VIDEO_STREAM_CONFIG_CAPS dshwo doc */
+
       /* I420 */
       if ((UuidCompare (&pin_mediatype->mediatype->subtype, (UUID *) &MEDIASUBTYPE_I420,
                   &rpcstatus) == 0 && rpcstatus == RPC_S_OK)
@@ -865,19 +952,29 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin,
               && rpcstatus == RPC_S_OK)) {
         video_info = (VIDEOINFOHEADER *) pin_mediatype->mediatype->pbFormat;
 
+        video_default->defaultWidth = video_info->bmiHeader.biWidth;
+        video_default->defaultHeight = video_info->bmiHeader.biHeight;
+        video_default->defaultFPS = (int) (10000000 / video_info->AvgTimePerFrame);
+        video_default->granularityWidth = vscc.OutputGranularityX;
+        video_default->granularityHeight = vscc.OutputGranularityY;
+
         mediacaps = gst_caps_new_simple ("video/x-raw-yuv",
-            "width", G_TYPE_INT, video_info->bmiHeader.biWidth,
-            "height", G_TYPE_INT, video_info->bmiHeader.biHeight,
-            "framerate", GST_TYPE_FRACTION,
-            (int) (10000000 / video_info->AvgTimePerFrame), 1, "format",
-            GST_TYPE_FOURCC, MAKEFOURCC ('I', '4', '2', '0'), NULL);
+          "width", GST_TYPE_INT_RANGE, vscc.MinOutputSize.cx, vscc.MaxOutputSize.cx,
+          "height", GST_TYPE_INT_RANGE, vscc.MinOutputSize.cy, vscc.MaxOutputSize.cy,
+          "framerate", GST_TYPE_FRACTION_RANGE,
+          (int) (10000000 / vscc.MaxFrameInterval), 1,
+          (int) (10000000 / vscc.MinFrameInterval), 1,
+          "format", GST_TYPE_FOURCC, MAKEFOURCC ('I', '4', '2', '0'), NULL);
 
         if (mediacaps) {
           src->pins_mediatypes =
               g_list_append (src->pins_mediatypes, pin_mediatype);
+          src->video_defaults =
+            g_list_append (src->video_defaults, video_default);
           gst_caps_append (caps, mediacaps);
         } else {
           gst_dshow_free_pin_mediatype (pin_mediatype);
+          g_free (video_default);
         }
         continue;
       }
@@ -890,23 +987,35 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin,
               && rpcstatus == RPC_S_OK)) {
         video_info = (VIDEOINFOHEADER *) pin_mediatype->mediatype->pbFormat;
 
+        video_default->defaultWidth = video_info->bmiHeader.biWidth;
+        video_default->defaultHeight = video_info->bmiHeader.biHeight;
+        video_default->defaultFPS = (int) (10000000 / video_info->AvgTimePerFrame);
+        video_default->granularityWidth = vscc.OutputGranularityX;
+        video_default->granularityHeight = vscc.OutputGranularityY;
+
         /* ffmpegcolorspace handles RGB24 in BIG_ENDIAN */
         mediacaps = gst_caps_new_simple ("video/x-raw-rgb",
-            "bpp", G_TYPE_INT, 24,
-            "depth", G_TYPE_INT, 24,
-            "width", G_TYPE_INT, video_info->bmiHeader.biWidth,
-            "height", G_TYPE_INT, video_info->bmiHeader.biHeight,
-            "framerate", GST_TYPE_FRACTION,
-            (int) (10000000 / video_info->AvgTimePerFrame), 1, "endianness",
-            G_TYPE_INT, G_BIG_ENDIAN, "red_mask", G_TYPE_INT, 255, "green_mask",
-            G_TYPE_INT, 65280, "blue_mask", G_TYPE_INT, 16711680, NULL);
+          "bpp", G_TYPE_INT, 24,
+          "depth", G_TYPE_INT, 24,
+          "width", GST_TYPE_INT_RANGE, vscc.MinOutputSize.cx, vscc.MaxOutputSize.cx,
+          "height", GST_TYPE_INT_RANGE, vscc.MinOutputSize.cy, vscc.MaxOutputSize.cy,
+          "framerate", GST_TYPE_FRACTION_RANGE,
+          (int) (10000000 / vscc.MaxFrameInterval), 1,
+          (int) (10000000 / vscc.MinFrameInterval), 1,
+          "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+          "red_mask", G_TYPE_INT, 255,
+          "green_mask", G_TYPE_INT, 65280,
+          "blue_mask", G_TYPE_INT, 16711680, NULL);
 
         if (mediacaps) {
           src->pins_mediatypes =
               g_list_append (src->pins_mediatypes, pin_mediatype);
+          src->video_defaults =
+            g_list_append (src->video_defaults, video_default);
           gst_caps_append (caps, mediacaps);
         } else {
           gst_dshow_free_pin_mediatype (pin_mediatype);
+          g_free (video_default);
         }
         continue;
       }
@@ -919,20 +1028,30 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin,
               && rpcstatus == RPC_S_OK)) {
         video_info = (VIDEOINFOHEADER *) pin_mediatype->mediatype->pbFormat;
 
+        video_default->defaultWidth = video_info->bmiHeader.biWidth;
+        video_default->defaultHeight = video_info->bmiHeader.biHeight;
+        video_default->defaultFPS = (int) (10000000 / video_info->AvgTimePerFrame);
+        video_default->granularityWidth = vscc.OutputGranularityX;
+        video_default->granularityHeight = vscc.OutputGranularityY;
+
         mediacaps = gst_caps_new_simple ("video/x-dv",
-            "systemstream", G_TYPE_BOOLEAN, FALSE,
-            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('d', 'v', 's', 'd'),
-            "framerate", GST_TYPE_FRACTION,
-            (int) (10000000 / video_info->AvgTimePerFrame), 1, "width",
-            G_TYPE_INT, video_info->bmiHeader.biWidth, "height", G_TYPE_INT,
-            video_info->bmiHeader.biHeight, NULL);
+          "systemstream", G_TYPE_BOOLEAN, FALSE,
+          "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('d', 'v', 's', 'd'),
+          "framerate", GST_TYPE_FRACTION_RANGE,
+          (int) (10000000 / vscc.MaxFrameInterval), 1,
+          (int) (10000000 / vscc.MinFrameInterval), 1,
+          "width", GST_TYPE_INT_RANGE, vscc.MinOutputSize.cx, vscc.MaxOutputSize.cx,
+          "height", GST_TYPE_INT_RANGE, vscc.MinOutputSize.cy, vscc.MaxOutputSize.cy, NULL);
 
         if (mediacaps) {
           src->pins_mediatypes =
               g_list_append (src->pins_mediatypes, pin_mediatype);
+          src->video_defaults =
+            g_list_append (src->video_defaults, video_default);
           gst_caps_append (caps, mediacaps);
         } else {
           gst_dshow_free_pin_mediatype (pin_mediatype);
+          g_free (video_default);
         }
         continue;
       }
@@ -943,20 +1062,33 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin,
           && (UuidCompare (&pin_mediatype->mediatype->formattype,
                   (UUID *) &FORMAT_DvInfo, &rpcstatus) == 0 && rpcstatus == RPC_S_OK)) {
 
+        video_info = (VIDEOINFOHEADER *) pin_mediatype->mediatype->pbFormat;
+
+        //No video size in caps when stream ? I do know if the following fields exist
+        video_default->defaultWidth = video_info->bmiHeader.biWidth;
+        video_default->defaultHeight = video_info->bmiHeader.biHeight;
+        video_default->defaultFPS = (int) (10000000 / video_info->AvgTimePerFrame);
+        video_default->granularityWidth = vscc.OutputGranularityX;
+        video_default->granularityHeight = vscc.OutputGranularityY;
+
         mediacaps = gst_caps_new_simple ("video/x-dv",
             "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
 
         if (mediacaps) {
           src->pins_mediatypes =
               g_list_append (src->pins_mediatypes, pin_mediatype);
+          src->video_defaults =
+            g_list_append (src->video_defaults, video_default);
           gst_caps_append (caps, mediacaps);
         } else {
           gst_dshow_free_pin_mediatype (pin_mediatype);
+          g_free (video_default);
         }
         continue;
       }
     } else {
       gst_dshow_free_pin_mediatype (pin_mediatype);
+      g_free (video_default);
     }
   }
 
index bf94e61f274e3998fa5cd537653d895e20ebc23f..415830719bacf971ced928190770b0f5e5eb32a8 100755 (executable)
@@ -42,6 +42,18 @@ G_BEGIN_DECLS
 typedef struct _GstDshowVideoSrc GstDshowVideoSrc;
 typedef struct _GstDshowVideoSrcClass GstDshowVideoSrcClass;
 
+/* video default properties associated to a video format (YUY2, I420, RGB24 ...) */
+typedef struct _GstCaptureVideoDefault
+{
+  gint defaultWidth;
+  gint defaultHeight;
+  gint defaultFPS;
+
+  gint granularityWidth; //will be removed when GST_TYPE_INT_RANGE_STEP exits
+  gint granularityHeight; //will be removed when GST_TYPE_INT_RANGE_STEP exits
+
+} GstCaptureVideoDefault;
+
 struct _GstDshowVideoSrc
 {
   GstPushSrc src;
@@ -55,6 +67,9 @@ struct _GstDshowVideoSrc
   /* list of caps created from the list of supported media types of the dshow capture filter */
   GstCaps *caps;
 
+  /* list of dshow default video properties from filter's capture pins */
+  GList *video_defaults;
+
   /* list of dshow media types from the filter's capture pins */
   GList *pins_mediatypes;