/* * Entagged Audio Tag library * Copyright (c) 2003-2005 Raphaƫl Slinckx <raphael@slinckx.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jaudiotagger.tag.mp4; import org.jaudiotagger.audio.generic.Utils; import org.jaudiotagger.audio.mp4.atom.Mp4BoxHeader; import org.jaudiotagger.tag.TagField; import org.jaudiotagger.tag.mp4.atom.Mp4DataBox; import org.jaudiotagger.tag.mp4.field.Mp4FieldType; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.logging.Logger; /** * This abstract class represents a link between piece of data, and how it is stored as an mp4 atom * <p/> * Note there isnt a one to one correspondance between a tag field and a box because some fields are represented * by multiple boxes, for example many of the MusicBrainz fields use the '----' box, which in turn uses one of mean, * name and data box. So an instance of a tag field maps to one item of data such as 'Title', but it may have to read * multiple boxes to do this. * <p/> * There are various subclasses that represent different types of fields */ public abstract class Mp4TagField implements TagField { // Logger Object public static Logger logger = Logger.getLogger("org.jaudiotagger.tag.mp4"); protected String id; //Just used by reverese dns class, so it knows the size of its aprent so it can detect end correctly protected Mp4BoxHeader parentHeader; protected Mp4TagField(String id) { this.id = id; } /** * Used by subclasses that canot identify their id until after they have been built such as ReverseDnsField * * @param data * @throws UnsupportedEncodingException */ protected Mp4TagField(ByteBuffer data) throws UnsupportedEncodingException { build(data); } /** * Used by reverese dns when reading from file, so can identify when there is a data atom * * @param parentHeader * @param data * @throws UnsupportedEncodingException */ protected Mp4TagField(Mp4BoxHeader parentHeader, ByteBuffer data) throws UnsupportedEncodingException { this.parentHeader = parentHeader; build(data); } protected Mp4TagField(String id, ByteBuffer data) throws UnsupportedEncodingException { this(id); build(data); } /** * @return field identifier */ public String getId() { return id; } public void isBinary(boolean b) { /* One cannot choose if an arbitrary block can be binary or not */ } public boolean isCommon() { return id.equals(Mp4FieldKey.ARTIST.getFieldName()) || id.equals(Mp4FieldKey.ALBUM.getFieldName()) || id.equals(Mp4FieldKey.TITLE.getFieldName()) || id.equals(Mp4FieldKey.TRACK.getFieldName()) || id.equals(Mp4FieldKey.DAY.getFieldName()) || id.equals(Mp4FieldKey.COMMENT.getFieldName()) || id.equals(Mp4FieldKey.GENRE.getFieldName()); } /** * @return field identifier as it will be held within the file */ protected byte[] getIdBytes() { return Utils.getDefaultBytes(getId(), "ISO-8859-1"); } /** * @return the data as it is held on file * @throws UnsupportedEncodingException */ protected abstract byte[] getDataBytes() throws UnsupportedEncodingException; /** * @return the field type of this field */ public abstract Mp4FieldType getFieldType(); /** * Processes the data and sets the position of the data buffer to just after the end of this fields data * ready for processing next field. * * @param data * @throws UnsupportedEncodingException */ protected abstract void build(ByteBuffer data) throws UnsupportedEncodingException; /** * Convert back to raw content, includes parent and data atom as views as one thing externally * * @return * @throws UnsupportedEncodingException */ public byte[] getRawContent() throws UnsupportedEncodingException { //logger.fine("Getting Raw data for:" + getId()); try { //Create Data Box byte[] databox = getRawContentDataOnly(); //Wrap in Parent box ByteArrayOutputStream outerbaos = new ByteArrayOutputStream(); outerbaos.write(Utils.getSizeBEInt32(Mp4BoxHeader.HEADER_LENGTH + databox.length)); outerbaos.write(Utils.getDefaultBytes(getId(), "ISO-8859-1")); outerbaos.write(databox); return outerbaos.toByteArray(); } catch (IOException ioe) { //This should never happen as were not actually writing to/from a file throw new RuntimeException(ioe); } } /** * Get raw content for the data component only * * @return * @throws UnsupportedEncodingException */ public byte[] getRawContentDataOnly() throws UnsupportedEncodingException { //logger.fine("Getting Raw data for:" + getId()); try { //Create Data Box ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] data = getDataBytes(); baos.write(Utils.getSizeBEInt32(Mp4DataBox.DATA_HEADER_LENGTH + data.length)); baos.write(Utils.getDefaultBytes(Mp4DataBox.IDENTIFIER, "ISO-8859-1")); baos.write(new byte[]{0}); baos.write(new byte[]{0, 0, (byte) getFieldType().getFileClassId()}); baos.write(new byte[]{0, 0, 0, 0}); baos.write(data); return baos.toByteArray(); } catch (IOException ioe) { //This should never happen as were not actually writing to/from a file throw new RuntimeException(ioe); } } }