/** * JEBML - Java library to read/write EBML/Matroska elements. * Copyright (C) 2004 Jory Stone <jebml@jory.info> * Based on Javatroska (C) 2002 John Cannon <spyder@matroska.org> * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jcodec.containers.mkv.ebml; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.FileChannel; import java.util.ArrayList; import org.jcodec.containers.mkv.Reader; public class MasterElement extends Element { protected long usedSize; public final ArrayList<Element> children = new ArrayList<Element>(); public MasterElement(byte[] type) { super(type); usedSize = 0; } public Element readNextChild(Reader reader) throws IOException { if (usedSize >= this.size) return null; long start = reader.getPos(); Element elem = reader.readNextElement(); // elem == 'null', only if 0x00 was read as first byte of id if (elem == null) { while (elem == null && reader.getPos() < reader.getAvailable()) { long pos = reader.getPos(); usedSize += (pos - start); // since elem == null, 0x00 was read instead of // element ID, one byte should be accounted for start = pos; elem = reader.readNextElement(); } if (elem == null || usedSize >= size) return null; } elem.setParent(this); usedSize += elem.getSize(); return elem; } /* Skip the element data */ @Override public void skipData(FileChannel source) throws IOException { // Skip the child elements or seek back if too much was read long l = size + dataOffset; source.position(l); usedSize = size; } public void addChildElement(Element elem) { if (elem == null) return; elem.setParent(this); children.add(elem); } public long mux(ByteChannel os) throws IOException { return os.write(mux()); } public ByteBuffer mux() { int size = (int) getDataSize(); ByteBuffer bb = ByteBuffer.allocate(id.length + Element.getEbmlSize(size)+size); bb.put(id); bb.put(ebmlBytes(size)); for (int i = 0; i < children.size(); i++) { Element elem = (Element) children.get(i); bb.put(elem.mux()); } bb.flip(); return bb; } private int getDataSize(){ int returnValue = 0; if (children != null && !children.isEmpty()){ // Either account for all the children for(Element e : children) returnValue += e.getSize(); } else { // Or just rely on size attribute if no children are present // this happens while reading the file returnValue += size; } return returnValue; } @Override public long getSize() { // 1. Start counting from the bottom long returnValue = getDataSize(); // 2. Account for size of all the children in EBML format returnValue += Element.getEbmlSize(returnValue); // 3. Finally add size of this element's header returnValue += id.length; return returnValue; } }