Parse meta data at ogg level.
authorLaurent Aimar <fenrir@videolan.org>
Sat, 6 Dec 2008 17:38:41 +0000 (18:38 +0100)
committerLaurent Aimar <fenrir@videolan.org>
Tue, 9 Dec 2008 20:13:02 +0000 (21:13 +0100)
It will allows to remove ugly hack in decoder.

modules/demux/flac.c
modules/demux/ogg.c
modules/demux/vorbis.h [new file with mode: 0644]

index 5203cff..17b45ff 100644 (file)
@@ -37,6 +37,7 @@
 #include <vlc_codec.h>
 #include <assert.h>
 #include <vlc_charset.h>
+#include "vorbis.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -567,88 +568,12 @@ static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, int i_data,
 static void ParseComment( demux_t *p_demux, const uint8_t *p_data, int i_data )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
-    int n;
-    int i_comment;
-
-    if( i_data < 8 )
-        return;
-
-    RM(4);
-
-    n = GetDWLE(p_data); RM(4);
-    if( n < 0 || n > i_data )
-        return;
-#if 0
-    if( n > 0 )
-    {
-        /* TODO report vendor string ? */
-        char *psz_vendor = psz_vendor = strndup( p_data, n );
-        msg_Dbg( p_demux, "FLAC: COMMENT vendor length=%d vendor=%s\n", n, psz_vendor );
-        free( psz_vendor );
-    }
-#endif
-    RM(n);
 
     if( i_data < 4 )
         return;
 
-    i_comment = GetDWLE(p_data); RM(4);
-    if( i_comment <= 0 )
-        return;
-
-    p_sys->p_meta = vlc_meta_New();
+    vorbis_ParseComment( &p_sys->p_meta, &p_data[4], i_data - 4 );
 
-    for( ; i_comment > 0; i_comment-- )
-    {
-        char *psz;
-        if( i_data < 4 )
-            break;
-        n = GetDWLE(p_data); RM(4);
-        if( n > i_data )
-            break;
-        if( n <= 0 )
-            continue;
-
-        psz = strndup( (const char*)p_data, n );
-        RM(n);
-
-        EnsureUTF8( psz );
-
-#define IF_EXTRACT(txt,var) \
-    if( !strncasecmp(psz, txt, strlen(txt)) ) \
-    { \
-        const char *oldval = vlc_meta_Get( p_sys->p_meta, vlc_meta_ ## var ); \
-        if( oldval ) \
-        { \
-            char * newval; \
-            if( asprintf( &newval, "%s,%s", oldval, &psz[strlen(txt)] ) == -1 ) \
-                newval = NULL; \
-            vlc_meta_Set( p_sys->p_meta, vlc_meta_ ## var, newval ); \
-            free( newval ); \
-        } \
-        else \
-            vlc_meta_Set( p_sys->p_meta, vlc_meta_ ## var, &psz[strlen(txt)] ); \
-    }
-        IF_EXTRACT("TITLE=", Title )
-        else IF_EXTRACT("ALBUM=", Album )
-        else IF_EXTRACT("TRACKNUMBER=", TrackNumber )
-        else IF_EXTRACT("ARTIST=", Artist )
-        else IF_EXTRACT("COPYRIGHT=", Copyright )
-        else IF_EXTRACT("DESCRIPTION=", Description )
-        else IF_EXTRACT("GENRE=", Genre )
-        else IF_EXTRACT("DATE=", Date )
-        else if( strchr( psz, '=' ) )
-        {
-            /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC,
-             * undocumented tags and replay gain ) */
-            char *p = strchr( psz, '=' );
-            *p++ = '\0';
-            vlc_meta_AddExtra( p_sys->p_meta, psz, p );
-        }
-#undef IF_EXTRACT
-        free( psz );
-    }
-#undef RM
 }
 
 static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, int i_data )
index 2ba2b01..577934c 100644 (file)
@@ -39,6 +39,8 @@
 
 #include <vlc_codecs.h>
 #include <vlc_bits.h>
+#include <vlc_charset.h>
+#include "vorbis.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -119,6 +121,9 @@ struct demux_sys_t
 
     /* after reading all headers, the first data page is stuffed into the relevant stream, ready to use */
     bool    b_page_waiting;
+
+    /* */
+    vlc_meta_t *p_meta;
 };
 
 /* OggDS headers for the new header format (used in ogm files) */
@@ -188,6 +193,9 @@ static void Ogg_EndOfStream( demux_t *p_demux );
 static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_stream );
 static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream );
 
+/* */
+static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8_t *p_headers, int i_headers );
+
 /* Logical bitstream headers */
 static void Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * );
 static void Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * );
@@ -234,6 +242,9 @@ static int Open( vlc_object_t * p_this )
     ogg_sync_init( &p_sys->oy );
     p_sys->b_page_waiting = false;
 
+    /* */
+    p_sys->p_meta = NULL;
+
     return VLC_SUCCESS;
 }
 
@@ -426,12 +437,19 @@ static int Demux( demux_t * p_demux )
 static int Control( demux_t *p_demux, int i_query, va_list args )
 {
     demux_sys_t *p_sys  = p_demux->p_sys;
+    vlc_meta_t *p_meta;
     int64_t *pi64;
     bool *pb_bool;
     int i;
 
     switch( i_query )
     {
+        case DEMUX_GET_META:
+            p_meta = (vlc_meta_t *)va_arg( args, vlc_meta_t* );
+            if( p_sys->p_meta )
+                vlc_meta_Merge( p_meta, p_sys->p_meta );
+            return VLC_SUCCESS;
+
         case DEMUX_HAS_UNSUPPORTED_META:
             pb_bool = (bool*)va_arg( args, bool* );
             *pb_bool = true;
@@ -674,6 +692,10 @@ static void Ogg_DecodePacket( demux_t *p_demux,
                     es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT,
                                     p_stream->p_es, &p_stream->fmt );
 
+                if( p_stream->i_headers > 0 )
+                    Ogg_ExtractMeta( p_demux, p_stream->fmt.i_codec,
+                                     p_stream->p_headers, p_stream->i_headers );
+
                 /* we're not at BOS anymore for this logical stream */
                 p_ogg->i_bos--;
             }
@@ -1396,6 +1418,11 @@ static void Ogg_EndOfStream( demux_t *p_demux )
     p_ogg->i_bitrate = 0;
     p_ogg->i_streams = 0;
     p_ogg->pp_stream = NULL;
+
+    /* */
+    if( p_ogg->p_meta )
+        vlc_meta_Delete( p_ogg->p_meta );
+    p_ogg->p_meta = NULL;
 }
 
 /**
@@ -1463,6 +1490,66 @@ static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *
 
     return !b_compatible;
 }
+static void Ogg_ExtractXiphMeta( demux_t *p_demux, const uint8_t *p_headers, int i_headers, int i_skip )
+{
+    demux_sys_t *p_ogg = p_demux->p_sys;
+
+    if( i_headers <= 2 )
+        return;
+
+    /* Skip first packet */
+    const int i_tmp = GetWBE( &p_headers[0] );
+    if( i_tmp > i_headers-2 )
+        return;
+    p_headers += 2 + i_tmp;
+    i_headers -= 2 + i_tmp;
+
+    if( i_headers <= 2 )
+        return;
+
+    /* */
+    int i_comment = GetWBE( &p_headers[0] );
+    const uint8_t *p_comment = &p_headers[2];
+    if( i_comment > i_headers - 2 )
+        return;
+
+    if( i_comment <= i_skip )
+        return;
+
+    /* TODO how to handle multiple comments properly ? */
+    vorbis_ParseComment( &p_ogg->p_meta, &p_comment[i_skip], i_comment - i_skip );
+}
+static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8_t *p_headers, int i_headers )
+{
+    demux_sys_t *p_ogg = p_demux->p_sys;
+
+    switch( i_codec )
+    {
+    /* 3 headers with the 2° one being the comments */
+    case VLC_FOURCC( 'v','o','r','b' ):
+        Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6 );
+        break;
+    case VLC_FOURCC( 't','h','e','o' ):
+        Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6 );
+        break;
+    case VLC_FOURCC( 's','p','x',' ' ):
+        Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 0 );
+        break;
+
+    /* TODO */
+    case VLC_FOURCC( 'k','a','t','e' ):
+    case VLC_FOURCC( 'f','l','a','c' ):
+    case VLC_FOURCC( 'c','m','m','l' ):
+        msg_Warn( p_demux, "Ogg_ExtractMeta does not support %4.4s", (const char*)&i_codec );
+        break;
+    /* No meta data */
+    case VLC_FOURCC( 'd','r','a','c' ):
+    default:
+        break;
+    }
+    if( p_ogg->p_meta )
+        p_demux->info.i_update |= INPUT_UPDATE_META;
+}
 
 static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream,
                                   ogg_packet *p_oggpacket )
diff --git a/modules/demux/vorbis.h b/modules/demux/vorbis.h
new file mode 100644 (file)
index 0000000..6a96fa1
--- /dev/null
@@ -0,0 +1,111 @@
+/*****************************************************************************
+ * vorbis.h: Vorbis Comment parser
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *
+ * 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.
+ *****************************************************************************/
+
+static inline void vorbis_ParseComment( vlc_meta_t **pp_meta, const uint8_t *p_data, int i_data )
+{
+    int n;
+    int i_comment;
+    if( i_data < 8 )
+        return;
+
+#define RM(x) do { i_data -= (x); p_data += (x); } while(0)
+    n = GetDWLE(p_data); RM(4);
+    if( n < 0 || n > i_data )
+        return;
+#if 0
+    if( n > 0 )
+    {
+        /* TODO report vendor string ? */
+        char *psz_vendor = psz_vendor = strndup( p_data, n );
+        free( psz_vendor );
+    }
+#endif
+    RM(n);
+
+    if( i_data < 4 )
+        return;
+
+    i_comment = GetDWLE(p_data); RM(4);
+    if( i_comment <= 0 )
+        return;
+
+    /* */
+    vlc_meta_t *p_meta = *pp_meta;
+    if( !p_meta )
+        *pp_meta = p_meta = vlc_meta_New();
+    if( !p_meta )
+        return;
+
+    for( ; i_comment > 0; i_comment-- )
+    {
+        char *psz;
+        if( i_data < 4 )
+            break;
+        n = GetDWLE(p_data); RM(4);
+        if( n > i_data )
+            break;
+        if( n <= 0 )
+            continue;
+
+        psz = strndup( (const char*)p_data, n );
+        RM(n);
+
+        EnsureUTF8( psz );
+
+#define IF_EXTRACT(txt,var) \
+    if( !strncasecmp(psz, txt, strlen(txt)) ) \
+    { \
+        const char *oldval = vlc_meta_Get( p_meta, vlc_meta_ ## var ); \
+        if( oldval ) \
+        { \
+            char * newval; \
+            if( asprintf( &newval, "%s,%s", oldval, &psz[strlen(txt)] ) == -1 ) \
+                newval = NULL; \
+            vlc_meta_Set( p_meta, vlc_meta_ ## var, newval ); \
+            free( newval ); \
+        } \
+        else \
+            vlc_meta_Set( p_meta, vlc_meta_ ## var, &psz[strlen(txt)] ); \
+    }
+        IF_EXTRACT("TITLE=", Title )
+        else IF_EXTRACT("ALBUM=", Album )
+        else IF_EXTRACT("TRACKNUMBER=", TrackNumber )
+        else IF_EXTRACT("ARTIST=", Artist )
+        else IF_EXTRACT("COPYRIGHT=", Copyright )
+        else IF_EXTRACT("DESCRIPTION=", Description )
+        else IF_EXTRACT("GENRE=", Genre )
+        else IF_EXTRACT("DATE=", Date )
+        else if( strchr( psz, '=' ) )
+        {
+            /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC,
+             * undocumented tags and replay gain ) */
+            char *p = strchr( psz, '=' );
+            *p++ = '\0';
+            vlc_meta_AddExtra( p_meta, psz, p );
+        }
+#undef IF_EXTRACT
+        free( psz );
+    }
+#undef RM
+}
+