[easytag/wip/gmodule-mp4v2] WIP Dynamically load libmp4v2 for MP4 tag support



commit 0aec81a9ba703bd2504fdc9edea9f6e75bdd821f
Author: David King <amigadave amigadave com>
Date:   Mon Jun 3 23:45:59 2013 +0100

    WIP Dynamically load libmp4v2 for MP4 tag support
    
    Fixes bug 701506.

 Makefile.am           |    2 +
 README                |    2 +-
 configure.ac          |   23 +--
 src/easytag.c         |   24 ++--
 src/et_core.c         |   40 ++++-
 src/mp4_header.c      |  208 ++++++++++++++++++---
 src/mp4_header.h      |    8 +-
 src/mp4_tag.c         |  484 +++++++++++++++++++++++++++++++++++++++++--------
 src/mp4_tag.h         |   43 +++--
 src/mp4_tag_private.h |   79 ++++++++
 10 files changed, 760 insertions(+), 153 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a4fec44..e8bc66e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,6 +20,7 @@ easytag_CPPFLAGS = \
        -I$(top_srcdir) \
        -I$(top_builddir) \
        $(DEPRECATED_CPPFLAGS) \
+       -DLIBDIR=\"$(LIBDIR)\" \
        -DLOCALEDIR=\"$(localedir)\" \
        -DPACKAGE_DATA_DIR=\"$(pkgdatadir)\"
 easytag_CFLAGS = \
@@ -94,6 +95,7 @@ easytag_headers = \
        src/mpeg_header.h \
        src/mp4_header.h \
        src/mp4_tag.h \
+       src/mp4_tag_private.h \
        src/musepack_header.h \
        src/ogg_header.h \
        src/ogg_tag.h \
diff --git a/README b/README
index 356bfaa..c6802aa 100644
--- a/README
+++ b/README
@@ -59,7 +59,7 @@ Installation
 * id3lib version greater than 3.7.12 (http://id3lib.sourceforge.net) (Recommended: id3lib-3.8.3)
 * libogg and libvorbis (http://www.vorbis.com) (if not deactivated by './configure --disable-ogg')
 * flac (http://flac.sourceforge.net) (if not deactivated by './configure --disable-flac')
-* taglib (http://taglib.github.com/) (if not deactivated by './configure --disable-mp4')
+* libmp4v2 (http://code.google.com/p/mp4v2/) (dynamically loaded at runtime)
 * wavpack (http://www.wavpack.com/) (if not deactivated by './configure --disable-wavpack')
 * libc 6 (glibc 2.1) or greater
 * intltool (if not deactivated by './configure --disable-nls')
diff --git a/configure.ac b/configure.ac
index fb7d8b5..690b087 100644
--- a/configure.ac
+++ b/configure.ac
@@ -236,21 +236,20 @@ AS_IF([test "x$have_mp3" = "xyes" -a "x$enable_id3v23" != "xno"],
        ID3LIB_VERSION="(id3lib-$ID3LIB_MAJOR.$ID3LIB_MINOR.$ID3LIB_PATCH)"],
       [have_id3lib=no])
 
-
 dnl ################################################
-dnl # taglib library
+dnl # libmp4v2 headers
 dnl ################################################
-
-TAGLIB_DEPS="taglib_c >= 1.6.0"
+GMODULE_DEPS="gmodule-2.0"
 AS_IF([test "x$enable_mp4" != "xno"],
-      [PKG_CHECK_EXISTS([$TAGLIB_DEPS], [have_taglib=yes], [have_taglib=no])],
-      [have_taglib=no])
+      [AC_CHECK_HEADER([mp4v2/mp4v2.h], [have_libmp4v2=yes],
+                       [have_libmp4v2=no])],
+      [have_libmp4v2=no])
 
-AS_IF([test "x$have_taglib" != "xno"],
-      [AC_DEFINE([ENABLE_MP4], [], [Define for taglib MP4 support])],
-      [TAGLIB_DEPS=""
+AS_IF([test "x$have_libmp4v2" != "xno"],
+      [AC_DEFINE([ENABLE_MP4], [], [Define for libmp4v2 MP4 support])],
+      [GMODULE_DEPS=""
        AS_IF([test "x$enable_mp4" = "xyes"],
-             [AC_MSG_ERROR([Taglib MP4 support requested but required dependencies ($TAGLIB_DEPS) not 
found])])])
+             [AC_MSG_ERROR([libmp4v2 support requested but required headers were not found])])])
 
 dnl ################################################
 dnl # WavPack library
@@ -268,7 +267,7 @@ AS_IF([test "x$have_wavpack" != "xno"],
 
 dnl Check the pkg-config dependencies
 GIO_DEPS="gio-2.0 >= 2.32.0" dnl For g_file_new_tmp()
-PKG_CHECK_MODULES([EASYTAG], [$GIO_DEPS $GTK_DEPS $OGG_DEPS $SPEEX_DEPS $FLAC_DEPS $ID3TAG_DEPS $TAGLIB_DEPS 
$WAVPACK_DEPS])
+PKG_CHECK_MODULES([EASYTAG], [$GMODULE_DEPS $GIO_DEPS $GTK_DEPS $OGG_DEPS $SPEEX_DEPS $FLAC_DEPS 
$ID3TAG_DEPS $WAVPACK_DEPS])
 
 dnl Check for winsock
 AC_SEARCH_LIBS([gethostbyname], [nsl socket], [],
@@ -347,7 +346,7 @@ echo ID3v2.3 tags support ....: $have_id3lib $ID3LIB_VERSION
 echo Ogg Vorbis file support .: $have_ogg
 echo Ogg Speex file support ..: $have_speex
 echo FLAC file support .......: $have_flac
-echo MP4 file support ........: $have_taglib
+echo MP4 file support ........: $have_libmp4v2
 echo WavPack support .........: $have_wavpack
 echo NLS/gettext .............: $USE_NLS
 echo Tests during make check .: $testing_utilities
diff --git a/src/easytag.c b/src/easytag.c
index eef0fbc..2d8243e 100644
--- a/src/easytag.c
+++ b/src/easytag.c
@@ -4006,24 +4006,24 @@ void Tag_Area_Display_Controls (ET_File *ETFile)
 
 #ifdef ENABLE_MP4
         case MP4_TAG:
-            gtk_widget_hide(GTK_WIDGET(DiscNumberLabel));
-            gtk_widget_hide(GTK_WIDGET(DiscNumberEntry));
-            gtk_widget_hide(GTK_WIDGET(ComposerLabel));
-            gtk_widget_hide(GTK_WIDGET(ComposerEntry));
+            gtk_widget_show (GTK_WIDGET (DiscNumberLabel));
+            gtk_widget_show (GTK_WIDGET (DiscNumberEntry));
+            gtk_widget_show (GTK_WIDGET (ComposerLabel));
+            gtk_widget_show (GTK_WIDGET (ComposerEntry));
             gtk_widget_hide(GTK_WIDGET(OrigArtistLabel));
             gtk_widget_hide(GTK_WIDGET(OrigArtistEntry));
             gtk_widget_hide(GTK_WIDGET(CopyrightLabel));
             gtk_widget_hide(GTK_WIDGET(CopyrightEntry));
             gtk_widget_hide(GTK_WIDGET(URLLabel));
             gtk_widget_hide(GTK_WIDGET(URLEntry));
-            gtk_widget_hide(GTK_WIDGET(EncodedByLabel));
-            gtk_widget_hide(GTK_WIDGET(EncodedByEntry));
-            gtk_widget_hide(GTK_WIDGET(PictureScrollWindow));
-            gtk_widget_hide (GTK_WIDGET (apply_image_toolitem));
-            gtk_widget_hide (GTK_WIDGET (remove_image_toolitem));
-            gtk_widget_hide (GTK_WIDGET (add_image_toolitem));
-            gtk_widget_hide (GTK_WIDGET (save_image_toolitem));
-            gtk_widget_hide (GTK_WIDGET (image_properties_toolitem));
+            gtk_widget_show (GTK_WIDGET (EncodedByLabel));
+            gtk_widget_show (GTK_WIDGET (EncodedByEntry));
+            gtk_widget_show (GTK_WIDGET (PictureScrollWindow));
+            gtk_widget_show (GTK_WIDGET (apply_image_toolitem));
+            gtk_widget_show (GTK_WIDGET (remove_image_toolitem));
+            gtk_widget_show (GTK_WIDGET (add_image_toolitem));
+            gtk_widget_show (GTK_WIDGET (save_image_toolitem));
+            gtk_widget_show (GTK_WIDGET (image_properties_toolitem));
             break;
 #endif
 
diff --git a/src/et_core.c b/src/et_core.c
index 0a9fefd..0832cfd 100644
--- a/src/et_core.c
+++ b/src/et_core.c
@@ -517,7 +517,18 @@ GList *ET_Add_File_To_File_List (gchar *filename)
             break;
 #ifdef ENABLE_MP4
         case MP4_TAG:
-            Mp4tag_Read_File_Tag(filename,FileTag);
+            {
+                EtMP4Tag *tag = ET_MP4_TAG (g_object_new (ET_TYPE_MP4_TAG,
+                                                          NULL));
+                if (et_mp4_tag_load (tag))
+                {
+                    Mp4tag_Read_File_Tag (tag, filename, FileTag);
+                }
+                else
+                {
+                    g_warning ("Failed to load module");
+                }
+            }
             break;
 #endif
 #ifdef ENABLE_WAVPACK
@@ -570,7 +581,18 @@ GList *ET_Add_File_To_File_List (gchar *filename)
 #endif
 #ifdef ENABLE_MP4
         case MP4_FILE:
-            Mp4_Header_Read_File_Info(filename,ETFileInfo);
+            {
+                EtMP4Tag *tag = ET_MP4_TAG (g_object_new (ET_TYPE_MP4_TAG,
+                                                          NULL));
+                if (et_mp4_tag_load (tag))
+                {
+                    Mp4_Header_Read_File_Info (tag, filename, ETFileInfo);
+                }
+                else
+                {
+                    g_warning ("Failed to load module");
+                }
+            }
             break;
 #endif
         case UNKNOWN_FILE:
@@ -3861,7 +3883,19 @@ gboolean ET_Save_File_Tag_To_HD (ET_File *ETFile)
             break;
 #ifdef ENABLE_MP4
         case MP4_TAG:
-            state = Mp4tag_Write_File_Tag(ETFile);
+            {
+                EtMP4Tag *tag = ET_MP4_TAG (g_object_new (ET_TYPE_MP4_TAG,
+                                                          NULL));
+                if (et_mp4_tag_load (tag))
+                {
+                    state = Mp4tag_Write_File_Tag (tag, ETFile);
+                }
+                else
+                {
+                    g_warning ("Failed to load module");
+                    state = FALSE;
+                }
+            }
             break;
 #endif
 #ifdef ENABLE_WAVPACK
diff --git a/src/mp4_header.c b/src/mp4_header.c
index 34a1d57..34b6fd5 100644
--- a/src/mp4_header.c
+++ b/src/mp4_header.c
@@ -16,10 +16,10 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-#include "config.h" // For definition of ENABLE_MP4
+#include <config.h> // For definition of ENABLE_MP4
 
 #ifdef ENABLE_MP4
 
@@ -37,8 +37,151 @@
 #include "log.h"
 #include "misc.h"
 #include "charset.h"
+#include "mp4_tag_private.h"
 
-#include <tag_c.h>
+#include <mp4v2/mp4v2.h>
+
+
+/****************
+ * Declarations *
+ ****************/
+
+static const struct
+{
+    uint8_t profile;
+    const char *format;
+    const char *subformat;
+} MP4AudioProfileToName[] = {
+    { MP4_MPEG4_AAC_MAIN_AUDIO_TYPE,       "MPEG", "4, AAC main", },
+    { MP4_MPEG4_AAC_LC_AUDIO_TYPE,         "MPEG", "4, AAC LC", },
+    { MP4_MPEG4_AAC_SSR_AUDIO_TYPE,        "MPEG", "4, AAC SSR", },
+    { MP4_MPEG4_AAC_LTP_AUDIO_TYPE,        "MPEG", "4, AAC LTP", },
+    { MP4_MPEG4_AAC_HE_AUDIO_TYPE,         "MPEG", "4, AAC HE", },
+    { MP4_MPEG4_AAC_SCALABLE_AUDIO_TYPE,   "MPEG", "4, AAC Scalable", },
+    { 7,                                   "MPEG", "4, TwinVQ", },
+    { MP4_MPEG4_CELP_AUDIO_TYPE,           "MPEG", "4, CELP", },
+    { MP4_MPEG4_HVXC_AUDIO_TYPE,           "MPEG", "4, HVXC", },
+    //  10, 11 unused
+    { MP4_MPEG4_TTSI_AUDIO_TYPE,           "MPEG", "4, TTSI", },
+    { MP4_MPEG4_MAIN_SYNTHETIC_AUDIO_TYPE, "MPEG", "4, Main Synthetic", },
+    { MP4_MPEG4_WAVETABLE_AUDIO_TYPE,      "MPEG", "4, Wavetable Syn", },
+    { MP4_MPEG4_MIDI_AUDIO_TYPE,           "MPEG", "4, General MIDI", },
+    { MP4_MPEG4_ALGORITHMIC_FX_AUDIO_TYPE, "MPEG", "4, Algo Syn and Audio FX", },
+    { 17,                                  "MPEG", "4, ER AAC LC", },
+    // 18 unused
+    { 19,                                  "MPEG", "4, ER AAC LTP", },
+    { 20,                                  "MPEG", "4, ER AAC Scalable", },
+    { 21,                                  "MPEG", "4, ER TwinVQ", },
+    { 22,                                  "MPEG", "4, ER BSAC", },
+    { 23,                                  "MPEG", "4, ER ACC LD", },
+    { 24,                                  "MPEG", "4, ER CELP", },
+    { 25,                                  "MPEG", "4, ER HVXC", },
+    { 26,                                  "MPEG", "4, ER HILN", },
+    { 27,                                  "MPEG", "4, ER Parametric", },
+};
+
+static const struct
+{
+    uint8_t profile;
+    const char *format;
+    const char *subformat;
+} AudioProfileToName[] = {
+    { MP4_MPEG2_AAC_MAIN_AUDIO_TYPE,       "MPEG",   "2, AAC Main" },
+    { MP4_MPEG2_AAC_LC_AUDIO_TYPE,         "MPEG",   "2, AAC LC" },
+    { MP4_MPEG2_AAC_SSR_AUDIO_TYPE,        "MPEG",   "2, AAC SSR" },
+    { MP4_MPEG2_AUDIO_TYPE,                "MPEG",   "2, Audio (13818-3)" },
+    { MP4_MPEG1_AUDIO_TYPE,                "MPEG",   "1, Audio (11172-3)" },
+    // mpeg4ip's private definitions
+    { MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE,  "PCM16",   "Little Endian" },
+    { MP4_VORBIS_AUDIO_TYPE,               "Vorbis",  "" },
+    { MP4_ALAW_AUDIO_TYPE,                 "G.711",   "aLaw" },
+    { MP4_ULAW_AUDIO_TYPE,                 "G.711",   "uLaw" },
+    { MP4_G723_AUDIO_TYPE,                 "G.723.1", "" },
+    { MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE,     "PCM16",   "Big Endian" },
+};
+
+#define NUMBER_OF(A) (sizeof(A) / sizeof(A[0]))
+
+
+/**************
+ * Prototypes *
+ **************/
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * getType:
+ *
+ * Returns a format/sub-format information. Taken from mp4.h/mp4info.
+ */
+static void getType(EtMP4Tag *tag, MP4FileHandle file, MP4TrackId trackId, const char **format, const char 
**subformat )
+{
+    EtMP4TagPrivate *priv = tag->priv;
+    unsigned i;
+    const char *media_data_name = priv->mp4v2_gettrackmediadataname (file,
+                                                                     trackId);
+
+    *format = _("Audio");
+    *subformat = _("Unknown");
+
+    if (media_data_name == NULL)
+    {
+        ;
+    } else if (strcasecmp(media_data_name, "samr") == 0)
+    {
+        *subformat = "AMR";
+    } else if (strcasecmp(media_data_name, "sawb") == 0)
+    {
+        *subformat = "AMR-WB";
+    } else if (strcasecmp(media_data_name, "mp4a") == 0)
+    {
+        u_int8_t type = priv->mp4v2_gettrackesdsobjecttypeid (file, trackId);
+
+        if( type == MP4_MPEG4_AUDIO_TYPE )
+        {
+            u_int8_t* pAacConfig = NULL;
+            u_int32_t aacConfigLength;
+
+            priv->mp4v2_gettrackesconfiguration (file, trackId, &pAacConfig,
+                                                 &aacConfigLength);
+
+            if (pAacConfig != NULL)
+            {
+                type = aacConfigLength >= 2 ? ((pAacConfig[0] >> 3) & 0x1f) : 0;
+                free(pAacConfig);
+
+                for (i = 0; i < NUMBER_OF(MP4AudioProfileToName); i++)
+                {
+                    if (type == MP4AudioProfileToName[i].profile)
+                    {
+                        *format = MP4AudioProfileToName[i].format;
+                        *subformat = MP4AudioProfileToName[i].subformat;
+                        return;
+                    }
+                }
+            }
+            *format = "MPEG";
+            *subformat = "4, Unknown";
+        } else
+        {
+            for (i = 0; i < NUMBER_OF(AudioProfileToName); i++)
+            {
+                if (type == AudioProfileToName[i].profile)
+                {
+                    *format = AudioProfileToName[i].format;
+                    *subformat = AudioProfileToName[i].subformat;
+                    return;
+                }
+            }
+        }
+    } else
+    {
+        *subformat = media_data_name;
+    }
+}
 
 
 /*
@@ -46,50 +189,51 @@
  *
  * Get header info into the ETFileInfo structure
  */
-gboolean Mp4_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+gboolean
+Mp4_Header_Read_File_Info (EtMP4Tag *tag, const gchar *filename,
+                           ET_File_Info *ETFileInfo)
 {
-    TagLib_File *file;
-    const TagLib_AudioProperties *properties;
+    EtMP4TagPrivate *priv;
+    MP4FileHandle file;
+    MP4TrackId trackId = 1;
+    //const char* trackType;
+    const char *format, *subformat;
 
     g_return_val_if_fail (filename != NULL && ETFileInfo != NULL, FALSE);
+    g_return_val_if_fail (ET_IS_MP4_TAG (tag), FALSE);
+
+    priv = tag->priv;
 
     /* Get size of file */
     ETFileInfo->size = Get_File_Size(filename);
 
-    if ((file = taglib_file_new_type(filename, TagLib_File_MP4)) == NULL )
+    if ((file = priv->mp4v2_read (filename)) == MP4_INVALID_FILE_HANDLE)
     {
         gchar *filename_utf8 = filename_to_display(filename);
-        //g_print(_("Error while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
-        Log_Print(LOG_ERROR,_("Error while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
+        //g_print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+        Log_Print(LOG_ERROR,_("ERROR while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
         g_free(filename_utf8);
         return FALSE;
     }
 
     /* Check for audio track */
-    if( !taglib_file_is_valid(file) )
+    if (priv->mp4v2_getnumberoftracks (file, MP4_AUDIO_TRACK_TYPE, 0) < 1)
     {
         gchar *filename_utf8 = filename_to_display(filename);
-        Log_Print (LOG_ERROR, _("File contains no audio track: '%s'"),
-                   filename_utf8);
+        Log_Print(LOG_ERROR,_("ERROR while opening file: '%s' (%s)."),filename_utf8,("Contains no audio 
track"));
+        priv->mp4v2_close (file, 0);
         g_free(filename_utf8);
         return FALSE;
     }
 
-    properties = taglib_file_audioproperties(file);
-    if (properties == NULL)
-    {
-        gchar *filename_utf8 = filename_to_display (filename);
-        Log_Print (LOG_ERROR, _("Error reading properties from file: '%s'"),
-                   filename_utf8);
-        g_free (filename_utf8);
-        taglib_file_free (file);
-        return FALSE;
-    }
+    /* Get the first track id (index 0) */
+    trackId = priv->mp4v2_findtrackid (file, 0, MP4_AUDIO_TRACK_TYPE, 0);
 
     /* Get format/subformat */
     {
-        ETFileInfo->mpc_version = g_strdup("MPEG");
-       ETFileInfo->mpc_profile = g_strdup("4, Unknown");
+        getType (tag, file, trackId, &format, &subformat);
+        ETFileInfo->mpc_version = g_strdup( format );
+        ETFileInfo->mpc_profile = g_strdup( subformat );
     }
 
     ETFileInfo->version = 4;
@@ -97,12 +241,14 @@ gboolean Mp4_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
     ETFileInfo->layer = 14;
 
     ETFileInfo->variable_bitrate = TRUE;
-    ETFileInfo->bitrate = taglib_audioproperties_bitrate(properties);
-    ETFileInfo->samplerate = taglib_audioproperties_samplerate(properties);
-    ETFileInfo->mode = taglib_audioproperties_channels(properties);
-    ETFileInfo->duration = taglib_audioproperties_length(properties);
+    ETFileInfo->bitrate = priv->mp4v2_gettrackbitrate (file, trackId) / 1000;
+    ETFileInfo->samplerate = priv->mp4v2_gettracktimescale (file, trackId);
+    ETFileInfo->mode = priv->mp4v2_gettrackaudiochannels (file, trackId);
+    ETFileInfo->duration = priv->mp4v2_convertfromtrackduration (file, trackId,
+                                                                 priv->mp4v2_gettrackduration (file, 
trackId),
+                                                                 MP4_SECS_TIME_SCALE);
 
-    taglib_file_free(file);
+    priv->mp4v2_close (file, 0);
     return TRUE;
 }
 
@@ -113,7 +259,9 @@ gboolean Mp4_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
  *
  * Display header info in the main window
  */
-gboolean Mp4_Header_Display_File_Info_To_UI(gchar *filename, ET_File_Info *ETFileInfo)
+gboolean
+Mp4_Header_Display_File_Info_To_UI (const gchar *filename,
+                                    ET_File_Info *ETFileInfo)
 {
     gchar *text;
     gchar *time = NULL;
diff --git a/src/mp4_header.h b/src/mp4_header.h
index a5374a5..fbc7a17 100644
--- a/src/mp4_header.h
+++ b/src/mp4_header.h
@@ -25,6 +25,7 @@
 
 
 #include "et_core.h"
+#include "mp4_tag.h"
 
 /****************
  * Declarations *
@@ -35,8 +36,9 @@
  * Prototypes *
  **************/
 
-gboolean Mp4_Header_Read_File_Info          (gchar *filename, ET_File_Info *ETFileInfo);
-gboolean Mp4_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo);
-
+gboolean Mp4_Header_Read_File_Info (EtMP4Tag *tag, const gchar *filename,
+                                    ET_File_Info *ETFileInfo);
+gboolean Mp4_Header_Display_File_Info_To_UI (const gchar *filename,
+                                             ET_File_Info *ETFileInfo);
 
 #endif /* __MP4_HEADER_H__ */
diff --git a/src/mp4_tag.c b/src/mp4_tag.c
index 48f55f3..55d504a 100644
--- a/src/mp4_tag.c
+++ b/src/mp4_tag.c
@@ -16,16 +16,23 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-#include "config.h" // For definition of ENABLE_MP4
+/* Portions of this code was borrowed from the MPEG4IP tools project */
+#include "config.h" /* For definition of ENABLE_MP4. */
 
 #ifdef ENABLE_MP4
 
 #include <gtk/gtk.h>
 #include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
 #include <stdlib.h>
 
 #include "mp4_tag.h"
@@ -36,121 +43,335 @@
 #include "misc.h"
 #include "et_core.h"
 #include "charset.h"
+#include "mp4_tag_private.h"
 
-#include <tag_c.h>
+#include <mp4v2/mp4v2.h>
 
+G_DEFINE_TYPE (EtMP4Tag, et_mp4_tag, G_TYPE_OBJECT);
+
+/**************
+ * Prototypes *
+ **************/
+static void et_mp4_tag_unload (EtMP4Tag *tag);
+
+/*************
+ * Functions *
+ *************/
+
+static void
+et_mp4_tag_finalize (GObject *object)
+{
+    et_mp4_tag_unload (ET_MP4_TAG (object));
+    G_OBJECT_CLASS (et_mp4_tag_parent_class)->finalize (object);
+}
+
+static void
+et_mp4_tag_init (EtMP4Tag *tag)
+{
+    tag->priv = G_TYPE_INSTANCE_GET_PRIVATE (tag, ET_TYPE_MP4_TAG,
+                                             EtMP4TagPrivate);
+}
+
+static void
+et_mp4_tag_class_init (EtMP4TagClass *klass)
+{
+    G_OBJECT_CLASS (klass)->finalize = et_mp4_tag_finalize;
+    g_type_class_add_private (klass, sizeof (EtMP4TagPrivate));
+}
+
+static gboolean
+et_mp4_tag_load_symbol (EtMP4Tag *tag, const gchar *name, gpointer *func_ptr)
+{
+    EtMP4TagPrivate *priv = tag->priv;
+
+    if (!g_module_symbol (priv->module, name, func_ptr))
+    {
+        g_warning ("Failed to lookup symbol '%s'", name);
+        return FALSE;
+    }
+
+    if (func_ptr == NULL)
+    {
+        g_warning ("Attempt to lookup symbol '%s' was NULL", name);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+et_mp4_tag_load_symbols (EtMP4Tag *tag)
+{
+    EtMP4TagPrivate *priv = tag->priv;
+    gsize i;
+    struct
+    {
+        const gchar *name;
+        gpointer *func_ptr;
+    } symbols[] =
+    {
+        { "MP4Read", (gpointer *)&priv->mp4v2_read },
+        { "MP4Modify", (gpointer *)&priv->mp4v2_modify },
+        { "MP4Close", (gpointer *)&priv->mp4v2_close },
+        { "MP4TagsAlloc", (gpointer *)&priv->mp4v2_tagsalloc },
+        { "MP4TagsFetch", (gpointer *)&priv->mp4v2_tagsfetch },
+        { "MP4TagsFree", (gpointer *)&priv->mp4v2_tagsfree },
+        { "MP4TagsStore", (gpointer *)&priv->mp4v2_tagsstore },
+        { "MP4TagsSetName", (gpointer *)&priv->mp4v2_tagssetname },
+        { "MP4TagsSetArtist", (gpointer *)&priv->mp4v2_tagssetartist },
+        { "MP4TagsSetAlbum", (gpointer *)&priv->mp4v2_tagssetalbum },
+        { "MP4TagsSetAlbumArtist", (gpointer *)&priv->mp4v2_tagssetalbumartist },
+        { "MP4TagsSetDisk", (gpointer *)&priv->mp4v2_tagssetdisk },
+        { "MP4TagsSetReleaseDate", (gpointer *)&priv->mp4v2_tagssetreleasedate },
+        { "MP4TagsSetTrack", (gpointer *)&priv->mp4v2_tagssettrack },
+        { "MP4TagsSetGenre", (gpointer *)&priv->mp4v2_tagssetgenre },
+        { "MP4TagsSetComments", (gpointer *)&priv->mp4v2_tagssetcomments },
+        { "MP4TagsSetComposer", (gpointer *)&priv->mp4v2_tagssetcomposer },
+        { "MP4TagsSetEncodedBy", (gpointer *)&priv->mp4v2_tagssetencodedby },
+        { "MP4TagsAddArtwork", (gpointer *)&priv->mp4v2_addartwork },
+        { "MP4TagsSetArtwork", (gpointer *)&priv->mp4v2_setartwork },
+        { "MP4TagsRemoveArtwork", (gpointer *)&priv->mp4v2_removeartwork },
+        { "MP4GetTrackMediaDataName", (gpointer *)&priv->mp4v2_gettrackmediadataname },
+        { "MP4GetTrackEsdsObjectTypeId", (gpointer *)&priv->mp4v2_gettrackesdsobjecttypeid },
+        { "MP4GetTrackESConfiguration", (gpointer *)&priv->mp4v2_gettrackesconfiguration },
+        { "MP4GetNumberOfTracks", (gpointer *)&priv->mp4v2_getnumberoftracks },
+        { "MP4FindTrackId", (gpointer *)&priv->mp4v2_findtrackid },
+        { "MP4GetTrackBitRate", (gpointer *)&priv->mp4v2_gettrackbitrate },
+        { "MP4GetTrackTimeScale", (gpointer *)&priv->mp4v2_gettracktimescale },
+        { "MP4GetTrackAudioChannels", (gpointer *)&priv->mp4v2_gettrackaudiochannels },
+        { "MP4GetTrackDuration", (gpointer *)&priv->mp4v2_gettrackduration },
+        { "MP4ConvertFromTrackDuration", (gpointer *)&priv->mp4v2_convertfromtrackduration },
+    };
+
+    for (i = 0; i < G_N_ELEMENTS (symbols); i++)
+    {
+        if (!et_mp4_tag_load_symbol (tag, symbols[i].name,
+                                     symbols[i].func_ptr))
+        {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+gboolean
+et_mp4_tag_load (EtMP4Tag *tag)
+{
+    gchar *path;
+    EtMP4TagPrivate *priv;
+
+    g_return_val_if_fail (ET_IS_MP4_TAG (tag), FALSE);
+
+    if (!g_module_supported ())
+    {
+        return FALSE;
+    }
+
+    path = g_module_build_path (LIBDIR, "mp4v2");
+
+    priv = tag->priv;
+    priv->module = g_module_open (path, G_MODULE_BIND_LAZY);
+
+    if (!priv->module)
+    {
+        gchar *utf8_path = g_filename_display_name (path);
+        Log_Print (LOG_WARNING, _("Unable to open mp4v2 library: '%s' (%s)"),
+                   utf8_path, g_module_error ());
+        g_free (utf8_path);
+        g_free (path);
+
+        return FALSE;
+    }
+
+    if (!et_mp4_tag_load_symbols (tag))
+    {
+        Log_Print (LOG_WARNING,
+                   _("Unable to load symbols from mp4v2 library"));
+        g_free (path);
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+et_mp4_tag_unload (EtMP4Tag *tag)
+{
+    g_return_if_fail (ET_IS_MP4_TAG (tag));
+
+    if (!g_module_close (tag->priv->module))
+    {
+        Log_Print (LOG_WARNING, _("Unable to close mp4v2 library (%s)"),
+                   g_module_error ());
+    }
+}
 
 /*
  * Mp4_Tag_Read_File_Tag:
  *
  * Read tag data into an Mp4 file.
  *
+ * cf. http://mp4v2.googlecode.com/svn/doc/1.9.0/api/example_2itmf_2tags_8c-example.html
+ *
  * Note:
  *  - for string fields, //if field is found but contains no info (strlen(str)==0), we don't read it
  *  - for track numbers, if they are zero, then we don't read it
  */
-gboolean Mp4tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+gboolean
+Mp4tag_Read_File_Tag (EtMP4Tag *tag, gchar *filename, File_Tag *FileTag)
 {
-    TagLib_File *mp4file;
-    TagLib_Tag *tag;
-    guint track;
-
+    EtMP4TagPrivate *priv;
+    MP4FileHandle mp4file = NULL;
+    const MP4Tags *mp4tags = NULL;
+    uint16_t track, track_total;
+    uint16_t disk, disktotal;
+    Picture *prev_pic = NULL;
+    gint pic_num;
+    const MP4TagArtwork *mp4artwork = NULL;
+
+    g_return_val_if_fail (ET_IS_MP4_TAG (tag), FALSE);
     g_return_val_if_fail (filename != NULL && FileTag != NULL, FALSE);
 
+    priv = tag->priv;
+
     /* Get data from tag */
-    mp4file = taglib_file_new_type(filename,TagLib_File_MP4);
-    if (mp4file == NULL)
+    mp4file = priv->mp4v2_read (filename);
+    if (mp4file == MP4_INVALID_FILE_HANDLE)
     {
         gchar *filename_utf8 = filename_to_display(filename);
-        Log_Print(LOG_ERROR,_("Error while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
+        Log_Print(LOG_ERROR,_("ERROR while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
         g_free(filename_utf8);
         return FALSE;
     }
 
-    /* Check for audio track */
-    if (!taglib_file_is_valid (mp4file))
+    mp4tags = priv->mp4v2_tagsalloc ();
+    if (!priv->mp4v2_tagsfetch (mp4tags, mp4file))
     {
-        gchar *filename_utf8 = filename_to_display (filename);
-        Log_Print (LOG_ERROR, _("File contains no audio track: '%s'"),
-                   filename_utf8);
-        g_free (filename_utf8);
-        taglib_file_free (mp4file);
+        gchar *filename_utf8 = filename_to_display(filename);
+        Log_Print(LOG_ERROR,_("ERROR reading tags from file: '%s' (%s)."),filename_utf8,_("MP4 format 
invalid"));
+        g_free(filename_utf8);
         return FALSE;
     }
 
-    tag = taglib_file_tag (mp4file);
-    if (tag == NULL)
-    {
-        gchar *filename_utf8 = filename_to_display (filename);
-        Log_Print (LOG_ERROR, _("Error reading tags from file: '%s'"),
-                   filename_utf8);
-        g_free (filename_utf8);
-        taglib_file_free (mp4file);
-        return FALSE;
-    }
+    /* TODO Add error detection */
 
     /*********
      * Title *
      *********/
-    FileTag->title = g_strdup(taglib_tag_title(tag));
+    if (mp4tags->name)
+        FileTag->title = g_strdup(mp4tags->name);
 
     /**********
      * Artist *
      **********/
-    FileTag->artist = g_strdup(taglib_tag_artist(tag));
+    if (mp4tags->artist)
+        FileTag->artist = g_strdup(mp4tags->artist);
 
     /*********
      * Album *
      *********/
-    FileTag->album = g_strdup(taglib_tag_album(tag));
+    if (mp4tags->album)
+        FileTag->album = g_strdup(mp4tags->album);
 
     /****************
      * Album Artist *
      ****************/
-    /* TODO: No album artist or disc number support in the TagLib C API! */
+    if (mp4tags->albumArtist)
+        FileTag->album_artist = g_strdup(mp4tags->albumArtist);
+
+    /**********************
+     * Disk / Total Disks *
+     **********************/
+    if (mp4tags->disk)
+    {
+       disk = mp4tags->disk->index, disktotal = mp4tags->disk->total;
+        if (disk != 0 && disktotal != 0)
+            FileTag->disc_number = g_strdup_printf("%d/%d",(gint)disk,(gint)disktotal);
+        else if (disk != 0)
+            FileTag->disc_number = g_strdup_printf("%d",(gint)disk);
+        else if (disktotal != 0)
+            FileTag->disc_number = g_strdup_printf("/%d",(gint)disktotal);
+        //if (disktotal != 0)
+        //    FileTag->disk_number_total = g_strdup_printf("%d",(gint)disktotal);
+    }
 
     /********
      * Year *
      ********/
-    FileTag->year = g_strdup_printf("%u", taglib_tag_year(tag));
+    if (mp4tags->releaseDate)
+        FileTag->year = g_strdup(mp4tags->releaseDate);
 
     /*************************
      * Track and Total Track *
      *************************/
-    track = taglib_tag_track(tag);
+    if (mp4tags->track)
+    {
 
-    if (track != 0)
-        FileTag->track = NUMBER_TRACK_FORMATED ? 
g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,(gint)track) : g_strdup_printf("%d",(gint)track);
-    /* TODO: No total track support in the TagLib C API! */
+       track = mp4tags->track->index, track_total = mp4tags->track->total;
+        if (track != 0)
+            FileTag->track = NUMBER_TRACK_FORMATED ? 
g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,(gint)track) : g_strdup_printf("%d",(gint)track);
+        if (track_total != 0)
+            FileTag->track_total = NUMBER_TRACK_FORMATED ? 
g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,(gint)track_total) : 
g_strdup_printf("%d",(gint)track_total);
+    }
 
     /*********
      * Genre *
      *********/
-    FileTag->genre = g_strdup(taglib_tag_genre(tag));
+    if (mp4tags->genre)
+        FileTag->genre = g_strdup(mp4tags->genre);
 
     /***********
      * Comment *
      ***********/
-    FileTag->comment = g_strdup(taglib_tag_comment(tag));
+    if (mp4tags->comments)
+        FileTag->comment = g_strdup(mp4tags->comments);
 
     /**********************
      * Composer or Writer *
      **********************/
-    /* TODO: No composer support in the TagLib C API! */
+    if (mp4tags->composer)
+        FileTag->composer = g_strdup(mp4tags->composer);
 
     /*****************
      * Encoding Tool *
      *****************/
-    /* TODO: No encode_by support in the TagLib C API! */
+    if (mp4tags->encodedBy)
+        FileTag->encoded_by = g_strdup(mp4tags->encodedBy);
+
+    /* Unimplemented
+    Tempo / BPM
+    MP4GetMetadataTempo(file, &string)
+    */
 
     /***********
      * Picture *
      ***********/
-    /* TODO: No encode_by support in the TagLib C API! */
+    // Version 1.9.1 of mp4v2 and up handle multiple cover art
+    mp4artwork = mp4tags->artwork;
+    for (pic_num = 0; pic_num < mp4tags->artworkCount; ++pic_num, ++mp4artwork)
+    {
+        Picture *pic;
+
+        pic = Picture_Allocate();
+        if (!prev_pic)
+            FileTag->picture = pic;
+        else
+            prev_pic->next = pic;
+        prev_pic = pic;
+
+        pic->size = mp4artwork->size;
+        pic->data = g_memdup(mp4artwork->data, pic->size);
+       /* mp4artwork->type gives image type. */
+        pic->type = ET_PICTURE_TYPE_FRONT_COVER;
+        pic->description = NULL;
+    }
+
 
     /* Free allocated data */
-    taglib_tag_free_strings();
-    taglib_file_free(mp4file);
+    priv->mp4v2_tagsfree (mp4tags);
+    priv->mp4v2_close (mp4file, 0);
 
     return TRUE;
 }
@@ -164,37 +385,47 @@ gboolean Mp4tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
  * Note:
  *  - for track numbers, we write 0's if one or the other is blank
  */
-gboolean Mp4tag_Write_File_Tag (ET_File *ETFile)
+gboolean
+Mp4tag_Write_File_Tag (EtMP4Tag *tag, ET_File *ETFile)
 {
+    EtMP4TagPrivate *priv;
     File_Tag *FileTag;
     gchar    *filename;
     gchar    *filename_utf8;
-    TagLib_File *mp4file = NULL;
-    TagLib_Tag *tag;
-    gboolean success;
+    MP4FileHandle mp4file = NULL;
+    const MP4Tags *mp4tags = NULL;
+    MP4TagDisk mp4disk;
+    MP4TagTrack mp4track;
+    MP4TagArtwork mp4artwork;
+    gint error = 0;
 
-    g_return_val_if_fail (ETFile != NULL || ETFile->FileTag != NULL, FALSE);
+    g_return_val_if_fail (ET_IS_MP4_TAG (tag), FALSE);
+    g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
+
+    /* extra initializers */
+    mp4disk.index  = 0;
+    mp4disk.total  = 0;
+    mp4track.index = 0;
+    mp4track.total = 0;
+
+    priv = tag->priv;
 
     FileTag = (File_Tag *)ETFile->FileTag->data;
     filename      = ((File_Name *)ETFile->FileNameCur->data)->value;
     filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
 
     /* Open file for writing */
-    mp4file = taglib_file_new_type(filename, TagLib_File_MP4);
-    if (mp4file == NULL)
+    mp4file = priv->mp4v2_modify (filename, 0);
+    if (mp4file == MP4_INVALID_FILE_HANDLE)
     {
-        Log_Print(LOG_ERROR,_("Error while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
+        Log_Print(LOG_ERROR,_("ERROR while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
         return FALSE;
     }
 
-    tag = taglib_file_tag (mp4file);
-    if (tag == NULL)
+    mp4tags = priv->mp4v2_tagsalloc ();
+    if (!priv->mp4v2_tagsfetch (mp4tags, mp4file))
     {
-        gchar *filename_utf8 = filename_to_display (filename);
-        Log_Print (LOG_ERROR, _("Error reading tags from file: '%s'"),
-                   filename_utf8);
-        g_free (filename_utf8);
-        taglib_file_free (mp4file);
+        Log_Print(LOG_ERROR,_("ERROR reading tags from file: '%s' (%s)."),filename_utf8,_("MP4 format 
invalid"));
         return FALSE;
     }
 
@@ -203,10 +434,10 @@ gboolean Mp4tag_Write_File_Tag (ET_File *ETFile)
      *********/
     if (FileTag->title && g_utf8_strlen(FileTag->title, -1) > 0)
     {
-        taglib_tag_set_title(tag, FileTag->title);
+        priv->mp4v2_tagssetname (mp4tags, FileTag->title);
     }else
     {
-        taglib_tag_set_title(tag,"");
+        priv->mp4v2_tagssetname (mp4tags, "");
     }
 
     /**********
@@ -214,10 +445,10 @@ gboolean Mp4tag_Write_File_Tag (ET_File *ETFile)
      **********/
     if (FileTag->artist && g_utf8_strlen(FileTag->artist, -1) > 0)
     {
-        taglib_tag_set_artist(tag,FileTag->artist);
+        priv->mp4v2_tagssetartist (mp4tags, FileTag->artist);
     }else
     {
-        taglib_tag_set_artist(tag,"");
+        priv->mp4v2_tagssetartist (mp4tags, "");
     }
 
     /*********
@@ -225,44 +456,92 @@ gboolean Mp4tag_Write_File_Tag (ET_File *ETFile)
      *********/
     if (FileTag->album && g_utf8_strlen(FileTag->album, -1) > 0)
     {
-        taglib_tag_set_album(tag,FileTag->album);
+        priv->mp4v2_tagssetalbum (mp4tags, FileTag->album);
     }else
     {
-        taglib_tag_set_album(tag,"");
+        priv->mp4v2_tagssetalbum (mp4tags, "");
     }
 
+    /****************
+     * Album Artist *
+     ****************/
+    if (FileTag->album_artist && g_utf8_strlen(FileTag->album_artist, -1) > 0)
+    {
+        priv->mp4v2_tagssetalbumartist (mp4tags, FileTag->album_artist);
+    }else
+    {
+        priv->mp4v2_tagssetalbumartist (mp4tags, "");
+    }
+
+    /**********************
+     * Disk / Total Disks *
+     **********************/
+    if (FileTag->disc_number && g_utf8_strlen(FileTag->disc_number, -1) > 0)
+    //|| FileTag->disc_number_total && g_utf8_strlen(FileTag->disc_number_total, -1) > 0)
+    {
+        /* At the present time, we manage only disk number like '1' or '1/2', we
+         * don't use disk number total... so here we try to decompose */
+        if (FileTag->disc_number)
+        {
+            gchar *dn_tmp = g_strdup(FileTag->disc_number);
+            gchar *tmp    = strchr(dn_tmp,'/');
+            if (tmp)
+            {
+                // A disc_number_total was entered
+                if ( (tmp+1) && atoi(tmp+1) )
+                    mp4disk.total = atoi(tmp+1);
+
+                // Fill disc_number
+                *tmp = '\0';
+                mp4disk.index = atoi(dn_tmp);
+            }else
+            {
+                mp4disk.index = atoi(FileTag->disc_number);
+            }
+            g_free(dn_tmp);
+        }
+        /*if (FileTag->disc_number)
+            mp4disk.index = atoi(FileTag->disc_number);
+        if (FileTag->disc_number_total)
+            mp4disk.total = atoi(FileTag->disc_number_total);
+        */
+    }
+    priv->mp4v2_tagssetdisk (mp4tags, &mp4disk);
 
     /********
      * Year *
      ********/
     if (FileTag->year && g_utf8_strlen(FileTag->year, -1) > 0)
     {
-        taglib_tag_set_year(tag,atoi(FileTag->year));
+        priv->mp4v2_tagssetreleasedate (mp4tags, FileTag->year);
     }else
     {
-        taglib_tag_set_year(tag,0);
+        priv->mp4v2_tagssetreleasedate (mp4tags, "");
     }
 
     /*************************
      * Track and Total Track *
      *************************/
-    if ( FileTag->track && g_utf8_strlen(FileTag->track, -1) > 0 )
-    {
-        taglib_tag_set_track(tag,atoi(FileTag->track));
-    }else
+    if ( (FileTag->track       && g_utf8_strlen(FileTag->track, -1) > 0)
+    ||   (FileTag->track_total && g_utf8_strlen(FileTag->track_total, -1) > 0) )
     {
-        taglib_tag_set_track(tag,0);
+        if (FileTag->track)
+            mp4track.index = atoi(FileTag->track);
+        if (FileTag->track_total)
+            mp4track.total = atoi(FileTag->track_total);
     }
+    priv->mp4v2_tagssettrack (mp4tags, &mp4track);
 
     /*********
      * Genre *
      *********/
     if (FileTag->genre && g_utf8_strlen(FileTag->genre, -1) > 0 )
     {
-        taglib_tag_set_genre(tag,FileTag->genre);
+        priv->mp4v2_tagssetgenre (mp4tags, FileTag->genre);
     }else
     {
-        taglib_tag_set_genre(tag,"");
+        //MP4DeleteMetadataGenre(mp4tags);
+        priv->mp4v2_tagssetgenre (mp4tags, "");
     }
 
     /***********
@@ -270,28 +549,73 @@ gboolean Mp4tag_Write_File_Tag (ET_File *ETFile)
      ***********/
     if (FileTag->comment && g_utf8_strlen(FileTag->comment, -1) > 0)
     {
-        taglib_tag_set_comment(tag,FileTag->comment);
+        priv->mp4v2_tagssetcomments (mp4tags, FileTag->comment);
     }else
     {
-        taglib_tag_set_comment(tag,"");
+        priv->mp4v2_tagssetcomments (mp4tags, "");
     }
 
     /**********************
      * Composer or Writer *
      **********************/
+    if (FileTag->composer && g_utf8_strlen(FileTag->composer, -1) > 0)
+    {
+        priv->mp4v2_tagssetcomposer (mp4tags, FileTag->composer);
+    }else
+    {
+        priv->mp4v2_tagssetcomposer (mp4tags, "");
+    }
 
     /*****************
      * Encoding Tool *
      *****************/
+    if (FileTag->encoded_by && g_utf8_strlen(FileTag->encoded_by, -1) > 0)
+    {
+        priv->mp4v2_tagssetencodedby (mp4tags, FileTag->encoded_by);
+    }else
+    {
+        priv->mp4v2_tagssetencodedby (mp4tags, "");
+    }
 
     /***********
      * Picture *
      ***********/
+    {
+        // Can handle only one picture...
+        Picture *pic;
+        if (mp4tags->artworkCount && mp4tags->artwork)
+            priv->mp4v2_removeartwork (mp4tags, 0);
+        priv->mp4v2_setartwork (mp4tags, 0, NULL);
+        for (pic = FileTag->picture; pic; pic = pic->next)
+        {
+            if (pic->type == ET_PICTURE_TYPE_FRONT_COVER)
+            {
+                 mp4artwork.data = pic->data;
+                 mp4artwork.size = pic->size;
+                 switch (pic->type) {
+                  case PICTURE_FORMAT_JPEG:
+                     mp4artwork.type = MP4_ART_JPEG;
+                     break;
+                  case PICTURE_FORMAT_PNG:
+                     mp4artwork.type = MP4_ART_PNG;
+                     break;
+                  default:
+                     mp4artwork.type = MP4_ART_UNDEFINED;
+                 }
+                 if (mp4tags->artworkCount)
+                     priv->mp4v2_setartwork (mp4tags, 0, &mp4artwork);
+                 else
+                     priv->mp4v2_addartwork (mp4tags, &mp4artwork);
+            }
+        }
+    }
 
-    success = taglib_file_save (mp4file) ? TRUE : FALSE;
-    taglib_file_free(mp4file);
+    priv->mp4v2_tagsstore (mp4tags, mp4file);
+    priv->mp4v2_tagsfree (mp4tags);
+    priv->mp4v2_close (mp4file, 0);
 
-    return success;
+    if (error) return FALSE;
+    else       return TRUE;
 }
 
 
diff --git a/src/mp4_tag.h b/src/mp4_tag.h
index 2b426c2..303610d 100644
--- a/src/mp4_tag.h
+++ b/src/mp4_tag.h
@@ -20,22 +20,41 @@
  */
 
 
-#ifndef __MP4_TAG_H__
-#define __MP4_TAG_H__
-
+#ifndef ET_MP4_TAG_H_
+#define ET_MP4_TAG_H_
 
 #include "et_core.h"
 
-/****************
- * Declarations *
- ****************/
+G_BEGIN_DECLS
+
+#define ET_TYPE_MP4_TAG (et_mp4_tag_get_type ())
+#define ET_MP4_TAG(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ET_TYPE_MP4_TAG, EtMP4Tag))
+#define ET_IS_MP4_TAG(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ET_TYPE_MP4_TAG))
+
+typedef struct _EtMP4Tag EtMP4Tag;
+typedef struct _EtMP4TagClass EtMP4TagClass;
+typedef struct _EtMP4TagPrivate EtMP4TagPrivate;
+
+struct _EtMP4Tag
+{
+    /*< private >*/
+    GObject parent_instance;
+    EtMP4TagPrivate *priv;
+};
+
+struct _EtMP4TagClass
+{
+    /*< private >*/
+    GObjectClass parent_class;
+};
 
+GType et_mp4_tag_get_type (void);
+gboolean et_mp4_tag_load (EtMP4Tag *tag);
 
+gboolean Mp4tag_Read_File_Tag  (EtMP4Tag *tag, gchar *filename,
+                                File_Tag *FileTag);
+gboolean Mp4tag_Write_File_Tag (EtMP4Tag *tag, ET_File *ETFile);
 
-/**************
- * Prototypes *
- **************/
-gboolean Mp4tag_Read_File_Tag  (gchar *filename, File_Tag *FileTag);
-gboolean Mp4tag_Write_File_Tag (ET_File *ETFile);
+G_END_DECLS
 
-#endif /* __MP4_TAG_H__ */
+#endif /* !ET_MP4_TAG_H_ */
diff --git a/src/mp4_tag_private.h b/src/mp4_tag_private.h
new file mode 100644
index 0000000..7515511
--- /dev/null
+++ b/src/mp4_tag_private.h
@@ -0,0 +1,79 @@
+/* EasyTAG - tag editor for audio files
+ * Copyright (C) 2013  David King <amigadave amigadave com>
+ *
+ * This program 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 program 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 program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <mp4v2/mp4v2.h>
+
+struct _EtMP4TagPrivate
+{
+    /*< private >*/
+    GModule *module;
+
+    MP4FileHandle (*mp4v2_read) (const gchar *filename);
+    MP4FileHandle (*mp4v2_modify) (const gchar *filename, guint32 flags);
+    void (*mp4v2_close) (MP4FileHandle handle, guint32 flags);
+
+    const MP4Tags* (*mp4v2_tagsalloc) (void);
+    gboolean (*mp4v2_tagsfetch) (const MP4Tags *tags, MP4FileHandle handle);
+    void (*mp4v2_tagsfree) (const MP4Tags *tags);
+    gboolean (*mp4v2_tagsstore) (const MP4Tags *tags, MP4FileHandle handle);
+
+    gboolean (*mp4v2_tagssetname) (const MP4Tags *tags, const gchar *str);
+    gboolean (*mp4v2_tagssetartist) (const MP4Tags *tags, const gchar *str);
+    gboolean (*mp4v2_tagssetalbum) (const MP4Tags *tags, const gchar *str);
+    gboolean (*mp4v2_tagssetalbumartist) (const MP4Tags *tags,
+                                          const gchar *str);
+    gboolean (*mp4v2_tagssetdisk) (const MP4Tags *tags,
+                                   const MP4TagDisk *disk);
+    gboolean (*mp4v2_tagssetreleasedate) (const MP4Tags *tags,
+                                          const gchar *str);
+    gboolean (*mp4v2_tagssettrack) (const MP4Tags *tags,
+                                    const MP4TagTrack *track);
+    gboolean (*mp4v2_tagssetgenre) (const MP4Tags *tags, const gchar *str);
+    gboolean (*mp4v2_tagssetcomments) (const MP4Tags *tags, const gchar *str);
+    gboolean (*mp4v2_tagssetcomposer) (const MP4Tags *tags, const gchar *str);
+    gboolean (*mp4v2_tagssetencodedby) (const MP4Tags *tags, const gchar *str);
+
+    gboolean (*mp4v2_addartwork) (const MP4Tags *tags,
+                                  const MP4TagArtwork *art);
+    gboolean (*mp4v2_setartwork) (const MP4Tags *tags, guint32 artwork,
+                                  const MP4TagArtwork *art);
+    gboolean (*mp4v2_removeartwork) (const MP4Tags *tags, guint32 artwork);
+
+    const gchar *(*mp4v2_gettrackmediadataname) (MP4FileHandle handle,
+                                                 MP4TrackId id);
+    guint8 (*mp4v2_gettrackesdsobjecttypeid) (MP4FileHandle handle,
+                                              MP4TrackId trackId);
+    gboolean (*mp4v2_gettrackesconfiguration) (MP4FileHandle handle,
+                                               MP4TrackId id,
+                                               guint8 **ppconfig,
+                                               guint32 *pconfigsize);
+    guint32 (*mp4v2_getnumberoftracks) (MP4FileHandle handle,
+                                        const gchar *type, guint8 subtype);
+    MP4TrackId (*mp4v2_findtrackid) (MP4FileHandle handle, guint16 index,
+                                     const gchar *type, guint8 subtype);
+    guint32 (*mp4v2_gettrackbitrate) (MP4FileHandle handle, MP4TrackId id);
+    guint32 (*mp4v2_gettracktimescale) (MP4FileHandle handle, MP4TrackId id);
+    int (*mp4v2_gettrackaudiochannels) (MP4FileHandle handle, MP4TrackId id);
+    MP4Duration (*mp4v2_gettrackduration) (MP4FileHandle handle,
+                                           MP4TrackId id);
+    guint64 (*mp4v2_convertfromtrackduration) (MP4FileHandle handle,
+                                               MP4TrackId id,
+                                               MP4Duration duration,
+                                               guint32 timescale);
+};



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]