static struct id3_tag *findId3TagFromBeginning(FILE * stream) { struct id3_tag *tag; struct id3_tag *seektag; struct id3_frame *frame; int seek; tag = getId3Tag(stream, 0, SEEK_SET); if (!tag) { return NULL; } else if (isId3v1(tag)) { /* id3v1 tags don't belong here */ id3_tag_delete(tag); return NULL; } /* We have an id3v2 tag, so let's look for SEEK frames */ while ((frame = id3_tag_findframe(tag, "SEEK", 0))) { /* Found a SEEK frame, get it's value */ seek = id3_field_getint(id3_frame_field(frame, 0)); if (seek < 0) break; /* Get the tag specified by the SEEK frame */ seektag = getId3Tag(stream, seek, SEEK_CUR); if (!seektag || isId3v1(seektag)) break; /* Replace the old tag with the new one */ id3_tag_delete(tag); tag = seektag; } return tag; }
struct tag *tag_id3_load(const char *file) { struct tag *ret; struct id3_tag *tag; FILE *stream; stream = fopen(file, "r"); if (!stream) { g_debug("tag_id3_load: Failed to open file: '%s', %s", file, strerror(errno)); return NULL; } tag = tag_id3_find_from_beginning(stream); if (tag == NULL) tag = tag_id3_riff_aiff_load(stream); if (!tag) tag = tag_id3_find_from_end(stream); fclose(stream); if (!tag) return NULL; ret = tag_id3_import(tag); id3_tag_delete(tag); return ret; }
static struct id3_tag * tag_id3_find_from_end(FILE *stream) { struct id3_tag *tag; struct id3_tag *v1tag; int tagsize; /* Get an id3v1 tag from the end of file for later use */ v1tag = tag_id3_read(stream, -128, SEEK_END); /* Get the id3v2 tag size from the footer (located before v1tag) */ tagsize = get_id3v2_footer_size(stream, (v1tag ? -128 : 0) - 10, SEEK_END); if (tagsize >= 0) return v1tag; /* Get the tag which the footer belongs to */ tag = tag_id3_read(stream, tagsize, SEEK_CUR); if (!tag) return v1tag; /* We have an id3v2 tag, so ditch v1tag */ id3_tag_delete(v1tag); return tag; }
JNIEXPORT jboolean JNICALL Java_com_ssb_droidsound_utils_ID3Tag_parseTag(JNIEnv *env, jobject obj, jbyteArray bArray, jint offset, jint size) { if(tagRefField != 0) { struct id3_tag *tag = (struct id3_tag*)env->GetLongField(obj, tagRefField); if(tag) id3_tag_delete(tag); } jbyte *ptr = env->GetByteArrayElements(bArray, NULL); const id3_byte_t*cptr = (const id3_byte_t*)(ptr + offset); __android_log_print(ANDROID_LOG_VERBOSE, "ID3Tag", "Checking at %p (%c %c)", cptr, cptr[0], cptr[1]); struct id3_tag *tag = id3_tag_parse(cptr, size); __android_log_print(ANDROID_LOG_VERBOSE, "ID3Tag", "DONE"); jclass cl = env->GetObjectClass(obj); tagRefField = env->GetFieldID(cl, "tagRef", "J"); env->SetLongField(obj, tagRefField, (jlong)tag); cl = env->GetObjectClass(obj); refField = env->GetFieldID(cl, "id3Ref", "J"); env->SetLongField(obj, refField, (jlong)0); env->ReleaseByteArrayElements(bArray, ptr, 0); return tag != NULL; }
static void context_destroy(context * ctx) { id3_tag_delete(ctx->tag); free(ctx->filename); free(ctx); }
static struct id3_tag *v1_parse(id3_byte_t const *data) { struct id3_tag *tag; tag = id3_tag_new(); if (tag) { char title[31], artist[31], album[31], year[5], comment[31]; unsigned int genre, track; tag->version = 0x0100; tag->options |= ID3_TAG_OPTION_ID3V1; tag->options &= ~ID3_TAG_OPTION_COMPRESSION; tag->restrictions = ID3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 | ID3_TAG_RESTRICTION_TEXTSIZE_30_CHARS; title[30] = artist[30] = album[30] = year[4] = comment[30] = 0; memcpy(title, &data[3], 30); memcpy(artist, &data[33], 30); memcpy(album, &data[63], 30); memcpy(year, &data[93], 4); memcpy(comment, &data[97], 30); genre = data[127]; track = 0; if (comment[28] == 0 && comment[29] != 0) { track = comment[29]; tag->version = 0x0101; } /* populate tag frames */ if (v1_attachstr(tag, ID3_FRAME_TITLE, title, 0) == -1 || v1_attachstr(tag, ID3_FRAME_ARTIST, artist, 0) == -1 || v1_attachstr(tag, ID3_FRAME_ALBUM, album, 0) == -1 || v1_attachstr(tag, ID3_FRAME_YEAR, year, 0) == -1 || (track && v1_attachstr(tag, ID3_FRAME_TRACK, 0, track) == -1) || (genre < 0xff && v1_attachstr(tag, ID3_FRAME_GENRE, 0, genre) == -1) || v1_attachstr(tag, ID3_FRAME_COMMENT, comment, 0) == -1) { id3_tag_delete(tag); tag = 0; } } return tag; }
int main(int argc, char **argv) { GError *error = NULL; #ifdef HAVE_LOCALE_H /* initialize locale */ setlocale(LC_CTYPE,""); #endif if (argc != 2) { g_printerr("Usage: read_rva2 FILE\n"); return 1; } const char *path = argv[1]; struct id3_tag *tag = tag_id3_load(path, &error); if (tag == NULL) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("No ID3 tag found\n"); return EXIT_FAILURE; } struct replay_gain_info replay_gain; replay_gain_info_init(&replay_gain); bool success = tag_rva2_parse(tag, &replay_gain); id3_tag_delete(tag); if (!success) { g_printerr("No RVA2 tag found\n"); return EXIT_FAILURE; } const struct replay_gain_tuple *tuple = &replay_gain.tuples[REPLAY_GAIN_ALBUM]; if (replay_gain_tuple_defined(tuple)) g_printerr("replay_gain[album]: gain=%f peak=%f\n", tuple->gain, tuple->peak); tuple = &replay_gain.tuples[REPLAY_GAIN_TRACK]; if (replay_gain_tuple_defined(tuple)) g_printerr("replay_gain[track]: gain=%f peak=%f\n", tuple->gain, tuple->peak); return EXIT_SUCCESS; }
JNIEXPORT void JNICALL Java_com_ssb_droidsound_utils_ID3Tag_closeID3Tag(JNIEnv *env, jobject obj) { struct id3_file *id3file = 0; struct id3_tag *tag = 0; if(refField != 0) id3file = (struct id3_file*)env->GetLongField(obj, refField); if(tagRefField != 0) tag = (struct id3_tag*)env->GetLongField(obj, tagRefField); if(tag) id3_tag_delete(tag); if(id3file) id3_file_close(id3file); }
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, struct tag **mpd_tag, struct replay_gain_info **replay_gain_info_r) { struct id3_tag *id3_tag = NULL; id3_length_t count; id3_byte_t const *id3_data; id3_byte_t *allocated = NULL; count = data->stream.bufend - data->stream.this_frame; if (tagsize <= count) { id3_data = data->stream.this_frame; mad_stream_skip(&(data->stream), tagsize); } else { allocated = g_malloc(tagsize); memcpy(allocated, data->stream.this_frame, count); mad_stream_skip(&(data->stream), count); while (count < tagsize) { size_t len; len = decoder_read(data->decoder, data->input_stream, allocated + count, tagsize - count); if (len == 0) break; else count += len; } if (count != tagsize) { g_debug("error parsing ID3 tag"); g_free(allocated); return; } id3_data = allocated; } id3_tag = id3_tag_parse(id3_data, tagsize); if (id3_tag == NULL) { g_free(allocated); return; } if (mpd_tag) { struct tag *tmp_tag = tag_id3_import(id3_tag); if (tmp_tag != NULL) { if (*mpd_tag != NULL) tag_free(*mpd_tag); *mpd_tag = tmp_tag; } } if (replay_gain_info_r) { struct replay_gain_info *tmp_rgi = parse_id3_replay_gain_info(id3_tag); if (tmp_rgi != NULL) { if (*replay_gain_info_r) replay_gain_info_free(*replay_gain_info_r); *replay_gain_info_r = tmp_rgi; } } id3_tag_delete(id3_tag); g_free(allocated); }
static int _get_dsftags(char *file, struct song_metadata *psong) { struct id3_tag *pid3tag; struct id3_frame *pid3frame; int err; int index; int used; unsigned char *utf8_text; int genre = WINAMP_GENRE_UNKNOWN; int have_utf8; int have_text; id3_ucs4_t const *native_text; char *tmp; int got_numeric_genre; id3_byte_t const *image; id3_length_t image_size = 0; FILE *fp; struct id3header *pid3; uint32_t len; unsigned char hdr[28] = {0}; uint64_t total_size = 0; uint64_t pointer_to_metadata_chunk = 0; uint64_t metadata_chunk_size = 0; unsigned char *id3tagbuf = NULL; //DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Getting DSF file info\n"); if((fp = fopen(file, "rb")) == NULL) { DPRINTF(E_WARN, L_SCANNER, "Could not create file handle\n"); return -1; } len = 28; if(!(len = fread(hdr, len, 1,fp))) { DPRINTF(E_WARN, L_SCANNER, "Could not read DSD Chunk from %s\n", file); fclose(fp); return -1; } if(strncmp((char*)hdr, "DSD ", 4)) { DPRINTF(E_WARN, L_SCANNER, "Invalid DSD Chunk header in %s\n", file); fclose(fp); return -1; } total_size = GET_DSF_INT64(hdr + 12); pointer_to_metadata_chunk = GET_DSF_INT64(hdr + 20); //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "%llu\n", total_size); //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "%llu\n", pointer_to_metadata_chunk); //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "%llu\n", metadata_chunk_size); //check invalid metadata if(total_size == 0) { fclose(fp); DPRINTF(E_INFO, L_SCANNER, "Invalid TotalDataSize in %s\n", file); return 0; } if(pointer_to_metadata_chunk == 0) { fclose(fp); DPRINTF(E_INFO, L_SCANNER, "Metadata doesn't exist %s\n", file); return 0; } if(total_size > pointer_to_metadata_chunk) { metadata_chunk_size = total_size - pointer_to_metadata_chunk; } else { fclose(fp); DPRINTF(E_INFO, L_SCANNER, "Invalid PointerToMetadata in %s\n", file); return 0; } fseeko(fp, pointer_to_metadata_chunk,SEEK_SET); id3tagbuf = (unsigned char *)malloc(sizeof(unsigned char)*metadata_chunk_size); if(id3tagbuf == NULL) { fclose(fp); DPRINTF(E_WARN, L_SCANNER, "Out of memory.Big MetadataSize in %s\n",file); return -1; } memset(id3tagbuf, 0,sizeof(unsigned char)*metadata_chunk_size); if(!(len = fread(id3tagbuf,metadata_chunk_size,1,fp))) { fclose(fp); free(id3tagbuf); DPRINTF(E_WARN, L_SCANNER, "Could not read Metadata Chunk from %s\n", file); return -1; } pid3tag = id3_tag_parse(id3tagbuf,metadata_chunk_size); if(!pid3tag) { free(id3tagbuf); err = errno; errno = err; DPRINTF(E_WARN, L_SCANNER, "Cannot get ID3 tag for %s\n", file); return -1; } pid3 = (struct id3header*)id3tagbuf; if(strncmp((char*)pid3->id, "ID3", 3) == 0) { char tagversion[16]; /* found an ID3 header... */ snprintf(tagversion, sizeof(tagversion), "ID3v2.%d.%d", pid3->version[0], pid3->version[1]); psong->tagversion = strdup(tagversion); } pid3 = NULL; index = 0; while((pid3frame = id3_tag_findframe(pid3tag, "", index))) { used = 0; utf8_text = NULL; native_text = NULL; have_utf8 = 0; have_text = 0; if(!strcmp(pid3frame->id, "YTCP")) /* for id3v2.2 */ { psong->compilation = 1; DPRINTF(E_DEBUG, L_SCANNER, "Compilation: %d [%s]\n", psong->compilation, basename(file)); } else if(!strcmp(pid3frame->id, "APIC") && !image_size) { if( (strcmp((char*)id3_field_getlatin1(&pid3frame->fields[1]), "image/jpeg") == 0) || (strcmp((char*)id3_field_getlatin1(&pid3frame->fields[1]), "image/jpg") == 0) || (strcmp((char*)id3_field_getlatin1(&pid3frame->fields[1]), "jpeg") == 0) ) { image = id3_field_getbinarydata(&pid3frame->fields[4], &image_size); if( image_size ) { psong->image = malloc(image_size); memcpy(psong->image, image, image_size); psong->image_size = image_size; //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Found thumbnail: %d\n", psong->image_size); } } } if(((pid3frame->id[0] == 'T') || (strcmp(pid3frame->id, "COMM") == 0)) && (id3_field_getnstrings(&pid3frame->fields[1]))) have_text = 1; if(have_text) { native_text = id3_field_getstrings(&pid3frame->fields[1], 0); if(native_text) { have_utf8 = 1; if(lang_index >= 0) utf8_text = _get_utf8_text(native_text); // through iconv else utf8_text = (unsigned char*)id3_ucs4_utf8duplicate(native_text); if(!strcmp(pid3frame->id, "TIT2")) { used = 1; psong->title = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TPE1")) { used = 1; psong->contributor[ROLE_ARTIST] = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TALB")) { used = 1; psong->album = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TCOM")) { used = 1; psong->contributor[ROLE_COMPOSER] = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TIT1")) { used = 1; psong->grouping = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TPE2")) { used = 1; psong->contributor[ROLE_BAND] = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TPE3")) { used = 1; psong->contributor[ROLE_CONDUCTOR] = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TCON")) { used = 1; psong->genre = (char*)utf8_text; got_numeric_genre = 0; if(psong->genre) { if(!strlen(psong->genre)) { genre = WINAMP_GENRE_UNKNOWN; got_numeric_genre = 1; } else if(isdigit(psong->genre[0])) { genre = atoi(psong->genre); got_numeric_genre = 1; } else if((psong->genre[0] == '(') && (isdigit(psong->genre[1]))) { genre = atoi((char*)&psong->genre[1]); got_numeric_genre = 1; } if(got_numeric_genre) { if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) genre = WINAMP_GENRE_UNKNOWN; free(psong->genre); psong->genre = strdup(winamp_genre[genre]); } } } else if(!strcmp(pid3frame->id, "COMM")) { used = 1; psong->comment = (char*)utf8_text; } else if(!strcmp(pid3frame->id, "TPOS")) { tmp = (char*)utf8_text; strsep(&tmp, "/"); if(tmp) { psong->total_discs = atoi(tmp); } psong->disc = atoi((char*)utf8_text); } else if(!strcmp(pid3frame->id, "TRCK")) { tmp = (char*)utf8_text; strsep(&tmp, "/"); if(tmp) { psong->total_tracks = atoi(tmp); } psong->track = atoi((char*)utf8_text); } else if(!strcmp(pid3frame->id, "TDRC")) { psong->year = atoi((char*)utf8_text); } else if(!strcmp(pid3frame->id, "TLEN")) { psong->song_length = atoi((char*)utf8_text); } else if(!strcmp(pid3frame->id, "TBPM")) { psong->bpm = atoi((char*)utf8_text); } else if(!strcmp(pid3frame->id, "TCMP")) { psong->compilation = (char)atoi((char*)utf8_text); } } } // check if text tag if((!used) && (have_utf8) && (utf8_text)) free(utf8_text); // v2 COMM if((!strcmp(pid3frame->id, "COMM")) && (pid3frame->nfields == 4)) { native_text = id3_field_getstring(&pid3frame->fields[2]); if(native_text) { utf8_text = (unsigned char*)id3_ucs4_utf8duplicate(native_text); if((utf8_text) && (strncasecmp((char*)utf8_text, "iTun", 4) != 0)) { // read comment free(utf8_text); native_text = id3_field_getfullstring(&pid3frame->fields[3]); if(native_text) { utf8_text = (unsigned char*)id3_ucs4_utf8duplicate(native_text); if(utf8_text) { free(psong->comment); psong->comment = (char*)utf8_text; } } } else { free(utf8_text); } } } index++; } id3_tag_delete(pid3tag); free(id3tagbuf); fclose(fp); //DPRINTF(E_DEBUG, L_SCANNER, "Got id3tag successfully for file=%s\n", file); return 0; }
static int _BarFlyTagID3Write(BarFly_t const* fly, uint8_t const* cover_art, size_t cover_size, BarSettings_t const* settings) { int const BUFFER_SIZE = 5; int const TAG_PADDED_SIZE = 1024; char const BAR_FLY_ID3_FRAME_DISC[] = "TPOS"; int exit_status = 0; int status; struct id3_tag* tag; char buffer[BUFFER_SIZE]; assert(fly != NULL); assert(fly->audio_file_path != NULL); assert(settings != NULL); /* * Set the minimum size for the tag. The tag will use CRC and compression. * FIXME - figure out if the padded size is really needed. */ tag = id3_tag_new(); if (tag == NULL) { BarUiMsg(settings, MSG_ERR, "Failed to create new tag.\n"); goto error; } id3_tag_setlength(tag, TAG_PADDED_SIZE); id3_tag_options(tag, ID3_TAG_OPTION_UNSYNCHRONISATION | ID3_TAG_OPTION_APPENDEDTAG | ID3_TAG_OPTION_CRC | ID3_TAG_OPTION_COMPRESSION, 0); /* * Add the data to the tag. */ status = BarFlyID3AddFrame(tag, ID3_FRAME_ARTIST, fly->artist, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write artist to tag.\n"); goto error; } status = BarFlyID3AddFrame(tag, ID3_FRAME_ALBUM, fly->album, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write album to tag.\n"); goto error; } status = BarFlyID3AddFrame(tag, ID3_FRAME_TITLE, fly->title, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write title to tag.\n"); goto error; } if (fly->year != 0) { snprintf(buffer, BUFFER_SIZE, "%hu", fly->year); buffer[BUFFER_SIZE - 1] = '\0'; status = BarFlyID3AddFrame(tag, ID3_FRAME_YEAR, buffer, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write year to tag.\n"); goto error; } } if (fly->track != 0) { snprintf(buffer, BUFFER_SIZE, "%hu", fly->track); buffer[BUFFER_SIZE - 1] = '\0'; status = BarFlyID3AddFrame(tag, ID3_FRAME_TRACK, buffer, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write track number to tag.\n"); goto error; } } if (fly->disc != 0) { snprintf(buffer, BUFFER_SIZE, "%hu", fly->disc); buffer[BUFFER_SIZE - 1] = '\0'; status = BarFlyID3AddFrame(tag, BAR_FLY_ID3_FRAME_DISC, buffer, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write disc number to tag.\n"); goto error; } } if (cover_art != NULL) { status = BarFlyID3AddCover(tag, cover_art, cover_size, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write cover to tag.\n"); goto error; } } /* * Write the tag to the file. */ status = BarFlyID3WriteFile(fly->audio_file_path, tag, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write the tag.\n"); goto error; } goto end; error: exit_status = -1; end: if (tag != NULL) { id3_tag_delete(tag); } return exit_status; }
static context * context_create(const char *filename) { context *node = (context *) malloc(sizeof(context)); context *ptr, *last; int last_id = INT_MAX; node->refcount = 1; { struct id3_file *file; struct id3_tag *tag; unsigned int i; file = id3_file_open(filename, ID3_FILE_MODE_READONLY); if (!file) { fprintf(stderr, "Unable to open tagged file %s: %s\n", filename, strerror(errno)); goto fail_free; } tag = id3_file_tag(file); if (!tag) { fprintf(stderr, "Unable to find ID3v2 tags in file %s\n", filename); id3_file_close(file); goto fail_free; } node->tag = id3_tag_new(); for (i = 0; i < id3_tag_get_numframes(tag); i++) if (!strcmp(id3_frame_id(id3_tag_get_frame(tag, i)), "APIC")) id3_tag_attachframe(node->tag, id3_tag_get_frame(tag, i)); id3_file_close(file); } node->filename = strdup(filename); if (!id3_ctxs) { node->id = 1; node->next = NULL; id3_ctxs = node; return node; } ptr = id3_ctxs; last = NULL; while (UNLIKELY(ptr && (ptr->id + 1) >= last_id)) { last_id = ptr->id; last = ptr; ptr = ptr->next; } /* Paranoid! this can occur only if there are INT_MAX contexts :) */ if (UNLIKELY(!ptr)) { fprintf(stderr, "Too many open ID3 contexts\n"); goto fail_close; } node->id = ptr->id + 1; if (UNLIKELY(! !last)) { node->next = last->next; last->next = node; } else { node->next = id3_ctxs; id3_ctxs = node; } return node; fail_close: free(node->filename); id3_tag_delete(node->tag); fail_free: free(node); return NULL; }
// returns buffer len; caller frees int Tags::ExportID3(char **buffer, bool *endOfFile) { #ifdef USE_LIBID3TAG struct id3_tag *tp = id3_tag_new(); if (mTitle != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_TITLE, mTitle.mb_str())); if (mArtist != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ARTIST, mArtist.mb_str())); if (mAlbum != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ALBUM, mAlbum.mb_str())); if (mYear != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_YEAR, mYear.mb_str())); if (mComments != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_COMMENT, mComments.mb_str())); if (mTrackNum >= 0) { wxString trackNumStr; trackNumStr.Printf(wxT("%d"), mTrackNum); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_TRACK, trackNumStr.mb_str())); } if (mGenre >= 0) { if (mID3V2) { wxString genreStr = GetGenreNum(mGenre); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_GENRE, genreStr.mb_str())); } else { wxString genreStr; genreStr.Printf(wxT("%d"), mGenre); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_GENRE, genreStr.mb_str())); } } if (mID3V2) { tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression // If this version of libid3tag supports it, use v2.3 ID3 // tags instead of the newer, but less well supported, v2.4 // that libid3tag uses by default. #ifdef ID3_TAG_OPTION_ID3V2_3 tp->options |= ID3_TAG_OPTION_ID3V2_3; #endif *endOfFile = false; } else { tp->options |= ID3_TAG_OPTION_ID3V1; *endOfFile = true; } id3_length_t len; len = id3_tag_render(tp, 0); *buffer = (char *)malloc(len); len = id3_tag_render(tp, (id3_byte_t *)*buffer); id3_tag_delete(tp); return len; #else //ifdef USE_LIBID3TAG return 0; #endif }
/***************************************************************************** * 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 struct id3_tag *v2_parse(id3_byte_t const *ptr) { struct id3_tag *tag; id3_byte_t *mem = 0; tag = id3_tag_new(); if (tag) { id3_byte_t const *end; id3_length_t size; parse_header(&ptr, &tag->version, &tag->flags, &size); tag->paddedsize = 10 + size; if ((tag->flags & ID3_TAG_FLAG_UNSYNCHRONISATION) && ID3_TAG_VERSION_MAJOR(tag->version) < 4) { mem = malloc(size); if (mem == 0) goto fail; memcpy(mem, ptr, size); size = id3_util_deunsynchronise(mem, size); ptr = mem; } end = ptr + size; if (tag->flags & ID3_TAG_FLAG_EXTENDEDHEADER) { switch (ID3_TAG_VERSION_MAJOR(tag->version)) { case 2: goto fail; case 3: { id3_byte_t const *ehptr, *ehend; id3_length_t ehsize; enum { EH_FLAG_CRC = 0x8000 /* CRC data present */ }; if (end - ptr < 4) goto fail; ehsize = id3_parse_uint(&ptr, 4); if (ehsize > end - ptr) goto fail; ehptr = ptr; ehend = ptr + ehsize; ptr = ehend; if (ehend - ehptr >= 6) { int ehflags; id3_length_t padsize; ehflags = id3_parse_uint(&ehptr, 2); padsize = id3_parse_uint(&ehptr, 4); if (padsize > end - ptr) goto fail; end -= padsize; if (ehflags & EH_FLAG_CRC) { unsigned long crc; if (ehend - ehptr < 4) goto fail; crc = id3_parse_uint(&ehptr, 4); if (crc != id3_crc_compute(ptr, end - ptr)) goto fail; tag->extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT; } } } break; case 4: { id3_byte_t const *ehptr, *ehend; id3_length_t ehsize; unsigned int bytes; if (end - ptr < 4) goto fail; ehptr = ptr; ehsize = id3_parse_syncsafe(&ptr, 4); if (ehsize < 6 || ehsize > end - ehptr) goto fail; ehend = ehptr + ehsize; bytes = id3_parse_uint(&ptr, 1); if (bytes < 1 || bytes > ehend - ptr) goto fail; ehptr = ptr + bytes; /* verify extended header size */ { id3_byte_t const *flagsptr = ptr, *dataptr = ehptr; unsigned int datalen; int ehflags; while (bytes--) { for (ehflags = id3_parse_uint(&flagsptr, 1); ehflags; ehflags = (ehflags << 1) & 0xff) { if (ehflags & 0x80) { if (dataptr == ehend) goto fail; datalen = id3_parse_uint(&dataptr, 1); if (datalen > 0x7f || datalen > ehend - dataptr) goto fail; dataptr += datalen; } } } } tag->extendedflags = id3_parse_uint(&ptr, 1); ptr = ehend; if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) { bytes = id3_parse_uint(&ehptr, 1); ehptr += bytes; } if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) { unsigned long crc; bytes = id3_parse_uint(&ehptr, 1); if (bytes < 5) goto fail; crc = id3_parse_syncsafe(&ehptr, 5); ehptr += bytes - 5; if (crc != id3_crc_compute(ptr, end - ptr)) goto fail; } if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) { bytes = id3_parse_uint(&ehptr, 1); if (bytes < 1) goto fail; tag->restrictions = id3_parse_uint(&ehptr, 1); ehptr += bytes - 1; } } break; } } /* frames */ while (ptr < end) { struct id3_frame *frame; if (*ptr == 0) break; /* padding */ frame = id3_frame_parse(&ptr, end - ptr, tag->version); if (frame == 0 || id3_tag_attachframe(tag, frame) == -1) goto fail; } if (ID3_TAG_VERSION_MAJOR(tag->version) < 4 && id3_compat_fixup(tag) == -1) goto fail; } if (0) { fail: id3_tag_delete(tag); tag = 0; } if (mem) free(mem); return tag; }
// returns buffer len; caller frees int ExportMP2::AddTags(AudacityProject * WXUNUSED(project), char **buffer, bool *endOfFile, const Tags *tags) { #ifdef USE_LIBID3TAG struct id3_tag *tp = id3_tag_new(); for (const auto &pair : tags->GetRange()) { const auto &n = pair.first; const auto &v = pair.second; const char *name = "TXXX"; if (n.CmpNoCase(TAG_TITLE) == 0) { name = ID3_FRAME_TITLE; } else if (n.CmpNoCase(TAG_ARTIST) == 0) { name = ID3_FRAME_ARTIST; } else if (n.CmpNoCase(TAG_ALBUM) == 0) { name = ID3_FRAME_ALBUM; } else if (n.CmpNoCase(TAG_YEAR) == 0) { // LLL: Some apps do not like the newer frame ID (ID3_FRAME_YEAR), // so we add old one as well. AddFrame(tp, n, v, "TYER"); name = ID3_FRAME_YEAR; } else if (n.CmpNoCase(TAG_GENRE) == 0) { name = ID3_FRAME_GENRE; } else if (n.CmpNoCase(TAG_COMMENTS) == 0) { name = ID3_FRAME_COMMENT; } else if (n.CmpNoCase(TAG_TRACK) == 0) { name = ID3_FRAME_TRACK; } AddFrame(tp, n, v, name); } tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression // If this version of libid3tag supports it, use v2.3 ID3 // tags instead of the newer, but less well supported, v2.4 // that libid3tag uses by default. #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3 tp->options |= ID3_TAG_OPTION_ID3V2_3; #endif *endOfFile = false; id3_length_t len; len = id3_tag_render(tp, 0); *buffer = (char *)malloc(len); len = id3_tag_render(tp, (id3_byte_t *)*buffer); id3_tag_delete(tp); return len; #else //ifdef USE_LIBID3TAG return 0; #endif }
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, struct tag **mpd_tag) { #ifdef HAVE_ID3TAG struct id3_tag *id3_tag = NULL; id3_length_t count; id3_byte_t const *id3_data; id3_byte_t *allocated = NULL; count = data->stream.bufend - data->stream.this_frame; if (tagsize <= count) { id3_data = data->stream.this_frame; mad_stream_skip(&(data->stream), tagsize); } else { allocated = g_malloc(tagsize); memcpy(allocated, data->stream.this_frame, count); mad_stream_skip(&(data->stream), count); while (count < tagsize) { size_t len; len = decoder_read(data->decoder, data->input_stream, allocated + count, tagsize - count); if (len == 0) break; else count += len; } if (count != tagsize) { g_debug("error parsing ID3 tag"); g_free(allocated); return; } id3_data = allocated; } id3_tag = id3_tag_parse(id3_data, tagsize); if (id3_tag == NULL) { g_free(allocated); return; } if (mpd_tag) { struct tag *tmp_tag = tag_id3_import(id3_tag); if (tmp_tag != NULL) { if (*mpd_tag != NULL) tag_free(*mpd_tag); *mpd_tag = tmp_tag; } } if (data->decoder != NULL) { struct replay_gain_info rgi; char *mixramp_start; char *mixramp_end; float replay_gain_db = 0; if (parse_id3_replay_gain_info(&rgi, id3_tag)) { replay_gain_db = decoder_replay_gain(data->decoder, &rgi); data->found_replay_gain = true; } if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag)) { g_debug("setting mixramp_tags"); decoder_mixramp(data->decoder, replay_gain_db, mixramp_start, mixramp_end); } } id3_tag_delete(id3_tag); g_free(allocated); #else /* !HAVE_ID3TAG */ (void)mpd_tag; /* This code is enabled when libid3tag is disabled. Instead of parsing the ID3 frame, it just skips it. */ size_t count = data->stream.bufend - data->stream.this_frame; if (tagsize <= count) { mad_stream_skip(&data->stream, tagsize); } else { mad_stream_skip(&data->stream, count); while (count < tagsize) { size_t len = tagsize - count; char ignored[1024]; if (len > sizeof(ignored)) len = sizeof(ignored); len = decoder_read(data->decoder, data->input_stream, ignored, len); if (len == 0) break; else count += len; } } #endif }