FileDocCategorySizeDatePackage
AbstractBox.javaAPI Docmp4parser 1.0-RC-179423Wed Dec 19 20:10:37 GMT 2012com.googlecode.mp4parser

AbstractBox

public abstract class AbstractBox extends Object implements com.coremedia.iso.boxes.Box
A basic on-demand parsing box. Requires the implementation of three methods to become a fully working box:
  1. {@link #_parseDetails(java.nio.ByteBuffer)}
  2. {@link #getContent(java.nio.ByteBuffer)}
  3. {@link #getContentSize()}
additionally this new box has to be put into the isoparser-default.properties file so that it is accessible by the PropertyBoxParserImpl

Fields Summary
public static int
MEM_MAP_THRESHOLD
private static Logger
LOG
protected String
type
private byte[]
userType
private com.coremedia.iso.boxes.ContainerBox
parent
private ByteBuffer
content
private ByteBuffer
deadBytes
Constructors Summary
protected AbstractBox(String type)



       
        this.type = type;
    
protected AbstractBox(String type, byte[] userType)

        this.type = type;
        this.userType = userType;
    
Methods Summary
protected abstract void_parseDetails(java.nio.ByteBuffer content)
Parse the box's fields and child boxes if any.

param
content the box's raw content beginning after the 4-cc field.

public voidgetBox(java.nio.channels.WritableByteChannel os)

        ByteBuffer bb = ByteBuffer.allocate(l2i(getSize()));
        getHeader(bb);
        if (content == null) {
            getContent(bb);
            if (deadBytes != null) {
                deadBytes.rewind();
                while (deadBytes.remaining() > 0) {
                    bb.put(deadBytes);
                }
            }
        } else {
            content.rewind();
            bb.put(content);
        }
        bb.rewind();
        os.write(bb);
    
protected abstract voidgetContent(java.nio.ByteBuffer byteBuffer)
Write the box's content into the given ByteBuffer. This must include flags and version in case of a full box. byteBuffer has been initialized with getSize() bytes.

param
byteBuffer the sink for the box's content

protected abstract longgetContentSize()
Get the box's content size without its header. This must be the exact number of bytes that getContent(ByteBuffer) writes.

return
Gets the box's content size in bytes
see
#getContent(java.nio.ByteBuffer)

private voidgetHeader(java.nio.ByteBuffer byteBuffer)

        if (isSmallBox()) {
            IsoTypeWriter.writeUInt32(byteBuffer, this.getSize());
            byteBuffer.put(IsoFile.fourCCtoBytes(getType()));
        } else {
            IsoTypeWriter.writeUInt32(byteBuffer, 1);
            byteBuffer.put(IsoFile.fourCCtoBytes(getType()));
            IsoTypeWriter.writeUInt64(byteBuffer, getSize());
        }
        if (UserBox.TYPE.equals(getType())) {
            byteBuffer.put(getUserType());
        }


    
public com.coremedia.iso.IsoFilegetIsoFile()

        return parent.getIsoFile();
    
public com.coremedia.iso.boxes.ContainerBoxgetParent()

        return parent;
    
public longgetSize()
Gets the full size of the box including header and content.

return
the box's size

        long size = (content == null ? getContentSize() : content.limit());
        size += (8 + // size|type
                (size >= ((1L << 32) - 8) ? 8 : 0) + // 32bit - 8 byte size and type
                (UserBox.TYPE.equals(getType()) ? 16 : 0));
        size += (deadBytes == null ? 0 : deadBytes.limit());
        return size;
    
public java.lang.StringgetType()

        return type;
    
public byte[]getUserType()

        return userType;
    
public booleanisParsed()
Check if details are parsed.

return
true whenever the content ByteBuffer is not null

        return content == null;
    
private booleanisSmallBox()

        return (content == null ? (getContentSize() + (deadBytes != null ? deadBytes.limit() : 0) + 8) : content.limit()) < 1L << 32;
    
public voidparse(java.nio.channels.ReadableByteChannel readableByteChannel, java.nio.ByteBuffer header, long contentSize, com.coremedia.iso.BoxParser boxParser)
Read the box's content from a byte channel without parsing it. Parsing is done on-demand.

param
readableByteChannel the (part of the) iso file to parse
param
contentSize expected contentSize of the box
param
boxParser creates inner boxes
throws
IOException in case of an I/O error.

        if (readableByteChannel instanceof FileChannel && contentSize > MEM_MAP_THRESHOLD) {
            // todo: if I map this here delayed I could use transferFrom/transferTo in the getBox method
            // todo: potentially this could speed up writing.
            //
            // It's quite expensive to map a file into the memory. Just do it when the box is larger than a MB.
            content = ((FileChannel) readableByteChannel).map(FileChannel.MapMode.READ_ONLY, ((FileChannel) readableByteChannel).position(), contentSize);
            ((FileChannel) readableByteChannel).position(((FileChannel) readableByteChannel).position() + contentSize);
        } else {
            assert contentSize < Integer.MAX_VALUE;
            content = ChannelHelper.readFully(readableByteChannel, contentSize);
        }
    
final synchronized voidparseDetails()
Parses the raw content of the box. It surrounds the actual parsing which is done

        if (content != null) {
            ByteBuffer content = this.content;
            this.content = null;
            content.rewind();
            _parseDetails(content);
            if (content.remaining() > 0) {
                deadBytes = content.slice();
            }
            assert verify(content);
        }
    
protected voidsetDeadBytes(java.nio.ByteBuffer newDeadBytes)
Sets the 'dead' bytes. These bytes are left if the content of the box has been parsed but not all bytes have been used up.

param
newDeadBytes the unused bytes with no meaning but required for bytewise reconstruction

        deadBytes = newDeadBytes;
    
public voidsetParent(com.coremedia.iso.boxes.ContainerBox parent)

        this.parent = parent;
    
private booleanverify(java.nio.ByteBuffer content)
Verifies that a box can be reconstructed byte-exact after parsing.

param
content the raw content of the box
return
true if raw content exactly matches the reconstructed content

        ByteBuffer bb = ByteBuffer.allocate(l2i(getContentSize() + (deadBytes != null ? deadBytes.limit() : 0)));
        getContent(bb);
        if (deadBytes != null) {
            deadBytes.rewind();
            while (deadBytes.remaining() > 0) {
                bb.put(deadBytes);
            }
        }
        content.rewind();
        bb.rewind();


        if (content.remaining() != bb.remaining()) {
            LOG.severe(this.getType() + ": remaining differs " + content.remaining() + " vs. " + bb.remaining());
            return false;
        }
        int p = content.position();
        for (int i = content.limit() - 1, j = bb.limit() - 1; i >= p; i--, j--) {
            byte v1 = content.get(i);
            byte v2 = bb.get(j);
            if (v1 != v2) {
                LOG.severe(String.format("%s: buffers differ at %d: %2X/%2X", this.getType(), i, v1, v2));
                byte[] b1 = new byte[content.remaining()];
                byte[] b2 = new byte[bb.remaining()];
                content.get(b1);
                bb.get(b2);
                System.err.println("original      : " + Hex.encodeHex(b1, 4));
                System.err.println("reconstructed : " + Hex.encodeHex(b2, 4));
                return false;
            }
        }
        return true;