/** * */ package mp4.util.atom; /** * The chunk offset table. */ public class StcoAtom extends LeafAtom { protected static final int ENTRIES_OFFSET = 4; protected static final int TABLE_OFFSET = 8; protected static final int _ENTRY_SIZE = 4; protected int entrySize = _ENTRY_SIZE; /** * Construct an empty atom */ public StcoAtom() { super(new byte[]{'s','t','c','o'}); } /** * Construct an empty atom */ public StcoAtom(byte[] atomType) { super(atomType); } /** * Copy constructor. Perform a deep copy. * @param old the version to copy */ public StcoAtom(StcoAtom old) { super(old); long numEntries = old.getNumEntries(); this.allocateData(numEntries); this.setNumEntries(numEntries); int entryNumber = 0; for (long i = 1; i <= numEntries; i++, entryNumber++) { this.setChunkOffset(entryNumber, old.getChunkOffset(i)); } } /** * Allocate space for the atom's data. */ @Override public void allocateData(long numEntries) { long size = TABLE_OFFSET + (numEntries * entrySize); super.allocateData(size); } /** * Return the number of entries in the stco table * @return the number of entries */ public long getNumEntries() { return data.getUnsignedInt(ENTRIES_OFFSET); } /** * Set the number of entries in the stco table * @param numEntries the number of entries */ public void setNumEntries(long numEntries) { data.addUnsignedInt(ENTRIES_OFFSET, numEntries); } /** * Get the chunk offset for the specified chunk. The chunk * values are 1 based, while the table is 0 based. * @param chunk the chunk number * @return the offset for the chunk */ public long getChunkOffset(long chunk) { if (chunk > Integer.MAX_VALUE) { return 0; } return data.getUnsignedInt(TABLE_OFFSET + ((int)(chunk - 1) * entrySize)); } /** * Set the chunk offset for specified table index * @param index the table index number * @param chunk the chunk offset */ public void setChunkOffset(int index, long chunk) { data.addUnsignedInt(TABLE_OFFSET + (index * entrySize), chunk); } /** * Split the atom at the specified chunk and fill the byte stream with * the contents of the new atom. Any entry prior to the specified * chunk is discarded, and the new table is created with the entries * subsequent to the specified chunk. * @param bs the byte stream where the new data is added * @param chunkNum the chunk where the atom should be split * @param chunkOffset the delta into the firstChunk (in the case we are splitting a chunk) */ public StcoAtom cut(long chunkNum, long chunkOffset) { return cut(chunkNum, chunkOffset, false); } public StcoAtom cut(long chunkNum, long chunkOffset, boolean force32) { // create the new table StcoAtom cutStco ; if (this instanceof Co64Atom && !force32) cutStco = new Co64Atom(); else cutStco = new StcoAtom(); long numEntries = getNumEntries(); if (force32) { for (long i=chunkNum;i<=numEntries;i++) { if (getChunkOffset(i) > 0xFFFFFFFFL) { numEntries = i-1; break; } } } cutStco.allocateData(numEntries - chunkNum + 1); cutStco.setNumEntries(numEntries - chunkNum + 1); int entryNumber = 0; for (long i = chunkNum; i <= numEntries; i++, entryNumber++) { cutStco.setChunkOffset(entryNumber, getChunkOffset(i)); } cutStco.setChunkOffset(0, cutStco.getChunkOffset(1) + chunkOffset); return cutStco; } public StcoAtom copy32Bit() { if (!(this instanceof Co64Atom)) return this; return cut(1, 0, true); } /** * Perform a fixup of the offsets in the stco atom. Each existing values * changes by the specified amount. The update must occur if any of the mp4 file * changes, e.g., when the mp4 file is cut. * @param delta the amount to update each offset */ public void fixupOffsets(long delta) { for (int i = 0; i < getNumEntries(); i++) { setChunkOffset(i, getChunkOffset(i+1) + delta); } } @Override public void accept(AtomVisitor v) throws AtomException { v.visit(this); } }