commit 4de151c1f657aa1d3ca672d56a926e41ee39a620
Author: John Poet <jpoet@mythtv.org>
Date:   Thu Oct 6 16:03:10 2016 -0600

    Add libvanc.
    Extract ancillary data from decklink input, and add vanc meta data to
    buffer.

diff --git a/configure.ac b/configure.ac
index 8402afd..6fb71a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3710,6 +3710,7 @@ gst-libs/gst/base/Makefile
 gst-libs/gst/player/Makefile
 gst-libs/gst/video/Makefile
 gst-libs/gst/audio/Makefile
+gst-libs/gst/vanc/Makefile
 sys/Makefile
 sys/dshowdecwrapper/Makefile
 sys/acmenc/Makefile
diff --git a/gst-libs/gst/Makefile.am b/gst-libs/gst/Makefile.am
index 7d0b309..5713334 100644
--- a/gst-libs/gst/Makefile.am
+++ b/gst-libs/gst/Makefile.am
@@ -11,11 +11,11 @@ WAYLAND_DIR=wayland
 endif
 
 SUBDIRS = uridownloader adaptivedemux interfaces basecamerabinsrc codecparsers \
-	 insertbin mpegts base video audio player $(GL_DIR) $(WAYLAND_DIR)
+	 insertbin mpegts base video audio player vanc $(GL_DIR) $(WAYLAND_DIR)
 
 noinst_HEADERS = gst-i18n-plugin.h gettext.h glib-compat-private.h
 DIST_SUBDIRS = uridownloader adaptivedemux interfaces gl basecamerabinsrc \
-	codecparsers insertbin mpegts wayland base video audio player
+	codecparsers insertbin mpegts wayland base video audio player vanc
 
 #dependencies
 video, audio: base
@@ -24,7 +24,7 @@ adaptivedemux: uridownloader
 
 INDEPENDENT_SUBDIRS = \
 	interfaces basecamerabinsrc codecparsers insertbin uridownloader \
-	mpegts base player $(GL_DIR) $(WAYLAND_DIR)
+	mpegts base player vanc $(GL_DIR) $(WAYLAND_DIR)
 
 .PHONY: independent-subdirs $(INDEPENDENT_SUBDIRS)
 
diff --git a/gst-libs/gst/vanc/Makefile.am b/gst-libs/gst/vanc/Makefile.am
new file mode 100644
index 0000000..6a24a33
--- /dev/null
+++ b/gst-libs/gst/vanc/Makefile.am
@@ -0,0 +1,19 @@
+
+#lib_LTLIBRARIES = libgstvanc-@GST_API_VERSION@.la
+plugin_LTLIBRARIES = libgstvanc-@GST_API_VERSION@.la
+
+libgstvanc_@GST_API_VERSION@_la_CFLAGS = \
+		$(GST_CFLAGS) \
+		$(GST_PLUGINS_BAD_CFLAGS)
+libgstvanc_@GST_API_VERSION@_la_LIBADD = \
+		$(GST_LIBS)
+libgstvanc_@GST_API_VERSION@_la_LDFLAGS = \
+		$(GST_LIB_LDFLAGS)
+
+libgstvanc_@GST_API_VERSION@_la_SOURCES     = gstvancmeta.c
+
+libgstvanc_@GST_API_VERSION@include_HEADERS = gstvancmeta.h
+
+libgstvanc_@GST_API_VERSION@includedir = \
+	$(includedir)/gstreamer-@GST_API_VERSION@/gst/vanc
+
diff --git a/gst-libs/gst/vanc/gstvancmeta.c b/gst-libs/gst/vanc/gstvancmeta.c
new file mode 100644
index 0000000..df154e9
--- /dev/null
+++ b/gst-libs/gst/vanc/gstvancmeta.c
@@ -0,0 +1,222 @@
+#include "gstvancmeta.h"
+
+GType
+  vanc_meta_api_get_type (void)
+{
+  static volatile GType type;
+  static const gchar *tags[] = { "VANC", NULL };
+
+  if (g_once_init_enter (&type)) {
+    GType _type = gst_meta_api_type_register ("VANCMetaAPI", tags);
+    g_once_init_leave (&type, _type);
+  }
+  return type;
+}
+
+gboolean
+  vanc_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
+{
+  VANCMeta *vmeta = (VANCMeta *) meta;
+  vmeta->data_id = vmeta->dbn_sdid = 0;
+  vmeta->data = NULL;
+  return TRUE;
+}
+
+gboolean
+  vanc_meta_transform (GstBuffer * transbuf, GstMeta * meta,
+                       GstBuffer * buffer, GQuark type, gpointer data)
+{
+  VANCMeta *vmeta = (VANCMeta *) meta;
+
+  /* we always need a copy, but is this the best way?  Can we just
+   * up-ref the GArray? */
+  gst_buffer_add_vanc_meta (transbuf, vmeta->data_id, vmeta->dbn_sdid,
+                            (uint16_t*)vmeta->data->data, vmeta->data->len,
+                            vmeta->checksum);
+  g_array_ref (vmeta->data);
+  return TRUE;
+}
+
+static void
+  vanc_meta_free (GstMeta * meta, GstBuffer * buffer)
+{
+  VANCMeta *vmeta = (VANCMeta *) meta;
+
+  g_array_unref (vmeta->data);
+}
+
+const GstMetaInfo
+  *vanc_meta_get_info (void)
+{
+  static const GstMetaInfo *meta_info = NULL;
+
+  if (g_once_init_enter (&meta_info)) {
+    const GstMetaInfo *mi = gst_meta_register (VANC_META_API_TYPE,
+                                               "VANCMeta",
+                                               sizeof (VANCMeta),
+                                               vanc_meta_init,
+                                               vanc_meta_free,
+                                               vanc_meta_transform);
+    g_once_init_leave (&meta_info, mi);
+  }
+  return meta_info;
+}
+
+VANCMeta *
+  gst_buffer_add_vanc_meta (GstBuffer * buffer,
+                            gint did, gint dbn_sdid,
+                            uint16_t * data, gsize size,
+                            uint16_t checksum)
+{
+  VANCMeta *vmeta;
+
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+
+  vmeta = (VANCMeta *) gst_buffer_add_meta (buffer,
+                                           GST_VANC_META_INFO, NULL);
+
+  vmeta->data_id = did;
+  vmeta->dbn_sdid = dbn_sdid;
+  vmeta->checksum = checksum;
+
+  vmeta->data = g_array_sized_new (FALSE, FALSE, sizeof(uint16_t), size);
+  g_array_append_vals (vmeta->data, data, size);
+
+  return vmeta;
+}
+
+int vanc_getDataID(const VANCMeta *meta)
+{
+  return meta->data_id;
+}
+
+gboolean vanc_isType1(const VANCMeta *meta)
+{
+  int lsb = meta->data_id & (0xFF&(~0x3));
+  return (lsb > 0x80);
+}
+
+gboolean vanc_isType2(const VANCMeta *meta)
+{
+  int lsb = meta->data_id & (0xFF&(~0x3));
+  return (lsb < 0x80) && lsb;
+}
+
+gboolean vanc_isMarkedForDelete(const VANCMeta *meta)
+{
+  return (meta->data_id & (0xFF&(~0x3))) == 0x80;
+}
+
+gboolean vanc_isSMPTE12_2(const VANCMeta *meta)
+{
+  return (0x60 == (meta->data_id  & 0xFF) &&
+          0x60 == (meta->dbn_sdid & 0xFF) &&
+          0x10 == meta->data->len);
+}
+
+gboolean vanc_isAFD(const VANCMeta *meta)
+{
+  return (0x41 == (meta->data_id  & 0xFF) &&
+          0x05 == (meta->dbn_sdid & 0xFF) &&
+          0x08 == meta->data->len);
+}
+
+gboolean vanc_isCDP(const VANCMeta *meta)
+{
+  return (0x61 == (meta->data_id  & 0xFF) &&
+          0x01 == (meta->dbn_sdid & 0xFF));
+}
+
+gboolean vanc_isSMPTE2020(const VANCMeta *meta)
+{
+  return (0x45 == (meta->data_id  & 0xFF) &&
+          0x09 >= (meta->dbn_sdid & 0xFF));
+}
+
+gboolean vanc_isAudioControl(const VANCMeta *meta, int group)
+{
+  int did = vanc_getDataID(meta) & 0xFF;
+  return (0xE0 <= did) && (did <= 0xE3) &&
+    ((0 == group) || ((did - 0xE0) == (4 - group)));
+}
+
+gboolean vanc_isAudioPacket(const VANCMeta *meta, int group)
+{
+  int did = vanc_getDataID(meta) & 0xFF;
+  return (0xE4 <= did) && (did <= 0xE7) &&
+    ((0 == group) || ((did - 0xE4) == (4 - group)));
+}
+
+int vanc_getDataBlockNumber(const VANCMeta *meta)
+{
+  return meta->dbn_sdid;
+}
+
+int vanc_getSecondaryDataID(const VANCMeta *meta)
+{
+  return meta->dbn_sdid;
+}
+
+int vanc_getDataCount(const VANCMeta *meta)
+{
+  return meta->data->len;
+}
+
+int vanc_getChecksum(const VANCMeta *meta)
+{
+  return meta->checksum;
+}
+
+/// Returns a const pointer to the data.
+GArray const *vanc_getData(const VANCMeta *meta)
+{
+  return meta->data;
+}
+
+#if 0
+QString vanc_getDataDescription(const VANCMeta *meta)
+{
+  int did = vanc_getDataID(meta) & 0xFF;
+  if ((did & 0xF0) == 0xE0)
+  {
+    if (did == 0xE0)
+      return "SMPTE 299 AG-4 control";
+    else if (did == 0xE1)
+      return "SMPTE 299 AG-3 control";
+    else if (did == 0xE2)
+      return "SMPTE 299 AG-2 control";
+    else if (did == 0xE3)
+      return "SMPTE 299 AG-1 control";
+    else if (did == 0xE4)
+      return "SMPTE 299 AG-4 packet";
+    else if (did == 0xE5)
+      return "SMPTE 299 AG-3 packet";
+    else if (did == 0xE6)
+      return "SMPTE 299 AG-2 packet";
+    else if (did == 0xE7)
+      return "SMPTE 299 AG-1 packet";
+  }
+  else if (isSMPTE12_2())
+    return "SMPTE 12-2 timecode";
+  else if (isAFD())
+    return "SMPTE 2016-3 AFD";
+  else if (isCDP())
+    return "SMPTE 334-2 CDP";
+  else if (isSMPTE2020())
+    return "SMPTE 2020 audio metadata";
+
+  return QString("Unknown ANC did(0x%1) sdid(0x%1) count(%1)")
+    .arg(vanc_getDataID(meta) & 0xFF, 2, 16, QChar('0'))
+    .arg(vanc_getSecondaryDataID(meta) & 0xFF, 2, 16, QChar('0'))
+    .arg(vanc_getDataCount(meta));
+}
+
+QString vanc_toString(const VANCMeta *meta) const
+{
+  return QString("ANC DID(0x%1) SDID(0x%2) DC(%3) %4")
+    .arg(getDataID(meta)&0xff,2,16,QChar('0'))
+    .arg(getSecondaryDataID(meta)&0xff,2,16,QChar('0'))
+    .arg(getDataCount(meta))
+    .arg(getDataDescription(meta));
+}
+#endif
diff --git a/gst-libs/gst/vanc/gstvancmeta.h b/gst-libs/gst/vanc/gstvancmeta.h
new file mode 100644
index 0000000..bf54932
--- /dev/null
+++ b/gst-libs/gst/vanc/gstvancmeta.h
@@ -0,0 +1,101 @@
+#ifndef _GST_VANC_META_H_
+#define _GST_VANC_META_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <gst/gst.h>
+#include <stdint.h>
+
+typedef struct _VANCMeta VANCMeta;
+
+struct _VANCMeta {
+  GstMeta          meta;
+  gint             data_id;
+  gint             dbn_sdid;
+  GArray          *data;
+  uint16_t         checksum;
+};
+
+GType vanc_meta_api_get_type (void);
+#define VANC_META_API_TYPE (vanc_meta_api_get_type())
+
+#define gst_buffer_get_vanc_meta(b)                             \
+  ((VANCMeta*)gst_buffer_get_meta((b),VANC_META_API_TYPE))
+
+/* implementation */
+const  GstMetaInfo *vanc_meta_get_info (void);
+#define GST_VANC_META_INFO (vanc_meta_get_info ())
+
+VANCMeta * gst_buffer_add_vanc_meta (GstBuffer * buffer,
+                                     gint did, gint dbn_sdid,
+                                     uint16_t * data, gsize size,
+                                     uint16_t checksum);
+
+/// Returns primary data id.
+int vanc_getDataID(const VANCMeta *meta);
+
+/// Returns true iff primary data id is of type 1
+gboolean vanc_isType1(const VANCMeta *meta);
+
+/// Returns true iff primary data id is of type 2
+gboolean vanc_isType2(const VANCMeta *meta);
+
+/// Return true iff marked for deletion
+gboolean vanc_isMarkedForDelete(const VANCMeta *meta);
+
+/// Returns true iff this packet contains a SMPTE 12-2 timecode.
+gboolean vanc_isSMPTE12_2(const VANCMeta *meta);
+
+/// Returns true iff this packet contains SMPTE 2016-3
+/// active format description and bar data.
+gboolean vanc_isAFD(const VANCMeta *meta);
+
+/// Returns true iff this packet contains SMPTE 334-2
+/// caption data (CEA-708 & CEA-608).
+gboolean vanc_isCDP(const VANCMeta *meta);
+
+/// Returns true iff this packet contains SMPTE 2020
+/// Audio metadata.
+gboolean vanc_isSMPTE2020(const VANCMeta *meta);
+
+/// Returns true if this packet contains SMPTE 299 audio control data.
+gboolean vanc_isAudioControl(const VANCMeta *meta, int group);
+
+/// Returns true if this packets contains SMPTE 299 audio data packets.
+gboolean vanc_isAudioPacket(const VANCMeta *meta, int group);
+
+/// Returns the data block number iff this is a type 1 packet,
+/// undefined for type 2 packets.
+int vanc_getDataBlockNumber(const VANCMeta *meta);
+
+/// Returns the secondary data if iff this is a type 2 packet,
+/// undefined for type 1 packets.
+int vanc_getSecondaryDataID(const VANCMeta *meta);
+
+/// Returns the number of data words.
+int vanc_getDataCount(const VANCMeta *meta);
+
+/// Returns the expected checksum of the data.
+int vanc_getChecksum(const VANCMeta *meta);
+
+/// Returns a const pointer to the data.
+GArray const *vanc_getData(const VANCMeta *meta);
+
+#if 0
+/// Returns a non-const pointer to the data.
+GArray *vanc_getData(const VANCMeta *meta) { return meta->data; }
+
+/// Returns a human readable descrption of the packet
+QString getDataDescription(const VANCMeta *meta) const;
+/// Returns a debugging description of the packet.
+QString toString(const VANCMeta *meta) const;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
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..84bf3b6
--- /dev/null
+++ b/sys/decklink/gstdecklinkvanc.cpp
@@ -0,0 +1,254 @@
+#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
+
+
+
+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: table_index = 6;  break;
+    case bmdModeHD1080p24:   table_index = 6;  break;
+    case bmdModeHD1080p25:   table_index = 6;  break;
+    case bmdModeHD1080p2997: table_index = 6;  break;
+    case bmdModeHD1080p30:   table_index = 6;  break;
+
+    case bmdModeHD1080i50:   table_index = 5;  break;
+    case bmdModeHD1080i5994: table_index = 5;  break;
+    case bmdModeHD1080i6000: table_index = 5;  break;
+
+    case bmdModeHD1080p50:   table_index = 6;  break;
+    case bmdModeHD1080p5994: table_index = 6;  break;
+    case bmdModeHD1080p6000: table_index = 6;  break;
+
+    case bmdModeHD720p50:    table_index = 4;  break;
+    case bmdModeHD720p5994:  table_index = 4;  break;
+    case bmdModeHD720p60:    table_index = 4;  break;
+
+    case bmdMode2k2398:      table_index = 7;  break;
+    case bmdMode2k24:        table_index = 7;  break;
+    case bmdMode2k25:        table_index = 7;  break;
+  };
+
+  return table_index;
+}
+
+static void convertANCToUInt16(std::vector<uint16_t> &data, BMDPixelFormat fmt,
+                               BMDDisplayMode dm, uint8_t * const * vanc,
+                               int vanc_count, int vanc_found)
+{
+  data.clear();
+
+  int row_length = 0;
+
+  if (bmdFormat10BitYUV != fmt)
+    return;
+
+  switch (dm) {
+    case bmdModeNTSC:        row_length = 720;   break;
+    case bmdModeNTSC2398:    row_length = 720;   break;
+    case bmdModePAL:         row_length = 720;   break;
+    case bmdModeNTSCp:       row_length = 720;   break;
+    case bmdModePALp:        row_length = 720;   break;
+
+    case bmdModeHD1080p2398: row_length = 1920;  break;
+    case bmdModeHD1080p24:   row_length = 1920;  break;
+    case bmdModeHD1080p25:   row_length = 1920;  break;
+    case bmdModeHD1080p2997: row_length = 1920;  break;
+    case bmdModeHD1080p30:   row_length = 1920;  break;
+
+    case bmdModeHD1080i50:   row_length = 1920;  break;
+    case bmdModeHD1080i5994: row_length = 1920;  break;
+    case bmdModeHD1080i6000: row_length = 1920;  break;
+
+    case bmdModeHD1080p50:   row_length = 1920;  break;
+    case bmdModeHD1080p5994: row_length = 1920;  break;
+    case bmdModeHD1080p6000: row_length = 1920;  break;
+
+    case bmdModeHD720p50:    row_length = 1280;  break;
+    case bmdModeHD720p5994:  row_length = 1280;  break;
+    case bmdModeHD720p60:    row_length = 1280;  break;
+
+    case bmdMode2k2398:      row_length = 2048;  break;
+    case bmdMode2k24:        row_length = 2048;  break;
+    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 uint8_t *ptr = vanc[i] + (j * 16);
+      uint32_t w0 = ((uint32_t*) ptr) [0];
+      uint32_t w1 = ((uint32_t*) ptr) [1];
+      uint32_t w2 = ((uint32_t*) ptr) [2];
+      uint32_t w3 = ((uint32_t*) 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 uint16_t checksum(const uint16_t *src, int src_size)
+{
+  uint16_t 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 packetizeComponentAncillary(GstDecklinkVideoSrc *self,
+                                        std::vector<uint16_t> &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;
+  }
+
+  const GstMetaInfo *vanc_info = vanc_meta_get_info ();
+
+  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..
+
+    uint16_t chksum_e = data[idx + 6 + data_count] & 0x1ff;
+    uint16_t 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);
+      GST_WARNING_OBJECT (self, "VANC meta added");
+    }
+
+    // printf("i: %d, did: %04X, sdid: %04X, data_count: %02X\n", i,
+    // did, dbn_sdid, data_count);
+  }
+}
+
+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
+};
+
+void ProcessVANC_init_log(void)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_decklink_video_src_debug, "decklinkvideosrc",
+                           0, "debug category for decklinkvideosrc element");
+
+}
+
+bool ProcessVANC(GstDecklinkVideoSrc *self,
+                 IDeckLinkVideoFrameAncillary *vanc_frame,
+                 GstBuffer * buffer)
+{
+  uint8_t *vanc[50];
+  const int vanc_cnt = sizeof(vanc) / sizeof(uint8_t *);
+  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<uint16_t> tmp_data;
+
+  convertANCToUInt16(tmp_data, fmt, dm, vanc, vanc_cnt, vanc_found);
+  packetizeComponentAncillary(self, tmp_data, buffer);
+
+  return true;
+}
diff --git a/sys/decklink/gstdecklinkvanc.h b/sys/decklink/gstdecklinkvanc.h
new file mode 100644
index 0000000..78e21a9
--- /dev/null
+++ b/sys/decklink/gstdecklinkvanc.h
@@ -0,0 +1,11 @@
+#ifndef _Decklink_VANC_H_
+#define _Decklink_VANC_H_
+
+#include "gstdecklinkvideosrc.h"
+
+void ProcessVANC_init_log(void);
+bool ProcessVANC(GstDecklinkVideoSrc *self,
+                 IDeckLinkVideoFrameAncillary *vanc_frame,
+                 GstBuffer * buffer);
+
+#endif
diff --git a/sys/decklink/gstdecklinkvideosrc.cpp b/sys/decklink/gstdecklinkvideosrc.cpp
index d82c5fc..ea46d03 100644
--- a/sys/decklink/gstdecklinkvideosrc.cpp
+++ b/sys/decklink/gstdecklinkvideosrc.cpp
@@ -23,8 +23,11 @@
 #include "config.h"
 #endif
 
+#include <gst/video/gstvideometa.h>
 #include "gstdecklinkvideosrc.h"
+#include "gstdecklinkvanc.h"
 #include <string.h>
+#include <vector>
 
 GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug);
 #define GST_CAT_DEFAULT gst_decklink_video_src_debug
@@ -41,7 +44,8 @@ enum
   PROP_DEVICE_NUMBER,
   PROP_BUFFER_SIZE,
   PROP_VIDEO_FORMAT,
-  PROP_TIMECODE_FORMAT
+  PROP_TIMECODE_FORMAT,
+  PROP_ENABLE_VANC
 };
 
 typedef struct
@@ -179,6 +183,11 @@ 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",
+                            0, GParamFlags (G_PARAM_READWRITE)));
+
   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,14 @@ 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",
+      "John Poet <jppoet@digital-nirvana.com");
+
   GST_DEBUG_CATEGORY_INIT (gst_decklink_video_src_debug, "decklinkvideosrc",
       0, "debug category for decklinkvideosrc element");
+
+  ProcessVANC_init_log();
 }
 
 static void
@@ -203,6 +218,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 +277,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 +312,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;
@@ -346,8 +368,7 @@ gst_decklink_video_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
     ret = self->input->config->SetInt (bmdDeckLinkConfigVideoInputConnection,
         gst_decklink_get_connection (self->connection));
     if (ret != S_OK) {
-      GST_ERROR_OBJECT (self,
-          "Failed to set configuration (input source): 0x%08x", ret);
+      GST_ERROR_OBJECT (self, "Failed to set configuration (input source)");
       return FALSE;
     }
 
@@ -356,7 +377,7 @@ gst_decklink_video_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
           bmdAnalogVideoFlagCompositeSetup75);
       if (ret != S_OK) {
         GST_ERROR_OBJECT (self,
-            "Failed to set configuration (composite setup): 0x%08x", ret);
+            "Failed to set configuration (composite setup)");
         return FALSE;
       }
     }
@@ -372,8 +393,7 @@ gst_decklink_video_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
           attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection,
           &autoDetection);
       if (ret != S_OK) {
-        GST_ERROR_OBJECT (self,
-            "Failed to get attribute (autodetection): 0x%08x", ret);
+        GST_ERROR_OBJECT (self, "Failed to get attribute (autodetection)");
         return FALSE;
       }
       if (autoDetection)
@@ -391,7 +411,7 @@ gst_decklink_video_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
   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: 0x%08x", ret);
+    GST_WARNING_OBJECT (self, "Failed to enable video input");
     return FALSE;
   }
 
@@ -661,6 +681,25 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
   vf->input = self->input->input;
   vf->input->AddRef ();
 
+  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 (!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();
+    }
+  }
+
   GST_BUFFER_TIMESTAMP (*buffer) = f->capture_time;
   GST_BUFFER_DURATION (*buffer) = f->capture_duration;
   gst_buffer_add_video_time_code_meta (*buffer, f->tc);
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;
