commit 14fbf78e691ff378e50a36649c36cfd69daf4f63
Author: John Poet <jpoet@mythtv.org>
Date:   Wed Nov 9 14:45:30 2016 -0700

diff --git a/sys/decklink/Makefile.am b/sys/decklink/Makefile.am
index d3ec19d..c735311 100644
--- a/sys/decklink/Makefile.am
+++ b/sys/decklink/Makefile.am
@@ -10,6 +10,7 @@ libgstdecklink_la_LIBADD = \
 	$(GST_PLUGINS_BASE_LIBS) \
 	-lgstaudio-@GST_API_VERSION@ \
 	-lgstvideo-@GST_API_VERSION@ \
+	$(top_builddir)/gst-libs/gst/vanc/libgstvanc-@GST_API_VERSION@.la
 	$(GST_BASE_LIBS) \
 	$(GST_LIBS) \
 	$(DECKLINK_LIBS) \
@@ -28,7 +29,8 @@ libgstdecklink_la_SOURCES = \
 	gstdecklinkaudiosink.cpp \
 	gstdecklinkvideosink.cpp \
 	gstdecklinkaudiosrc.cpp \
-	gstdecklinkvideosrc.cpp
+	gstdecklinkvideosrc.cpp \
+	gstdecklinkvanc.cpp
 
 if DECKLINK_OSX
 libgstdecklink_la_SOURCES += \
@@ -44,6 +46,7 @@ noinst_HEADERS = \
 	gstdecklinkvideosink.h \
 	gstdecklinkaudiosrc.h \
 	gstdecklinkvideosrc.h \
+	gstdecklinkvanc.h \
 	linux/DeckLinkAPIConfiguration.h \
 	linux/DeckLinkAPIDeckControl.h \
 	linux/DeckLinkAPIDiscovery.h \
@@ -65,5 +68,3 @@ EXTRA_DIST = \
 	linux/DeckLinkAPIDispatch.cpp \
 	osx/DeckLinkAPIDispatch.cpp \
 	win/DeckLinkAPIDispatch.cpp
-
-
diff --git a/sys/decklink/gstdecklinkvanc.cpp b/sys/decklink/gstdecklinkvanc.cpp
new file mode 100644
index 0000000..cfa3c1f
--- /dev/null
+++ b/sys/decklink/gstdecklinkvanc.cpp
@@ -0,0 +1,302 @@
+/* GStreamer
+ * Copyright (C) 2012 Daniel Kristjansson <danielk@digital-nirvana.com>
+ * Copyright (C) 2015 Gavin Hurlbut <gavin@digital-nirvana.com>
+ * Copyright (C) 2016 John Poet <jppoet@digital-nirvana.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+
+#include <vector>
+
+#include <gst/vanc/gstvancmeta.h>
+#include "gstdecklinkvanc.h"
+
+#include <gst/gst.h>
+#include "gstdecklink.h"
+#include "gstdecklinkaudiosink.h"
+#include "gstdecklinkvideosink.h"
+#include "gstdecklinkaudiosrc.h"
+#include "gstdecklinkvideosrc.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug);
+#define GST_CAT_DEFAULT gst_decklink_video_src_debug
+
+void gst_processVANC_init_log(void)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_decklink_video_src_debug, "decklinkvideosrc",
+                           0, "debug category for decklinkvideosrc element");
+
+}
+
+static int getANCTableIndex(BMDDisplayMode dm)
+{
+  int table_index = -1;
+
+  switch (dm) {
+    case bmdModeNTSC:
+     table_index = 0;
+     break;
+    case bmdModeNTSC2398:
+     table_index = 0;
+     break;
+    case bmdModePAL:
+     table_index = 2;
+     break;
+    case bmdModeNTSCp:
+     table_index = 1;
+     break;
+    case bmdModePALp:
+     table_index = 3;
+     break;
+
+    case bmdModeHD1080p2398:
+    case bmdModeHD1080p24:
+    case bmdModeHD1080p25:
+    case bmdModeHD1080p2997:
+    case bmdModeHD1080p30:
+     table_index = 6;
+     break;
+
+    case bmdModeHD1080i50:
+    case bmdModeHD1080i5994:
+    case bmdModeHD1080i6000:
+     table_index = 5;
+     break;
+
+    case bmdModeHD1080p50:
+    case bmdModeHD1080p5994:
+    case bmdModeHD1080p6000:
+     table_index = 6;
+     break;
+
+    case bmdModeHD720p50:
+    case bmdModeHD720p5994:
+    case bmdModeHD720p60:
+    table_index = 4;
+    break;
+
+    case bmdMode2k2398:
+    case bmdMode2k24:
+    case bmdMode2k25:
+     table_index = 7;
+     break;
+  };
+
+  return table_index;
+}
+
+static void convertANCToUInt16(std::vector<guint16> &data, BMDPixelFormat fmt,
+                               BMDDisplayMode dm, guint8 * const * vanc,
+                               int vanc_count, int vanc_found)
+{
+  data.clear();
+
+  int row_length = 0;
+
+  // VANC data for "HD" resolutions is only available in the 10BitYUV mode.
+  // TODO: Add support for "SD" resolutions.
+  if (bmdFormat10BitYUV != fmt)
+    return;
+
+  switch (dm) {
+    case bmdModeNTSC:
+    case bmdModeNTSC2398:
+    case bmdModePAL:
+    case bmdModeNTSCp:
+    case bmdModePALp:
+     row_length = 720;
+     break;
+
+    case bmdModeHD1080p2398:
+    case bmdModeHD1080p24:
+    case bmdModeHD1080p25:
+    case bmdModeHD1080p2997:
+    case bmdModeHD1080p30:
+     row_length = 1920;
+     break;
+
+    case bmdModeHD1080i50:
+    case bmdModeHD1080i5994:
+    case bmdModeHD1080i6000:
+     row_length = 1920;
+     break;
+
+    case bmdModeHD1080p50:
+    case bmdModeHD1080p5994:
+    case bmdModeHD1080p6000:
+     row_length = 1920;
+     break;
+
+    case bmdModeHD720p50:
+    case bmdModeHD720p5994:
+    case bmdModeHD720p60:
+     row_length = 1280;
+     break;
+
+    case bmdMode2k2398:
+    case bmdMode2k24:
+    case bmdMode2k25:
+     row_length = 2048;
+     break;
+  };
+
+  if (row_length == 0)
+    return;
+
+  int yuv_samples = row_length * 2; // 4:2:2 Y==row_length, U+V==row_length
+  // each sample contains 12 10 bit words and 8 stuffing bits
+  int v210_samples = yuv_samples / 12;
+
+  // pre-reserve space to avoid many mallocs
+  data.reserve(6 * vanc_found * v210_samples);
+
+  int k = 0;
+
+  for (int i = 0; k < vanc_found && i < vanc_count; ++i) {
+    if (!vanc[i])
+      continue;
+
+    ++k;
+
+    for (int j = 0; j < v210_samples; ++j) {
+      const guint8 *ptr = vanc[i] + (j * 16);
+      guint32 w0 = ((guint32*) ptr) [0];
+      guint32 w1 = ((guint32*) ptr) [1];
+      guint32 w2 = ((guint32*) ptr) [2];
+      guint32 w3 = ((guint32*) ptr) [3];
+
+      data.push_back((w0 >> 10) & 0x3ff);
+      data.push_back(w1 & 0x3ff);
+      data.push_back((w1 >> 20) & 0x3ff);
+      data.push_back((w2 >> 10) & 0x3ff);
+      data.push_back(w3 & 0x3ff);
+      data.push_back((w3 >> 20) & 0x3ff);
+    }
+  }
+}
+
+static guint16 checksum(const guint16 *src, int src_size)
+{
+  guint16 sum = 0;
+
+  for (int i = 0; i < src_size; ++i)
+    sum += (src[i] & 0x1FF);
+
+  return sum & 0x1FF;
+}
+
+/// Returns true iff packet is not marked for deletion and
+/// is of type 1 or type 2.
+static bool usableVANC(int data_id, int sdid)
+{
+  int lsb = data_id & (0xFF&(~0x3));
+  return (lsb > 0x80 || (lsb < 0x80 && lsb && sdid != 0x0));
+}
+
+
+static void addMeta(GstDecklinkVideoSrc *self,
+                    std::vector<guint16> &data,
+                    GstBuffer * buffer)
+{
+  const int min_adp_size = 7;
+
+  if (data.size() < min_adp_size)
+  {
+    GST_WARNING_OBJECT (self, "VANC data size too small");
+    return;
+  }
+
+  for (unsigned int idx = 0; idx < data.size() - min_adp_size; ++idx) {
+    if ((data[idx]  & (~0x3)) != (0x000 & (~0x3)) ||
+        (data[idx + 1] & (~0x3)) != (0x3ff & (~0x3)) ||
+        (data[idx + 2] & (~0x3)) != (0x3ff & (~0x3))) {
+      continue;
+    }
+
+    int did      = data[idx + 3]; // primary data id
+    int dbn_sdid = data[idx + 4]; // data block number (type 1), or
+                                  // secondary data id (type 2)
+
+    int data_count = data[idx + 5] & 0xff;
+
+    if (idx + 6 + data_count >= data.size())
+      continue; // if checksum past the end of the buffer..
+
+    guint16 chksum_e = data[idx + 6 + data_count] & 0x1ff;
+    guint16 chksum_c = checksum(&data[idx + 3], data_count + 3);
+
+    if (chksum_c != chksum_e)
+      continue;
+
+    if (usableVANC(did, dbn_sdid)) {
+      gst_buffer_add_vanc_meta (buffer, did, dbn_sdid, &data[idx + 6],
+                                data_count, chksum_e);
+    }
+  }
+}
+
+static int vancTable[][7] = {
+  { 1, 22, 263, 285, 526, 528, 0 }, // 480i
+  { 1, 44, 525, 528, 0 }, // 480p
+  { 1, 22, 311, 335, 624, 626, 0 }, // 576i
+  { 1, 44, 621, 626, 0 }, // 576p
+  { 1, 25, 746, 750, 0 }, // 720p
+  { 1, 20, 561, 583, 1124, 1126, 0 }, // 1080i
+  { 1, 41, 1122, 1126, 0 }, // 1080p
+  { 1, 2000, 0 } // 2k
+};
+
+bool gst_processVANC(GstDecklinkVideoSrc *self,
+                 IDeckLinkVideoFrameAncillary *vanc_frame,
+                 GstBuffer * buffer)
+{
+  guint8 *vanc[50];
+  const int vanc_cnt = sizeof(vanc) / sizeof(guint8 *);
+  int vanc_start, vanc_end;
+  int vanc_index;
+
+  BMDDisplayMode dm  = vanc_frame->GetDisplayMode();
+  int vanc_found = 0;
+
+  vanc_index = getANCTableIndex(dm);
+
+  if (vanc_index < 0) {
+    vanc_frame->Release();
+    GST_WARNING_OBJECT (self, "Bad VANC index");
+    return false;
+  }
+
+  for (int i = 0, k = 0; vancTable[vanc_index][i]; i += 2) {
+    vanc_start = vancTable[vanc_index][i];
+    vanc_end   = vancTable[vanc_index][i + 1];
+
+    for (int j = vanc_start; j <= vanc_end; ++j) {
+      if (vanc_frame->GetBufferForVerticalBlankingLine
+          (j, (void**) &vanc[k]) == S_OK) {
+        ++k;
+        ++vanc_found;
+      }
+    }
+  }
+
+  BMDPixelFormat fmt = vanc_frame->GetPixelFormat();
+  std::vector<guint16> tmp_data;
+
+  convertANCToUInt16(tmp_data, fmt, dm, vanc, vanc_cnt, vanc_found);
+  addMeta(self, tmp_data, buffer);
+
+  return true;
+}
diff --git a/sys/decklink/gstdecklinkvanc.h b/sys/decklink/gstdecklinkvanc.h
new file mode 100644
index 0000000..7548ae7
--- /dev/null
+++ b/sys/decklink/gstdecklinkvanc.h
@@ -0,0 +1,32 @@
+/* GStreamer
+ * Copyright (C) 2012 Daniel Kristjansson <danielk@digital-nirvana.com>
+ * Copyright (C) 2015 Gavin Hurlbut <gavin@digital-nirvana.com>
+ * Copyright (C) 2016 John Poet <jppoet@digital-nirvana.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+
+#ifndef _Decklink_VANC_H_
+#define _Decklink_VANC_H_
+
+#include "gstdecklinkvideosrc.h"
+
+void gst_processVANC_init_log(void);
+bool gst_processVANC(GstDecklinkVideoSrc *self,
+                 IDeckLinkVideoFrameAncillary *vanc_frame,
+                 GstBuffer * buffer);
+
+#endif
diff --git a/sys/decklink/gstdecklinkvideosrc.cpp b/sys/decklink/gstdecklinkvideosrc.cpp
index 0e81034..14a76db 100644
--- a/sys/decklink/gstdecklinkvideosrc.cpp
+++ b/sys/decklink/gstdecklinkvideosrc.cpp
@@ -23,7 +23,9 @@
 #include "config.h"
 #endif
 
+#include <gst/video/gstvideometa.h>
 #include "gstdecklinkvideosrc.h"
+#include "gstdecklinkvanc.h"
 #include <string.h>
 
 GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug);
@@ -41,7 +43,8 @@ enum
   PROP_DEVICE_NUMBER,
   PROP_BUFFER_SIZE,
   PROP_VIDEO_FORMAT,
-  PROP_TIMECODE_FORMAT
+  PROP_TIMECODE_FORMAT,
+  PROP_ENABLE_VANC
 };
 
 typedef struct
@@ -179,6 +182,12 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass)
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
               G_PARAM_CONSTRUCT)));
 
+  g_object_class_install_property (gobject_class, PROP_ENABLE_VANC,
+      g_param_spec_boolean ("enable-vanc", "Enable VANC processing",
+                            "Create VANC metatdata", FALSE,
+                            (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));
@@ -188,8 +197,15 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass)
       "Video/Src", "Decklink Source", "David Schleef <ds@entropywave.com>, "
       "Sebastian Dröge <sebastian@centricular.com>");
 
+  gst_element_class_set_static_metadata (element_class, "Decklink VANC Source",
+      "Video/Subtitle", "Decklink VANC Source",
+      "Gavin Hurlbut <gavin@digital-nirvana.com>, "
+      "John Poet <jppoet@digital-nirvana.com");
+
   GST_DEBUG_CATEGORY_INIT (gst_decklink_video_src_debug, "decklinkvideosrc",
       0, "debug category for decklinkvideosrc element");
+
+  gst_processVANC_init_log();
 }
 
 static void
@@ -203,6 +219,7 @@ gst_decklink_video_src_init (GstDecklinkVideoSrc * self)
   self->buffer_size = DEFAULT_BUFFER_SIZE;
   self->video_format = GST_DECKLINK_VIDEO_FORMAT_AUTO;
   self->timecode_format = bmdTimecodeRP188Any;
+  self->enable_vanc = FALSE;
 
   gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
   gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
@@ -261,6 +278,9 @@ gst_decklink_video_src_set_property (GObject * object, guint property_id,
           gst_decklink_timecode_format_from_enum ((GstDecklinkTimecodeFormat)
           g_value_get_enum (value));
       break;
+    case PROP_ENABLE_VANC:
+      self->enable_vanc = g_value_get_boolean(value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -293,6 +313,9 @@ gst_decklink_video_src_get_property (GObject * object, guint property_id,
       g_value_set_enum (value,
           gst_decklink_timecode_format_to_enum (self->timecode_format));
       break;
+    case PROP_ENABLE_VANC:
+      g_value_set_boolean (value, self->enable_vanc);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -657,6 +680,30 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
 
   GST_BUFFER_TIMESTAMP (*buffer) = f->capture_time;
   GST_BUFFER_DURATION (*buffer) = f->capture_duration;
+
+  if (self->enable_vanc) {
+    if (f->format != bmdFormat10BitYUV) {
+      GST_WARNING_OBJECT (self, "VANC requires 10bit YUV (v210)");
+    } else {
+      IDeckLinkVideoFrameAncillary  *vanc_frame = NULL;
+
+      if (f->frame->GetAncillaryData(&vanc_frame) == S_OK) {
+        if (!gst_processVANC(self, vanc_frame, *buffer))
+        {
+          GST_WARNING_OBJECT (self, "Failed to parse ancillary data.");
+        }
+      } else {
+        GST_WARNING_OBJECT (self, "No Ancillary data vailable.");
+      }
+
+      vanc_frame->Release();
+    }
+  }
+
+  // NOTE: if the following line is ABOVE the call to gst_processVANC(),
+  // then the GstVANCMeta does NOT show up at the receiving end.  Does
+  // moving this down here mean that the time code meta now does not
+  // show up?  Or is it just that the time code meta needs to be last?
   gst_buffer_add_video_time_code_meta (*buffer, f->tc);
 
   GST_DEBUG_OBJECT (self,
diff --git a/sys/decklink/gstdecklinkvideosrc.h b/sys/decklink/gstdecklinkvideosrc.h
index b1e7351..996bfdf 100644
--- a/sys/decklink/gstdecklinkvideosrc.h
+++ b/sys/decklink/gstdecklinkvideosrc.h
@@ -62,12 +62,13 @@ struct _GstDecklinkVideoSrc
 
   GstDecklinkInput *input;
 
-  GCond cond;
-  GMutex lock;
+  GCond    cond;
+  GMutex   lock;
   gboolean flushing;
-  GQueue current_frames;
+  GQueue   current_frames;
 
-  guint buffer_size;
+  guint    buffer_size;
+  gboolean enable_vanc;
 
   GstClockTime internal_base_time;
   GstClockTime external_base_time;
