/***************************************************************************** * ParseID3Tag : parse an id3tag into the info structures *****************************************************************************/ static void ParseID3Tag( demux_meta_t *p_demux_meta, const uint8_t *p_data, int i_size ) { struct id3_tag *p_id3_tag; struct id3_frame *p_frame; vlc_meta_t *p_meta; int i; p_id3_tag = id3_tag_parse( p_data, i_size ); if( !p_id3_tag ) return; if( !p_demux_meta->p_meta ) p_demux_meta->p_meta = vlc_meta_New(); p_meta = p_demux_meta->p_meta; #define ID_IS( a ) (!strcmp( p_frame->id, a )) #define DESCR_IS( a) strstr( (char*)p_frame->description, a ) #define GET_STRING(frame,fidx) id3_ucs4_latin1duplicate( id3_field_getstring( &(frame)->fields[fidx] ) ) /* */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "UFID", i )) != NULL; i++ ) { const char *psz_owner = id3_field_getlatin1( &p_frame->fields[0] ); if( !strncmp( psz_owner, "http://musicbrainz.org", 22 ) ) { id3_byte_t const * p_ufid; id3_length_t i_ufidlen; p_ufid = id3_field_getbinarydata( &p_frame->fields[1], &i_ufidlen ); char *psz_ufid = strndup( p_ufid, i_ufidlen ); vlc_meta_SetTrackID( p_meta, psz_ufid ); free( psz_ufid ); } } /* User defined text (TXXX) */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "TXXX", i )) != NULL; i++ ) { /* 3 fields: 'encoding', 'description', 'value' */ char *psz_name = GET_STRING( p_frame, 1 ); char *psz_value = GET_STRING( p_frame, 2 ); vlc_meta_AddExtra( p_meta, psz_name, psz_value ); #if 0 if( !strncmp( psz_name, "MusicBrainz Artist Id", 21 ) ) vlc_meta_SetArtistID( p_meta, psz_value ); if( !strncmp( psz_desc, "MusicBrainz Album Id", 20 ) ) vlc_meta_SetAlbumID( p_meta, psz_value ); #endif free( psz_name ); free( psz_value ); } /* Relative volume adjustment */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "RVA2", i )) != NULL; i++ ) { /* 2 fields: 'latin1', 'binary' */ const char *psz_type = id3_field_getlatin1( &p_frame->fields[0] ); if( !strcasecmp( psz_type, "track" ) || !strcasecmp( psz_type, "album" ) || !strcasecmp( psz_type, "normalize" ) ) { id3_byte_t const * p_data; id3_length_t i_data; p_data = id3_field_getbinarydata( &p_frame->fields[1], &i_data ); while( i_data >= 4 ) { const unsigned int i_peak_size = p_data[3]; const float f_temp = GetWBE( &p_data[1] ); const float f_gain = f_temp / 512.0; char psz_value[32]; if( i_data < i_peak_size + 4 ) break; /* only master volume */ if( p_data[0] == 0x01 ) { snprintf( psz_value, sizeof(psz_value), "%f", f_gain ); if( !strcasecmp( psz_type, "album" ) ) vlc_meta_AddExtra( p_meta, "REPLAYGAIN_ALBUM_GAIN", psz_value ); else vlc_meta_AddExtra( p_meta, "REPLAYGAIN_TRACK_GAIN", psz_value ); /* XXX I have no idea what peak unit is ... */ } i_data -= 4+i_peak_size; } } } /* TODO 'RGAD' if it is used somewhere */ /* T--- Text informations */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "T", i )) != NULL; i++ ) { unsigned i_strings; /* Special case TXXX is not the same beast */ if( ID_IS( "TXXX" ) ) continue; i_strings = id3_field_getnstrings( &p_frame->fields[1] ); while( i_strings > 0 ) { char *psz_temp = id3_ucs4_utf8duplicate( id3_field_getstrings( &p_frame->fields[1], --i_strings ) ); if( ID_IS( ID3_FRAME_GENRE ) ) { char *psz_endptr; int i_genre = strtol( psz_temp, &psz_endptr, 10 ); if( psz_temp != psz_endptr && i_genre >= 0 && i_genre < NUM_GENRES ) { vlc_meta_SetGenre( p_meta, ppsz_genres[atoi(psz_temp)]); } else { /* Unknown genre */ vlc_meta_SetGenre( p_meta,psz_temp ); } } else if( ID_IS( ID3_FRAME_TITLE ) ) { vlc_meta_SetTitle( p_meta, psz_temp ); } else if( ID_IS( ID3_FRAME_ARTIST ) ) { vlc_meta_SetArtist( p_meta, psz_temp ); } else if( ID_IS( ID3_FRAME_YEAR ) ) { vlc_meta_SetDate( p_meta, psz_temp ); } else if( ID_IS( ID3_FRAME_COMMENT ) ) { vlc_meta_SetDescription( p_meta, psz_temp ); } else if( DESCR_IS( "Copyright" ) ) { vlc_meta_SetCopyright( p_meta, psz_temp ); } else if( DESCR_IS( "Publisher" ) ) { vlc_meta_SetPublisher( p_meta, psz_temp ); } else if( DESCR_IS( "Track number/position in set" ) ) { vlc_meta_SetTrackNum( p_meta, psz_temp ); } else if( DESCR_IS( "Album/movie/show title" ) ) { vlc_meta_SetAlbum( p_meta, psz_temp ); } else if( DESCR_IS( "Encoded by" ) ) { vlc_meta_SetEncodedBy( p_meta, psz_temp ); } else if( ID_IS ( "APIC" ) ) { msg_Dbg( p_demux_meta, "** Has APIC **" ); } else if( p_frame->description ) { /* Unhandled meta */ vlc_meta_AddExtra( p_meta, (char*)p_frame->description, psz_temp ); } free( psz_temp ); } } id3_tag_delete( p_id3_tag ); #undef GET_STRING #undef DESCR_IS #undef ID_IS }
static int CdTextParse( vlc_meta_t ***ppp_tracks, int *pi_tracks, const uint8_t *p_buffer, int i_buffer ) { char *pppsz_info[128][0x10]; int i_track_last = -1; if( i_buffer < 4 ) return -1; memset( pppsz_info, 0, sizeof(pppsz_info) ); for( int i = 0; i < (i_buffer-4)/18; i++ ) { const uint8_t *p_block = &p_buffer[4 + 18*i]; char psz_text[12+1]; const int i_pack_type = p_block[0]; if( i_pack_type < 0x80 || i_pack_type > 0x8f ) continue; const int i_track_number = (p_block[1] >> 0)&0x7f; const int i_extension_flag = ( p_block[1] >> 7)& 0x01; if( i_extension_flag ) continue; //const int i_sequence_number = p_block[2]; //const int i_charater_position = (p_block[3] >> 0) &0x0f; //const int i_block_number = (p_block[3] >> 4) &0x07; /* TODO unicode support * I need a sample */ //const int i_unicode = ( p_block[3] >> 7)&0x01; //const int i_crc = (p_block[4+12] << 8) | (p_block[4+13] << 0); /* */ memcpy( psz_text, &p_block[4], 12 ); psz_text[12] = '\0'; /* */ int i_track = i_track_number; char *psz_track = &psz_text[0]; while( i_track <= 127 && psz_track < &psz_text[12] ) { //fprintf( stderr, "t=%d psz_track=%p end=%p", i_track, (void *)psz_track, (void *)&psz_text[12] ); if( *psz_track ) { astrcat( &pppsz_info[i_track][i_pack_type-0x80], psz_track ); i_track_last = __MAX( i_track_last, i_track ); } i_track++; psz_track += 1 + strlen(psz_track); } } if( i_track_last < 0 ) return -1; vlc_meta_t **pp_tracks = calloc( i_track_last+1, sizeof(*pp_tracks) ); if( !pp_tracks ) goto exit; for( int j = 0; j < 0x10; j++ ) { for( int i = 0; i <= i_track_last; i++ ) { /* */ if( pppsz_info[i][j] ) EnsureUTF8( pppsz_info[i][j] ); /* */ const char *psz_default = pppsz_info[0][j]; const char *psz_value = pppsz_info[i][j]; if( !psz_value && !psz_default ) continue; vlc_meta_t *p_track = pp_tracks[i]; if( !p_track ) { p_track = pp_tracks[i] = vlc_meta_New(); if( !p_track ) continue; } switch( j ) { case 0x00: /* Album/Title */ if( i == 0 ) { vlc_meta_SetAlbum( p_track, psz_value ); } else { if( psz_value ) vlc_meta_SetTitle( p_track, psz_value ); if( psz_default ) vlc_meta_SetAlbum( p_track, psz_default ); } break; case 0x01: /* Performer */ vlc_meta_SetArtist( p_track, psz_value ? psz_value : psz_default ); break; case 0x05: /* Messages */ vlc_meta_SetDescription( p_track, psz_value ? psz_value : psz_default ); break; case 0x07: /* Genre */ vlc_meta_SetGenre( p_track, psz_value ? psz_value : psz_default ); break; /* FIXME unsupported: * 0x02: songwriter * 0x03: composer * 0x04: arrenger * 0x06: disc id */ } } } /* */ exit: for( int j = 0; j < 0x10; j++ ) for( int i = 0; i <= i_track_last; i++ ) free( pppsz_info[i][j] ); *ppp_tracks = pp_tracks; *pi_tracks = i_track_last+1; return pp_tracks ? 0 : -1; }
static void ParseAPEvXTag( demux_meta_t *p_demux_meta, const uint8_t *p_data, int i_data ) { vlc_meta_t *p_meta; bool b_start; bool b_end; const uint8_t *p_header = NULL; int i_entry; if( i_data < APE_TAG_HEADERSIZE ) return; b_start = !strncmp( (char*)&p_data[0], "APETAGEX", 8 ); b_end = !strncmp( (char*)&p_data[i_data-APE_TAG_HEADERSIZE], "APETAGEX", 8 ); if( !b_end && !b_start ) return; if( !p_demux_meta->p_meta ) p_demux_meta->p_meta = vlc_meta_New(); p_meta = p_demux_meta->p_meta; if( b_start ) { p_header = &p_data[0]; p_data += APE_TAG_HEADERSIZE; i_data -= APE_TAG_HEADERSIZE; } if( b_end ) { p_header = &p_data[i_data-APE_TAG_HEADERSIZE]; i_data -= APE_TAG_HEADERSIZE; } if( i_data <= 0 ) return; i_entry = GetDWLE( &p_header[8+4+4] ); if( i_entry <= 0 ) return; while( i_entry > 0 && i_data >= 10 ) { const int i_size = GetDWLE( &p_data[0] ); const uint32_t flags = GetDWLE( &p_data[4] ); char psz_name[256]; int n; strlcpy( psz_name, (char*)&p_data[8], sizeof(psz_name) ); n = strlen( psz_name ); if( n <= 0 ) break; p_data += 8+n+1; i_data -= 8+n+1; if( i_data < i_size ) break; /* Retreive UTF-8 fields only */ if( ((flags>>1) & 0x03) == 0x00 ) { /* FIXME list are separated by '\0' */ char *psz_value = strndup( (char*)&p_data[0], i_size ); EnsureUTF8( psz_name ); EnsureUTF8( psz_value ); #define IS(s) (!strcasecmp( psz_name, s ) ) if( IS( "Title" ) ) vlc_meta_SetTitle( p_meta, psz_value ); else if( IS( "Artist" ) ) vlc_meta_SetArtist( p_meta, psz_value ); else if( IS( "Album" ) ) vlc_meta_SetAlbum( p_meta, psz_value ); else if( IS( "Publisher" ) ) vlc_meta_SetPublisher( p_meta, psz_value ); else if( IS( "Track" ) ) { char *p = strchr( psz_value, '/' ); if( p ) *p++ = '\0'; vlc_meta_SetTrackNum( p_meta, psz_value ); } else if( IS( "Comment" ) ) vlc_meta_SetDescription( p_meta, psz_value ); else if( IS( "Copyright" ) ) vlc_meta_SetCopyright( p_meta, psz_value ); else if( IS( "Year" ) ) vlc_meta_SetDate( p_meta, psz_value ); else if( IS( "Genre" ) ) vlc_meta_SetGenre( p_meta, psz_value ); else if( IS( "Language" ) ) vlc_meta_SetLanguage( p_meta, psz_value ); else vlc_meta_AddExtra( p_meta, psz_name, psz_value ); #undef IS free( psz_value ); } p_data += i_size; i_data -= i_size; i_entry--; } }