package org.jcodec.containers.mkv.elements;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import org.jcodec.containers.mkv.Reader;
public class Block {
private long pos;
private long size;
private int headerSize;
public Block(long pos, long size) {
this.pos = pos;
this.size = size;
}
public static Block create(long pos, long size) {
return new Block(pos, size);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Offset: ").append(pos).append("\n");
sb.append("Size: ").append(size).append("\n");
return sb.toString();
}
public void seekParsePrint(FileChannel source, File parent) throws Exception {
boolean keyFrame;
ByteBuffer data = ByteBuffer.allocate((int) this.size);
source.position(pos);
source.read(data);
System.out.println("Offset: " + pos);
System.out.println("Size: "+size);
int trackNr = (int) Reader.getEbmlVInt(data);
int index = data.position(); // skip bytes that
// contain Track Nr
// Read timecode
int blockTimecode1 = data.get() & 0xFF;
int blockTimecode2 = data.get() & 0xFF;
int timecode = (blockTimecode1 << 8) | blockTimecode2;
// Read flags
byte flags = data.get();
int keyFlag = flags & 0x80;
if (keyFlag > 0)
keyFrame = true;
else
keyFrame = false;
int laceFlags = flags & 0x06;
// Increase the HeaderSize by the number of bytes we have read
if (laceFlags != 0x00) {
long[] sizes = null;
// We have lacing
byte lacesCount = data.get();
// HeaderSize += 1;
if (laceFlags == 0x02) { // Xiph Lacing
System.out.println("Lacing: Xiph");
sizes = readXiphLaceSizes(data, lacesCount);
int currentFramePos = headerSize;
for (int i = 0; i < sizes.length; i++) {
int s = (int) sizes[i];
System.out.println("Lace Size: " + s);
byte[] frameData = new byte[s];
if (frameData.length + currentFramePos > data.limit()) {
System.err.println("file offset [" + pos + "] laced frame [" + i + "] position+size ("
+ (frameData.length + currentFramePos) + ") exceeds block... skipping read");
return;
}
System.arraycopy(data, currentFramePos, frameData, 0, frameData.length);
currentFramePos += s;
}
} else if (laceFlags == 0x06) { // EBML Lacing
System.out.println("Lacing: EBML");
sizes = readEBMLLaceSizes(data, lacesCount);
System.out.println("Header Size: " + headerSize);
int currentFramePos = headerSize;
for (int i = 0; i < sizes.length; i++) {
int s = (int) sizes[i];
System.out.println("Lace Size: " + s);
byte[] frameData = new byte[s];
if (frameData.length + currentFramePos > data.limit()) {
System.err.println("file offset [" + pos + "] laced frame [" + i + "] position+size ("
+ (frameData.length + currentFramePos) + ") exceeds block... skipping read");
return;
}
System.arraycopy(data, currentFramePos, frameData, 0, frameData.length);
currentFramePos += s;
}
} else if (laceFlags == 0x04) { // Fixed Size Lacing
this.headerSize = index;
int laceSize = (int)((this.size - this.headerSize) / lacesCount);
System.out.println("Lace Size: " + laceSize);
System.out.println("Lace Count: " + lacesCount);
for (int i = 0; i < lacesCount; i++) {
byte[] frameData = new byte[laceSize];
if ((frameData.length*(i+1) + headerSize) > data.limit()) {
System.err.println("file offset [" + pos + "] laced frame [" + i + "] position+size ("
+ ((frameData.length*(i+1) + headerSize)) + ") exceeds block... skipping read");
return;
}
System.arraycopy(data, index, frameData, 0, frameData.length);
index += laceSize;
}
} else {
throw new RuntimeException("Unsupported lacing type flag.");
}
} else {
System.out.println("Lacing: n/a");
}
System.out.println("Timecode: " + timecode);
System.out.println("Track Nr: " + trackNr);
System.out.println("KeyFrame: " + keyFrame);
byte[] frameData = new byte[data.limit() - index];
System.arraycopy(data, index, frameData, 0, frameData.length);
}
private long[] readXiphLaceSizes(ByteBuffer data, byte count) {
long [] sizes = new long[count+1];
sizes[count] = (int)this.size;
for (int l = 0; l < count; l++) {
short laceSize = 255;
while (laceSize == 255) {
laceSize = (short)(data.get() & 0xFF);
sizes[l] += laceSize;
}
// Update the size of the last block
sizes[count] -= sizes[l];
}
headerSize = data.position();
sizes[count] -= headerSize;
return sizes;
}
public long[] readEBMLLaceSizes(ByteBuffer data, short count) {
long[] sizes = new long[count + 1];
sizes[count] = (int) this.size;
// This uses the DataSource.getBytePosition() for finding the header
// size
// because of the trouble of finding the byte size of sized ebml coded
// integers
sizes[0] = Reader.getEbmlVInt(data);
sizes[count] -= sizes[0];
long laceSize = sizes[0];
long laceSizeDiff = 0;
for (int l = 1; l < count; l++) {
laceSizeDiff = Reader.getSignedEbmlVInt(data);
laceSize += laceSizeDiff;
sizes[l] = laceSize;
// Update the size of the last block
sizes[count] -= sizes[l];
}
headerSize = data.position();
sizes[count] -= headerSize;
return sizes;
}
//
// public int[] readXiphLaceSizes(int index, short LaceCount) {
// int [] LaceSizes = new int[LaceCount+1];
// LaceSizes[LaceCount] = (int)this.getSize();
//
// //long ByteStartPos = source.getFilePointer();
//
// for (int l = 0; l < LaceCount; l++) {
// short LaceSizeByte = 255;
// while (LaceSizeByte == 255) {
// LaceSizeByte = (short)(data[index++] & 0xFF);
// HeaderSize += 1;
// LaceSizes[l] += LaceSizeByte;
// }
// // Update the size of the last block
// LaceSizes[LaceCount] -= LaceSizes[l];
// }
// //long ByteEndPos = source.getFilePointer();
//
// LaceSizes[LaceCount] -= HeaderSize;
//
// return LaceSizes;
// }
public byte[] readData(FileChannel ds) throws IOException {
ByteBuffer buff = ByteBuffer.allocate((int)this.size);
ds.position(this.pos);
ds.read(buff);
return buff.array();
}
}