package org.jcodec.containers.mkv; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import org.jcodec.common.FileChannelWrapper; import org.jcodec.common.IntArrayList; import org.jcodec.common.NIOUtils; import org.jcodec.common.tools.MathUtil; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * @author The JCodec project * */ public abstract class MKVEdit { public static final int[] MASTER_ELEMENTS = new int[] { 0x1A45DFA3, 0x1B538667, 0x7E5B, 0x7E7B, 0x18538067, 0x114D9B74, 0x4DBB, 0x1549A966, 0x6924, 0x1F43B675, 0x5854, 0xA0, 0x75A1, 0xA6, 0x8E, 0xE8, 0x1654AE6B, 0xAE, 0x6624, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE9, 0x6D80, 0x6240, 0x5034, 0x5035, 0x1C53BB6B, 0xBB, 0xB7, 0xDB, 0x1941A469, 0x61A7, 0x1043A770, 0x45B9, 0xB6, 0x8F, 0x80, 0x6944, 0x6911, 0x1254C367, 0x7373, 0x63C0, 0x67C8 }; public static final int[] TOP_ELEMENTS = new int[] { 0x1A45DFA3, 0x18538067 }; protected void run(FileChannelWrapper ch, FileChannelWrapper out) throws IOException { ByteBuffer inBuf = NIOUtils.fetchFrom(ch, 65536); List<ByteBuffer> list = new ArrayList<ByteBuffer>(); IntArrayList tags = new IntArrayList(); long tagPos = 0; while (inBuf.hasRemaining()) { int tagLen = inBuf.position(); int type = readVIntNoMask(inBuf); if (!ofAClass(TOP_ELEMENTS, type)) break; long size = readVInt(inBuf); tagLen = inBuf.position() - tagLen + (int) size; if (inBuf.remaining() < size) { ch.position(tagPos); inBuf = NIOUtils.fetchFrom(ch, (int) (size + 16)); continue; } System.out.println(type + ": " + size); tags.add(type); list.add(processLevel(NIOUtils.read(inBuf, (int) size), "+")); tagPos += tagLen; } for (int i = 0; i < tags.size(); i++) { ByteBuffer tag = ByteBuffer.allocate(16); writeVIntNoMask(tag, tags.get(i)); ByteBuffer bb = list.get(i); writeVInt(tag, bb.remaining()); tag.flip(); out.write(tag); out.write(bb.duplicate()); } // Copy the rest ch.position(tagPos); NIOUtils.copy(ch, out, ch.size() - ch.position()); } private static boolean ofAClass(int[] set, int type) { for (int i = 0; i < set.length; i++) if (set[i] == type) return true; return false; } private ByteBuffer processLevel(ByteBuffer read, String level) { List<ByteBuffer> list = new ArrayList<ByteBuffer>(); IntArrayList tags = new IntArrayList(); int outSize = 0; while (read.hasRemaining()) { int type = readVIntNoMask(read); long size = readVInt(read); System.out.println(type + ": " + size); ByteBuffer contents = NIOUtils.read(read, (int) size); ByteBuffer outBuf; if (ofAClass(MASTER_ELEMENTS, type)) { outBuf = processLevel(contents, level + "+"); } else { outBuf = edit(type, contents); } list.add(outBuf); tags.add(type); outSize += 16 + outBuf.remaining(); } ByteBuffer out = ByteBuffer.allocate(outSize); for (int i = 0; i < tags.size(); i++) { writeVIntNoMask(out, tags.get(i)); ByteBuffer bb = list.get(i); writeVInt(out, bb.remaining()); out.put(bb); } out.flip(); return out; } protected abstract ByteBuffer edit(int type, ByteBuffer contents); /** * Reads EBML variable length integer * * @param bb * @return */ public static int readVIntNoMask(ByteBuffer bb) { int ret = bb.get() & 0xff; int len = 7 - MathUtil.log2(ret); if (len > 3) throw new RuntimeException("Max 4 bytes expected"); for (int i = 0; i < len; i++) { ret = (ret << 8) | (bb.get() & 0xff); } return ret; } /** * Reads EBML variable length integer 64 bit * * @param bb * @return */ public static long readVInt(ByteBuffer bb) { long ret = bb.get() & 0xff; int len = 7 - MathUtil.log2(ret); ret &= (1 << (7 - len)) - 1; for (int i = 0; i < len; i++) { ret = (ret << 8) | (bb.get() & 0xff); } return ret; } public static void writeVInt(ByteBuffer bb, long num) { int log = MathUtil.log2(num); if (log >= 56) throw new RuntimeException("Can not encode more then 56 bits"); int bytes = log / 7; log = bytes << 3; int mask = 0x80 >>> bytes; for (; log >= 0; log -= 8, mask = 0) { bb.put((byte) (mask | (num >>> log))); } } public static void writeVIntNoMask(ByteBuffer bb, int num) { int log = MathUtil.log2(num); log &= ~7; for (; log >= 0; log -= 8) { bb.put((byte) (num >>> log)); } } }