From ef2cb71d5fcad4dc3cbc239368a7bb9c30206761 Mon Sep 17 00:00:00 2001 From: Eric Buehl Date: Wed, 19 Mar 2008 18:14:17 +0000 Subject: Add an OFA element, the successor of MusicBrainz TRM fingerprinting. Original commit message from CVS: Based on a patch by: Eric Buehl * configure.ac: * ext/ofa/Makefile.am: * ext/ofa/gstofa.c: (gst_ofa_base_init), (gst_ofa_finalize), (gst_ofa_class_init), (create_fingerprint), (gst_ofa_event), (gst_ofa_init), (gst_ofa_transform_ip), (gst_ofa_get_property), (plugin_init): * ext/ofa/gstofa.h: Add an OFA element, the successor of MusicBrainz TRM fingerprinting. Fixes bug #351309. --- ext/ofa/Makefile.am | 18 ++++ ext/ofa/gstofa.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ext/ofa/gstofa.h | 79 +++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 ext/ofa/Makefile.am create mode 100644 ext/ofa/gstofa.c create mode 100644 ext/ofa/gstofa.h (limited to 'ext/ofa') diff --git a/ext/ofa/Makefile.am b/ext/ofa/Makefile.am new file mode 100644 index 000000000..346ec8d85 --- /dev/null +++ b/ext/ofa/Makefile.am @@ -0,0 +1,18 @@ +plugin_LTLIBRARIES = libgstofa.la + +libgstofa_la_SOURCES = gstofa.c + +libgstofa_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(OFA_CFLAGS) + +libgstofa_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstaudio-$(GST_MAJORMINOR) \ + $(GST_BASE_LIBS) \ + $(GST_LIBS) \ + $(OFA_LIBS) + +libgstofa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstofa.h diff --git a/ext/ofa/gstofa.c b/ext/ofa/gstofa.c new file mode 100644 index 000000000..cf86048df --- /dev/null +++ b/ext/ofa/gstofa.c @@ -0,0 +1,250 @@ +/* GStreamer + * + * gstofa.c + * + * Copyright (C) 2006 M. Derezynski + * Copyright (C) 2008 Eric Buehl + * Copyright (C) 2008 Sebastian Dröge + * + * 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 St, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "gstofa.h" + +#define PAD_CAPS \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, 2 ], " \ + "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ + "width = (int) { 16 }, " \ + "depth = (int) { 16 }, " \ + "signed = (boolean) true" + +GST_DEBUG_CATEGORY_STATIC (gst_ofa_debug); +#define GST_CAT_DEFAULT gst_ofa_debug + +enum +{ + PROP_0, + PROP_FINGERPRINT, +}; + + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_ofa_debug, "ofa", 0, "ofa element"); + +GST_BOILERPLATE_FULL (GstOFA, gst_ofa, GstAudioFilter, + GST_TYPE_AUDIO_FILTER, _do_init); + +static void gst_ofa_finalize (GObject * object); +static void gst_ofa_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static GstFlowReturn gst_ofa_transform_ip (GstBaseTransform * trans, + GstBuffer * buf); +static gboolean gst_ofa_event (GstBaseTransform * trans, GstEvent * event); + +static void +gst_ofa_base_init (gpointer g_class) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + GstAudioFilterClass *audio_filter_class = (GstAudioFilterClass *) g_class; + GstCaps *caps; + + gst_element_class_set_details_simple (gstelement_class, "OFA", + "MusicIP Fingerprinting element", + "Find a music fingerprint using MusicIP's libofa", + "Milosz Derezynski , Eric Buehl "); + + 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_ofa_finalize (GObject * object) +{ + GstOFA *ofa = GST_OFA (object); + + if (ofa->adapter) { + g_object_unref (ofa->adapter); + ofa->adapter = NULL; + } + + g_free (ofa->fingerprint); + ofa->fingerprint = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_ofa_class_init (GstOFAClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseTransformClass *gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass); + + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ofa_get_property); + + g_object_class_install_property (gobject_class, PROP_FINGERPRINT, + g_param_spec_string ("fingerprint", "Resulting fingerprint", + "Resulting fingerprint", NULL, G_PARAM_READABLE)); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ofa_finalize); + + gstbasetrans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_ofa_transform_ip); + gstbasetrans_class->event = GST_DEBUG_FUNCPTR (gst_ofa_event); + gstbasetrans_class->passthrough_on_same_caps = TRUE; +} + +static void +create_fingerprint (GstOFA * ofa) +{ + GstBuffer *buf; + gint rate = GST_AUDIO_FILTER (ofa)->format.rate; + gint channels = GST_AUDIO_FILTER (ofa)->format.channels; + gint width = GST_AUDIO_FILTER (ofa)->format.width / 8; + gint endianness = + (GST_AUDIO_FILTER (ofa)->format. + bigend) ? OFA_BIG_ENDIAN : OFA_LITTLE_ENDIAN; + GstTagList *tags; + + buf = + gst_adapter_take_buffer (ofa->adapter, + gst_adapter_available (ofa->adapter)); + + ofa->fingerprint = g_strdup (ofa_create_print (GST_BUFFER_DATA (buf), + endianness, + GST_BUFFER_SIZE (buf) / width, rate, (channels == 2) ? 1 : 0)); + + gst_buffer_unref (buf); + + tags = gst_tag_list_new (); + gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, + GST_TAG_OFA_FINGERPRINT, ofa->fingerprint, NULL); + gst_element_found_tags (GST_ELEMENT (ofa), tags); + + ofa->record = FALSE; +} + +static gboolean +gst_ofa_event (GstBaseTransform * trans, GstEvent * event) +{ + GstOFA *ofa = GST_OFA (trans); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_NEWSEGMENT: + gst_adapter_clear (ofa->adapter); + ofa->record = TRUE; + g_free (ofa->fingerprint); + ofa->fingerprint = NULL; + break; + case GST_EVENT_EOS: + /* we got to the end of the stream but never generated a fingerprint + * (probably under 135 seconds) + */ + if (!ofa->fingerprint) + create_fingerprint (ofa); + break; + default: + break; + } + + return TRUE; +} + +static void +gst_ofa_init (GstOFA * ofa, GstOFAClass * g_class) +{ + gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (ofa), TRUE); + + ofa->fingerprint = NULL; + ofa->record = TRUE; + + ofa->adapter = gst_adapter_new (); +} + +static GstFlowReturn +gst_ofa_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstOFA *ofa = GST_OFA (trans); + guint64 nframes; + GstClockTime duration; + gint rate = GST_AUDIO_FILTER (ofa)->format.rate; + gint channels = GST_AUDIO_FILTER (ofa)->format.channels; + gint width = GST_AUDIO_FILTER (ofa)->format.width / 8; + + g_return_val_if_fail (rate > 0 && channels > 0 + && width > 0, GST_FLOW_NOT_NEGOTIATED); + + if (ofa->record) + gst_adapter_push (ofa->adapter, gst_buffer_copy (buf)); + + nframes = + gst_util_uint64_scale (gst_adapter_available (ofa->adapter), 1, + channels * width); + duration = + GST_FRAMES_TO_CLOCK_TIME (gst_adapter_available (ofa->adapter), rate); + + if (duration >= 135 * GST_SECOND && ofa->fingerprint == NULL) + create_fingerprint (ofa); + + return GST_FLOW_OK; +} + +static void +gst_ofa_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstOFA *ofa = GST_OFA (object); + + switch (prop_id) { + case PROP_FINGERPRINT: + g_value_set_string (value, ofa->fingerprint); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean ret; + + ret = gst_element_register (plugin, "ofa", GST_RANK_NONE, GST_TYPE_OFA); + + if (ret) { + /* TODO: get this into core */ + gst_tag_register (GST_TAG_OFA_FINGERPRINT, GST_TAG_FLAG_META, + G_TYPE_STRING, "ofa fingerprint", "OFA fingerprint", NULL); + } + + return ret; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "ofa", + "Calculate MusicIP fingerprint from audio files", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/ofa/gstofa.h b/ext/ofa/gstofa.h new file mode 100644 index 000000000..347a8ca4c --- /dev/null +++ b/ext/ofa/gstofa.h @@ -0,0 +1,79 @@ +/* GStreamer + * + * gstofa.h + * + * Copyright (C) 2006 M. Derezynski + * Copyright (C) 2008 Eric Buehl + * Copyright (C) 2008 Sebastian Dröge + * + * + * 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 St, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_OFA_H__ +#define __GST_OFA_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_OFA \ + (gst_ofa_get_type()) +#define GST_OFA(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OFA,GstOFA)) +#define GST_OFA_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OFA,GstOFAClass)) +#define GST_IS_OFA(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OFA)) +#define GST_IS_OFA_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OFA)) + +#define GST_TAG_OFA_FINGERPRINT "ofa-fingerprint" + +typedef struct _GstOFA GstOFA; +typedef struct _GstOFAClass GstOFAClass; + + +/** + * GstOFA: + * + * Opaque #GstOFA data structure + */ + +struct _GstOFA +{ + GstAudioFilter element; + + /*< private > */ + + GstAdapter *adapter; + char *fingerprint; + gboolean record; +}; + +struct _GstOFAClass +{ + GstAudioFilterClass parent_class; +}; + +GType gst_ofa_get_type (void); + +G_END_DECLS + +#endif /* __GST_OFA_H__ */ -- cgit v1.2.1