[tracker-miners/wip/jfelder/audio-writeback] writeback: Add GStreamer based writeback module
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker-miners/wip/jfelder/audio-writeback] writeback: Add GStreamer based writeback module
- Date: Tue, 12 Feb 2019 13:59:48 +0000 (UTC)
commit dd4c21bd4f7420f3c78df154aa857dd72be31a03
Author: Saiful B. Khan <saifulbkhan gmail com>
Date: Tue Feb 12 14:32:38 2019 +0100
writeback: Add GStreamer based writeback module
GStreamer's tag setting API allows saving a more inclusive set
of tags, compared to taglib, but is limited to a few formats based
on the availability of relevant muxers.
https://bugzilla.gnome.org/show_bug.cgi?id=792202
meson.build | 2 +
src/tracker-writeback/meson.build | 4 +
.../tracker-writeback-gstreamer.c | 913 +++++++++++++++++++++
3 files changed, 919 insertions(+)
---
diff --git a/meson.build b/meson.build
index ef48e77a6..21309e75e 100644
--- a/meson.build
+++ b/meson.build
@@ -70,6 +70,7 @@ glib = dependency('glib-2.0', version: '>' + glib_required)
gmodule = dependency('gmodule-2.0', version: '>' + glib_required)
gobject = dependency('gobject-2.0', version: '>' + glib_required)
gstreamer = dependency('gstreamer-1.0', required: false)
+gstreamer_audio = dependency('gstreamer-audio-1.0', required: false)
gstreamer_pbutils = dependency('gstreamer-pbutils-1.0', required: false)
gstreamer_tag = dependency('gstreamer-tag-1.0', required: false)
gupnp_dlna = dependency('gupnp-dlna-2.0', version: '> 0.9.4', required: false)
@@ -455,6 +456,7 @@ if have_tracker_writeback
summary += [
'\nWriteback Formats:',
' Audio files using Taglib: ' + taglib.found().to_string(),
+ ' Audio files using GStreamer: ' + (gstreamer.found() and gstreamer_tag.found() and
gstreamer_audio.found()).to_string(),
' XMP: ' + exempi.found().to_string(),
]
endif
diff --git a/src/tracker-writeback/meson.build b/src/tracker-writeback/meson.build
index ad3d86e0d..4c4c4629c 100644
--- a/src/tracker-writeback/meson.build
+++ b/src/tracker-writeback/meson.build
@@ -10,6 +10,10 @@ if taglib.found()
modules += [['writeback-taglib', 'tracker-writeback-taglib.c', [tracker_miner, tracker_sparql,
tracker_miners_common_dep, taglib]]]
endif
+if gstreamer.found() and gstreamer_tag.found() and gstreamer_audio.found()
+ modules += [['writeback-gstreamer', 'tracker-writeback-gstreamer.c', [tracker_miner, tracker_sparql,
tracker_miners_common_dep, gstreamer, gstreamer_tag]]]
+endif
+
if exempi.found()
modules += [['writeback-xmp', 'tracker-writeback-xmp.c', [tracker_miner, tracker_sparql,
tracker_miners_common_dep, exempi]]]
endif
diff --git a/src/tracker-writeback/tracker-writeback-gstreamer.c
b/src/tracker-writeback/tracker-writeback-gstreamer.c
new file mode 100644
index 000000000..8a9660e14
--- /dev/null
+++ b/src/tracker-writeback/tracker-writeback-gstreamer.c
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2019, Saiful B. Khan <saifulbkhan.gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gst/gst.h>
+#include <gst/tag/tag.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <libtracker-sparql/tracker-ontologies.h>
+#include <libtracker-sparql/tracker-sparql.h>
+
+#include "tracker-writeback-file.h"
+
+#define TRACKER_TYPE_WRITEBACK_GSTREAMER (tracker_writeback_gstreamer_get_type ())
+
+typedef struct TrackerWritebackGstreamer TrackerWritebackGstreamer;
+typedef struct TrackerWritebackGstreamerClass TrackerWritebackGstreamerClass;
+typedef struct TrackerWritebackGstreamerElements TagElements;
+
+typedef enum {
+ GST_AUTOPLUG_SELECT_TRY,
+ GST_AUTOPLUG_SELECT_EXPOSE,
+ GST_AUTOPLUG_SELECT_SKIP
+} GstAutoplugSelectResult;
+
+typedef GstElement *(*TrackerWritebackGstAddTaggerElem) (GstElement *pipeline,
+ GstPad *srcpad,
+ GstTagList *tags);
+
+struct TrackerWritebackGstreamerElements {
+ GstElement *pipeline;
+ GstElement *sink;
+ GHashTable *taggers;
+ GstTagList *tags;
+ gboolean sink_linked;
+};
+
+struct TrackerWritebackGstreamer {
+ TrackerWritebackFile parent_instance;
+};
+
+struct TrackerWritebackGstreamerClass {
+ TrackerWritebackFileClass parent_class;
+};
+
+static GType tracker_writeback_gstreamer_get_type (void) G_GNUC_CONST;
+static gboolean writeback_gstreamer_update_file_metadata (TrackerWritebackFile *writeback_file,
+ GFile *file,
+ GPtrArray *values,
+ TrackerSparqlConnection *connection,
+ GCancellable *cancellable,
+ GError **error);
+static const gchar* const *writeback_gstreamer_content_types (TrackerWritebackFile
*writeback_file);
+static gchar *writeback_gstreamer_get_artist_name (TrackerSparqlConnection *connection,
+ const gchar *urn);
+static gchar *writeback_gstreamer_get_album_name (TrackerSparqlConnection *connection,
+ const gchar *urn);
+static gchar *writeback_gstreamer_get_album_artist (TrackerSparqlConnection *connection,
+ const gchar *urn);
+static gchar *writeback_gstreamer_get_disc_number (TrackerSparqlConnection *connection,
+ const gchar *urn);
+static gchar *writeback_gstreamer_get_publisher_name (TrackerSparqlConnection *connection,
+ const gchar *urn);
+static gchar *writeback_gstreamer_get_artwork_url (TrackerSparqlConnection *connection,
+ const gchar *urn);
+
+G_DEFINE_DYNAMIC_TYPE (TrackerWritebackGstreamer, tracker_writeback_gstreamer, TRACKER_TYPE_WRITEBACK_FILE);
+
+static void
+tracker_writeback_gstreamer_class_init (TrackerWritebackGstreamerClass *klass)
+{
+ TrackerWritebackFileClass *writeback_file_class = TRACKER_WRITEBACK_FILE_CLASS (klass);
+
+ gst_init (NULL, NULL);
+
+ writeback_file_class->update_file_metadata = writeback_gstreamer_update_file_metadata;
+ writeback_file_class->content_types = writeback_gstreamer_content_types;
+}
+
+static void
+tracker_writeback_gstreamer_class_finalize (TrackerWritebackGstreamerClass *klass)
+{
+}
+
+static void
+tracker_writeback_gstreamer_init (TrackerWritebackGstreamer *wbg)
+{
+}
+
+static const gchar* const *
+writeback_gstreamer_content_types (TrackerWritebackFile *writeback_file)
+{
+ static const gchar *content_types[] = {
+ "audio/flac",
+ "audio/x-flac",
+ "audio/mpeg",
+ "audio/x-mpeg",
+ "audio/mp3",
+ "audio/x-mp3",
+ "audio/mpeg3",
+ "audio/x-mpeg3",
+ "audio/x-ac3",
+ "audio/ogg",
+ "audio/x-ogg",
+ NULL
+ };
+
+ return content_types;
+}
+
+static gboolean
+link_named_pad (GstPad *srcpad,
+ GstElement *element,
+ const gchar *sinkpadname)
+{
+ GstPad *sinkpad;
+ GstPadLinkReturn result;
+
+ sinkpad = gst_element_get_static_pad (element, sinkpadname);
+ if (sinkpad == NULL) {
+ sinkpad = gst_element_get_request_pad (element, sinkpadname);
+ }
+ result = gst_pad_link (srcpad, sinkpad);
+ gst_object_unref (sinkpad);
+
+ if (GST_PAD_LINK_SUCCESSFUL (result)) {
+ return TRUE;
+ } else {
+ gchar *srcname = gst_pad_get_name (srcpad);
+ gchar *sinkname = gst_pad_get_name (sinkpad);
+ g_warning ("couldn't link %s to %s: %d", srcname, sinkname, result);
+ return FALSE;
+ }
+}
+
+static GstElement *
+flac_tagger (GstElement *pipeline,
+ GstPad *srcpad,
+ GstTagList *tags)
+{
+ GstElement *tagger = NULL;
+
+ tagger = gst_element_factory_make ("flactag", NULL);
+ if (tagger == NULL)
+ return NULL;
+
+ gst_bin_add (GST_BIN (pipeline), tagger);
+ if (!link_named_pad (srcpad, tagger, "sink"))
+ return NULL;
+
+ gst_element_set_state (tagger, GST_STATE_PAUSED);
+ if (tags != NULL) {
+ gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
+ }
+ return tagger;
+}
+
+static GstElement *
+mp3_tagger (GstElement *pipeline,
+ GstPad *srcpad,
+ GstTagList *tags)
+{
+ GstElement *mux = NULL;
+
+ /* try id3mux first, since it's more supported and
+ * writes id3v2.3 tags rather than v2.4. */
+ mux = gst_element_factory_make ("id3mux", NULL);
+ if (mux == NULL)
+ mux = gst_element_factory_make ("id3v2mux", NULL);
+
+ if (mux == NULL)
+ return NULL;
+
+ gst_bin_add (GST_BIN (pipeline), mux);
+ if (!link_named_pad (srcpad, mux, "sink")) {
+ g_warning ("couldn't link decoded pad to id3 muxer");
+ return NULL;
+ }
+
+ gst_element_set_state (mux, GST_STATE_PAUSED);
+ if (tags != NULL) {
+ gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
+ }
+ g_debug ("id3 tagger created");
+ return mux;
+}
+
+static GstElement *
+vorbis_tagger (GstElement *pipeline,
+ GstPad *srcpad,
+ GstTagList *tags)
+{
+ GstElement *mux;
+ GstElement *tagger;
+ GstElement *parser;
+
+ mux = gst_element_factory_make ("oggmux", NULL);
+ parser = gst_element_factory_make ("vorbisparse", NULL);
+ tagger = gst_element_factory_make ("vorbistag", NULL);
+ if (mux == NULL || parser == NULL || tagger == NULL)
+ goto error;
+
+ gst_bin_add_many (GST_BIN (pipeline), parser, tagger, mux, NULL);
+ if (!link_named_pad (srcpad, parser, "sink"))
+ return NULL;
+ if (!gst_element_link_many (parser, tagger, mux, NULL))
+ return NULL;
+
+ gst_element_set_state (parser, GST_STATE_PAUSED);
+ gst_element_set_state (tagger, GST_STATE_PAUSED);
+ gst_element_set_state (mux, GST_STATE_PAUSED);
+ if (tags != NULL) {
+ gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
+ }
+ return mux;
+
+ error:
+ if (parser != NULL)
+ g_object_unref (parser);
+ if (tagger != NULL)
+ g_object_unref (tagger);
+ if (mux != NULL)
+ g_object_unref (mux);
+ return NULL;
+}
+
+static GstElement *
+mp4_tagger (GstElement *pipeline,
+ GstPad *srcpad,
+ GstTagList *tags)
+{
+ GstElement *mux;
+
+ mux = gst_element_factory_make ("mp4mux", NULL);
+ if (mux == NULL)
+ return NULL;
+
+ gst_bin_add (GST_BIN (pipeline), mux);
+ if (!link_named_pad (srcpad, mux, "audio_%u"))
+ return NULL;
+
+ gst_element_set_state (mux, GST_STATE_PAUSED);
+ if (tags != NULL) {
+ gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
+ }
+ return mux;
+}
+
+static void
+pad_added_cb (GstElement *decodebin,
+ GstPad *pad,
+ TagElements *element)
+{
+ TrackerWritebackGstAddTaggerElem add_tagger_func = NULL;
+ GstElement *retag_end;
+ GstCaps *caps;
+ gchar *caps_str = NULL;
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+
+ if (element->sink_linked) {
+ GError *error;
+ error = g_error_new (GST_STREAM_ERROR,
+ GST_STREAM_ERROR_FORMAT,
+ "Unable to write tags to this file as it contains multiple streams");
+ gst_element_post_message (decodebin, gst_message_new_error (GST_OBJECT (decodebin), error,
NULL));
+ g_error_free (error);
+ return;
+ }
+
+ /* find a tagger function that accepts the caps */
+ caps = gst_pad_query_caps (pad, NULL);
+ caps_str = gst_caps_to_string (caps);
+ g_debug ("finding tagger for src caps %s", caps_str);
+ g_free (caps_str);
+
+ g_hash_table_iter_init (&iter, element->taggers);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GstCaps *tagger_caps;
+ const gchar *media_type = (const gchar *)key;
+
+ if (strcmp (media_type, "audio/mpeg") == 0)
+ tagger_caps = gst_caps_from_string ("audio/mpeg, mpegversion=(int)1");
+ else if (strcmp (media_type, "audio/mp4") == 0)
+ tagger_caps = gst_caps_from_string ("audio/mpeg, mpegversion=(int){ 2, 4 }");
+ else if (strcmp (media_type, "audio/x-ac3") == 0)
+ tagger_caps = gst_caps_from_string ("audio/x-ac3, channels=(int)[ 1, 6 ], rate=(int)[
1, 2147483647 ]");
+ else
+ tagger_caps = gst_caps_from_string (media_type);
+
+ if (gst_caps_is_always_compatible (caps, tagger_caps)) {
+ caps_str = gst_caps_to_string (tagger_caps);
+ g_debug ("matched sink caps %s", caps_str);
+ g_free (caps_str);
+
+ gst_caps_unref (tagger_caps);
+ add_tagger_func = (TrackerWritebackGstAddTaggerElem) value;
+ break;
+ }
+ gst_caps_unref (tagger_caps);
+ }
+ gst_caps_unref (caps);
+
+ /* add retagging element(s) */
+ if (add_tagger_func == NULL) {
+ GError *error;
+ error = g_error_new (GST_STREAM_ERROR,
+ GST_STREAM_ERROR_FORMAT,
+ "Unable to write tags to this file as it is not encoded in a supported
format");
+ gst_element_post_message (decodebin, gst_message_new_error (GST_OBJECT (decodebin), error,
NULL));
+ g_error_free (error);
+ return;
+ }
+ retag_end = add_tagger_func (element->pipeline, pad, element->tags);
+
+ /* link to the sink */
+ gst_element_link (retag_end, element->sink);
+ element->sink_linked = TRUE;
+}
+
+static gboolean
+factory_src_caps_intersect (GstElementFactory *factory,
+ GstCaps *caps)
+{
+ const GList *templates;
+ const GList *l;
+
+ templates = gst_element_factory_get_static_pad_templates (factory);
+ for (l = templates; l != NULL; l = l->next) {
+ GstStaticPadTemplate *t = l->data;
+ GstCaps *tcaps;
+
+ if (t->direction != GST_PAD_SRC) {
+ continue;
+ }
+
+ tcaps = gst_static_pad_template_get_caps (t);
+ if (gst_caps_can_intersect (tcaps, caps)) {
+ gst_caps_unref (tcaps);
+ return TRUE;
+ }
+ gst_caps_unref (tcaps);
+ }
+ return FALSE;
+}
+
+static GstAutoplugSelectResult
+autoplug_select_cb (GstElement *decodebin,
+ GstPad *pad,
+ GstCaps *caps,
+ GstElementFactory *factory,
+ TagElements *element)
+{
+ GstCaps *src_caps;
+ gboolean is_any;
+ gboolean is_raw;
+ gboolean is_demuxer;
+
+ is_demuxer = (strstr (gst_element_factory_get_klass (factory), "Demuxer") != NULL);
+ if (is_demuxer) {
+ /* allow demuxers, since we're going to remux later */
+ return GST_AUTOPLUG_SELECT_TRY;
+ }
+
+ src_caps = gst_caps_new_any ();
+ is_any = gst_element_factory_can_src_all_caps (factory, src_caps); /* or _any_caps? */
+ gst_caps_unref (src_caps);
+ if (is_any) {
+ /* this is something like id3demux (though that will match the
+ * above check), allow it so we can get to the actual decoder later
+ */
+ return GST_AUTOPLUG_SELECT_TRY;
+ }
+
+ src_caps = gst_caps_from_string ("audio/x-raw");
+ is_raw = factory_src_caps_intersect (factory, src_caps);
+ gst_caps_unref (src_caps);
+
+ if (is_raw == FALSE) {
+ /*this is probably a parser or something, allow it */
+ return GST_AUTOPLUG_SELECT_TRY;
+ }
+
+ /* don't allow decoders */
+ return GST_AUTOPLUG_SELECT_EXPOSE;
+}
+
+static void
+writeback_gstreamer_save (TagElements *element,
+ GFile *file,
+ GError **error)
+{
+ GstElement *pipeline = NULL;
+ GstElement *urisrc = NULL;
+ GstElement *decodebin = NULL;
+ GOutputStream *stream = NULL;
+ GError *io_error = NULL;
+ GstBus *bus;
+ gboolean done;
+ gchar *uri = g_file_get_uri (file);
+
+ g_debug ("saving metadata for uri: %s", uri);
+
+ stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &io_error));
+ if (io_error != NULL) {
+ goto gio_error;
+ }
+
+ /* set up pipeline */
+ pipeline = gst_pipeline_new ("pipeline");
+ element->pipeline = pipeline;
+ element->sink_linked = FALSE;
+
+ urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, "urisrc", NULL);
+ if (urisrc == NULL) {
+ g_warning ("Failed to create gstreamer 'source' element from uri %s", uri);
+ goto out;
+ }
+ decodebin = gst_element_factory_make ("decodebin", "decoder");
+ if (decodebin == NULL) {
+ g_warning ("Failed to create a 'decodebin' element");
+ goto out;
+ }
+
+ element->sink = gst_element_factory_make ("giostreamsink", "sink");
+ if (element->sink == NULL) {
+ g_warning ("Failed to create a 'sink' element");
+ goto out;
+ }
+ g_object_set (element->sink, "stream", stream, NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), urisrc, decodebin, element->sink, NULL);
+ gst_element_link (urisrc, decodebin);
+
+ g_signal_connect_data (decodebin, "pad-added", G_CALLBACK (pad_added_cb), element, NULL, 0);
+ g_signal_connect_data (decodebin, "autoplug-select", G_CALLBACK (autoplug_select_cb), element, NULL,
0);
+
+ /* run pipeline .. */
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ bus = gst_element_get_bus (pipeline);
+ done = FALSE;
+ while (done == FALSE) {
+ GstMessage *message;
+
+ message = gst_bus_timed_pop (bus, GST_CLOCK_TIME_NONE);
+ if (message == NULL) {
+ g_debug ("breaking out of bus polling loop");
+ break;
+ }
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ {
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_error (message, &gerror, &debug);
+ g_warning ("caught error: %s (%s)", gerror->message, debug);
+
+ g_propagate_error (error, gerror);
+ done = TRUE;
+
+ g_free (debug);
+ }
+ break;
+
+ case GST_MESSAGE_EOS:
+ g_debug ("got eos message");
+ done = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ gst_message_unref (message);
+ }
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+ if (g_output_stream_close (stream, NULL, &io_error) == FALSE) {
+ goto gio_error;
+ }
+ g_object_unref (stream);
+ stream = NULL;
+
+ if (*error == NULL)
+ goto out;
+
+ gio_error:
+ if (io_error != NULL)
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", io_error->message);
+ out:
+ if (pipeline != NULL)
+ gst_object_unref (GST_OBJECT (pipeline));
+}
+
+static GstSample *
+generate_gst_sample_from_image (const GValue *val)
+{
+ GstSample *img_sample = NULL;
+ GMappedFile *mapped_file = NULL;
+ GError *err = NULL;
+ GByteArray *byte_arr = NULL;
+ gchar *filename;
+ const gchar *image_url = g_value_get_string (val);
+
+ filename = g_filename_from_uri (image_url, NULL, &err);
+ if (err != NULL) {
+ g_warning ("could not get filename for url (%s): %s", image_url, err->message);
+ g_clear_error (&err);
+ return img_sample;
+ }
+
+ mapped_file = g_mapped_file_new (filename, TRUE, &err);
+ if (err != NULL) {
+ g_warning ("encountered error reading image file (%s): %s", filename, err->message);
+ g_error_free (err);
+ } else {
+ GBytes *bytes = g_mapped_file_get_bytes (mapped_file);
+ byte_arr = g_bytes_unref_to_array (bytes);
+ img_sample = gst_tag_image_data_to_image_sample (byte_arr->data,
+ byte_arr->len, GST_TAG_IMAGE_TYPE_NONE);
+ }
+
+ g_byte_array_unref (byte_arr);
+ g_mapped_file_unref (mapped_file);
+ return img_sample;
+}
+
+static gboolean
+writeback_gstreamer_set (TagElements *element,
+ const gchar *tag,
+ const GValue *val)
+{
+ GstSample *img_sample;
+ GValue newval = {0, };
+
+ if (element->tags == NULL) {
+ element->tags = gst_tag_list_new_empty ();
+ }
+
+ g_value_init (&newval, gst_tag_get_type (tag));
+
+ if (g_strcmp0 (tag, GST_TAG_DATE_TIME) == 0) {
+ GstDateTime *datetime;
+
+ /* assumes date-time in ISO8601 string format */
+ datetime = gst_date_time_new_from_iso8601_string (g_value_get_string (val));
+ g_value_take_boxed (&newval, datetime);
+ } else if (g_strcmp0 (tag, GST_TAG_IMAGE) == 0) {
+ img_sample = generate_gst_sample_from_image (val);
+ if (img_sample == NULL) {
+ g_warning ("failed to set image as tag");
+ return FALSE;
+ }
+ g_value_take_boxed (&newval, img_sample);
+ } else {
+ g_value_transform (val, &newval);
+ }
+
+ g_debug ("Setting %s", tag);
+ gst_tag_list_add_values (element->tags, GST_TAG_MERGE_APPEND, tag, &newval, NULL);
+ g_value_unset (&newval);
+
+ return TRUE;
+}
+
+static gboolean
+writeback_gstreamer_update_file_metadata (TrackerWritebackFile *writeback,
+ GFile *file,
+ GPtrArray *values,
+ TrackerSparqlConnection *connection,
+ GCancellable *cancellable,
+ GError **error)
+{
+ guint n;
+ gboolean ret = FALSE;
+ TagElements *element = (TagElements *) g_malloc (sizeof (TagElements));
+
+ element->tags = NULL;
+ element->taggers = g_hash_table_new (g_str_hash, g_str_equal);
+
+ if (gst_element_factory_find ("giostreamsink") == NULL) {
+ g_warning ("giostreamsink not found, can't tag anything");
+ g_hash_table_unref (element->taggers);
+ g_free (element);
+ return ret;
+ } else {
+ if (gst_element_factory_find ("vorbistag") &&
+ gst_element_factory_find ("vorbisparse") &&
+ gst_element_factory_find ("oggmux")) {
+ g_debug ("ogg vorbis tagging available");
+ g_hash_table_insert (element->taggers, "audio/x-vorbis", (gpointer) vorbis_tagger);
+ }
+
+ if (gst_element_factory_find ("flactag")) {
+ g_debug ("flac tagging available");
+ g_hash_table_insert (element->taggers, "audio/x-flac", flac_tagger);
+ }
+
+ if (gst_element_factory_find ("id3v2mux") ||
+ gst_element_factory_find ("id3mux")) {
+ g_debug ("id3 tagging available");
+ g_hash_table_insert (element->taggers, "audio/mpeg", mp3_tagger);
+ }
+
+ if (gst_element_factory_find ("mp4mux")) {
+ g_debug ("mp4 tagging available");
+ g_hash_table_insert (element->taggers, "audio/mp4", mp4_tagger);
+ g_hash_table_insert (element->taggers, "audio/x-ac3", mp4_tagger);
+ }
+ }
+
+ for (n = 0; n < values->len; n++) {
+ const GStrv row = g_ptr_array_index (values, n);
+ GValue val = G_VALUE_INIT;
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NIE "title") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_TITLE, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "performer") == 0) {
+ gchar *artist_name = writeback_gstreamer_get_artist_name (connection, row[3]);
+
+ if (artist_name) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, artist_name);
+ writeback_gstreamer_set (element, GST_TAG_ARTIST, &val);
+ g_free (artist_name);
+ }
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "musicAlbum") == 0) {
+ gchar *album_name = writeback_gstreamer_get_album_name (connection, row[3]);
+ gchar *album_artist = writeback_gstreamer_get_album_artist (connection, row[3]);
+
+ if (album_name) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, album_name);
+ writeback_gstreamer_set (element, GST_TAG_ALBUM, &val);
+ g_free (album_name);
+ }
+ g_value_unset(&val);
+ if (album_artist) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, album_artist);
+ writeback_gstreamer_set (element, GST_TAG_ALBUM_ARTIST, &val);
+ g_free (album_artist);
+ }
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NIE "comment") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_COMMENT, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "genre") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_GENRE, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "trackNumber") == 0) {
+ g_value_init (&val, G_TYPE_INT);
+ g_value_set_int (&val, atoi(row[3]));
+ writeback_gstreamer_set (element, GST_TAG_TRACK_NUMBER, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "artwork") == 0) {
+ gchar *artwork_url = writeback_gstreamer_get_artwork_url(connection, row[3]);
+
+ if (artwork_url) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, artwork_url);
+ writeback_gstreamer_set (element, GST_TAG_IMAGE, &val);
+ g_free (artwork_url);
+ }
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NIE "contentCreated") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_DATE_TIME, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "internationalStandardRecordingCode") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_ISRC, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "lyrics") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_LYRICS, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "composer") == 0) {
+ gchar *composer_name = writeback_gstreamer_get_artist_name (connection, row[3]);
+
+ if (composer_name) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, composer_name);
+ writeback_gstreamer_set (element, GST_TAG_COMPOSER, &val);
+ g_free (composer_name);
+ }
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NMM "musicAlbumDisc") == 0) {
+ gchar *disc_number = writeback_gstreamer_get_disc_number (connection, row[3]);
+
+ if (disc_number) {
+ g_value_init (&val, G_TYPE_INT);
+ g_value_set_int (&val, atoi(disc_number));
+ writeback_gstreamer_set (element, GST_TAG_ALBUM_VOLUME_NUMBER, &val);
+ }
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NCO "publisher") == 0) {
+ gchar *publisher = writeback_gstreamer_get_publisher_name (connection, row[3]);
+
+ if (publisher) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, publisher);
+ writeback_gstreamer_set (element, GST_TAG_PUBLISHER, &val);
+ }
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NIE "description") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_DESCRIPTION, &val);
+ }
+
+ if (g_strcmp0 (row[2], TRACKER_PREFIX_NIE "keyword") == 0) {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, row[3]);
+ writeback_gstreamer_set (element, GST_TAG_KEYWORDS, &val);
+ }
+
+ g_value_unset (&val);
+ }
+
+ writeback_gstreamer_save (element, file, error);
+
+ if (*error != NULL) {
+ g_warning ("Error (%s) occured while attempting to write tags", (*error)->message);
+ } else {
+ ret = TRUE;
+ }
+
+ if (element->tags != NULL)
+ gst_tag_list_unref (element->tags);
+ if (element->taggers != NULL)
+ g_hash_table_unref (element->taggers);
+ g_free (element);
+
+ return ret;
+}
+
+static gchar *
+writeback_gstreamer_get_from_query (TrackerSparqlConnection *connection,
+ const gchar *urn,
+ const gchar *query,
+ const gchar *field)
+{
+ TrackerSparqlCursor *cursor;
+ GError *error = NULL;
+ gchar *value = NULL;
+
+ cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
+
+ if (error || !cursor || !tracker_sparql_cursor_next (cursor, NULL, NULL)) {
+ g_warning ("Couldn't find %s for entity with urn '%s', %s",
+ field,
+ urn,
+ error ? error->message : "no such value was found");
+
+ if (error) {
+ g_error_free (error);
+ }
+ } else {
+ value = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
+ }
+
+ g_clear_object (&cursor);
+
+ return value;
+}
+
+static gchar *
+writeback_gstreamer_get_artist_name (TrackerSparqlConnection *connection,
+ const gchar *urn)
+{
+ gchar *val, *query;
+
+ query = g_strdup_printf ("SELECT ?artistName WHERE {<%s> nmm:artistName ?artistName}", urn);
+ val = writeback_gstreamer_get_from_query (connection, urn, query, "artist name");
+ g_free (query);
+
+ return val;
+}
+
+static gchar *
+writeback_gstreamer_get_album_name (TrackerSparqlConnection *connection,
+ const gchar *urn)
+{
+ gchar *val, *query;
+
+ query = g_strdup_printf ("SELECT ?albumName WHERE {<%s> dc:title ?albumName}", urn);
+ val = writeback_gstreamer_get_from_query (connection, urn, query, "album name");
+ g_free (query);
+
+ return val;
+}
+
+static gchar *
+writeback_gstreamer_get_album_artist (TrackerSparqlConnection *connection,
+ const gchar *urn)
+{
+ gchar *artist_urn, *val, *query;
+
+ query = g_strdup_printf ("SELECT ?albumArtist WHERE {<%s> nmm:albumArtist ?albumName}", urn);
+ artist_urn = writeback_gstreamer_get_from_query (connection, urn, query, "album artist");
+ val = writeback_gstreamer_get_artist_name (connection, artist_urn);
+ g_free(query);
+ g_free(artist_urn);
+
+ return val;
+}
+
+static gchar *
+writeback_gstreamer_get_disc_number (TrackerSparqlConnection *connection,
+ const gchar *urn)
+{
+ gchar *val, *query;
+
+ query = g_strdup_printf ("SELECT ?setNumber WHERE {<%s> nmm:setNumber ?setNumber}", urn);
+ val = writeback_gstreamer_get_from_query (connection, urn, query, "set number");
+ g_free (query);
+
+ return val;
+}
+
+static gchar *
+writeback_gstreamer_get_publisher_name (TrackerSparqlConnection *connection,
+ const gchar *urn)
+{
+ gchar *val, *query;
+
+ query = g_strdup_printf ("SELECT ?name WHERE {<%s> nco:fullname ?name}", urn);
+ val = writeback_gstreamer_get_from_query (connection, urn, query, "fullname");
+ g_free (query);
+
+ return val;
+}
+
+static gchar *
+writeback_gstreamer_get_artwork_url (TrackerSparqlConnection *connection,
+ const gchar *urn)
+{
+ gchar *val, *query;
+
+ query = g_strdup_printf ("SELECT ?url WHERE {<%s> nie:url ?url}", urn);
+ val = writeback_gstreamer_get_from_query (connection, urn, query, "image URL");
+ g_free (query);
+
+ return val;
+}
+
+TrackerWriteback *
+writeback_module_create (GTypeModule *module)
+{
+ tracker_writeback_gstreamer_register_type (module);
+ return g_object_new (TRACKER_TYPE_WRITEBACK_GSTREAMER, NULL);
+}
+
+const gchar *
+const *writeback_module_get_rdf_types (void)
+{
+ static const gchar *rdftypes[] = {
+ TRACKER_PREFIX_NFO "Audio",
+ NULL
+ };
+
+ return rdftypes;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]