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;
}
}
}