plugins: add built-in video parsers as "vaapiparse" element.
authorGwenole Beauchesne <gwenole.beauchesne@intel.com>
Mon, 28 Apr 2014 15:44:03 +0000 (17:44 +0200)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Fri, 13 Jun 2014 15:17:07 +0000 (17:17 +0200)
The built-in video parsers elements are built into a single DSO named
libgstvaapi_parse.so. The various video parsers could be accessed as
vaapiparse_CODEC.

For now, this only includes a modified version of h264parse so that to
support H.264 MVC encoded streams.

12 files changed:
Makefile.am
configure.ac
ext/Makefile.am
gst/vaapi/Makefile.am
gst/vaapi/gstvaapiparse.c [new file with mode: 0644]
gst/vaapi/gstvaapiparse.h [new file with mode: 0644]
patches/Makefile.am [new file with mode: 0644]
patches/videoparsers/0001-plugins-compile-the-built-in-video-parsers-as-vaapip.patch [new file with mode: 0644]
patches/videoparsers/0002-h264parse-fix-build-with-GStreamer-1.2.patch [new file with mode: 0644]
patches/videoparsers/0003-h264parse-add-initial-support-for-MVC-NAL-units.patch [new file with mode: 0644]
patches/videoparsers/Makefile.am [new file with mode: 0644]
patches/videoparsers/series.frag [new file with mode: 0644]

index 1d08eba..b333201 100644 (file)
@@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 
 AUTOMAKE_OPTIONS = foreign
 
-SUBDIRS = debian.upstream ext gst-libs gst pkgconfig tests docs
+SUBDIRS = debian.upstream ext gst-libs gst pkgconfig tests docs patches
 
 # Extra clean files so that maintainer-clean removes *everything*
 MAINTAINERCLEANFILES = \
index 8bcea9f..e383de4 100644 (file)
@@ -105,6 +105,11 @@ dnl Initialize libtool
 LT_PREREQ([2.2])
 LT_INIT
 
+AC_ARG_ENABLE(builtin_videoparsers,
+    AS_HELP_STRING([--enable-builtin-videoparsers],
+                   [enable built-in videoparsers @<:@default=yes@:>@]),
+    [], [enable_builtin_videoparsers="yes"])
+
 AC_ARG_ENABLE(builtin_codecparsers,
     AS_HELP_STRING([--enable-builtin-codecparsers],
                    [enable built-in codecparsers @<:@default=yes@:>@]),
@@ -449,6 +454,10 @@ AM_CONDITIONAL([USE_LOCAL_CODEC_PARSERS_VP8],
     [test "$ac_cv_have_gst_vp8_parser" != "yes"])
 AM_CONDITIONAL([USE_BUILTIN_LIBVPX], [test "$enable_builtin_libvpx" = "yes"])
 
+dnl ... video parsers
+AM_CONDITIONAL([USE_LOCAL_VIDEO_PARSERS],
+    [test "$enable_builtin_videoparsers" = "yes"])
+
 case $GST_API_VERSION in
 0.10)   lt_bias=gst0_vaapi_lt_current_bias;;
 1.0)    lt_bias=gst1_vaapi_lt_current_bias;;
@@ -868,6 +877,8 @@ debian.upstream/libgstvaapi-x11.install.in
     gst-libs/gst/video/Makefile
     gst/Makefile
     gst/vaapi/Makefile
+    patches/Makefile
+    patches/videoparsers/Makefile
     pkgconfig/Makefile
     pkgconfig/gstreamer-vaapi-$GST_PKG_VERSION.pc:\
 pkgconfig/gstreamer-vaapi.pc.in
index 7aea0dd..9915e4a 100644 (file)
@@ -37,6 +37,21 @@ codecparsers_source_h = \
 
 EXTRA_DIST += $(codecparsers_source_h:%.h=$(codecparsers_srcdir)/%.h)
 
+videoparsers_srcdir = \
+       $(top_srcdir)/ext/codecparsers/gst/videoparsers
+
+videoparsers_source_c = \
+       gsth264parse.c          \
+       $(NULL)
+
+EXTRA_DIST += $(videoparsers_source_c:%.c=$(videoparsers_srcdir)/%.c)
+
+videoparsers_source_h = \
+       gsth264parse.h          \
+       $(NULL)
+
+EXTRA_DIST += $(videoparsers_source_h:%.h=$(videoparsers_srcdir)/%.h)
+
 videoutils_srcdir = \
        $(top_srcdir)/ext/videoutils/gst-libs/gst/video
 
index 5f0c659..a52bc2f 100644 (file)
@@ -163,6 +163,73 @@ libgstvaapi_la_LIBADD =    \
 libgstvaapi_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstvaapi_la_LIBTOOLFLAGS = --tag=disable-static
 
+BUILT_SOURCES =
+
+if USE_LOCAL_VIDEO_PARSERS
+plugin_LTLIBRARIES += libgstvaapi_parse.la
+
+libgstvaapi_parse_gen_source_c = \
+       gsth264parse.c          \
+       $(NULL)
+
+libgstvaapi_parse_gen_source_h = \
+       gsth264parse.h          \
+       $(NULL)
+
+libgstvaapi_parse_gen_sources = \
+       $(libgstvaapi_parse_gen_source_c) \
+       $(libgstvaapi_parse_gen_source_h) \
+       $(NUL)
+
+libgstvaapi_parse_source_c = gstvaapiparse.c $(libgstvaapi_parse_gen_source_c)
+libgstvaapi_parse_source_h = gstvaapiparse.h $(libgstvaapi_parse_gen_source_h)
+
+libgstvaapi_parse_la_SOURCES           = $(libgstvaapi_parse_source_c)
+noinst_HEADERS                        += $(libgstvaapi_parse_source_h)
+
+libgstvaapi_parse_la_CFLAGS = \
+       -DGST_USE_UNSTABLE_API                  \
+       -I$(top_srcdir)/gst-libs                \
+       -I$(top_builddir)/gst-libs              \
+       $(GST_CFLAGS)                           \
+       $(GST_BASE_CFLAGS)                      \
+       $(GST_PLUGINS_BASE_CFLAGS)              \
+       $(GST_VIDEO_CFLAGS)
+
+libgstvaapi_parse_la_LIBADD = \
+       $(top_builddir)/gst-libs/gst/codecparsers/libgstvaapi-codecparsers.la \
+       $(GST_LIBS)                             \
+       $(GST_BASE_LIBS)                        \
+       $(GST_PLUGINS_BASE_LIBS)                \
+       $(GST_VIDEO_LIBS) -lgstpbutils-$(GST_PKG_VERSION)
+
+libgstvaapi_parse_la_LDFLAGS           = $(GST_PLUGIN_LDFLAGS)
+libgstvaapi_parse_la_LIBTOOLFLAGS      = --tag=disable-static
+
+videoparsers_sources_dir = \
+       $(top_srcdir)/ext/codecparsers/gst/videoparsers
+videoparsers_patches_dir = \
+       $(top_srcdir)/patches/videoparsers
+include $(videoparsers_patches_dir)/series.frag
+videoparsers_patches = \
+       $(videoparsers_patches_base:%=$(top_srcdir)/patches/videoparsers/%)
+
+videoparsers.prepare.stamp: $(videoparsers_patches)
+       @for f in $(libgstvaapi_parse_gen_sources); do  \
+         cp -f $(videoparsers_sources_dir)/$$f $$f;    \
+       done
+       @for f in $(videoparsers_patches); do           \
+         patch -p3 < $$f;                              \
+       done
+       @touch $@
+
+BUILT_SOURCES += videoparsers.prepare.stamp
+endif
+
+CLEANFILES = \
+       videoparsers.prepare.stamp      \
+       $(libgstvaapi_parse_gen_sources)
+
 EXTRA_DIST = \
        $(libgstvaapi_enc_source_c)     \
        $(libgstvaapi_enc_source_h)     \
@@ -176,6 +243,8 @@ EXTRA_DIST = \
        $(libgstvaapi_1_0p_source_h)    \
        $(libgstvaapi_0_10_source_c)    \
        $(libgstvaapi_0_10_source_h)    \
+       $(libgstvaapi_parse_source_c)   \
+       $(libgstvaapi_parse_source_h)   \
        $(NULL)
 
 # Extra clean files so that maintainer-clean removes *everything*
diff --git a/gst/vaapi/gstvaapiparse.c b/gst/vaapi/gstvaapiparse.c
new file mode 100644 (file)
index 0000000..3b1e9fc
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  gstvaapiparse.c - Recent enough GStreamer video parsers
+ *
+ *  Copyright (C) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+ *  Copyright (C) 2009 Tim-Philipp Müller <tim centricular net>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+#include "gst/vaapi/sysdeps.h"
+#include <gst/gst.h>
+#include "gstvaapiparse.h"
+#include "gsth264parse.h"
+
+#define PLUGIN_NAME     "vaapiparse"
+#define PLUGIN_DESC     "VA-API based elements"
+#define PLUGIN_LICENSE  "LGPL"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  gboolean failure = FALSE;
+
+  failure |= !gst_element_register (plugin, "vaapiparse_h264",
+      GST_RANK_PRIMARY + 2, GST_TYPE_H264_PARSE);
+
+  return !failure;
+}
+
+#if GST_CHECK_VERSION(1,0,0)
+/* XXX: use PLUGIN_NAME when GST_PLUGIN_DEFINE is fixed to use
+   G_STRINGIFY() for name argument, instead of plain #name */
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+    vaapiparse, PLUGIN_DESC, plugin_init,
+    PACKAGE_VERSION, PLUGIN_LICENSE, PACKAGE, PACKAGE_BUGREPORT)
+#else
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+    PLUGIN_NAME, PLUGIN_DESC, plugin_init,
+    PACKAGE_VERSION, PLUGIN_LICENSE, PACKAGE, PACKAGE_BUGREPORT)
+#endif
diff --git a/gst/vaapi/gstvaapiparse.h b/gst/vaapi/gstvaapiparse.h
new file mode 100644 (file)
index 0000000..03e74d9
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  gstvaapiparse.h - Recent enough GStreamer video parsers
+ *
+ *  Copyright (C) 2014 Intel Corporation
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+#ifndef GST_VAAPI_PARSE_H
+#define GST_VAAPI_PARSE_H
+
+/* vaapiparse_h264 */
+#define _GstH264Parse                   _GstVaapiH264Parse
+#define GstH264Parse                    GstVaapiH264Parse
+#define _GstH264ParseClass              _GstVaapiH264ParseClass
+#define GstH264ParseClass               GstVaapiH264ParseClass
+#define gst_h264_parse                  gst_vaapi_h264_parse
+#define gst_h264_parse_init             gst_vaapi_h264_parse_init
+#define gst_h264_parse_class_init       gst_vaapi_h264_parse_class_init
+#define gst_h264_parse_parent_class     gst_vaapi_h264_parse_parent_class
+#define gst_h264_parse_get_type         gst_vaapi_h264_parse_get_type
+
+#endif /* GST_VAAPI_PARSE_H */
diff --git a/patches/Makefile.am b/patches/Makefile.am
new file mode 100644 (file)
index 0000000..0512fca
--- /dev/null
@@ -0,0 +1,4 @@
+SUBDIRS = videoparsers
+
+# Extra clean files so that maintainer-clean removes *everything*
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/patches/videoparsers/0001-plugins-compile-the-built-in-video-parsers-as-vaapip.patch b/patches/videoparsers/0001-plugins-compile-the-built-in-video-parsers-as-vaapip.patch
new file mode 100644 (file)
index 0000000..978cf31
--- /dev/null
@@ -0,0 +1,41 @@
+From c78b4c043dc41f071bdf170ecb001dc1aef8f5f6 Mon Sep 17 00:00:00 2001
+From: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
+Date: Mon, 28 Apr 2014 17:44:03 +0200
+Subject: [PATCH 1/3] plugins: compile the built-in video parsers as
+ "vaapiparse" element.
+
+The built-in video parsers elements are built into a single DSO named
+libgstvaapi_parse.so. The various video parsers could be accessed as
+vaapiparse_CODEC.
+---
+ configure.ac              |    9 ++++++++
+ gst/vaapi/Makefile.am     |   35 ++++++++++++++++++++++++++++++
+ gst/vaapi/gsth264parse.c  |    4 +++-
+ gst/vaapi/gstvaapiparse.c |   53 +++++++++++++++++++++++++++++++++++++++++++++
+ gst/vaapi/gstvaapiparse.h |   36 ++++++++++++++++++++++++++++++
+ 5 files changed, 136 insertions(+), 1 deletion(-)
+ create mode 100644 gst/vaapi/gstvaapiparse.c
+ create mode 100644 gst/vaapi/gstvaapiparse.h
+
+diff --git a/gst/vaapi/gsth264parse.c b/gst/vaapi/gsth264parse.c
+index 9105d7f..4246b6e 100644
+--- a/gst/vaapi/gsth264parse.c
++++ b/gst/vaapi/gsth264parse.c
+@@ -26,6 +26,7 @@
+ #  include "config.h"
+ #endif
++#include "gstvaapiparse.h"
+ #include <gst/base/base.h>
+ #include <gst/pbutils/pbutils.h>
+ #include <gst/video/video.h>
+@@ -105,7 +106,8 @@ gst_h264_parse_class_init (GstH264ParseClass * klass)
+   GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
+   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+-  GST_DEBUG_CATEGORY_INIT (h264_parse_debug, "h264parse", 0, "h264 parser");
++  GST_DEBUG_CATEGORY_INIT (h264_parse_debug, "vaapiparse_h264", 0,
++      "h264 parser");
+   gobject_class->finalize = gst_h264_parse_finalize;
+   gobject_class->set_property = gst_h264_parse_set_property;
diff --git a/patches/videoparsers/0002-h264parse-fix-build-with-GStreamer-1.2.patch b/patches/videoparsers/0002-h264parse-fix-build-with-GStreamer-1.2.patch
new file mode 100644 (file)
index 0000000..29f979e
--- /dev/null
@@ -0,0 +1,26 @@
+From 4a209977a61d156433dddbf21bddc0b1e89098e1 Mon Sep 17 00:00:00 2001
+From: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
+Date: Mon, 28 Apr 2014 17:17:04 +0200
+Subject: [PATCH 2/3] h264parse: fix build with GStreamer 1.2.
+
+---
+ gst/vaapi/gsth264parse.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/gst/vaapi/gsth264parse.c b/gst/vaapi/gsth264parse.c
+index 4246b6e..7c970ee 100644
+--- a/gst/vaapi/gsth264parse.c
++++ b/gst/vaapi/gsth264parse.c
+@@ -148,7 +148,9 @@ gst_h264_parse_init (GstH264Parse * h264parse)
+ {
+   h264parse->frame_out = gst_adapter_new ();
+   gst_base_parse_set_pts_interpolation (GST_BASE_PARSE (h264parse), FALSE);
++#if GST_CHECK_VERSION(1,3,0)
+   GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (h264parse));
++#endif
+ }
+-- 
+1.7.9.5
+
diff --git a/patches/videoparsers/0003-h264parse-add-initial-support-for-MVC-NAL-units.patch b/patches/videoparsers/0003-h264parse-add-initial-support-for-MVC-NAL-units.patch
new file mode 100644 (file)
index 0000000..9316510
--- /dev/null
@@ -0,0 +1,99 @@
+From 3ef58fae7a578f72c4607b57434ed54a0ee9ee1d Mon Sep 17 00:00:00 2001
+From: Sreerenj Balachandran <sreerenj.balachandran@intel.com>
+Date: Tue, 19 Mar 2013 14:23:00 +0200
+Subject: [PATCH 3/3] h264parse: add initial support for MVC NAL units.
+
+Initial support for MVC NAL units. It is only needed to propagate the
+complete set of NAL units downstream at this time.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=696135
+
+Signed-off-by: Sreerenj Balachandran <sreerenj.balachandran@intel.com>
+Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
+---
+ gst/vaapi/gsth264parse.c |   20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+diff --git a/gst/vaapi/gsth264parse.c b/gst/vaapi/gsth264parse.c
+index 7c970ee..e9b9481 100644
+--- a/gst/vaapi/gsth264parse.c
++++ b/gst/vaapi/gsth264parse.c
+@@ -415,7 +415,7 @@ gst_h264_parser_store_nal (GstH264Parse * h264parse, guint id,
+   GstBuffer *buf, **store;
+   guint size = nalu->size, store_size;
+-  if (naltype == GST_H264_NAL_SPS) {
++  if (naltype == GST_H264_NAL_SPS || naltype == GST_H264_NAL_SUBSET_SPS) {
+     store_size = GST_H264_MAX_SPS_COUNT;
+     store = h264parse->sps_nals;
+     GST_DEBUG_OBJECT (h264parse, "storing sps %u", id);
+@@ -534,6 +534,7 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu)
+   switch (nal_type) {
+     case GST_H264_NAL_SPS:
++    case GST_H264_NAL_SUBSET_SPS:
+       pres = gst_h264_parser_parse_sps (nalparser, nalu, &sps, TRUE);
+       /* arranged for a fallback sps.id, so use that one and only warn */
+       if (pres != GST_H264_PARSER_OK)
+@@ -594,14 +595,17 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu)
+     case GST_H264_NAL_SLICE_DPB:
+     case GST_H264_NAL_SLICE_DPC:
+     case GST_H264_NAL_SLICE_IDR:
++    case GST_H264_NAL_SLICE_EXT:
+       /* don't need to parse the whole slice (header) here */
+-      if (*(nalu->data + nalu->offset + 1) & 0x80) {
++      if (*(nalu->data + nalu->offset + nalu->header_bytes) & 0x80) {
+         /* means first_mb_in_slice == 0 */
+         /* real frame data */
+         GST_DEBUG_OBJECT (h264parse, "first_mb_in_slice = 0");
+         h264parse->frame_start = TRUE;
+       }
+       GST_DEBUG_OBJECT (h264parse, "frame start: %i", h264parse->frame_start);
++      if (nal_type == GST_H264_NAL_SLICE_EXT && !GST_H264_IS_MVC_NALU (nalu))
++        break;
+       {
+         GstH264SliceHdr slice;
+@@ -677,7 +681,8 @@ gst_h264_parse_collect_nal (GstH264Parse * h264parse, const guint8 * data,
+   /* coded slice NAL starts a picture,
+    * i.e. other types become aggregated in front of it */
+   h264parse->picture_start |= (nal_type == GST_H264_NAL_SLICE ||
+-      nal_type == GST_H264_NAL_SLICE_DPA || nal_type == GST_H264_NAL_SLICE_IDR);
++      nal_type == GST_H264_NAL_SLICE_DPA || nal_type == GST_H264_NAL_SLICE_IDR
++      || nal_type == GST_H264_NAL_SLICE_EXT);
+   /* consider a coded slices (IDR or not) to start a picture,
+    * (so ending the previous one) if first_mb_in_slice == 0
+@@ -687,16 +692,18 @@ gst_h264_parse_collect_nal (GstH264Parse * h264parse, const guint8 * data,
+    * and also works with broken frame_num in NAL
+    * (where spec-wise would fail) */
+   nal_type = nnalu.type;
+-  complete = h264parse->picture_start && (nal_type >= GST_H264_NAL_SEI &&
+-      nal_type <= GST_H264_NAL_AU_DELIMITER);
++  complete = h264parse->picture_start && ((nal_type >= GST_H264_NAL_SEI &&
++          nal_type <= GST_H264_NAL_AU_DELIMITER) ||
++      (nal_type >= 14 && nal_type <= 18));
+   GST_LOG_OBJECT (h264parse, "next nal type: %d %s", nal_type,
+       _nal_name (nal_type));
+   complete |= h264parse->picture_start && (nal_type == GST_H264_NAL_SLICE
+       || nal_type == GST_H264_NAL_SLICE_DPA
++      || nal_type == GST_H264_NAL_SLICE_EXT
+       || nal_type == GST_H264_NAL_SLICE_IDR) &&
+       /* first_mb_in_slice == 0 considered start of frame */
+-      (nnalu.data[nnalu.offset + 1] & 0x80);
++      (nnalu.data[nnalu.offset + nnalu.header_bytes] & 0x80);
+   GST_LOG_OBJECT (h264parse, "au complete: %d", complete);
+@@ -960,6 +967,7 @@ gst_h264_parse_handle_frame (GstBaseParse * parse,
+     }
+     if (nalu.type == GST_H264_NAL_SPS ||
++        nalu.type == GST_H264_NAL_SUBSET_SPS ||
+         nalu.type == GST_H264_NAL_PPS ||
+         (h264parse->have_sps && h264parse->have_pps)) {
+       gst_h264_parse_process_nal (h264parse, &nalu);
+-- 
+1.7.9.5
+
diff --git a/patches/videoparsers/Makefile.am b/patches/videoparsers/Makefile.am
new file mode 100644 (file)
index 0000000..ec3b761
--- /dev/null
@@ -0,0 +1,6 @@
+include series.frag
+
+EXTRA_DIST = series.frag $(videoparsers_patches_base)
+
+# Extra clean files so that maintainer-clean removes *everything*
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/patches/videoparsers/series.frag b/patches/videoparsers/series.frag
new file mode 100644 (file)
index 0000000..13612c5
--- /dev/null
@@ -0,0 +1,7 @@
+# sources.frag - Generated list of patches for videoparsers (-*- makefile -*-)
+
+videoparsers_patches_base = \
+       0001-plugins-compile-the-built-in-video-parsers-as-vaapip.patch \
+       0002-h264parse-fix-build-with-GStreamer-1.2.patch               \
+       0003-h264parse-add-initial-support-for-MVC-NAL-units.patch      \
+       $(NULL)