[vlc-devel] [PATCH v2] demux: wav: support parsing INFO chunk

Zhao, Gang gang.zhao.42 at gmail.com
Sun Sep 13 04:20:49 CEST 2020


Parse INFO chunk and save meta data to vlc.

Feature ticket: https://trac.videolan.org/vlc/ticket/6587

Signed-off-by: Zhao, Gang <gang.zhao.42 at gmail.com>
---
Tested this patch with the four wav files in ticket 6587. It can get and save
meta data from INFO chunk.

v2:
Fixed a typo in commit log

 modules/demux/wav.c | 216 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 207 insertions(+), 9 deletions(-)

diff --git modules/demux/wav.c modules/demux/wav.c
index 4b0623412f..75b37d5be0 100644
--- modules/demux/wav.c
+++ modules/demux/wav.c
@@ -36,6 +36,8 @@
 #include <vlc_demux.h>
 #include <vlc_aout.h>
 #include <vlc_codecs.h>
+#include <vlc_charset.h>
+#include <vlc_meta.h>
 
 #include "windows_audio_commons.h"
 
@@ -58,26 +60,96 @@ typedef struct
     uint32_t i_channel_mask;
     uint8_t i_chans_to_reorder;            /* do we need channel reordering */
     uint8_t pi_chan_table[AOUT_CHAN_MAX];
+
+    vlc_meta_t    *p_meta;
 } demux_sys_t;
 
 enum wav_chunk_id {
+    wav_chunk_id_list,
     wav_chunk_id_data,
     wav_chunk_id_ds64,
     wav_chunk_id_fmt,
+
+    wav_info_chunk_id_iarl,
+    wav_info_chunk_id_iart,
+    wav_info_chunk_id_icms,
+    wav_info_chunk_id_icmt,
+    wav_info_chunk_id_icop,
+    wav_info_chunk_id_icrd,
+    wav_info_chunk_id_icrp,
+    wav_info_chunk_id_idim,
+    wav_info_chunk_id_idpi,
+    wav_info_chunk_id_ieng,
+    wav_info_chunk_id_ignr,
+    wav_info_chunk_id_ikey,
+    wav_info_chunk_id_ilgt,
+    wav_info_chunk_id_imed,
+    wav_info_chunk_id_inam,
+    wav_info_chunk_id_iplt,
+    wav_info_chunk_id_iprd,
+    wav_info_chunk_id_isbj,
+    wav_info_chunk_id_isft,
+    wav_info_chunk_id_ishp,
+    wav_info_chunk_id_isrc,
+    wav_info_chunk_id_isrf,
+    wav_info_chunk_id_itch,
 };
 
-static const struct wav_chunk_id_key
+struct wav_chunk_id_key
 {
     enum wav_chunk_id id;
     char key[5];
-} wav_chunk_id_key_list[] =  {
+};
+
+static const struct wav_chunk_id_key wav_chunk_id_key_list[] =  {
     /* Alphabetical order */
+    { wav_chunk_id_list, "LIST" },
     { wav_chunk_id_data, "data" },
     { wav_chunk_id_ds64, "ds64" },
     { wav_chunk_id_fmt,  "fmt " },
 };
 static const size_t wav_chunk_id_key_count = ARRAY_SIZE(wav_chunk_id_key_list);
 
+static const struct wav_chunk_id_key wav_info_chunk_id_key_list[] =  {
+    /* Alphabetical order */
+    { wav_info_chunk_id_iarl, "IARL" },
+    { wav_info_chunk_id_iart, "IART" },
+    { wav_info_chunk_id_icms, "ICMS" },
+    { wav_info_chunk_id_icmt, "ICMT" },
+    { wav_info_chunk_id_icop, "ICOP" },
+    { wav_info_chunk_id_icrd, "ICRD" },
+    { wav_info_chunk_id_icrp, "ICRP" },
+    { wav_info_chunk_id_idim, "IDIM" },
+    { wav_info_chunk_id_idpi, "IDPI" },
+    { wav_info_chunk_id_ieng, "IENG" },
+    { wav_info_chunk_id_ignr, "IGNR" },
+    { wav_info_chunk_id_ikey, "IKEY" },
+    { wav_info_chunk_id_ilgt, "ILGT" },
+    { wav_info_chunk_id_imed, "IMED" },
+    { wav_info_chunk_id_inam, "INAM" },
+    { wav_info_chunk_id_iplt, "IPLT" },
+    { wav_info_chunk_id_iprd, "IPRD" },
+    { wav_info_chunk_id_isbj, "ISBJ" },
+    { wav_info_chunk_id_isft, "ISFT" },
+    { wav_info_chunk_id_ishp, "ISHP" },
+    { wav_info_chunk_id_isrc, "ISRC" },
+    { wav_info_chunk_id_isrf, "ISRF" },
+    { wav_info_chunk_id_itch, "ITCH" },
+};
+static const size_t wav_info_chunk_id_key_count = ARRAY_SIZE(wav_info_chunk_id_key_list);
+
+static const char *wav_info_chunk_id_to_key(enum wav_chunk_id id)
+{
+    if ( id < wav_info_chunk_id_iarl || id > wav_info_chunk_id_itch )
+        return "";
+
+    for ( int i = 0; i < wav_info_chunk_id_key_count; i++)
+        if (wav_info_chunk_id_key_list[i].id == id)
+            return wav_info_chunk_id_key_list[i].key;
+
+    return "";
+}
+
 static int
 wav_chunk_CompareCb(const void *a, const void *b)
 {
@@ -190,13 +262,14 @@ static int ChunkSkip( demux_t *p_demux, uint32_t i_size )
 }
 
 static int ChunkGetNext( demux_t *p_demux, enum wav_chunk_id *p_id,
-                         uint32_t *pi_size )
+                         uint32_t *pi_size,
+                         const struct wav_chunk_id_key *id_key_list, size_t count )
 {
 #ifndef NDEBUG
     /* assert that keys are in alphabetical order */
-    for( size_t i = 0; i < wav_chunk_id_key_count - 1; ++i )
-        assert( strcmp( wav_chunk_id_key_list[i].key,
-                        wav_chunk_id_key_list[i + 1].key ) < 0 );
+    for( size_t i = 0; i < count - 1; ++i )
+        assert( strcmp( id_key_list[i].key,
+                        id_key_list[i + 1].key ) < 0 );
 #endif
 
     for( ;; )
@@ -206,8 +279,8 @@ static int ChunkGetNext( demux_t *p_demux, enum wav_chunk_id *p_id,
             return VLC_EGENERIC;
 
         const struct wav_chunk_id_key *id =
-            bsearch( p_peek, wav_chunk_id_key_list, wav_chunk_id_key_count,
-                     sizeof(*wav_chunk_id_key_list), wav_chunk_CompareCb );
+            bsearch( p_peek, id_key_list, count,
+                     sizeof(*id_key_list), wav_chunk_CompareCb );
         uint32_t i_size = GetDWLE( p_peek + 4 );
 
         if( id == NULL )
@@ -317,6 +390,10 @@ static void Close ( vlc_object_t * p_this )
     demux_sys_t *p_sys   = p_demux->p_sys;
 
     es_format_Clean( &p_sys->fmt );
+
+    if( p_sys->p_meta )
+        vlc_meta_Delete( p_sys->p_meta );
+
     free( p_sys );
 }
 
@@ -591,6 +668,114 @@ error:
     return VLC_EGENERIC;
 }
 
+static int ChunkParseINFO( demux_t *p_demux, uint32_t i_size )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const uint8_t *p_peek;
+    enum wav_chunk_id id;
+    uint32_t chunk_size;
+    int meta_type;
+    char *meta_value;
+
+    if( (p_sys->p_meta = vlc_meta_New()) == NULL )
+    {
+        msg_Err( p_demux, "vlc_meta_New failed" );
+        return VLC_EGENERIC;
+    }
+
+    while (i_size > 0) {
+        int ret = ChunkGetNext( p_demux, &id, &chunk_size,
+                                wav_info_chunk_id_key_list,
+                                wav_info_chunk_id_key_count );
+        if ( ret != VLC_SUCCESS )
+            break;
+
+        /* chunk id(4 bytes) and chunk size(4 bytes) consumed by ChunkGetNext */
+        i_size -= 8;
+
+        if( chunk_size == 0 )
+        {
+            msg_Err( p_demux, "invalid chunk with a size 0" );
+            return VLC_EGENERIC;
+        }
+
+        if( vlc_stream_Peek( p_demux->s, &p_peek, chunk_size ) != chunk_size )
+            return VLC_EGENERIC;
+
+        meta_value = strndup( (const char *)p_peek, chunk_size );
+        if (meta_value == NULL)
+            return VLC_EGENERIC;
+
+        EnsureUTF8( meta_value );
+
+        meta_type = -1;
+        switch( id )
+        {
+        case wav_info_chunk_id_iart:
+            meta_type = vlc_meta_Artist;
+            break;
+        case wav_info_chunk_id_icop:
+            meta_type = vlc_meta_Copyright;
+            break;
+        case wav_info_chunk_id_icrd:
+            meta_type = vlc_meta_Date;
+            break;
+        case wav_info_chunk_id_ignr:
+            meta_type = vlc_meta_Genre;
+            break;
+        case wav_info_chunk_id_inam:
+            meta_type = vlc_meta_Title;
+            break;
+        default:
+            /* no relevant meta type in vlc */
+            break;
+        }
+
+        msg_Dbg( p_demux, "id: %s meta_type: %d meta_value: %s\n",
+                 wav_info_chunk_id_to_key(id), meta_type, meta_value );
+
+        if (meta_type != -1)
+            vlc_meta_Set( p_sys->p_meta, meta_type, meta_value );
+        else
+            vlc_meta_AddExtra( p_sys->p_meta,
+                               wav_info_chunk_id_key_list[id].key, meta_value );
+
+        free( meta_value );
+        if( ChunkSkip( p_demux, chunk_size ) != VLC_SUCCESS )
+            return VLC_EGENERIC;
+
+        i_size -= chunk_size;
+    }
+
+    return VLC_SUCCESS;
+}
+
+static int ChunkParseLIST( demux_t *p_demux, uint32_t i_size )
+{
+    const uint8_t *p_peek;
+
+    /* must have four bytes list type */
+    if( i_size < 4 )
+    {
+        msg_Err( p_demux, "invalid 'LIST' chunk" );
+        return VLC_EGENERIC;
+    }
+
+    if( vlc_stream_Peek( p_demux->s, &p_peek, 4 ) != 4 )
+        return VLC_EGENERIC;
+
+    /* only care about INFO chunk */
+    if ( memcmp(p_peek, "INFO", 4) != 0 ) {
+        return ChunkSkip( p_demux, i_size );
+    }
+
+    if ( vlc_stream_Read( p_demux->s, NULL, 4) != 4 )
+        return VLC_EGENERIC;
+
+    i_size -= 4;
+    return ChunkParseINFO( p_demux, i_size );
+}
+
 static int Open( vlc_object_t * p_this )
 {
     demux_t     *p_demux = (demux_t*)p_this;
@@ -629,8 +814,13 @@ static int Open( vlc_object_t * p_this )
 
     bool eof = false;
     enum wav_chunk_id id;
-    while( !eof && ( ChunkGetNext( p_demux, &id, &i_size ) ) == VLC_SUCCESS )
+    while( !eof )
     {
+        int ret = ChunkGetNext( p_demux, &id, &i_size,
+                                wav_chunk_id_key_list, wav_chunk_id_key_count );
+        if (ret != VLC_SUCCESS )
+            break;
+
         if( i_size == 0 )
         {
             msg_Err( p_demux, "invalid chunk with a size 0");
@@ -676,6 +866,14 @@ static int Open( vlc_object_t * p_this )
                 if( ChunkParseFmt( p_demux, i_size ) != VLC_SUCCESS )
                     goto error;
                 break;
+            case wav_chunk_id_list:
+                if( ChunkParseLIST( p_demux, i_size ) != VLC_SUCCESS )
+                    goto error;
+                break;
+            default:
+                if( ChunkSkip( p_demux, i_size ) != VLC_SUCCESS )
+                    goto error;
+                break;
         }
     }
 
-- 
2.25.1



More information about the vlc-devel mailing list