decklink: Add initial 10bit support for YUV modes
authorVivia Nikolaidou <vivia@ahiru.eu>
Thu, 14 Apr 2016 15:26:33 +0000 (18:26 +0300)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 3 May 2016 12:57:23 +0000 (15:57 +0300)
https://bugzilla.gnome.org/show_bug.cgi?id=742878

sys/decklink/gstdecklink.cpp
sys/decklink/gstdecklink.h
sys/decklink/gstdecklinkvideosink.cpp
sys/decklink/gstdecklinkvideosink.h
sys/decklink/gstdecklinkvideosrc.cpp
sys/decklink/gstdecklinkvideosrc.h

index df47797..8b4c5b6 100644 (file)
@@ -112,6 +112,34 @@ gst_decklink_connection_get_type (void)
 }
 
 GType
+gst_decklink_video_format_get_type (void)
+{
+  static gsize id = 0;
+  static const GEnumValue types[] = {
+    {GST_DECKLINK_VIDEO_FORMAT_AUTO, "auto", "Auto"},
+    {GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV, "8bit-yuv", "bmdFormat8BitYUV"},
+    {GST_DECKLINK_VIDEO_FORMAT_10BIT_YUV, "10bit-yuv", "bmdFormat10BitYUV"},
+    {GST_DECKLINK_VIDEO_FORMAT_8BIT_ARGB, "8bit-argb", "bmdFormat8BitARGB"},
+    {GST_DECKLINK_VIDEO_FORMAT_8BIT_BGRA, "8bit-bgra", "bmdFormat8BitBGRA"},
+    /*
+    {GST_DECKLINK_VIDEO_FORMAT_10BIT_RGB, "10bit-rgb", "bmdFormat10BitRGB"},
+    {GST_DECKLINK_VIDEO_FORMAT_12BIT_RGB, "12bit-rgb", "bmdFormat12BitRGB"},
+    {GST_DECKLINK_VIDEO_FORMAT_12BIT_RGBLE, "12bit-rgble", "bmdFormat12BitRGBLE"},
+    {GST_DECKLINK_VIDEO_FORMAT_10BIT_RGBXLE, "10bit-rgbxle", "bmdFormat10BitRGBXLE"},
+    {GST_DECKLINK_VIDEO_FORMAT_10BIT_RGBX, "10bit-rgbx", "bmdFormat10BitRGBX"},
+    */
+    {0, NULL, NULL}
+  };
+
+  if (g_once_init_enter (&id)) {
+    GType tmp = g_enum_register_static ("GstDecklinkVideoFormat", types);
+    g_once_init_leave (&id, tmp);
+  }
+
+  return (GType) id;
+}
+
+GType
 gst_decklink_audio_connection_get_type (void)
 {
   static gsize id = 0;
@@ -183,6 +211,27 @@ static const GstDecklinkMode modes[] = {
   {bmdMode4K2160p60, 3840, 2160, 60, 1, false, UHD}
 };
 
+static const struct
+{
+  BMDPixelFormat format;
+  gint bpp;
+  GstVideoFormat vformat;
+} formats[] = {
+  /* *INDENT-OFF* */
+  {bmdFormat8BitYUV, 2, GST_VIDEO_FORMAT_UYVY},  /* auto */
+  {bmdFormat8BitYUV, 2, GST_VIDEO_FORMAT_UYVY},
+  {bmdFormat10BitYUV, 4, GST_VIDEO_FORMAT_v210},
+  {bmdFormat8BitARGB, 4, GST_VIDEO_FORMAT_ARGB},
+  {bmdFormat8BitBGRA, 4, GST_VIDEO_FORMAT_BGRA},
+/* Not yet supported
+  {bmdFormat10BitRGB, FIXME, FIXME},
+  {bmdFormat12BitRGB, FIXME, FIXME},
+  {bmdFormat12BitRGBLE, FIXME, FIXME},
+  {bmdFormat10BitRGBXLE, FIXME, FIXME},
+  {bmdFormat10BitRGBX, FIXME, FIXME} */
+  /* *INDENT-ON* */
+};
+
 const GstDecklinkMode *
 gst_decklink_get_mode (GstDecklinkModeEnum e)
 {
@@ -293,6 +342,31 @@ gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode)
   return displayMode;
 }
 
+const BMDPixelFormat
+gst_decklink_pixel_format_from_type (GstDecklinkVideoFormat t)
+{
+  return formats[t].format;
+}
+
+const gint
+gst_decklink_bpp_from_type (GstDecklinkVideoFormat t)
+{
+  return formats[t].bpp;
+}
+
+const GstDecklinkVideoFormat
+gst_decklink_type_from_video_format (GstVideoFormat f)
+{
+  guint i;
+
+  for (i = 1; i < G_N_ELEMENTS (formats); i++) {
+    if (formats[i].vformat == f)
+      return (GstDecklinkVideoFormat) i;
+  }
+  g_assert_not_reached ();
+  return GST_DECKLINK_VIDEO_FORMAT_AUTO;
+}
+
 static const BMDVideoConnection connections[] = {
   0,                            /* auto */
   bmdVideoConnectionSDI,
@@ -315,6 +389,21 @@ gst_decklink_get_connection (GstDecklinkConnectionEnum e)
   return connections[e];
 }
 
+static gboolean
+gst_decklink_caps_get_pixel_format (GstCaps * caps, BMDPixelFormat * format)
+{
+  GstVideoInfo vinfo;
+  GstVideoFormat f;
+
+  if (gst_video_info_from_caps (&vinfo, caps) == FALSE) {
+    GST_ERROR ("Could not get video info from caps: %" GST_PTR_FORMAT, caps);
+    return FALSE;
+  }
+
+  f = vinfo.finfo->format;
+  return gst_decklink_type_from_video_format (f);
+}
+
 static GstStructure *
 gst_decklink_mode_get_structure (GstDecklinkModeEnum e, BMDPixelFormat f)
 {
@@ -363,13 +452,29 @@ gst_decklink_mode_get_caps (GstDecklinkModeEnum e, BMDPixelFormat f)
   GstCaps *caps;
 
   caps = gst_caps_new_empty ();
-  gst_caps_append_structure (caps, gst_decklink_mode_get_structure (e, f));
+  caps =
+      gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e, f));
 
   return caps;
 }
 
 GstCaps *
-gst_decklink_mode_get_template_caps (void)
+gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e)
+{
+  GstCaps *caps;
+  guint i;
+
+  caps = gst_caps_new_empty ();
+  for (i = 1; i < G_N_ELEMENTS (formats); i++)
+    caps =
+        gst_caps_merge_structure (caps, gst_decklink_mode_get_structure (e,
+            formats[i].format));
+
+  return caps;
+}
+
+GstCaps *
+gst_decklink_pixel_format_get_caps (BMDPixelFormat f)
 {
   int i;
   GstCaps *caps;
@@ -377,26 +482,41 @@ gst_decklink_mode_get_template_caps (void)
 
   caps = gst_caps_new_empty ();
   for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) {
-    s = gst_decklink_mode_get_structure ((GstDecklinkModeEnum) i,
-        bmdFormat8BitYUV);
-    gst_caps_append_structure (caps, s);
-    s = gst_decklink_mode_get_structure ((GstDecklinkModeEnum) i,
-        bmdFormat8BitARGB);
-    gst_caps_append_structure (caps, s);
+    s = gst_decklink_mode_get_structure ((GstDecklinkModeEnum) i, f);
+    caps = gst_caps_merge_structure (caps, s);
   }
 
   return caps;
 }
 
+GstCaps *
+gst_decklink_mode_get_template_caps (void)
+{
+  int i;
+  GstCaps *caps;
+
+  caps = gst_caps_new_empty ();
+  for (i = 1; i < (int) G_N_ELEMENTS (modes); i++)
+    caps =
+        gst_caps_merge (caps,
+        gst_decklink_mode_get_caps_all_formats ((GstDecklinkModeEnum) i));
+
+  return caps;
+}
+
 const GstDecklinkMode *
-gst_decklink_find_mode_for_caps (GstCaps * caps)
+gst_decklink_find_mode_and_format_for_caps (GstCaps * caps,
+    BMDPixelFormat * format)
 {
   int i;
   GstCaps *mode_caps;
 
+  g_return_val_if_fail (gst_caps_is_fixed (caps), NULL);
+  if (!gst_decklink_caps_get_pixel_format (caps, format))
+    return NULL;
+
   for (i = 1; i < (int) G_N_ELEMENTS (modes); i++) {
-    mode_caps =
-        gst_decklink_mode_get_caps ((GstDecklinkModeEnum) i, bmdFormat8BitYUV);
+    mode_caps = gst_decklink_mode_get_caps ((GstDecklinkModeEnum) i, *format);
     if (gst_caps_can_intersect (caps, mode_caps)) {
       gst_caps_unref (mode_caps);
       return gst_decklink_get_mode ((GstDecklinkModeEnum) i);
@@ -407,6 +527,14 @@ gst_decklink_find_mode_for_caps (GstCaps * caps)
   return NULL;
 }
 
+const GstDecklinkMode *
+gst_decklink_find_mode_for_caps (GstCaps * caps)
+{
+  BMDPixelFormat format;
+
+  return gst_decklink_find_mode_and_format_for_caps (caps, &format);
+}
+
 #define GST_TYPE_DECKLINK_CLOCK \
   (gst_decklink_clock_get_type())
 #define GST_DECKLINK_CLOCK(obj) \
index bfe7d9f..6559206 100644 (file)
@@ -23,6 +23,7 @@
 #define _GST_DECKLINK_H_
 
 #include <gst/gst.h>
+#include <gst/video/video.h>
 #ifdef G_OS_UNIX
 #include "linux/DeckLinkAPI.h"
 #endif
@@ -109,6 +110,25 @@ typedef enum {
 #define GST_TYPE_DECKLINK_AUDIO_CONNECTION (gst_decklink_audio_connection_get_type ())
 GType gst_decklink_audio_connection_get_type (void);
 
+typedef enum {
+  GST_DECKLINK_VIDEO_FORMAT_AUTO,
+  GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV, /* bmdFormat8BitYUV */
+  GST_DECKLINK_VIDEO_FORMAT_10BIT_YUV, /* bmdFormat10BitYUV */
+  GST_DECKLINK_VIDEO_FORMAT_8BIT_ARGB, /* bmdFormat8BitARGB */
+  GST_DECKLINK_VIDEO_FORMAT_8BIT_BGRA, /* bmdFormat8BitBGRA */
+  GST_DECKLINK_VIDEO_FORMAT_10BIT_RGB, /* bmdFormat10BitRGB */
+  GST_DECKLINK_VIDEO_FORMAT_12BIT_RGB, /* bmdFormat12BitRGB */
+  GST_DECKLINK_VIDEO_FORMAT_12BIT_RGBLE, /* bmdFormat12BitRGBLE */
+  GST_DECKLINK_VIDEO_FORMAT_10BIT_RGBXLE, /* bmdFormat10BitRGBXLE */
+  GST_DECKLINK_VIDEO_FORMAT_10BIT_RGBX, /* bmdFormat10BitRGBX */
+} GstDecklinkVideoFormat;
+#define GST_TYPE_DECKLINK_VIDEO_FORMAT (gst_decklink_video_format_get_type ())
+GType gst_decklink_video_format_get_type (void);
+
+const BMDPixelFormat gst_decklink_pixel_format_from_type (GstDecklinkVideoFormat t);
+const gint gst_decklink_bpp_from_type (GstDecklinkVideoFormat t);
+const GstDecklinkVideoFormat gst_decklink_type_from_video_format (GstVideoFormat f);
+
 typedef struct _GstDecklinkMode GstDecklinkMode;
 struct _GstDecklinkMode {
   BMDDisplayMode mode;
@@ -195,5 +215,8 @@ GstDecklinkInput *  gst_decklink_acquire_nth_input (gint n, GstElement * src, gb
 void                gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio);
 
 const GstDecklinkMode * gst_decklink_find_mode_for_caps (GstCaps * caps);
+const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format);
+GstCaps * gst_decklink_mode_get_caps_all_formats (GstDecklinkModeEnum e);
+GstCaps * gst_decklink_pixel_format_get_caps (BMDPixelFormat f);
 
 #endif
index eebeff3..551e5d7 100644 (file)
@@ -118,7 +118,8 @@ enum
 {
   PROP_0,
   PROP_MODE,
-  PROP_DEVICE_NUMBER
+  PROP_DEVICE_NUMBER,
+  PROP_VIDEO_FORMAT
 };
 
 static void gst_decklink_video_sink_set_property (GObject * object,
@@ -205,6 +206,13 @@ gst_decklink_video_sink_class_init (GstDecklinkVideoSinkClass * klass)
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
               G_PARAM_CONSTRUCT)));
 
+  g_object_class_install_property (gobject_class, PROP_VIDEO_FORMAT,
+      g_param_spec_enum ("video-format", "Video format",
+          "Video format type to use for playback",
+          GST_TYPE_DECKLINK_VIDEO_FORMAT, GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV,
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+              G_PARAM_CONSTRUCT)));
+
   templ_caps = gst_decklink_mode_get_template_caps ();
   templ_caps = gst_caps_make_writable (templ_caps);
   /* For output we support any framerate and only really care about timestamps */
@@ -226,6 +234,7 @@ gst_decklink_video_sink_init (GstDecklinkVideoSink * self)
 {
   self->mode = GST_DECKLINK_MODE_NTSC;
   self->device_number = 0;
+  self->video_format = GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV;
 
   gst_base_sink_set_max_lateness (GST_BASE_SINK_CAST (self), 20 * GST_MSECOND);
   gst_base_sink_set_qos_enabled (GST_BASE_SINK_CAST (self), TRUE);
@@ -244,6 +253,21 @@ gst_decklink_video_sink_set_property (GObject * object, guint property_id,
     case PROP_DEVICE_NUMBER:
       self->device_number = g_value_get_int (value);
       break;
+    case PROP_VIDEO_FORMAT:
+      self->video_format = (GstDecklinkVideoFormat) g_value_get_enum (value);
+      switch (self->video_format) {
+        case GST_DECKLINK_VIDEO_FORMAT_AUTO:
+        case GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV:
+        case GST_DECKLINK_VIDEO_FORMAT_10BIT_YUV:
+        case GST_DECKLINK_VIDEO_FORMAT_8BIT_ARGB:
+        case GST_DECKLINK_VIDEO_FORMAT_8BIT_BGRA:
+          break;
+        default:
+          GST_ELEMENT_WARNING (GST_ELEMENT (self), CORE, NOT_IMPLEMENTED,
+              ("Format %d not supported", self->video_format), (NULL));
+          break;
+      }
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -263,6 +287,9 @@ gst_decklink_video_sink_get_property (GObject * object, guint property_id,
     case PROP_DEVICE_NUMBER:
       g_value_set_int (value, self->device_number);
       break;
+    case PROP_VIDEO_FORMAT:
+      g_value_set_enum (value, self->video_format);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -293,13 +320,23 @@ gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
       GStreamerVideoOutputCallback (self));
 
   if (self->mode == GST_DECKLINK_MODE_AUTO) {
-    mode = gst_decklink_find_mode_for_caps (caps);
+    BMDPixelFormat f;
+    mode = gst_decklink_find_mode_and_format_for_caps (caps, &f);
     if (mode == NULL) {
       GST_WARNING_OBJECT (self,
           "Failed to find compatible mode for caps  %" GST_PTR_FORMAT, caps);
       return FALSE;
     }
+    if (self->video_format != GST_DECKLINK_VIDEO_FORMAT_AUTO &&
+        gst_decklink_pixel_format_from_type (self->video_format) != f) {
+      GST_WARNING_OBJECT (self, "Failed to set pixel format to %d",
+          self->video_format);
+      return FALSE;
+    }
   } else {
+    /* We don't have to give the format in EnableVideoOutput. Therefore,
+     * even if it's AUTO, we have it stored in self->info and set it in
+     * gst_decklink_video_sink_prepare */
     mode = gst_decklink_get_mode (self->mode);
     g_assert (mode != NULL);
   };
@@ -327,10 +364,19 @@ gst_decklink_video_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink);
   GstCaps *mode_caps, *caps;
 
-  if (self->mode == GST_DECKLINK_MODE_AUTO)
+  if (self->mode == GST_DECKLINK_MODE_AUTO
+      && self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
     mode_caps = gst_decklink_mode_get_template_caps ();
+  else if (self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
+    mode_caps = gst_decklink_mode_get_caps_all_formats (self->mode);
+  else if (self->mode == GST_DECKLINK_MODE_AUTO)
+    mode_caps =
+        gst_decklink_pixel_format_get_caps (gst_decklink_pixel_format_from_type
+        (self->video_format));
   else
-    mode_caps = gst_decklink_mode_get_caps (self->mode, bmdFormat8BitYUV);
+    mode_caps =
+        gst_decklink_mode_get_caps (self->mode,
+        gst_decklink_pixel_format_from_type (self->video_format));
   mode_caps = gst_caps_make_writable (mode_caps);
   /* For output we support any framerate and only really care about timestamps */
   gst_caps_map_in_place (mode_caps, reset_framerate, NULL);
@@ -454,6 +500,9 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
   GstClockTime latency, render_delay;
   GstClockTimeDiff ts_offset;
   gint i;
+  GstDecklinkVideoFormat caps_format;
+  BMDPixelFormat format;
+  gint bpp;
 
   GST_DEBUG_OBJECT (self, "Preparing buffer %p", buffer);
 
@@ -462,6 +511,10 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
     return GST_FLOW_ERROR;
   }
 
+  caps_format = gst_decklink_type_from_video_format (self->info.finfo->format);
+  format = gst_decklink_pixel_format_from_type (caps_format);
+  bpp = gst_decklink_bpp_from_type (caps_format);
+
   timestamp = GST_BUFFER_TIMESTAMP (buffer);
   duration = GST_BUFFER_DURATION (buffer);
   if (duration == GST_CLOCK_TIME_NONE) {
@@ -499,8 +552,8 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
     running_time = 0;
 
   ret = self->output->output->CreateVideoFrame (self->info.width,
-      self->info.height, self->info.stride[0], bmdFormat8BitYUV,
-      bmdFrameFlagDefault, &frame);
+      self->info.height, self->info.stride[0], format, bmdFrameFlagDefault,
+      &frame);
   if (ret != S_OK) {
     GST_ELEMENT_ERROR (self, STREAM, FAILED,
         (NULL), ("Failed to create video frame: 0x%08x", ret));
@@ -516,7 +569,7 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
   frame->GetBytes ((void **) &outdata);
   indata = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0);
   for (i = 0; i < self->info.height; i++) {
-    memcpy (outdata, indata, GST_VIDEO_FRAME_WIDTH (&vframe) * 2);
+    memcpy (outdata, indata, GST_VIDEO_FRAME_WIDTH (&vframe) * bpp);
     indata += GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
     outdata += frame->GetRowBytes ();
   }
index 16b2fbb..fb95591 100644 (file)
@@ -51,6 +51,7 @@ struct _GstDecklinkVideoSink
 
   GstDecklinkModeEnum mode;
   gint device_number;
+  GstDecklinkVideoFormat video_format;
 
   GstVideoInfo info;
 
index 2b304f9..5dc69e5 100644 (file)
@@ -39,7 +39,8 @@ enum
   PROP_MODE,
   PROP_CONNECTION,
   PROP_DEVICE_NUMBER,
-  PROP_BUFFER_SIZE
+  PROP_BUFFER_SIZE,
+  PROP_VIDEO_FORMAT
 };
 
 typedef struct
@@ -161,6 +162,13 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass)
           G_MAXINT, DEFAULT_BUFFER_SIZE,
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
 
+  g_object_class_install_property (gobject_class, PROP_VIDEO_FORMAT,
+      g_param_spec_enum ("video-format", "Video format",
+          "Video format type to use for input (Only use auto for mode=auto)",
+          GST_TYPE_DECKLINK_VIDEO_FORMAT, GST_DECKLINK_VIDEO_FORMAT_AUTO,
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+              G_PARAM_CONSTRUCT)));
+
   templ_caps = gst_decklink_mode_get_template_caps ();
   gst_element_class_add_pad_template (element_class,
       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, templ_caps));
@@ -183,6 +191,7 @@ gst_decklink_video_src_init (GstDecklinkVideoSrc * self)
   self->connection = DEFAULT_CONNECTION;
   self->device_number = 0;
   self->buffer_size = DEFAULT_BUFFER_SIZE;
+  self->video_format = GST_DECKLINK_VIDEO_FORMAT_AUTO;
 
   gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
   gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
@@ -202,6 +211,13 @@ gst_decklink_video_src_set_property (GObject * object, guint property_id,
   switch (property_id) {
     case PROP_MODE:
       self->mode = (GstDecklinkModeEnum) g_value_get_enum (value);
+      /* setting the default value for caps_mode here: if mode==auto then we
+       * configure caps_mode from the caps, if mode!=auto we set caps_mode to
+       * the same value as the mode. so self->caps_mode is essentially
+       * self->mode with mode=auto filtered into whatever we got from the
+       * negotiation */
+      if (self->mode != GST_DECKLINK_MODE_AUTO)
+        self->caps_mode = self->mode;
       break;
     case PROP_CONNECTION:
       self->connection = (GstDecklinkConnectionEnum) g_value_get_enum (value);
@@ -212,6 +228,23 @@ gst_decklink_video_src_set_property (GObject * object, guint property_id,
     case PROP_BUFFER_SIZE:
       self->buffer_size = g_value_get_uint (value);
       break;
+    case PROP_VIDEO_FORMAT:
+      self->video_format = (GstDecklinkVideoFormat) g_value_get_enum (value);
+      switch (self->video_format) {
+        case GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV:
+        case GST_DECKLINK_VIDEO_FORMAT_10BIT_YUV:
+        case GST_DECKLINK_VIDEO_FORMAT_8BIT_ARGB:
+        case GST_DECKLINK_VIDEO_FORMAT_8BIT_BGRA:
+          self->caps_format =
+              gst_decklink_pixel_format_from_type (self->video_format);
+        case GST_DECKLINK_VIDEO_FORMAT_AUTO:
+          break;
+        default:
+          GST_ELEMENT_WARNING (GST_ELEMENT (self), CORE, NOT_IMPLEMENTED,
+              ("Format %d not supported", self->video_format), (NULL));
+          break;
+      }
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -237,6 +270,9 @@ gst_decklink_video_src_get_property (GObject * object, guint property_id,
     case PROP_BUFFER_SIZE:
       g_value_set_uint (value, self->buffer_size);
       break;
+    case PROP_VIDEO_FORMAT:
+      g_value_set_enum (value, self->video_format);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -262,6 +298,7 @@ gst_decklink_video_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
   const GstDecklinkMode *mode;
   BMDVideoInputFlags flags;
   HRESULT ret;
+  BMDPixelFormat format;
 
   GST_DEBUG_OBJECT (self, "Setting caps %" GST_PTR_FORMAT, caps);
 
@@ -329,8 +366,8 @@ gst_decklink_video_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
   mode = gst_decklink_get_mode (self->mode);
   g_assert (mode != NULL);
 
-  ret = self->input->input->EnableVideoInput (mode->mode,
-      bmdFormat8BitYUV, flags);
+  format = self->caps_format;
+  ret = self->input->input->EnableVideoInput (mode->mode, format, flags);
   if (ret != S_OK) {
     GST_WARNING_OBJECT (self, "Failed to enable video input");
     return FALSE;
@@ -351,14 +388,16 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
 {
   GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (bsrc);
   GstCaps *mode_caps, *caps;
+  BMDPixelFormat format;
+  GstDecklinkModeEnum mode;
 
   g_mutex_lock (&self->lock);
-  if (self->caps_mode != GST_DECKLINK_MODE_AUTO)
-    mode_caps = gst_decklink_mode_get_caps (self->caps_mode, self->caps_format);
-  else
-    mode_caps = gst_decklink_mode_get_caps (self->mode, self->caps_format);
+  mode = self->caps_mode;
+  format = self->caps_format;
   g_mutex_unlock (&self->lock);
 
+  mode_caps = gst_decklink_mode_get_caps (mode, format);
+
   if (filter) {
     caps =
         gst_caps_intersect_full (filter, mode_caps, GST_CAPS_INTERSECT_FIRST);
@@ -501,6 +540,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
   VideoFrame *vf;
   CaptureFrame *f;
   GstCaps *caps;
+  gboolean caps_changed = FALSE;
 
   g_mutex_lock (&self->lock);
   while (g_queue_is_empty (&self->current_frames) && !self->flushing) {
@@ -518,21 +558,48 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
   }
 
   g_mutex_lock (&self->lock);
-  if (self->mode == GST_DECKLINK_MODE_AUTO &&
-      (self->caps_mode != f->mode || self->caps_format != f->format)) {
-    GST_DEBUG_OBJECT (self, "Mode/Format changed from %d/%d to %d/%d",
-        self->caps_mode, self->caps_format, f->mode, f->format);
-    self->caps_mode = f->mode;
-    self->caps_format = f->format;
-    g_mutex_unlock (&self->lock);
+  if (self->caps_mode != f->mode) {
+    if (self->mode == GST_DECKLINK_MODE_AUTO) {
+      GST_DEBUG_OBJECT (self, "Mode changed from %d to %d", self->caps_mode,
+          f->mode);
+      caps_changed = TRUE;
+      self->caps_mode = f->mode;
+    } else {
+      g_mutex_unlock (&self->lock);
+      if (f)
+        capture_frame_free (f);
+      GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+          ("Invalid mode in captured frame"),
+          ("Mode set to %d but captured %d", self->caps_mode, f->mode));
+      return GST_FLOW_NOT_NEGOTIATED;
+    }
+  }
+  if (self->caps_format != f->format) {
+    if (self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO) {
+      GST_DEBUG_OBJECT (self, "Format changed from %d to %d", self->caps_format,
+          f->format);
+      caps_changed = TRUE;
+      self->caps_format = f->format;
+    } else {
+      g_mutex_unlock (&self->lock);
+      if (f)
+        capture_frame_free (f);
+      GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+          ("Invalid pixel format in captured frame"),
+          ("Format set to %d but captured %d", self->caps_format, f->format));
+      return GST_FLOW_NOT_NEGOTIATED;
+    }
+  }
+
+  g_mutex_unlock (&self->lock);
+  if (caps_changed) {
     caps = gst_decklink_mode_get_caps (f->mode, f->format);
     gst_video_info_from_caps (&self->info, caps);
     gst_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps);
     gst_element_post_message (GST_ELEMENT_CAST (self),
         gst_message_new_latency (GST_OBJECT_CAST (self)));
     gst_caps_unref (caps);
-  } else {
-    g_mutex_unlock (&self->lock);
+
   }
 
   f->frame->GetBytes ((gpointer *) & data);
@@ -576,10 +643,7 @@ gst_decklink_video_src_query (GstBaseSrc * bsrc, GstQuery * query)
         const GstDecklinkMode *mode;
 
         g_mutex_lock (&self->lock);
-        if (self->caps_mode != GST_DECKLINK_MODE_AUTO)
-          mode = gst_decklink_get_mode (self->caps_mode);
-        else
-          mode = gst_decklink_get_mode (self->mode);
+        mode = gst_decklink_get_mode (self->caps_mode);
         g_mutex_unlock (&self->lock);
 
         min = gst_util_uint64_scale_ceil (GST_SECOND, mode->fps_d, mode->fps_n);
@@ -754,6 +818,11 @@ gst_decklink_video_src_change_state (GstElement * element,
         ret = GST_STATE_CHANGE_FAILURE;
         goto out;
       }
+      if (self->mode == GST_DECKLINK_MODE_AUTO &&
+          self->video_format != GST_DECKLINK_VIDEO_FORMAT_AUTO) {
+        GST_WARNING_OBJECT (self, "Warning: mode=auto and format!=auto may \
+                            not work");
+      }
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
       g_mutex_lock (&self->input->lock);
index b83495f..1309364 100644 (file)
@@ -57,6 +57,7 @@ struct _GstDecklinkVideoSrc
   gint device_number;
 
   GstVideoInfo info;
+  GstDecklinkVideoFormat video_format;
 
   GstDecklinkInput *input;