/** * 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.FileChannel; import java.util.Arrays; import org.jcodec.common.Assert; import org.jcodec.containers.mkv.Type; /** * Defines the basic EBML element. Subclasses may provide child element access. * * @author John Cannon */ public class Element { protected Element parent; public Type type; public byte[] id = { 0x00 }; public long size = 0; protected ByteBuffer data; protected boolean dataRead = false; public long offset; public long dataOffset; /** Creates a new instance of Element */ public Element(byte[] type) { this.id = type; } /** * Read the element data * * @throws IOException */ public void readData(FileChannel source) throws IOException { // Setup a buffer for it's data try { this.data = ByteBuffer.allocate((int) size); // Read the data while (source.read(data) != -1 && data.hasRemaining()) ; dataRead = true; this.data.flip(); } catch (OutOfMemoryError oome) { System.out.println("OutOfMemoryError while trying to read " + size + " bytes for element at " + offset); throw oome; } } /** * Skip the element data * @throws IOException */ public void skipData(FileChannel source) throws IOException { if (!dataRead) { // Skip the data source.position(dataOffset+size); dataRead = true; } } /** * Get the total size of this element */ public long getSize() { long totalSize = 0; if (data != null && data.limit() > 0) { // If possible, get real data size // First count the real data size totalSize += data.limit(); // Then add the encoded lengh of data totalSize += Element.getEbmlSize(data.limit()); } else { //if no data is present, try the same with size attribute totalSize += this.size; totalSize += Element.getEbmlSize(size); } totalSize += id.length; return totalSize; } /** * Getter for property type. * * @return Value of property type. * */ public byte[] getId() { return id; } /** * Setter for property parent. * * @param parent * New value of property parent. * */ public void setParent(Element parent) { this.parent = parent; } public boolean isSameEbmlId(byte[] typeId) { return Arrays.equals(this.id, typeId); } public boolean isSameMatroskaType(Type elemType) { return this.isSameEbmlId(elemType.id); } public long mux(FileChannel os) throws IOException { ByteBuffer bb = mux(); return os.write(bb); } public ByteBuffer mux() { int sizeSize = getEbmlSize(data.limit()); ByteBuffer bb = ByteBuffer.allocate(id.length + sizeSize + data.limit()); bb.put(id); byte[] size = ebmlBytes(data.limit(), sizeSize); bb.put(size); Assert.assertEquals(this.type+" data seems to be read already or not ready for reading", 0, data.position()); Assert.assertEquals(data.capacity(), data.limit()); bb.put(data); bb.flip(); data.flip(); return bb; } public static byte[] ebmlBytes(long value) { int size = getEbmlSize(value); return ebmlBytes(value, size); } public static byte[] ebmlBytes(long val, int num) { byte[] b = new byte[num]; for (int idx = 0; idx < num; idx++) { // Rightmost bytes should go to end of array to preserve big-endian // notation b[num - idx - 1] = (byte) ((val >>> (8 * idx)) & 0xFFL); } b[0] |= 0x80 >>> (num - 1); return b; } public static int getEbmlSize(long v) { if (v == 0) return 1; long oneMask = 0x7F; // 0x3F 0x80 long twoMask = 0x3F80; // 0x1F 0xC0 0x00 long threeMask = 0x1FC000; // 0x0F 0xE0 0x00 0x00 long fourMask = 0x0FE00000; // 0x07 0xF0 0x00 0x00 0x00 long fiveMask = 0x07F0000000L; // 0x03 0xF8 0x00 0x00 0x00 0x00 long sixMask = 0x03F800000000L; // 0x01 0xFC 0x00 0x00 0x00 0x00 0x00 long sevenMask = 0x01FC0000000000L; // 0x00 0xFE 0x00 0x00 0x00 0x00 0x00 0x00 long eightMask = 0xFE000000000000L; long[] allMasks = new long[] { 0, oneMask, twoMask, threeMask, fourMask, fiveMask, sixMask, sevenMask, eightMask }; int size = 8; while (size > 0 && (v & allMasks[size]) == 0) size--; return size; } }