package org.jcodec.containers.mkv; import static org.jcodec.containers.mkv.MKVType.Seek; import static org.jcodec.containers.mkv.MKVType.SeekHead; import static org.jcodec.containers.mkv.MKVType.SeekID; import static org.jcodec.containers.mkv.MKVType.SeekPosition; import static org.jcodec.containers.mkv.MKVType.createByType; import static org.jcodec.containers.mkv.boxes.EbmlUint.calculatePayloadSize; import org.jcodec.containers.mkv.boxes.EbmlBase; import org.jcodec.containers.mkv.boxes.EbmlBin; import org.jcodec.containers.mkv.boxes.EbmlMaster; import org.jcodec.containers.mkv.boxes.EbmlUint; import org.jcodec.containers.mkv.util.EbmlUtil; import java.lang.System; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed under FreeBSD License * * EBML IO implementation * * @author The JCodec project * */ public class SeekHeadFactory { List<SeekHeadFactory.SeekMock> a; long currentDataOffset = 0; public SeekHeadFactory() { this.a = new ArrayList<SeekHeadFactory.SeekMock>(); } public void add(EbmlBase e) { SeekHeadFactory.SeekMock z = SeekMock.make(e); z.dataOffset = currentDataOffset; z.seekPointerSize = calculatePayloadSize(z.dataOffset); currentDataOffset += z.size; // System.out.println("Added id:"+Reader.printAsHex(z.id)+" offset:"+z.dataOffset+" size:"+z.size+" seekpointer size:"+z.seekPointerSize); a.add(z); } public EbmlMaster indexSeekHead(){ int seekHeadSize = computeSeekHeadSize(); EbmlMaster seekHead = createByType(SeekHead); for(SeekHeadFactory.SeekMock z : a){ EbmlMaster seek = createByType(Seek); EbmlBin seekId = createByType(SeekID); seekId.setBuf(ByteBuffer.wrap(z.id)); seek.add(seekId); EbmlUint seekPosition = createByType(SeekPosition); seekPosition.setUint(z.dataOffset+seekHeadSize); if (seekPosition.data.limit() != z.seekPointerSize) System.err.println("estimated size of seekPosition differs from the one actually used. ElementId: "+EbmlUtil.toHexString(z.id)+" "+seekPosition.getData().limit()+" vs "+z.seekPointerSize); seek.add(seekPosition); seekHead.add(seek); } ByteBuffer mux = seekHead.getData(); if (mux.limit() != seekHeadSize) System.err.println("estimated size of seekHead differs from the one actually used. "+mux.limit()+" vs "+seekHeadSize); return seekHead; } public int computeSeekHeadSize() { int seekHeadSize = estimateSize(); boolean reindex = false; do { reindex = false; for (SeekMock z : a) { int minSize = calculatePayloadSize(z.dataOffset + seekHeadSize); if (minSize > z.seekPointerSize) { System.out.println("Size "+seekHeadSize+" seems too small for element "+EbmlUtil.toHexString(z.id)+" increasing size by one."); z.seekPointerSize +=1; seekHeadSize += 1; reindex = true; break; } else if (minSize < z.seekPointerSize) { throw new RuntimeException("Downsizing the index is not well thought through."); } } } while (reindex); return seekHeadSize; } int estimateSize() { int s = SeekHead.id.length + 1; s += estimeteSeekSize(a.get(0).id.length, 1); for (int i = 1; i < a.size(); i++) { s += estimeteSeekSize(a.get(i).id.length, a.get(i).seekPointerSize); } return s; } public static int estimeteSeekSize(int idLength, int offsetSizeInBytes) { int seekIdSize = SeekID.id.length + EbmlUtil.ebmlLength(idLength) + idLength; int seekPositionSize = SeekPosition.id.length + EbmlUtil.ebmlLength(offsetSizeInBytes) + offsetSizeInBytes; int seekSize = Seek.id.length + EbmlUtil.ebmlLength(seekIdSize + seekPositionSize) + seekIdSize + seekPositionSize; return seekSize; } public static class SeekMock { public long dataOffset; byte[] id; int size; int seekPointerSize; public static SeekHeadFactory.SeekMock make(EbmlBase e) { SeekHeadFactory.SeekMock z = new SeekMock(); z.id = e.id; z.size = (int) e.size(); return z; } } }