![JAR search and dependency download from the Maven repository](/logo.png)
org.jcodec.containers.mkv.Reader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcodec Show documentation
Show all versions of jcodec Show documentation
Pure Java implementation of video/audio codecs and formats
/**
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
*
* 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;
import static java.lang.Integer.toHexString;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import org.jcodec.containers.mkv.ebml.Element;
/**
* EBMLReader.java
*
* Created on November 18, 2002, 4:03 PM
*
* @version 1.0
*/
/**
* JEBML Intro
*
*
* The following are the basic steps of how reading in JEBML works.
*
*
* - 1. The EbmlReader class reads the element header (id+size) and looks it
* up in the supplied DocType class.
*
- 2. The correct element type (Binary, UInteger, String, etc) is created
* using the DocType data, BinaryElement is default element type for unknown
* elements.
*
- 3. The MatroskaDocType has the ids of all the elements staticly declared.
*
* So to easily find out what an element is, you can use some code like the
* following code
*
*
* Element level1;
* // ... fill level1
* if (level1.equals(MatroskaDocType.SegmentInfo_Id)) {
* // Your Code Here
* }
*
*
* - 4. To get the actual data for an Element you call the readData method, if
* you just want to skip it use skipData().
*
- 5. MasterElements are special, they have the readNextChild() method which
* returns the next child element, returning null when all the children have
* been read (it keeps track of the current inputstream position).
*
- The usage method for JEBML is very close to libebml/libmatroska.
*
*
*
* Reads EBML elements from a DataSource
and looks them up in the
* provided DocType
.
*
* @author (c) 2002 John Cannon
* @author (c) 2004 Jory Stone
*/
public class Reader {
protected FileChannel ds;
/**
* Creates a new EBMLReader
reading from the DataSource
* source
. The DocType doc
is used to validate the
* document.
*
* @param source
* DataSource to read from
* @param doc
* DocType to use to validate the docment
*/
public Reader(FileChannel source) {
this.ds = source;
}
public long getPos() throws IOException {
return 0;
}
public long getAvailable() throws IOException {
return 0;
}
public static String bytesToHex(byte[] bts) {
StringBuilder sb = new StringBuilder();
if (bts == null)
return "";
for (byte b : bts) {
sb.append(" 0x").append(toHexString(b & 0xFF).toUpperCase());
}
return sb.toString();
}
public static String printAsHex(byte[] a){
StringBuilder sb = new StringBuilder();
for(byte b: a)
sb.append(String.format("0x%02x ", b&0xff));
return sb.toString();
}
public Element readNextElement() throws IOException {
// Read the type.
long offset = ds.position();
byte[] typeId = getRawEbmlBytes(ds);
if (typeId == null)
// Failed to read type id
// one byte was read from source (this should be reflected in
// 'usedSize' of a master element)
return null;
// Read the size.
byte[] ebmlCodedElementSize = getEbmlBytes(ds);
long size = bytesToLong(ebmlCodedElementSize);
// Zero sized element is valid
if (size == 0)
;
// according to MatroskaDocType.createElement return value is never
// 'null'
Element elem = Type.createElementById(typeId);
elem.offset = offset;
// Set it's size
elem.size = size;
// if (ebmlCodedElementSize == null) {
// elem.setHeaderSize(1); // if data array is null, only one byte was read;
// } else {
// elem.setHeaderSize(ebmlCodedElementSize.length);
// }
return elem;
}
/**
* Reads an (Unsigned) EBML code from the DataSource and encodes it into a
* long. This size should be cast into an int for actual use as Java only
* allows upto 32-bit file I/O operations.
*
* @return ebml size
* @throws IOException
*/
static public long getEbmlVInt(FileChannel source) throws IOException {
// Begin loop with byte set to newly read byte.
ByteBuffer bufferForFirstByte = ByteBuffer.allocate(1);
source.read(bufferForFirstByte);
bufferForFirstByte.flip();
byte firstByte = bufferForFirstByte.get();
int numBytes = getEbmlSizeByFirstByte(firstByte);
if (numBytes == 0)
// Invalid size
return 0;
if (numBytes == 1)
return firstByte & ((0xFF >>> (numBytes)));
// Setup space to store the bits
ByteBuffer bb = ByteBuffer.allocate(numBytes);
// Clear the 1 at the front of this byte, all the way to the beginning
// of the size
bb.put((byte) (firstByte & ((0xFF >>> (numBytes)))));
// Read the rest of the size.
source.read(bb);
// Put this into a long
return bytesToLong(bb.array());
}
/**
* Reads an Signed EBML code from the DataSource and encodes it into a long.
* This size should be cast into an int for actual use as Java only allows
* upto 32-bit file I/O operations.
*
* @return ebml size
* @throws IOException
*/
static public long getSignedEbmlVInt(FileChannel is) throws IOException {
// Begin loop with byte set to newly read byte.
byte[] vInt = getEbmlBytes(is);
long uInt = bytesToLong(vInt);
return uInt - signedComplement[vInt.length];
}
public static long bytesToLong(ByteBuffer data) {
if (data == null)
return 0;
long value = 0;
while(data.position() < data.limit())
value = (value << 8) | (data.get() & 0xff);
return value;
}
public static long bytesToLong(byte[] data) {
if (data == null)
return 0;
long value = 0;
for (int i = 0; i < data.length; i++)
value = (value << 8) | (data[i] & 0xff);
return value;
}
static public int getEbmlSizeByFirstByte(byte b){
int numBytes = 0;
// Begin by counting the bits unset before the first '1'.
long mask = 0x0080;
for (int i = 0; i < 8; i++) {
// Start at left, shift to right.
if ((b & mask) == mask) { // One found
// Set number of bytes in size =
// i+1 ( we must count the 1 too)
numBytes = i + 1;
// exit loop by pushing i out of the limit
i = 8;
}
mask >>>= 1;
}
return numBytes;
}
/**
* Reads an (Unsigned) EBML code from the DataSource and encodes it into a
* long. This size should be cast into an int for actual use as Java only
* allows upto 32-bit file I/O operations.
*
* @return ebml size
*/
static public long getEbmlVInt(ByteBuffer bb) {
// Begin loop with byte set to newly read byte.
byte firstByte = bb.get();
int numBytes = getEbmlSizeByFirstByte(firstByte);
if (numBytes == 0)
// Invalid size
return 0;
if (numBytes == 1)
return firstByte & ((0xFF >>> (numBytes)));
// Setup space to store the bits
byte[] data = new byte[numBytes];
// Clear the 1 at the front of this byte, all the way to the beginning
// of the size
data[0] = (byte) (firstByte & ((0xFF >>> (numBytes))));
// Read the rest of the size.
bb.get(data, 1, data.length - 1);
long longValue = bytesToLong(data);
// Put this into a long
return longValue;
}
/**
* Reads an (Unsigned) EBML code from the DataSource and encodes it into a
* long. This size should be cast into an int for actual use as Java only
* allows upto 32-bit file I/O operations.
*
* @return ebml size
*/
static public long getEbmlVInt(ByteBuffer source, int offset) {
// Begin loop with byte set to newly read byte.
byte firstByte = source.get(offset);
int numBytes = getEbmlSizeByFirstByte(firstByte);
if (numBytes == 0)
// Invalid size
return 0;
if (numBytes == 1)
return firstByte & ((0xFF >>> (numBytes)));
// Setup space to store the bits
byte[] data = new byte[numBytes];
// Clear the 1 at the front of this byte, all the way to the beginning
// of the size
data[0] = (byte) (firstByte & ((0xFF >>> (numBytes))));
// Read the rest of the size.
System.arraycopy(source.array(), offset + 1, data, 1, numBytes - 1);
long longValue = bytesToLong(data);
// Put this into a long
return longValue;
}
public static final long[] signedComplement = {0, 0x3F, 0x1FFF, 0x0FFFFF, 0x07FFFFFF, 0x03FFFFFFFFL, 0x01FFFFFFFFFFL, 0x00FFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL};
public static String asHex(int i){
return "0x"+printAsHex(new byte[]{(byte)i});
}
/**
* Reads an Signed EBML code from the DataSource and encodes it into a long.
* This size should be cast into an int for actual use as Java only allows
* upto 32-bit file I/O operations.
*
* @return ebml size
*/
/**
* Reads an Signed EBML code from the DataSource and encodes it into a long.
* This size should be cast into an int for actual use as Java only allows
* upto 32-bit file I/O operations.
*
* @return ebml size
*/
static public long getSignedEbmlVInt(ByteBuffer source) {
// Begin loop with byte set to newly read byte.
byte[] bytes = getVIntEbmlBytes(source);
if (bytes == null)
throw new RuntimeException("Can't convert byte 0x"+Integer.toHexString(source.get(source.position()-1) & 0xFF).toUpperCase()+" to first ebml byte.");
return bytesToLong(bytes) - signedComplement[bytes.length];
}
// /**
// * Reads an Signed EBML code from the DataSource and encodes it into a long.
// * This size should be cast into an int for actual use as Java only allows
// * upto 32-bit file I/O operations.
// *
// * @return ebml size
// * @throws IOException
// */
// static public long readSignedEBMLCode(FileChannel source) throws IOException {
//
// // Begin loop with byte set to newly read byte.
// ByteBuffer bufferForFirstByte = ByteBuffer.allocate(1);
// source.read(bufferForFirstByte);
// bufferForFirstByte.flip();
// byte firstByte = bufferForFirstByte.get();
// int numBytes = 0;
//
// // Begin by counting the bits unset before the first '1'.
// long mask = 0x0080;
// for (int i = 0; i < 8; i++) {
// // Start at left, shift to right.
// if ((firstByte & mask) == mask) { // One found
// // Set number of bytes in size = i+1 ( we must count the 1 too)
// numBytes = i + 1;
// // exit loop by pushing i out of the limit
// i = 8;
// }
// mask >>>= 1;
// }
// if (numBytes == 0)
// // Invalid size
// return 0;
//
// // Setup space to store the bits
// byte[] data = new byte[numBytes];
//
// // Clear the 1 at the front of this byte, all the way to the beginning
// // of the size
// data[0] = (byte) (firstByte & ((0xFF >>> (numBytes))));
//
// if (numBytes > 1) {
// // Read the rest of the size.
// source.read(data, 1, numBytes - 1);
// }
//
// // Put this into a long
// long size = 0;
// long n = 0;
// for (int i = 0; i < numBytes; i++) {
// n = ((long) data[numBytes - 1 - i] << 56) >>> 56;
// size = size | (n << (8 * i));
// }
//
// // Sign it ;)
// if (numBytes == 1) {
// size -= 63;
//
// } else if (numBytes == 2) {
// size -= 8191;
//
// } else if (numBytes == 3) {
// size -= 1048575;
//
// } else if (numBytes == 4) {
// size -= 134217727;
// }
//
// return size;
// }
static public byte[] getVIntEbmlBytes(ByteBuffer source) {
// Begin loop with byte set to newly read byte.
byte firstByte = source.get();
int numBytes = getEbmlSizeByFirstByte(firstByte);
if (numBytes == 0)
// Invalid size
return null;
// Setup space to store the bits
ByteBuffer data = ByteBuffer.allocate(numBytes);
// Clear the 1 at the front of this byte, all the way to the beginning of the size
data.put((byte) (firstByte & ((0xFF >>> (numBytes)))));
// Read the rest of the size.
int remaining = numBytes-1;
while(remaining > 0){
data.put(source.get());
remaining--;
}
return data.array();
}
static public byte[] getEbmlBytes(FileChannel source) throws IOException {
// Begin loop with byte set to newly read byte.
ByteBuffer bufferForFirstByte = ByteBuffer.allocate(1);
source.read(bufferForFirstByte);
bufferForFirstByte.flip();
byte firstByte = (byte) bufferForFirstByte.get();
int numBytes = getEbmlSizeByFirstByte(firstByte);
if (numBytes == 0)
// Invalid size
return null;
if (numBytes == 1)
return new byte[]{(byte) (firstByte & ((0xFF >>> (numBytes))))};
// Setup space to store the bits
ByteBuffer data = ByteBuffer.allocate(numBytes);
// Clear the 1 at the front of this byte, all the way to the beginning
// of the size
data.put((byte) (firstByte & ((0xFF >>> (numBytes)))));
// Read the rest of the size.
source.read(data);
return data.array();
}
/**
* Reads an EBML id from the DataSource. EBML ids have length encoded inside
* of them For instance, all one-byte ids have first byte set to '1', like
* 0xA3, 0xE7, whereas the two-byte ids have first byte set to '0' and
* second byte set to '1', thus: 0x4286 (ebml version) or 0x42F7 (ebml read
* version)
*
* @return byte array filled with the ebml size, (size bits included)
* @throws IOException
*/
static public byte[] getRawEbmlBytes(FileChannel source) throws IOException {
// Begin loop with byte set to newly read byte.
if (source.position() == source.size())
return null;
ByteBuffer bufferForFirstByte = ByteBuffer.allocate(1);
source.read(bufferForFirstByte);
bufferForFirstByte.flip();
byte firstByte = bufferForFirstByte.get();
int numBytes = getEbmlSizeByFirstByte(firstByte);
if (numBytes == 0)
// Invalid element
return null;
if (numBytes == 1)
return bufferForFirstByte.array();
// Setup space to store the bits
ByteBuffer data = ByteBuffer.allocate(numBytes);
data.put(firstByte);
// Clear the 1 at the front of this byte, all the way to the beginning
// of the size
// Read the rest of the size.
source.read(data);
return data.array();
}
public Element readNextFirstLevelElement() throws IOException {
if (ds.position() == ds.size())
return null;
long offset = ds.position();
byte[] typeId = getRawEbmlBytes(ds);
while (typeId == null || !Type.isFirstLevelHeader(typeId)){
offset++;
ds.position(offset);
typeId = getRawEbmlBytes(ds);
}
byte[] data = getEbmlBytes(ds);
long elementSize = bytesToLong(data);
Element elem = Type.createElementById(typeId);
elem.size = elementSize;
elem.offset = offset;
return elem;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy