FileDocCategorySizeDatePackage
AsfStreamer.javaAPI DocJaudiotagger 2.0.48063Wed Mar 30 16:11:50 BST 2011org.jaudiotagger.audio.asf.io

AsfStreamer

public class AsfStreamer extends Object
This class creates a modified copy of an ASF file.
author
Christian Laireiter

Fields Summary
Constructors Summary
Methods Summary
private voidcopyChunk(org.jaudiotagger.audio.asf.data.GUID guid, java.io.InputStream source, java.io.OutputStream destination)
Simply copies a chunk from source to destination.
The method assumes, that the GUID has already been read and will write the provided one to the destination.
The chunk length however will be read and used to determine the amount of bytes to copy.

param
guid GUID of the current chunk.
param
source source of an ASF chunk, which is to be located at the chunk length field.
param
destination the destination to copy the chunk to.
throws
IOException on I/O errors.

        final long chunkSize = Utils.readUINT64(source);
        destination.write(guid.getBytes());
        Utils.writeUINT64(chunkSize, destination);
        Utils.copy(source, destination, chunkSize - 24);
    
public voidcreateModifiedCopy(java.io.InputStream source, java.io.OutputStream dest, java.util.List modifiers)
Reads the source and applies the modifications provided by the given modifiers, and puts it to dest.
Each {@linkplain ChunkModifier modifier} is used only once, so if one should be used multiple times, it should be added multiple times into the list.

param
source the source ASF file
param
dest the destination to write the modified version to.
param
modifiers list of chunk modifiers to apply.
throws
IOException on I/O errors.

        final List<ChunkModifier> modders = new ArrayList<ChunkModifier>();
        if (modifiers != null) {
            modders.addAll(modifiers);
        }
        // Read and check ASF GUID
        final GUID readGUID = Utils.readGUID(source);
        if (GUID.GUID_HEADER.equals(readGUID)) {
            // used to calculate differences
            long totalDiff = 0;
            long chunkDiff = 0;

            // read header information
            final long headerSize = Utils.readUINT64(source);
            final long chunkCount = Utils.readUINT32(source);
            final byte[] reserved = new byte[2];
            reserved[0] = (byte) (source.read() & 0xFF);
            reserved[1] = (byte) (source.read() & 0xFF);

            /*
             * bos will get all unmodified and modified header chunks. This is
             * necessary, because the header chunk (and file properties chunk)
             * need to be adjusted but are written in front of the others.
             */
            final ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // fileHeader will get the binary representation of the file
            // properties chunk, without GUID
            byte[] fileHeader = null;

            // Iterate through all chunks
            for (long i = 0; i < chunkCount; i++) {
                // Read GUID
                final GUID curr = Utils.readGUID(source);
                // special case for file properties chunk
                if (GUID.GUID_FILE.equals(curr)) {
                    final ByteArrayOutputStream tmp = new ByteArrayOutputStream();
                    final long size = Utils.readUINT64(source);
                    Utils.writeUINT64(size, tmp);
                    Utils.copy(source, tmp, size - 24);
                    fileHeader = tmp.toByteArray();
                } else {
                    /*
                     * Now look for ChunkModifier objects which modify the
                     * current chunk
                     */
                    boolean handled = false;
                    for (int j = 0; j < modders.size() && !handled; j++) {
                        if (modders.get(j).isApplicable(curr)) {
                            // alter current chunk
                            final ModificationResult result = modders.get(j)
                                    .modify(curr, source, bos);
                            // remember size differences.
                            chunkDiff += result.getChunkCountDifference();
                            totalDiff += result.getByteDifference();
                            // remove current modifier from index.
                            modders.remove(j);
                            handled = true;
                        }
                    }
                    if (!handled) {
                        // copy chunks which are not modified.
                        copyChunk(curr, source, bos);
                    }
                }
            }
            // Now apply the left modifiers.
            for (final ChunkModifier curr : modders) {
                // chunks, which were not in the source file, will be added to
                // the destination
                final ModificationResult result = curr.modify(null, null, bos);
                chunkDiff += result.getChunkCountDifference();
                totalDiff += result.getByteDifference();
            }
            /*
             * Now all header objects have been read or manipulated and stored
             * in the internal buffer (bos).
             */
            // write ASF GUID
            dest.write(readGUID.getBytes());
            // write altered header object size
            Utils.writeUINT64(headerSize + totalDiff, dest);
            // write altered number of chunks
            Utils.writeUINT32(chunkCount + chunkDiff, dest);
            // write the reserved 2 bytes (0x01,0x02).
            dest.write(reserved);
            // write the new file header
            modifyFileHeader(new ByteArrayInputStream(fileHeader), dest,
                    totalDiff);
            // write the header objects (chunks)
            dest.write(bos.toByteArray());
            // copy the rest of the file (data and index)
            Utils.flush(source, dest);
        } else {
            throw new IllegalArgumentException("No ASF header object.");
        }
    
private voidmodifyFileHeader(java.io.InputStream source, java.io.OutputStream destination, long fileSizeDiff)
This is a slight variation of {@link #copyChunk(GUID, InputStream, OutputStream)}, it only handles file property chunks correctly.
The copied chunk will have the file size field modified by the given fileSizeDiff value.

param
source source of file properties chunk, located at its chunk length field.
param
destination the destination to copy the chunk to.
param
fileSizeDiff the difference which should be applied. (negative values would subtract the stored file size)
throws
IOException on I/O errors.

        destination.write(GUID.GUID_FILE.getBytes());
        final long chunkSize = Utils.readUINT64(source);
        Utils.writeUINT64(chunkSize, destination);
        destination.write(Utils.readGUID(source).getBytes());
        final long fileSize = Utils.readUINT64(source);
        Utils.writeUINT64(fileSize + fileSizeDiff, destination);
        Utils.copy(source, destination, chunkSize - 48);