I'm building an mp3 tagging application using the JAudiotagger library. My application reads the mp3 metadata fine, and can write the metadata fine too, except for the artworks. So my problem is as follows:

When I add a number of artworks in the mp3 file, and save it, the file is getting bigger, which makes sense. But when I remove one or all the artworks, the file size doesn't get smaller.

The actual problem lies in the ID3v2 tag of my mp3 file. When I remove an artwork, it is actually removed from the tag, but the tag size itself doesn't shrink at all.

The method I'm using when deleting an artwork is this:

    // Get the artworkList from the parentFrame.
    List<Artwork> list = parentFrame.getArtworkList();

    // Get the tag from the parentFrame's mp3File.
    AbstractID3v2Tag tag = parentFrame.getTag();

   // Get the index of the artwork the user is currently looking at (and
   // wants to delete too).
   int visibleArtworkIndex = parentFrame.getVisibleArtworkIndex();

   // Remove it from the list.
   list.remove(visibleArtworkIndex);

   // Update the parentFrame's copy of the artworkList.
   parentFrame.setArtworkList(list);

   // Update the tag (delete its whole artwork field).
   tag.deleteArtworkField();

   // If the list has more artworks left, add them to the tag.
   if (!list.isEmpty()) {
       Iterator<Artwork> iterator = list.iterator();
       while (iterator.hasNext()) {
           try {
               tag.addField(iterator.next());
           } catch (FieldDataInvalidException e1) {
               e1.printStackTrace();
           }
       }
   }

, which actually removes an artwork from the list, and then updates the tag itself by deleting all of its artworks and copying them all over again from the updated list.

My attempts for a solution were:

  • To create a new tag from the updated old tag (after calling tag.deleteArtworkField()), then adding the artworks to the new tag, but the new tag had the same size as the old one.

  • To trim the mp3 file just before saving it by using tag.adjustPadding(File fileToBeTrimmed, int sizeToStoreTagBeforeAudioInBytes, long audioStartByte), which adjusts the length of the padding at the beginning of the MP3 file.
    The problem here is that I know the wrong tag size only and not the correct, so I can't trim the mp3 correctly and I end up losing audio data.

To illustrate the problem better I have included some images:

The mp3 file before:

before

The mp3 file after the removal of one artwork. Notice the tag kept its previous size although it has less artworks:

after wrong

And how the file should be:

after correct

I hope anyone has any ideas. Thanks in advance.

有帮助吗?

解决方案

This is fixed as of today.

By default jaudiotagger does not reclaim space when you make the metadata smaller, but now if you set

TagOptionSingleton.getInstance().setId3v2PaddingWillShorten(true);

before saving changes it will reclaim unnecessary padding to give the minimum file size possible.

其他提示

That is actually intended behavior, this is a sort of optimization.

When you add data to the ID3v2 tag and there is not enough space, the entire file needs to be rewritten to make enough space. When you remove data, the ID3v2 is just updated to contain the data and unused space is only simply marked as free (it would be recycled when you add more data again).

Look for a "release unused space in tag" call in your library. You need to tell it explicitly that the free space should be released.

Edit: Looking at the Javadoc, I believe you need to set this option before working with your files:

TagOptionSingleton.getInstance().setId3v2PaddingWillShorten(true);

The methods

TagOptionSingleton.getInstance().setId3v2PaddingWillShorten(true);
TagOptionSingleton.getInstance().setOriginalSavedAfterAdjustingID3v2Padding(true);

seem to be not fully implemented (as of Jan 2018). For example, check http://www.jthink.net/jaudiotagger/maven/apidocs/org/jaudiotagger/tag/mp4/Mp4TagCreator.html to see that the class Mp4TagCreator has not implemented a padding when converting the metadata to raw data:

padding - TODO padding parameter currently ignored

For mp3-files I have a workaround, using the library mp3agic https://github.com/mpatric/mp3agic. Unlike jaudiotagger, which was last updated 2015, it is still updated. On android you need to use version 0.9.0, since 0.9.1 uses java.nio.file-classes, which is not supported by android, https://github.com/mpatric/mp3agic/issues/141.

The workaround is to simply create a new tag and copy the tag data, then write it to a new file. If succesfull, replace the old file with the new file. The new file will be smaller than the original file if you do not copy the cover image. I believe this should be also possible with jaudiotagger, but did not manage to do so. Here is how with mp3agic:

                        try {
                        Mp3File song = new Mp3File(location,false);

                        if (song.hasId3v2Tag()){
                            ID3v2 oritag=song.getId3v2Tag();
                            byte[] image=oritag.getAlbumImage();
                            if(image!=null){
                                if (image.length > 10) {
                                    song = new Mp3File(location, true);
                                    oritag=song.getId3v2Tag();
                                    ID3v24Tag newtag = new ID3v24Tag();

                                    // copy metadata

                                    newtag.setArtist(oritag.getArtist());
                                    newtag.setArtistUrl(oritag.getArtistUrl());
                                    newtag.setOriginalArtist(oritag.getOriginalArtist());
                                    newtag.setArtistUrl(oritag.getArtistUrl());

                                    newtag.setAlbum(oritag.getAlbum());
                                    newtag.setAlbumArtist(oritag.getAlbumArtist());

                                    newtag.setAudiofileUrl(oritag.getAudiofileUrl());
                                    newtag.setAudioSourceUrl(oritag.getAudioSourceUrl());
                                    newtag.setUrl(oritag.getUrl());

                                    newtag.setGenre(oritag.getGenre());
                                    newtag.setGrouping(oritag.getGrouping());

                                    newtag.setTitle(oritag.getTitle());
                                    newtag.setTrack(oritag.getTrack());

                                    newtag.setPublisher(oritag.getPublisher());
                                    newtag.setPublisherUrl(oritag.getPublisherUrl());
                                    newtag.setCopyright(oritag.getCopyright());
                                    newtag.setCopyrightUrl(oritag.getCopyrightUrl());
                                    newtag.setComposer(oritag.getComposer());
                                    newtag.setCommercialUrl(oritag.getCommercialUrl());
                                    newtag.setComment(oritag.getComment());
                                    newtag.setYear(oritag.getYear());
                                    newtag.setKey(oritag.getKey());
                                    newtag.setRadiostationUrl(oritag.getRadiostationUrl());
                                    newtag.setPaymentUrl(oritag.getPaymentUrl());

                                    song.setId3v2Tag(newtag);


                                    try {
                                        song.save(location + "intermed");

                                        File from = new File(location + "intermed");
// if successfull then replace old file with new file
                                        if(from.exists()) {
                                            File file = new File(location);
                                            long sizeold = file.length();
                                            file.delete();
                                            File to = new File(location);
                                            long sizenew = from.length();
                                            from.renameTo(to);
                                            freedspace += sizeold - sizenew;
                                            }

                                    } catch (IOException | NotSupportedException e) {
                                        e.printStackTrace();

                                    }
                                }
                            }
                        }

                    } catch (IOException | UnsupportedTagException | InvalidDataException e) {
                        e.printStackTrace();
                    }

Remarks: I implemented this in my AudioCleanup-App, https://play.google.com/store/apps/details?id=com.gluege.audiocleanup&hl=en, it works on mp3-files. I did not manage to remove album covers in other file types. If someone has a solution, please share it.

I dislike the Id3-standard, especially the padding. It's a waste of precious space on smartphones. I have seen albums where each song contained the same 1MB cover image.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top