decklinkvideosrc: Add automatic mode detection
authorFlorian Langlois <florian.langlois@fr.thalesgroup.com>
Thu, 8 Jan 2015 15:42:31 +0000 (16:42 +0100)
committerSebastian Dröge <sebastian@centricular.com>
Thu, 8 Jan 2015 17:16:12 +0000 (18:16 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=739284

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

index de85a4b0cc96d52c2041b554e569c773516324e0..67ec9f6e22202fe70c1868cf16e32acd2e931723 100644 (file)
@@ -38,6 +38,8 @@ gst_decklink_mode_get_type (void)
 {
   static gsize id = 0;
   static const GEnumValue modes[] = {
+    {GST_DECKLINK_MODE_AUTO, "auto", "Automatic detection"},
+
     {GST_DECKLINK_MODE_NTSC, "ntsc", "NTSC SD 60i"},
     {GST_DECKLINK_MODE_NTSC2398, "ntsc2398", "NTSC SD 60i (24 fps)"},
     {GST_DECKLINK_MODE_PAL, "pal", "PAL SD 50i"},
@@ -140,6 +142,8 @@ gst_decklink_audio_connection_get_type (void)
 #define HD 1, 1, false, true
 
 static const GstDecklinkMode modes[] = {
+  {bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC},     // default is ntsc
+
   {bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC},
   {bmdModeNTSC2398, 720, 486, 24000, 1001, true, NTSC},
   {bmdModePAL, 720, 576, 25, 1, true, PAL},
@@ -181,11 +185,113 @@ static const GstDecklinkMode modes[] = {
 const GstDecklinkMode *
 gst_decklink_get_mode (GstDecklinkModeEnum e)
 {
-  if (e < GST_DECKLINK_MODE_NTSC || e > GST_DECKLINK_MODE_3184p60)
+  if (e < GST_DECKLINK_MODE_AUTO || e > GST_DECKLINK_MODE_3184p60)
     return NULL;
   return &modes[e];
 }
 
+const GstDecklinkModeEnum
+gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode)
+{
+  GstDecklinkModeEnum displayMode = GST_DECKLINK_MODE_NTSC;
+  switch (mode) {
+    case bmdModeNTSC:
+      displayMode = GST_DECKLINK_MODE_NTSC;
+      break;
+    case bmdModeNTSC2398:
+      displayMode = GST_DECKLINK_MODE_NTSC2398;
+      break;
+    case bmdModePAL:
+      displayMode = GST_DECKLINK_MODE_PAL;
+      break;
+    case bmdModeNTSCp:
+      displayMode = GST_DECKLINK_MODE_NTSC_P;
+      break;
+    case bmdModePALp:
+      displayMode = GST_DECKLINK_MODE_PAL_P;
+      break;
+    case bmdModeHD1080p2398:
+      displayMode = GST_DECKLINK_MODE_1080p2398;
+      break;
+    case bmdModeHD1080p24:
+      displayMode = GST_DECKLINK_MODE_1080p24;
+      break;
+    case bmdModeHD1080p25:
+      displayMode = GST_DECKLINK_MODE_1080p25;
+      break;
+    case bmdModeHD1080p2997:
+      displayMode = GST_DECKLINK_MODE_1080p2997;
+      break;
+    case bmdModeHD1080p30:
+      displayMode = GST_DECKLINK_MODE_1080p30;
+      break;
+    case bmdModeHD1080i50:
+      displayMode = GST_DECKLINK_MODE_1080i50;
+      break;
+    case bmdModeHD1080i5994:
+      displayMode = GST_DECKLINK_MODE_1080i5994;
+      break;
+    case bmdModeHD1080i6000:
+      displayMode = GST_DECKLINK_MODE_1080i60;
+      break;
+    case bmdModeHD1080p50:
+      displayMode = GST_DECKLINK_MODE_1080p50;
+      break;
+    case bmdModeHD1080p5994:
+      displayMode = GST_DECKLINK_MODE_1080p5994;
+      break;
+    case bmdModeHD1080p6000:
+      displayMode = GST_DECKLINK_MODE_1080p60;
+      break;
+    case bmdModeHD720p50:
+      displayMode = GST_DECKLINK_MODE_720p50;
+      break;
+    case bmdModeHD720p5994:
+      displayMode = GST_DECKLINK_MODE_720p5994;
+      break;
+    case bmdModeHD720p60:
+      displayMode = GST_DECKLINK_MODE_720p60;
+      break;
+    case bmdMode2k2398:
+      displayMode = GST_DECKLINK_MODE_2048p2398;
+      break;
+    case bmdMode2k24:
+      displayMode = GST_DECKLINK_MODE_2048p24;
+      break;
+    case bmdMode2k25:
+      displayMode = GST_DECKLINK_MODE_2048p25;
+      break;
+    case bmdMode4K2160p2398:
+      displayMode = GST_DECKLINK_MODE_3184p2398;
+      break;
+    case bmdMode4K2160p24:
+      displayMode = GST_DECKLINK_MODE_3184p24;
+      break;
+    case bmdMode4K2160p25:
+      displayMode = GST_DECKLINK_MODE_3184p25;
+      break;
+    case bmdMode4K2160p2997:
+      displayMode = GST_DECKLINK_MODE_3184p2997;
+      break;
+    case bmdMode4K2160p30:
+      displayMode = GST_DECKLINK_MODE_3184p30;
+      break;
+    case bmdMode4K2160p50:
+      displayMode = GST_DECKLINK_MODE_3184p50;
+      break;
+    case bmdMode4K2160p5994:
+      displayMode = GST_DECKLINK_MODE_3184p5994;
+      break;
+    case bmdMode4K2160p60:
+      displayMode = GST_DECKLINK_MODE_3184p60;
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+  return displayMode;
+}
+
 static const BMDVideoConnection connections[] = {
   0,                            /* auto */
   bmdVideoConnectionSDI,
@@ -346,9 +452,21 @@ public:
 
   virtual HRESULT STDMETHODCALLTYPE
       VideoInputFormatChanged (BMDVideoInputFormatChangedEvents,
-      IDeckLinkDisplayMode *, BMDDetectedVideoInputFormatFlags)
+      IDeckLinkDisplayMode * mode, BMDDetectedVideoInputFormatFlags)
   {
-    GST_FIXME ("Video input format change not supported yet");
+    GST_INFO ("Video input format changed");
+
+    g_mutex_lock (&m_input->lock);
+    m_input->input->PauseStreams ();
+    m_input->input->EnableVideoInput (mode->GetDisplayMode (),
+        bmdFormat8BitYUV, bmdVideoInputEnableFormatDetection);
+    m_input->input->FlushStreams ();
+    m_input->input->StartStreams ();
+    m_input->mode =
+        gst_decklink_get_mode (gst_decklink_get_mode_enum_from_bmd
+        (mode->GetDisplayMode ()));
+    g_mutex_unlock (&m_input->lock);
+
     return S_OK;
   }
 
@@ -362,7 +480,9 @@ public:
     if (m_input->got_video_frame) {
       GstClockTime capture_time = clock_time -
           gst_element_get_base_time (m_input->videosrc);
-      m_input->got_video_frame (m_input->videosrc, video_frame, capture_time);
+      m_input->got_video_frame (m_input->videosrc, video_frame,
+          gst_decklink_get_mode_enum_from_bmd (m_input->mode->mode),
+          capture_time);
     }
 
     if (m_input->got_audio_packet) {
@@ -493,6 +613,12 @@ init_devices (gpointer data)
       GST_WARNING ("selected device does not have config interface");
     }
 
+    ret = decklink->QueryInterface (IID_IDeckLinkAttributes,
+        (void **) &devices[i].input.attributes);
+    if (ret != S_OK) {
+      GST_WARNING ("selected device does not have attributes interface");
+    }
+
     ret = iterator->Next (&decklink);
     i++;
 
index ddf570844bd06135668cbf7a32338fef16c608e6..6c3df144f74502413e452d6da3b918ebca3abfcf 100644 (file)
@@ -44,6 +44,8 @@
 #endif /* _MSC_VER */
 
 typedef enum {
+  GST_DECKLINK_MODE_AUTO,
+
   GST_DECKLINK_MODE_NTSC,
   GST_DECKLINK_MODE_NTSC2398,
   GST_DECKLINK_MODE_PAL,
@@ -122,6 +124,7 @@ struct _GstDecklinkMode {
 };
 
 const GstDecklinkMode * gst_decklink_get_mode (GstDecklinkModeEnum e);
+const GstDecklinkModeEnum gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode);
 const BMDVideoConnection gst_decklink_get_connection (GstDecklinkConnectionEnum e);
 GstCaps * gst_decklink_mode_get_caps (GstDecklinkModeEnum e);
 GstCaps * gst_decklink_mode_get_template_caps (void);
@@ -152,13 +155,14 @@ struct _GstDecklinkInput {
   IDeckLink *device;
   IDeckLinkInput *input;
   IDeckLinkConfiguration *config;
+  IDeckLinkAttributes *attributes;
   GstClock *clock;
 
   /* Everything below protected by mutex */
   GMutex lock;
 
   /* Set by the video source */
-  void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstClockTime capture_time);
+  void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, GstClockTime capture_time);
   /* Configured mode or NULL */
   const GstDecklinkMode *mode;
 
index 2f3f74e9a42eacd9a8aa56005db06c1237220811..3dff4d5b10d0efd9cff549c0e38a2ade9b758283 100644 (file)
@@ -29,7 +29,7 @@
 GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug);
 #define GST_CAT_DEFAULT gst_decklink_video_src_debug
 
-#define DEFAULT_MODE (GST_DECKLINK_MODE_NTSC)
+#define DEFAULT_MODE (GST_DECKLINK_MODE_AUTO)
 #define DEFAULT_CONNECTION (GST_DECKLINK_CONNECTION_AUTO)
 #define DEFAULT_BUFFER_SIZE (5)
 
@@ -46,6 +46,7 @@ typedef struct
 {
   IDeckLinkVideoInputFrame *frame;
   GstClockTime capture_time;
+  GstDecklinkModeEnum mode;
 } CaptureFrame;
 
 static void
@@ -169,6 +170,7 @@ static void
 gst_decklink_video_src_init (GstDecklinkVideoSrc * self)
 {
   self->mode = DEFAULT_MODE;
+  self->caps_mode = DEFAULT_MODE;
   self->connection = DEFAULT_CONNECTION;
   self->device_number = 0;
   self->buffer_size = DEFAULT_BUFFER_SIZE;
@@ -249,7 +251,7 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
   GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (bsrc);
   GstCaps *mode_caps, *caps;
 
-  mode_caps = gst_decklink_mode_get_caps (self->mode);
+  mode_caps = gst_decklink_mode_get_caps (self->caps_mode);
   if (filter) {
     caps =
         gst_caps_intersect_full (filter, mode_caps, GST_CAPS_INTERSECT_FIRST);
@@ -263,7 +265,8 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
 
 static void
 gst_decklink_video_src_got_frame (GstElement * element,
-    IDeckLinkVideoInputFrame * frame, GstClockTime capture_time)
+    IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode,
+    GstClockTime capture_time)
 {
   GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element);
 
@@ -284,6 +287,7 @@ gst_decklink_video_src_got_frame (GstElement * element,
     f = (CaptureFrame *) g_malloc0 (sizeof (CaptureFrame));
     f->frame = frame;
     f->capture_time = capture_time;
+    f->mode = mode;
     frame->AddRef ();
     g_queue_push_tail (&self->current_frames, f);
     g_cond_signal (&self->cond);
@@ -301,6 +305,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
   VideoFrame *vf;
   CaptureFrame *f;
   GstClockTime timestamp, duration;
+  GstCaps *caps;
 
   g_mutex_lock (&self->lock);
   while (g_queue_is_empty (&self->current_frames) && !self->flushing) {
@@ -316,6 +321,14 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
     return GST_FLOW_FLUSHING;
   }
 
+  if (self->caps_mode != f->mode) {
+    self->caps_mode = f->mode;
+    caps = gst_decklink_mode_get_caps (self->caps_mode);
+    gst_video_info_from_caps (&self->info, caps);
+    gst_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps);
+    gst_caps_unref (caps);
+  }
+
   f->frame->GetBytes ((gpointer *) & data);
   data_size = self->info.size;
 
@@ -362,7 +375,7 @@ gst_decklink_video_src_query (GstBaseSrc * bsrc, GstQuery * query)
         GstClockTime min, max;
         const GstDecklinkMode *mode;
 
-        mode = gst_decklink_get_mode (self->mode);
+        mode = gst_decklink_get_mode (self->caps_mode);
 
         min = gst_util_uint64_scale_ceil (GST_SECOND, mode->fps_d, mode->fps_n);
         max = self->buffer_size * min;
@@ -414,6 +427,8 @@ static gboolean
 gst_decklink_video_src_open (GstDecklinkVideoSrc * self)
 {
   const GstDecklinkMode *mode;
+  BMDVideoInputFlags flags;
+  bool autoDetection;
   GstCaps *caps;
   HRESULT ret;
 
@@ -446,11 +461,31 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self)
     }
   }
 
+  flags = bmdVideoInputFlagDefault;
+  if (self->mode == GST_DECKLINK_MODE_AUTO) {
+    if (self->input->attributes) {
+      ret =
+          self->input->
+          attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection,
+          &autoDetection);
+      if (ret != S_OK) {
+        GST_ERROR_OBJECT (self, "Failed to get attribute (autodetection)");
+        return FALSE;
+      }
+      if (autoDetection)
+        flags |= bmdVideoInputEnableFormatDetection;
+    }
+    if (!autoDetection) {
+      GST_ERROR_OBJECT (self, "Failed to activate auto-detection");
+      return FALSE;
+    }
+  }
+
   mode = gst_decklink_get_mode (self->mode);
   g_assert (mode != NULL);
 
   ret = self->input->input->EnableVideoInput (mode->mode,
-      bmdFormat8BitYUV, bmdVideoOutputFlagDefault);
+      bmdFormat8BitYUV, flags);
   if (ret != S_OK) {
     GST_WARNING_OBJECT (self, "Failed to enable video input");
     gst_decklink_release_nth_input (self->device_number,
@@ -463,7 +498,8 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self)
   self->input->got_video_frame = gst_decklink_video_src_got_frame;
   g_mutex_unlock (&self->input->lock);
 
-  caps = gst_decklink_mode_get_caps (self->mode);
+  self->caps_mode = gst_decklink_get_mode_enum_from_bmd (mode->mode);
+  caps = gst_decklink_mode_get_caps (self->caps_mode);
   gst_video_info_from_caps (&self->info, caps);
   gst_caps_unref (caps);
 
index da2da7dd0bd9832593da41445c08132d1a6ff253..6cbf2af722bdec95ca9cbd80403e104c85ac22fe 100644 (file)
@@ -51,6 +51,7 @@ struct _GstDecklinkVideoSrc
   GstPushSrc parent;
 
   GstDecklinkModeEnum mode;
+  GstDecklinkModeEnum caps_mode;
   GstDecklinkConnectionEnum connection;
   gint device_number;