diff --git a/configure.ac b/configure.ac index d7b3460c39..7bdd03e94e 100644 --- a/configure.ac +++ b/configure.ac @@ -732,6 +732,16 @@ AG_GST_CHECK_FEATURE(CELT, [celt], celt, [ AC_SUBST(CELT_LIBS) ]) +dnl *** chromaprint *** +translit(dnm, m, l) AM_CONDITIONAL(USE_CHROMAPRINT, true) +AG_GST_CHECK_FEATURE(CHROMAPRINT, [chromaprint], chromaprint, [ + PKG_CHECK_MODULES(CHROMAPRINT, libchromaprint, HAVE_CHROMAPRINT="yes", [ + HAVE_CHROMAPRINT="no" + ]) + AC_SUBST(CHROMAPRINT_CFLAGS) + AC_SUBST(CHROMAPRINT_LIBS) +]) + dnl *** Cog *** translit(dnm, m, l) AM_CONDITIONAL(USE_COG, true) AG_GST_CHECK_FEATURE(COG, [Cog plugin], cog, [ @@ -1777,6 +1787,7 @@ AM_CONDITIONAL(USE_APEXSINK, false) AM_CONDITIONAL(USE_BZ2, false) AM_CONDITIONAL(USE_CDAUDIO, false) AM_CONDITIONAL(USE_CELT, false) +AM_CONDITIONAL(USE_CHROMAPRINT, false) AM_CONDITIONAL(USE_COG, false) AM_CONDITIONAL(USE_CURL, false) AM_CONDITIONAL(USE_DC1394, false) @@ -2034,6 +2045,7 @@ ext/apexsink/Makefile ext/bz2/Makefile ext/cdaudio/Makefile ext/celt/Makefile +ext/chromaprint/Makefile ext/cog/Makefile ext/curl/Makefile ext/dc1394/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index dc62386cab..a1636f6904 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -58,6 +58,12 @@ else CELT_DIR= endif +if USE_CHROMAPRINT +CHROMAPRINT_DIR=chromaprint +else +CHROMAPRINT_DIR= +endif + if USE_COG COG_DIR=cog else @@ -397,6 +403,7 @@ SUBDIRS=\ $(BZ2_DIR) \ $(CDAUDIO_DIR) \ $(CELT_DIR) \ + $(CHROMAPRINT_DIR) \ $(COG_DIR) \ $(CURL_DIR) \ $(DC1394_DIR) \ @@ -456,6 +463,7 @@ DIST_SUBDIRS = \ bz2 \ cdaudio \ celt \ + chromaprint \ cog \ curl \ dc1394 \ diff --git a/ext/chromaprint/Makefile.am b/ext/chromaprint/Makefile.am new file mode 100644 index 0000000000..115d8c2988 --- /dev/null +++ b/ext/chromaprint/Makefile.am @@ -0,0 +1,14 @@ +plugin_LTLIBRARIES = libgstchromaprint.la + +libgstchromaprint_la_SOURCES = gstchromaprint.c gstchromaprint.h + +libgstchromaprint_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ + $(CHROMAPRINT_CFLAGS) +libgstchromaprint_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(GST_LIBS) \ + $(CHROMAPRINT_LIBS) +libgstchromaprint_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstchromaprint_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstchromaprint.h diff --git a/ext/chromaprint/gstchromaprint.c b/ext/chromaprint/gstchromaprint.c new file mode 100644 index 0000000000..2d6f9e7c48 --- /dev/null +++ b/ext/chromaprint/gstchromaprint.c @@ -0,0 +1,322 @@ +/* vim:si:et:sw=2:sts=2:ts=8 + * + * GStreamer + * + * gstchromaprint.c + * + * Copyright (C) 2006 M. Derezynski + * Copyright (C) 2008 Eric Buehl + * Copyright (C) 2008 Sebastian Dröge + * Copyright (C) 2011 Lukáš Lalinský + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-chromaprint + * + * FIXME:Describe chromaprint here. + * + * + * Example launch line + * |[ + * gst-launch -v -m fakesrc ! chromaprint ! fakesink silent=TRUE + * filesrc location= ! decodebin ! audioconvert ! chromaprint ! fakesink sync=0 silent=TRUE + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "gstchromaprint.h" + +#define DEFAULT_MAX_DURATION 120 + +#define PAD_CAPS \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, 2 ], " \ + "endianness = (int) { BYTE_ORDER }, " \ + "width = (int) { 16 }, " \ + "depth = (int) { 16 }, " \ + "signed = (boolean) true" + +GST_DEBUG_CATEGORY_STATIC (gst_chromaprint_debug); +#define GST_CAT_DEFAULT gst_chromaprint_debug + +enum +{ + PROP_0, + PROP_FINGERPRINT, + PROP_MAX_DURATION +}; + + +GST_BOILERPLATE (GstChromaprint, gst_chromaprint, GstElement, + GST_TYPE_AUDIO_FILTER); + +static void gst_chromaprint_finalize (GObject * object); +static void gst_chromaprint_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_chromaprint_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static GstFlowReturn gst_chromaprint_transform_ip (GstBaseTransform * trans, + GstBuffer * buf); +static gboolean gst_chromaprint_event (GstBaseTransform * trans, + GstEvent * event); + +/* GObject vmethod implementations */ + +static void +gst_chromaprint_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstAudioFilterClass *audio_filter_class = (GstAudioFilterClass *) g_class; + GstCaps *caps; + + gst_element_class_set_details_simple (element_class, + "Chromaprint", + "Chromaprint fingerprinting element", + "Find an audio fingerprint using the Chromaprint library", + "Lukáš Lalinský "); + + caps = gst_caps_from_string (PAD_CAPS); + gst_audio_filter_class_add_pad_templates (audio_filter_class, caps); + gst_caps_unref (caps); +} + +static void +gst_chromaprint_class_init (GstChromaprintClass * klass) +{ + GObjectClass *gobject_class; + GstBaseTransformClass *gstbasetrans_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass); + + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_chromaprint_set_property); + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_chromaprint_get_property); + + g_object_class_install_property (gobject_class, PROP_FINGERPRINT, + g_param_spec_string ("fingerprint", "Resulting fingerprint", + "Resulting fingerprint", NULL, G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, PROP_MAX_DURATION, + g_param_spec_uint ("duration", "Duration limit", + "Number of seconds of audio to use for fingerpriting", + 0, G_MAXUINT, DEFAULT_MAX_DURATION, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_chromaprint_finalize); + + gstbasetrans_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_chromaprint_transform_ip); + gstbasetrans_class->event = GST_DEBUG_FUNCPTR (gst_chromaprint_event); + gstbasetrans_class->passthrough_on_same_caps = TRUE; +} + +static void +gst_chromaprint_reset (GstChromaprint * chromaprint) +{ + if (chromaprint->fingerprint) { + chromaprint_dealloc (chromaprint->fingerprint); + chromaprint->fingerprint = NULL; + } + + chromaprint->nsamples = 0; + chromaprint->duration = 0; + chromaprint->record = TRUE; +} + +static void +gst_chromaprint_create_fingerprint (GstChromaprint * chromaprint) +{ + GstTagList *tags; + + if (chromaprint->duration <= 3) + return; + + GST_DEBUG ("Generating fingerprint based on %d seconds of audio", + chromaprint->duration); + chromaprint_finish (chromaprint->context); + chromaprint_get_fingerprint (chromaprint->context, &chromaprint->fingerprint); + chromaprint->record = FALSE; + + tags = gst_tag_list_new (); + gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, + GST_TAG_CHROMAPRINT_FINGERPRINT, chromaprint->fingerprint, NULL); + gst_element_found_tags (GST_ELEMENT (chromaprint), tags); +} + +static void +gst_chromaprint_init (GstChromaprint * chromaprint, + GstChromaprintClass * gclass) +{ + gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (chromaprint), TRUE); + + chromaprint->context = chromaprint_new (CHROMAPRINT_ALGORITHM_DEFAULT); + chromaprint->fingerprint = NULL; + chromaprint->max_duration = DEFAULT_MAX_DURATION; + gst_chromaprint_reset (chromaprint); +} + +static void +gst_chromaprint_finalize (GObject * object) +{ + GstChromaprint *chromaprint = GST_CHROMAPRINT (object); + + chromaprint->record = FALSE; + + if (chromaprint->context) { + chromaprint_free (chromaprint->context); + chromaprint->context = NULL; + } + + if (chromaprint->fingerprint) { + chromaprint_dealloc (chromaprint->fingerprint); + chromaprint->fingerprint = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GstFlowReturn +gst_chromaprint_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstChromaprint *chromaprint = GST_CHROMAPRINT (trans); + guint nsamples; + gint rate = GST_AUDIO_FILTER (chromaprint)->format.rate; + gint channels = GST_AUDIO_FILTER (chromaprint)->format.channels; + + g_return_val_if_fail (rate > 0 && channels > 0, GST_FLOW_NOT_NEGOTIATED); + + if (!chromaprint->record) + return GST_FLOW_OK; + + nsamples = GST_BUFFER_SIZE (buf) / (channels * 2); + if (!nsamples) + return GST_FLOW_OK; + + if (!chromaprint->nsamples) { + chromaprint_start (chromaprint->context, rate, channels); + } + chromaprint->nsamples += nsamples; + chromaprint->duration = chromaprint->nsamples / rate; + + chromaprint_feed (chromaprint->context, GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf) / 2); + + if (chromaprint->duration >= chromaprint->max_duration + && !chromaprint->fingerprint) { + gst_chromaprint_create_fingerprint (chromaprint); + } + + return GST_FLOW_OK; +} + +static gboolean +gst_chromaprint_event (GstBaseTransform * trans, GstEvent * event) +{ + GstChromaprint *chromaprint = GST_CHROMAPRINT (trans); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_NEWSEGMENT: + GST_DEBUG ("Got %s event, clearing buffer", GST_EVENT_TYPE_NAME (event)); + gst_chromaprint_reset (chromaprint); + break; + case GST_EVENT_EOS: + if (!chromaprint->fingerprint) { + gst_chromaprint_create_fingerprint (chromaprint); + } + break; + default: + break; + } + + return TRUE; +} + +static void +gst_chromaprint_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstChromaprint *chromaprint = GST_CHROMAPRINT (object); + + switch (prop_id) { + case PROP_MAX_DURATION: + chromaprint->max_duration = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_chromaprint_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstChromaprint *chromaprint = GST_CHROMAPRINT (object); + + switch (prop_id) { + case PROP_FINGERPRINT: + g_value_set_string (value, chromaprint->fingerprint); + break; + case PROP_MAX_DURATION: + g_value_set_uint (value, chromaprint->max_duration); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean ret; + + GST_DEBUG_CATEGORY_INIT (gst_chromaprint_debug, "chromaprint", + 0, "chromaprint element"); + + GST_DEBUG ("libchromaprint %s", chromaprint_get_version ()); + + ret = gst_element_register (plugin, "chromaprint", GST_RANK_NONE, + GST_TYPE_CHROMAPRINT); + + if (ret) { + gst_tag_register (GST_TAG_CHROMAPRINT_FINGERPRINT, GST_TAG_FLAG_META, + G_TYPE_STRING, "chromaprint fingerprint", "Chromaprint fingerprint", + NULL); + } + + return ret; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "chromaprint", + "Calculate Chromaprint fingerprint from audio files", + plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/") diff --git a/ext/chromaprint/gstchromaprint.h b/ext/chromaprint/gstchromaprint.h new file mode 100644 index 0000000000..892bacc870 --- /dev/null +++ b/ext/chromaprint/gstchromaprint.h @@ -0,0 +1,84 @@ +/* vim:si:et:sw=2:sts=2:ts=8 + * + * GStreamer + * + * gstchromaprint.h + * + * Copyright (C) 2006 M. Derezynski + * Copyright (C) 2008 Eric Buehl + * Copyright (C) 2008 Sebastian Dröge + * Copyright (C) 2011 Lukáš Lalinský <> + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_CHROMAPRINT_H__ +#define __GST_CHROMAPRINT_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_CHROMAPRINT \ + (gst_chromaprint_get_type()) +#define GST_CHROMAPRINT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CHROMAPRINT,GstChromaprint)) +#define GST_CHROMAPRINT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CHROMAPRINT,GstChromaprintClass)) +#define GST_IS_CHROMAPRINT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CHROMAPRINT)) +#define GST_IS_CHROMAPRINT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CHROMAPRINT)) + +#define GST_TAG_CHROMAPRINT_FINGERPRINT "chromaprint-fingerprint" + +typedef struct _GstChromaprint GstChromaprint; +typedef struct _GstChromaprintClass GstChromaprintClass; + +/** + * GstChromaprint: + * + * Opaque #GstChromaprint data structure + */ + +struct _GstChromaprint +{ + GstAudioFilter element; + + /*< private > */ + + ChromaprintContext *context; + char *fingerprint; + gboolean record; + guint64 nsamples; + guint duration; + guint max_duration; +}; + +struct _GstChromaprintClass +{ + GstAudioFilterClass parent_class; +}; + +GType gst_chromaprint_get_type (void); + +G_END_DECLS + +#endif /* __GST_CHROMAPRINT_H__ */