/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.container.mkv.type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import com.ttProject.container.mkv.MkvBlockTag;
import com.ttProject.container.mkv.MkvMasterTag;
import com.ttProject.container.mkv.Type;
import com.ttProject.frame.IFrame;
import com.ttProject.frame.h264.SliceFrame;
import com.ttProject.unit.UnitComparator;
import com.ttProject.unit.extra.EbmlValue;
/**
* Cluster
* @author taktod
*/
public class Cluster extends MkvMasterTag {
/** logger */
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(Cluster.class);
/** duration of this cluster */
private long duration;
/** trackIds on process. */
private Set<Integer> trackIdSet = new HashSet<Integer>();
/** list of blocks. */
private List<SimpleBlock> blockList = new ArrayList<SimpleBlock>();
/** for sort. */
private static UnitComparator comparator = new UnitComparator();
/**
* constructor
* @param size
*/
public Cluster(EbmlValue size) {
super(Type.Cluster, size);
}
/**
* constructor
*/
public Cluster() {
this(new EbmlValue());
}
/**
* constructor
* @param position
*/
public Cluster(long position) {
this();
setPosition((int)position);
}
/**
* set the position.
* @param position
*/
public void setPosition(long position) {
super.setPosition((int)position);
}
/**
* setup timeinformation.
* @param pts
* @param timebase
* @param duration
* @throws Exception
*/
public void setupTimeinfo(long pts, long timebase, long duration) throws Exception {
setPts(pts);
setTimebase(timebase);
this.duration = duration;
Timecode timecode = new Timecode();
timecode.setValue(pts);
addChild(timecode);
}
/**
* check trackId(sign as progress trackId)
* @param trackId
*/
public void checkTrackId(int trackId) {
trackIdSet.add(trackId);
}
/**
* add frame.
* @param trackId
* @param frame
* @return IFrame If not added, return frame. if added, return null.
*/
public IFrame addFrame(int trackId, IFrame frame) throws Exception {
// TODO can be complete before all track passed.
// check the pts, in cluster or not.
int pts = (int)(getTimebase() * frame.getPts() / frame.getTimebase() - getPts());
if(pts <= 0) {
return null;
}
if(pts >= 0 && pts < duration) {
// inside.
setupSimpleBlock(trackId, frame, pts);
return null;
}
trackIdSet.remove((Integer)trackId);
return frame;
}
/**
* hold data as simpleBlock.
* @param trackId
* @param frame
* @throws Exception
*/
private void setupSimpleBlock(int trackId, IFrame frame, int clusterPts) throws Exception {
switch(frame.getCodecType()) {
case H264:
// h264 deal with only sliceFrame.(need to deal with sei?)
if(!(frame instanceof SliceFrame)) {
return;
}
break;
default:
break;
}
SimpleBlock simpleBlock = new SimpleBlock();
simpleBlock.addFrame(trackId, frame, clusterPts);
blockList.add(simpleBlock);
}
/**
* check the exist of progress trackIds.
* @return
*/
public boolean isCompleteCluster() {
return trackIdSet.isEmpty();
}
/**
* setup complete the cluster.
*/
public void setupComplete() {
Collections.sort(blockList, comparator);
for(MkvBlockTag blockTag : blockList) {
addChild(blockTag);
}
blockList.clear();
}
}