guint id3demux_calc_id3v2_tag_size (GstBuffer * buf) { guint8 *data, flags; guint size; g_assert (buf != NULL); g_assert (GST_BUFFER_SIZE (buf) >= ID3V2_HDR_SIZE); data = GST_BUFFER_DATA (buf); /* Check for 'ID3' string at start of buffer */ if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') { GST_DEBUG ("No ID3v2 tag in data"); return 0; } /* Read the flags */ flags = data[5]; /* Read the size from the header */ size = read_synch_uint (data + 6, 4); if (size == 0) return ID3V2_HDR_SIZE; size += ID3V2_HDR_SIZE; /* Expand the read size to include a footer if there is one */ if ((flags & ID3V2_HDR_FLAG_FOOTER)) size += 10; GST_DEBUG ("ID3v2 tag, size: %u bytes", size); return size; }
gboolean id3demux_id3v2_parse_frame (ID3TagsWorking * work) { const gchar *tag_name; gboolean result = FALSE; gint i; guint8 *frame_data = work->hdr.frame_data; guint frame_data_size = work->cur_frame_size; gchar *tag_str = NULL; GArray *tag_fields = NULL; guint8 *uu_data = NULL; #ifdef HAVE_ZLIB guint8 *uncompressed_data = NULL; #endif /* Check that the frame id is valid */ for (i = 0; i < 5 && work->frame_id[i] != '\0'; i++) { if (!g_ascii_isalnum (work->frame_id[i])) { GST_DEBUG ("Encountered invalid frame_id"); return FALSE; } } /* Can't handle encrypted frames right now (in case we ever do, we'll have * to do the decryption after the un-unsynchronisation and decompression, * not here) */ if (work->frame_flags & ID3V2_FRAME_FORMAT_ENCRYPTION) { GST_WARNING ("Encrypted frames are not supported"); return FALSE; } tag_name = gst_tag_from_id3_tag (work->frame_id); if (tag_name == NULL && strncmp (work->frame_id, "RVA2", 4) != 0 && strncmp (work->frame_id, "TXXX", 4) != 0 && strncmp (work->frame_id, "TDAT", 4) != 0 && strncmp (work->frame_id, "UFID", 4) != 0) { return FALSE; } if (work->frame_flags & (ID3V2_FRAME_FORMAT_COMPRESSION | ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR)) { if (work->hdr.frame_data_size <= 4) return FALSE; if (ID3V2_VER_MAJOR (work->hdr.version) == 3) { work->parse_size = GST_READ_UINT32_BE (frame_data); } else { work->parse_size = read_synch_uint (frame_data, 4); } frame_data += 4; frame_data_size -= 4; GST_LOG ("Un-unsynced data size %d (of %d)", work->parse_size, frame_data_size); if (work->parse_size > frame_data_size) { GST_WARNING ("ID3v2 frame %s data has invalid size %d (>%d)", work->frame_id, work->parse_size, frame_data_size); return FALSE; } } /* in v2.3 the frame sizes are not syncsafe, so the entire tag had to be * unsynced. In v2.4 the frame sizes are syncsafe so it's just the frame * data that needs un-unsyncing, but not the frame headers. */ if (ID3V2_VER_MAJOR (work->hdr.version) == 4) { if ((work->hdr.flags & ID3V2_HDR_FLAG_UNSYNC) != 0 || ((work->frame_flags & ID3V2_FRAME_FORMAT_UNSYNCHRONISATION) != 0)) { GST_DEBUG ("Un-unsyncing frame %s", work->frame_id); uu_data = id3demux_ununsync_data (frame_data, &frame_data_size); frame_data = uu_data; GST_MEMDUMP ("ID3v2 frame (un-unsyced)", frame_data, frame_data_size); } } work->parse_size = frame_data_size; if (work->frame_flags & ID3V2_FRAME_FORMAT_COMPRESSION) { #ifdef HAVE_ZLIB uLongf destSize = work->parse_size; Bytef *dest, *src; uncompressed_data = g_malloc (work->parse_size); dest = (Bytef *) uncompressed_data; src = (Bytef *) frame_data; if (uncompress (dest, &destSize, src, frame_data_size) != Z_OK) { g_free (uncompressed_data); g_free (uu_data); return FALSE; } if (destSize != work->parse_size) { GST_WARNING ("Decompressing ID3v2 frame %s did not produce expected size %d bytes (got %lu)", tag_name, work->parse_size, destSize); g_free (uncompressed_data); g_free (uu_data); return FALSE; } work->parse_data = uncompressed_data; #else GST_WARNING ("Compressed ID3v2 tag frame could not be decompressed" " because gstid3demux was compiled without zlib support"); g_free (uu_data); return FALSE; #endif } else { work->parse_data = frame_data; } if (work->frame_id[0] == 'T') { if (strcmp (work->frame_id, "TDAT") == 0) { parse_obsolete_tdat_frame (work); result = TRUE; } else if (strcmp (work->frame_id, "TXXX") == 0) { /* Handle user text frame */ tag_str = parse_user_text_identification_frame (work, &tag_name); } else { /* Text identification frame */ tag_fields = parse_text_identification_frame (work); } } else if (work->frame_id[0] == 'W' && strcmp (work->frame_id, "WXXX") != 0) { /* URL link frame: ISO-8859-1 encoded, one frame per tag */ tag_str = parse_url_link_frame (work, &tag_name); } else if (!strcmp (work->frame_id, "COMM")) { /* Comment */ result = parse_comment_frame (work); } else if (!strcmp (work->frame_id, "APIC")) { /* Attached picture */ result = parse_picture_frame (work); } else if (!strcmp (work->frame_id, "RVA2")) { /* Relative volume */ result = parse_relative_volume_adjustment_two (work); } else if (!strcmp (work->frame_id, "UFID")) { /* Unique file identifier */ tag_str = parse_unique_file_identifier (work, &tag_name); } #ifdef HAVE_ZLIB if (work->frame_flags & ID3V2_FRAME_FORMAT_COMPRESSION) { g_free (uncompressed_data); uncompressed_data = NULL; work->parse_data = frame_data; } #endif if (tag_str != NULL) { /* g_print ("Tag %s value %s\n", tag_name, tag_str); */ result = id3v2_tag_to_taglist (work, tag_name, tag_str); g_free (tag_str); } if (tag_fields != NULL) { if (strcmp (work->frame_id, "TCON") == 0) { /* Genre strings need special treatment */ result |= id3v2_genre_fields_to_taglist (work, tag_name, tag_fields); } else { gint t; for (t = 0; t < tag_fields->len; t++) { tag_str = g_array_index (tag_fields, gchar *, t); if (tag_str != NULL && tag_str[0] != '\0') result |= id3v2_tag_to_taglist (work, tag_name, tag_str); } } free_tag_strings (tag_fields); } g_free (uu_data); return result; }