FileDocCategorySizeDatePackage
AsfFileReader.javaAPI DocJaudiotagger 2.0.410667Wed Mar 30 16:11:52 BST 2011org.jaudiotagger.audio.asf

AsfFileReader.java

/*
 * Entagged Audio Tag library
 * Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
 * 
 * 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.audio.asf;

import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.asf.data.AsfHeader;
import org.jaudiotagger.audio.asf.data.AudioStreamChunk;
import org.jaudiotagger.audio.asf.data.MetadataContainer;
import org.jaudiotagger.audio.asf.data.MetadataDescriptor;
import org.jaudiotagger.audio.asf.io.*;
import org.jaudiotagger.tag.asf.AsfTag;
import org.jaudiotagger.audio.asf.util.TagConverter;
import org.jaudiotagger.audio.asf.util.Utils;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.generic.AudioFileReader;
import org.jaudiotagger.audio.generic.GenericAudioHeader;
import org.jaudiotagger.logging.ErrorMessage;
import org.jaudiotagger.tag.TagException;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * This reader can read ASF files containing any content (stream type). <br>
 * 
 * @author Christian Laireiter
 */
public class AsfFileReader extends AudioFileReader {

    /**
     * Logger instance
     */
    private final static Logger LOGGER = Logger
            .getLogger("org.jaudiotagger.audio.asf");

    /**
     * This reader will be configured to read tag and audio header information.<br>
     */
    private final static AsfHeaderReader HEADER_READER;

    static {
        final List<Class<? extends ChunkReader>> readers = new ArrayList<Class<? extends ChunkReader>>();
        readers.add(ContentDescriptionReader.class);
        readers.add(ContentBrandingReader.class);
        readers.add(MetadataReader.class);
        readers.add(LanguageListReader.class);

        // Create the header extension object reader with just content
        // description reader as well
        // as extended content description reader.
        final AsfExtHeaderReader extReader = new AsfExtHeaderReader(readers,
                true);
        readers.add(FileHeaderReader.class);
        readers.add(StreamChunkReader.class);
        HEADER_READER = new AsfHeaderReader(readers, true);
        HEADER_READER.setExtendedHeaderReader(extReader);
    }

    /**
     * Determines if the "isVbr" field is set in the extended content
     * description.<br>
     * 
     * @param header
     *            the header to look up.
     * @return <code>true</code> if "isVbr" is present with a
     *         <code>true</code> value.
     */
    private boolean determineVariableBitrate(final AsfHeader header) {
        assert header != null;
        boolean result = false;
        final MetadataContainer extDesc = header
                .findExtendedContentDescription();
        if (extDesc != null) {
            final List<MetadataDescriptor> descriptors = extDesc
                    .getDescriptorsByName("IsVBR");
            if (descriptors != null && !descriptors.isEmpty()) {
                result = Boolean.TRUE.toString().equals(
                        descriptors.get(0).getString());
            }
        }
        return result;
    }

    /**
     * Creates a generic audio header instance with provided data from header.
     * 
     * @param header
     *            ASF header which contains the information.
     * @return generic audio header representation.
     * @throws CannotReadException
     *             If header does not contain mandatory information. (Audio
     *             stream chunk and file header chunk)
     */
    private GenericAudioHeader getAudioHeader(final AsfHeader header)
            throws CannotReadException {
        final GenericAudioHeader info = new GenericAudioHeader();
        if (header.getFileHeader() == null) {
            throw new CannotReadException(
                    "Invalid ASF/WMA file. File header object not available.");
        }
        if (header.getAudioStreamChunk() == null) {
            throw new CannotReadException(
                    "Invalid ASF/WMA file. No audio stream contained.");
        }
        info.setBitrate(header.getAudioStreamChunk().getKbps());
        info.setChannelNumber((int) header.getAudioStreamChunk()
                .getChannelCount());
        info.setEncodingType("ASF (audio): "
                + header.getAudioStreamChunk().getCodecDescription());
        info
                .setLossless(header.getAudioStreamChunk()
                        .getCompressionFormat() == AudioStreamChunk.WMA_LOSSLESS);
        info.setPreciseLength(header.getFileHeader().getPreciseDuration());
        info.setSamplingRate((int) header.getAudioStreamChunk()
                .getSamplingRate());
        info.setVariableBitRate(determineVariableBitrate(header));
        return info;
    }

    /**
     * (overridden)
     * 
     * @see org.jaudiotagger.audio.generic.AudioFileReader#getEncodingInfo(java.io.RandomAccessFile)
     */
    @Override
    protected GenericAudioHeader getEncodingInfo(final RandomAccessFile raf)
            throws CannotReadException, IOException {
        raf.seek(0);
        GenericAudioHeader info;
        try {
            final AsfHeader header = AsfHeaderReader.readInfoHeader(raf);
            if (header == null) {
                throw new CannotReadException(
                        "Some values must have been "
                                + "incorrect for interpretation as asf with wma content.");
            }
            info = getAudioHeader(header);
        } catch (final Exception e) {
            if (e instanceof IOException) {
                throw (IOException) e;
            } else if (e instanceof CannotReadException) {
                throw (CannotReadException) e;
            } else {
                throw new CannotReadException("Failed to read. Cause: "
                        + e.getMessage(), e);
            }
        }
        return info;
    }

    /**
     * Creates a tag instance with provided data from header.
     * 
     * @param header
     *            ASF header which contains the information.
     * @return generic audio header representation.
     */
    private AsfTag getTag(final AsfHeader header) {
        return TagConverter.createTagOf(header);
    }

    /**
     * (overridden)
     * 
     * @see org.jaudiotagger.audio.generic.AudioFileReader#getTag(java.io.RandomAccessFile)
     */
    @Override
    protected AsfTag getTag(final RandomAccessFile raf)
            throws CannotReadException, IOException {
        raf.seek(0);
        AsfTag tag;
        try {
            final AsfHeader header = AsfHeaderReader.readTagHeader(raf);
            if (header == null) {
                throw new CannotReadException(
                        "Some values must have been "
                                + "incorrect for interpretation as asf with wma content.");
            }

            tag = TagConverter.createTagOf(header);

        } catch (final Exception e) {
            logger.severe(e.getMessage());
            if (e instanceof IOException) {
                throw (IOException) e;
            } else if (e instanceof CannotReadException) {
                throw (CannotReadException) e;
            } else {
                throw new CannotReadException("Failed to read. Cause: "
                        + e.getMessage());
            }
        }
        return tag;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AudioFile read(final File f) throws CannotReadException,
            IOException, TagException, ReadOnlyFileException,
            InvalidAudioFrameException {
        if (!f.canRead()) {
            throw new CannotReadException(
                    ErrorMessage.GENERAL_READ_FAILED_DO_NOT_HAVE_PERMISSION_TO_READ_FILE
                            .getMsg(f.getAbsolutePath()));
        }
        InputStream stream = null;
        try {
            stream = new FullRequestInputStream(new BufferedInputStream(
                    new FileInputStream(f)));
            final AsfHeader header = HEADER_READER.read(Utils.readGUID(stream),
                    stream, 0);
            if (header == null) {
                throw new CannotReadException(ErrorMessage.ASF_HEADER_MISSING
                        .getMsg(f.getAbsolutePath()));
            }
            if (header.getFileHeader() == null) {
                throw new CannotReadException(
                        ErrorMessage.ASF_FILE_HEADER_MISSING.getMsg(f
                                .getAbsolutePath()));
            }

            // Just log a warning because file seems to play okay
            if (header.getFileHeader().getFileSize().longValue() != f.length()) {
                logger
                        .warning(ErrorMessage.ASF_FILE_HEADER_SIZE_DOES_NOT_MATCH_FILE_SIZE
                                .getMsg(f.getAbsolutePath(), header
                                        .getFileHeader().getFileSize()
                                        .longValue(), f.length()));
            }

            return new AudioFile(f, getAudioHeader(header), getTag(header));

        } catch (final CannotReadException e) {
            throw e;
        } catch (final Exception e) {
            throw new CannotReadException("\"" + f + "\" :" + e, e);
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
            } catch (final Exception ex) {
                LOGGER.severe("\"" + f + "\" :" + ex);
            }
        }
    }

}