>From e14fbe8bea83a5131520d8925792b899032fcc47 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Tue, 3 Sep 2013 04:23:12 +0000 Subject: [PATCH 1/4] Add a libav based generic media extractor. --- configure.ac | 45 +++- src/tracker-extract/Makefile.am | 19 ++ src/tracker-extract/tracker-extract-libav.c | 369 ++++++++++++++++++++++++++++ 3 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 src/tracker-extract/tracker-extract-libav.c diff --git a/configure.ac b/configure.ac index 090ebd2..91cb91a 100644 --- a/configure.ac +++ b/configure.ac @@ -1646,7 +1646,7 @@ AM_CONDITIONAL(HAVE_GDKPIXBUF, test "x$have_gdkpixbuf" = "xyes") AC_ARG_ENABLE(generic-media-extractor, AS_HELP_STRING([--enable-generic-media-extractor=ARG], - [enables one of the (gstreamer, xine, external, auto) generic media extractor backends [[default=auto]]]),, + [enables one of the (gstreamer, xine, libav, external, auto) generic media extractor backends [[default=auto]]]),, [enable_generic_media_extractor=auto]) PKG_CHECK_MODULES(GSTREAMER, @@ -1666,6 +1666,30 @@ PKG_CHECK_MODULES(XINE, AC_SUBST(XINE_CFLAGS) AC_SUBST(XINE_LIBS) +PKG_CHECK_MODULES(AVFORMAT, + [libavformat >= 0.8.4], + [have_libavformat=yes], + [have_libavformat=no]) + +AC_SUBST(AVFORMAT_CFLAGS) +AC_SUBST(AVFORMAT_LIBS) + +PKG_CHECK_MODULES(AVCODEC, + [libavcodec >= 0.8.4], + [have_libavcodec=yes], + [have_libavcodec=no]) + +AC_SUBST(AVCODEC_CFLAGS) +AC_SUBST(AVCODEC_LIBS) + +PKG_CHECK_MODULES(AVCODEC, + [libavutil >= 0.8.4], + [have_libavutil=yes], + [have_libavutil=no]) + +AC_SUBST(AVUTIL_CFLAGS) +AC_SUBST(AVUTIL_LIBS) + if test "x$enable_generic_media_extractor" = "xauto"; then if test "$have_libgstreamer" = "yes"; then have_generic_media_handler="yes" @@ -1673,6 +1697,9 @@ if test "x$enable_generic_media_extractor" = "xauto"; then elif test "$have_libxine" = "yes"; then have_generic_media_handler_app="Xine" have_generic_media_handler="yes" + elif test "$have_libav" = "yes"; then + have_generic_media_handler_app="libav" + have_generic_media_handler="yes" else have_generic_media_handler="?" have_generic_media_handler_app="An external generic_media player will be called" @@ -1691,6 +1718,13 @@ elif test "x$enable_generic_media_extractor" = "xxine"; then else AC_MSG_ERROR([Couldn't find libxine]) fi +elif test "x$enable_generic_media_extractor" = "xlibav"; then + if test "$have_libavformat" = "yes" && test "$have_libavcodec" = "yes" && test "$have_libavutil" = "yes"; then + have_generic_media_handler_app="libav" + have_generic_media_handler="yes" + else + AC_MSG_ERROR([Couldn't find libav]) + fi else have_generic_media_handler="?" have_generic_media_handler_app="An external generic media player will be called" @@ -1700,17 +1734,26 @@ if test "$have_generic_media_handler_app" = "GStreamer"; then AC_DEFINE(HAVE_GSTREAMER, [], [Define if we have GStreamer]) AM_CONDITIONAL(HAVE_GSTREAMER, true) AM_CONDITIONAL(HAVE_LIBXINE, false) + AM_CONDITIONAL(HAVE_LIBAV, false) AM_CONDITIONAL(USING_EXTERNAL_GENERIC_MEDIA_PLAYER, false) elif test "$have_generic_media_handler_app" = "Xine"; then AC_DEFINE(HAVE_LIBXINE, [], [Define if we have Libxine]) AM_CONDITIONAL(HAVE_LIBXINE, true) AM_CONDITIONAL(HAVE_GSTREAMER, false) + AM_CONDITIONAL(HAVE_LIBAV, false) AM_CONDITIONAL(USING_EXTERNAL_GENERIC_MEDIA_PLAYER, false) +elif test "$have_generic_media_handler_app" = "libav"; then + AC_DEFINE(HAVE_GSTREAMER, [], [Define if we have libav]) + AM_CONDITIONAL(HAVE_LIBAV, true) + AM_CONDITIONAL(HAVE_GSTREAMER, false) + AM_CONDITIONAL(HAVE_LIBXINE, false) + AM_CONDITIONAL(USING_EXTERNAL_GENERIC_MEDIA_PLAYER, false) else AC_DEFINE(USING_EXTERNAL_GENERIC_MEDIA_PLAYER, [], [Define that Tracker will try to use external generic media players]) AM_CONDITIONAL(USING_EXTERNAL_GENERIC_MEDIA_PLAYER, true) AM_CONDITIONAL(HAVE_GSTREAMER, false) AM_CONDITIONAL(HAVE_LIBXINE, false) + AM_CONDITIONAL(HAVE_LIBAV, false) fi ########################################################################### diff --git a/src/tracker-extract/Makefile.am b/src/tracker-extract/Makefile.am index d8d175f..bb8296e 100644 --- a/src/tracker-extract/Makefile.am +++ b/src/tracker-extract/Makefile.am @@ -42,6 +42,7 @@ rules_files = \ 15-playlist.rule \ 90-gstreamer-generic.rule \ 90-text-generic.rule \ + 90-libav-generic.rule \ 91-gstreamer-generic-dlna.rule \ 92-xine-generic.rule \ 93-mplayer-generic.rule \ @@ -173,6 +174,11 @@ extractmodules_LTLIBRARIES += libextract-icon.la rules_DATA += 10-ico.rule endif +if HAVE_LIBAV +extractmodules_LTLIBRARIES += libextract-libav.la +rules_DATA += 90-libav-generic.rule +endif + # ABW libextract_abw_la_SOURCES = tracker-extract-abw.c libextract_abw_la_CFLAGS = $(TRACKER_EXTRACT_MODULES_CFLAGS) @@ -491,6 +497,19 @@ libextract_iso_la_LIBADD = \ $(TRACKER_EXTRACT_MODULES_LIBS) \ $(LIBOSINFO_LIBS) +# libav +libextract_libav_la_SOURCES = tracker-extract-libav.c +libextract_libav_la_CFLAGS = $(TRACKER_EXTRACT_MODULES_CFLAGS) +libextract_libav_la_LDFLAGS = $(module_flags) +libextract_libav_la_LIBADD = \ + $(top_builddir)/src/libtracker-extract/libtracker-extract- TRACKER_API_VERSION@.la \ + $(top_builddir)/src/libtracker-common/libtracker-common.la \ + $(BUILD_LIBS) \ + $(TRACKER_EXTRACT_MODULES_LIBS) \ + $(AVFORMAT_LIBS) \ + $(AVUTIL_LIBS) + + # # Binaries # diff --git a/src/tracker-extract/tracker-extract-libav.c b/src/tracker-extract/tracker-extract-libav.c new file mode 100644 index 0000000..04ed3a0 --- /dev/null +++ b/src/tracker-extract/tracker-extract-libav.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2013 Jolla Ltd. + * Contact: Andrew den Exter + * + * 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 + +#include +#include + +#include + +#include + +#include +#include +#include + +static void +open_insert (TrackerSparqlBuilder *preupdate, const char *graph, const gchar *iri, const gchar *type) +{ + tracker_sparql_builder_insert_open (preupdate, NULL); + if (graph) { + tracker_sparql_builder_graph_open(preupdate, graph); + } + + tracker_sparql_builder_subject_iri (preupdate, iri); + tracker_sparql_builder_predicate (preupdate, "a"); + tracker_sparql_builder_object (preupdate, type); +} + +static void +close_insert (TrackerSparqlBuilder *preupdate, const char *graph) +{ + if (graph) { + tracker_sparql_builder_graph_close (preupdate); + } + tracker_sparql_builder_insert_close (preupdate); +} + +static void +delete_value (TrackerSparqlBuilder *preupdate, const gchar *iri, const gchar *predicate, + const char *value) +{ + tracker_sparql_builder_delete_open (preupdate, NULL); + tracker_sparql_builder_subject_iri (preupdate, iri); + tracker_sparql_builder_predicate (preupdate, predicate); + tracker_sparql_builder_object_variable (preupdate, value); + tracker_sparql_builder_delete_close (preupdate); + + tracker_sparql_builder_where_open (preupdate); + tracker_sparql_builder_subject_iri (preupdate, iri); + tracker_sparql_builder_predicate (preupdate, predicate); + tracker_sparql_builder_object_variable (preupdate, value); + tracker_sparql_builder_where_close (preupdate); +} + +static void +set_value_iri (TrackerSparqlBuilder *metadata, const gchar *predicate, const gchar *iri) +{ + tracker_sparql_builder_predicate (metadata, predicate); + tracker_sparql_builder_object_iri (metadata, iri); +} + +static void +set_value_double (TrackerSparqlBuilder *metadata, const gchar *predicate, gdouble value) +{ + tracker_sparql_builder_predicate (metadata, predicate); + tracker_sparql_builder_object_double (metadata, value); +} + +static void +set_value_int64 (TrackerSparqlBuilder *metadata, const gchar *predicate, gint64 value) +{ + tracker_sparql_builder_predicate (metadata, predicate); + tracker_sparql_builder_object_int64 (metadata, value); +} + +static void +set_value_string (TrackerSparqlBuilder *metadata, const gchar *predicate, const gchar *value) +{ + tracker_sparql_builder_predicate (metadata, predicate); + tracker_sparql_builder_object_unvalidated (metadata, value); +} + +static gchar * +create_artist (TrackerSparqlBuilder *preupdate, const gchar *graph, const char *name) +{ + gchar *uri = tracker_sparql_escape_uri_printf ("urn:artist:%s", name); + + open_insert (preupdate, graph, uri, "nmm:Artist"); + set_value_string (preupdate, "nmm:artistName", name); + close_insert (preupdate, graph); + + return uri; +} + +G_MODULE_EXPORT gboolean +tracker_extract_get_metadata (TrackerExtractInfo *info) +{ + GFile *file; + TrackerSparqlBuilder *metadata; + TrackerSparqlBuilder *preupdate; + const gchar *graph; + gchar *absoluteFilePath; + gchar *uri; + AVFormatContext *format = NULL; + AVStream *audio_stream = NULL; + AVStream *video_stream = NULL; + int streamIndex; + AVDictionaryEntry *tag = NULL; + const char *title; + + av_register_all(); + + file = tracker_extract_info_get_file (info); + metadata = tracker_extract_info_get_metadata_builder (info); + preupdate = tracker_extract_info_get_preupdate_builder (info); + graph = tracker_extract_info_get_graph (info); + + uri = g_file_get_uri (file); + + absoluteFilePath = g_file_get_path (file); + if (avformat_open_input(&format, absoluteFilePath, NULL, NULL)) { + g_free (absoluteFilePath); + return FALSE; + } + g_free (absoluteFilePath); + + streamIndex = av_find_best_stream (format, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); + if (streamIndex >= 0) { + audio_stream = format->streams[streamIndex]; + } + + streamIndex = av_find_best_stream (format, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); + if (streamIndex >= 0) { + video_stream = format->streams[streamIndex]; + } + + if (!audio_stream && !video_stream) { + avformat_free_context(format); + return FALSE; + } + + if (video_stream) { + tracker_sparql_builder_predicate (metadata, "a"); + tracker_sparql_builder_object (metadata, "nmm:Video"); + + if (video_stream->codec->width > 0 && video_stream->codec->height > 0) { + set_value_int64 (metadata, "nfo:width", video_stream->codec->width); + set_value_int64 (metadata, "nfo:height", video_stream->codec->height); + } + + if (video_stream->avg_frame_rate.num > 0) { + gdouble frame_rate + = (gdouble) video_stream->avg_frame_rate.num + / video_stream->avg_frame_rate.den; + set_value_double (metadata, "nfo:frameRate", frame_rate); + } + + if (video_stream->duration > 0) { + gint64 duration = av_rescale(video_stream->duration, video_stream->time_base.num, + video_stream->time_base.den); + set_value_int64 (metadata, "nfo:duration", duration); + } + + if (video_stream->sample_aspect_ratio.num > 0) { + gdouble aspect_ratio + = (gdouble) video_stream->sample_aspect_ratio.num + / video_stream->sample_aspect_ratio.den; + set_value_double (metadata, "nfo:aspectRatio", aspect_ratio); + } + + if (video_stream->nb_frames > 0) { + set_value_int64 (metadata, "nfo:frameCount", video_stream->nb_frames); + } + + if ((tag = av_dict_get (format->metadata, "synopsis", NULL, 0))) { + set_value_string (metadata, "nmm:synopsis", tag->value); + } + + if ((tag = av_dict_get (format->metadata, "episode_sort", NULL, 0))) { + set_value_int64 (metadata, "nmm:episodeNumber", atoi(tag->value)); + } + + if ((tag = av_dict_get (format->metadata, "season_number", NULL, 0))) { + set_value_int64 (metadata, "nmm:season", atoi(tag->value)); + } + + } else if (audio_stream) { + const char *album_title = NULL; + const char *album_artist = NULL; + gchar *album_artist_uri = NULL; + gchar *performer_uri = NULL; + + tracker_sparql_builder_predicate (metadata, "a"); + tracker_sparql_builder_object (metadata, "nmm:MusicPiece"); + tracker_sparql_builder_object (metadata, "nfo:Audio"); + + if (audio_stream->duration > 0) { + gint64 duration = av_rescale(audio_stream->duration, audio_stream->time_base.num, + audio_stream->time_base.den); + set_value_int64 (metadata, "nfo:duration", duration); + } + + if ((tag = av_dict_get (format->metadata, "track", NULL, 0))) { + int track = atoi(tag->value); + if (track > 0) { + set_value_int64 (metadata, "nmm:trackNumber", track); + } + } + + if ((tag = av_dict_get (format->metadata, "album", NULL, 0))) { + album_title = tag->value; + } + + if (album_title && (tag = av_dict_get (format->metadata, "album_artist", NULL, 0))) { + album_artist_uri = create_artist (preupdate, graph, tag->value); + album_artist = tag->value; + } + + if ((tag = av_dict_get (format->metadata, "artist", tag, 0))) { + performer_uri = create_artist (preupdate, graph, tag->value); + if (!album_artist) { + album_artist = tag->value; + } + } + + if (!performer_uri && (tag = av_dict_get (format->metadata, "performer", tag, 0))) { + performer_uri = create_artist (preupdate, graph, tag->value); + if (!album_artist) { + album_artist = tag->value; + } + } + + if (performer_uri) { + set_value_iri (metadata, "nmm:performer", performer_uri); + + if (album_title && album_artist_uri) { + g_free(performer_uri); + } + } else if (album_artist_uri) { + set_value_iri (metadata, "nmm:performer", album_artist_uri); + } + + if ((tag = av_dict_get (format->metadata, "composer", tag, 0))) { + gchar *composer_uri = create_artist (preupdate, graph, tag->value); + set_value_iri (metadata, "nmm:composer", composer_uri); + g_free(composer_uri); + } + + + if (album_title) { + int disc = 1; + gchar *disc_uri; + gchar *album_uri = tracker_sparql_escape_uri_printf ("urn:album:%s", album_title); + + open_insert (preupdate, graph, album_uri, "nmm:MusicAlbum"); + set_value_string (preupdate, "nmm:albumTitle", album_title); + if (album_artist_uri) { + set_value_iri (preupdate, "nmm:albumArtist", album_artist_uri); + } else if (performer_uri) { + set_value_iri (preupdate, "nmm:albumArtist", performer_uri); + } + close_insert (preupdate, graph); + + + if ((tag = av_dict_get (format->metadata, "disc", NULL, 0))) { + disc = atoi (tag->value); + } + + disc_uri = tracker_sparql_escape_uri_printf ("urn:album-disc:%s:Disc%d", + album_title, + disc); + + delete_value (preupdate, disc_uri, "nmm:setNumber", "unknown"); + delete_value (preupdate, disc_uri, "nmm:albumDiscAlbum", "unknown"); + + open_insert (preupdate, graph, disc_uri, "nmm:MusicAlbumDisc"); + set_value_int64 (preupdate, "nmm:setNumber", disc); + set_value_iri (preupdate, "nmm:albumDiscAlbum", album_uri); + close_insert (preupdate, graph); + + set_value_iri (metadata, "nmm:musicAlbumDisc", disc_uri); + set_value_iri (metadata, "nmm:musicAlbum", album_uri); + + g_free (disc_uri); + g_free (album_uri); + } + + tracker_media_art_process (NULL, + 0, + NULL, + TRACKER_MEDIA_ART_ALBUM, + album_artist, + album_title, + uri); + } + + if (audio_stream) { + if (audio_stream->codec->sample_rate > 0) { + set_value_int64 (metadata, "nfo:sampleRate", audio_stream->codec->sample_rate); + } + if (audio_stream->codec->channels > 0) { + set_value_int64 (metadata, "nfo:channels", audio_stream->codec->channels); + } + } + + if (format->bit_rate > 0) { + set_value_int64 (metadata, "nfo:averageBitrate", format->bit_rate); + } + + + if ((tag = av_dict_get (format->metadata, "comment", NULL, 0))) { + set_value_string (metadata, "nie:comment", tag->value); + } + + if ((tag = av_dict_get (format->metadata, "copyright", NULL, 0))) { + set_value_string (metadata, "nie:copyright", tag->value); + } + + if ((tag = av_dict_get (format->metadata, "creation_time", NULL, 0))) { + gchar *content_created = tracker_date_guess (tag->value); + if (content_created) { + set_value_string (metadata, "nie:contentCreated", content_created); + g_free (content_created); + } + } + + if ((tag = av_dict_get (format->metadata, "description", NULL, 0))) { + set_value_string (metadata, "nie:description", tag->value); + } + + if ((tag = av_dict_get (format->metadata, "genre", NULL, 0))) { + set_value_string (metadata, "nfo:genre", tag->value); + } + + if ((tag = av_dict_get (format->metadata, "language", NULL, 0))) { + set_value_string (metadata, "nfo:language", tag->value); + } + + if ((tag = av_dict_get (format->metadata, "title", NULL, 0))) { + title = tag->value; + } + + tracker_guarantee_title_from_file (metadata, "nie:title", title, uri, NULL); + + g_free (uri); + + avformat_free_context (format); + + return TRUE; +} -- 1.8.4.2