/**
*
*/
package mp4.util.atom;
import java.io.DataOutput;
import java.io.IOException;
import mp4.util.MP4Log;
/**
* The container atom for a single track of a presentation. Movie presentation typically
* have two tracks, one for sound and one for video.
*/
public class TrakAtom extends ContainerAtom {
// the track header
private TkhdAtom tkhd;
// the track reference container
private TrefAtom tref;
// the media information
private MdiaAtom mdia;
// the edit list container
private EdtsAtom edts;
// user data
private UdtaAtom udta;
/**
* Constructor
*/
public TrakAtom() {
super(new byte[]{'t','r','a','k'});
}
/**
* Copy constructor. Performs a deep copy.
* @param old the version to copy
*/
public TrakAtom(TrakAtom old) {
super(old);
tkhd = new TkhdAtom(old.tkhd);
if (old.tref != null) {
tref = new TrefAtom(old.tref);
}
mdia = new MdiaAtom(old.mdia);
if (old.edts != null) {
edts = new EdtsAtom(old.edts);
}
if (old.udta != null) {
udta = new UdtaAtom(old.udta);
}
}
/**
* Return the track header atom.
* @return the track header atom
*/
public TkhdAtom getTkhd() {
return tkhd;
}
/**
* Set the track header atom
* @param tkhd the new track header atom
*/
public void setTkhd(TkhdAtom tkhd) {
this.tkhd = tkhd;
}
/**
* Return the track reference atom, or null if there isn't one.
* @return the track reference atom
*/
public TrefAtom getTref() {
return tref;
}
/**
* Set the track reference atom
* @param tref the new track reference atom
*/
public void setTref(TrefAtom tref) {
this.tref = tref;
}
/**
* Return the media container
* @return the media container
*/
public MdiaAtom getMdia() {
return mdia;
}
/**
* Set the media container atom
* @param mdia the new media container
*/
public void setMdia(MdiaAtom mdia) {
this.mdia = mdia;
}
/**
* Return the edit list atom
* @return return edit list atom
*/
public EdtsAtom getEdts() {
return edts;
}
/**
* Set the track edit list.
* @param edts the new track edit list
*/
public void setEdts(EdtsAtom edts) {
this.edts = edts;
}
/**
* Return the user-data atom
* @return the user-data atom
*/
public UdtaAtom getUdta() {
return udta;
}
/**
* Set the user-data atom
* @param udta the new user-data atom
*/
public void setUdta(UdtaAtom udta) {
this.udta = udta;
}
/**
* Add an atom to the container. If the atom is not recognized, then
* a run-time error is thrown.
* @param child the atom to add to the trak atom
*/
@Override
public void addChild(Atom child) {
if (child instanceof TkhdAtom) {
tkhd = (TkhdAtom) child;
}
else if (child instanceof TrefAtom) {
tref = (TrefAtom) child;
}
else if (child instanceof MdiaAtom) {
mdia = (MdiaAtom) child;
}
else if (child instanceof EdtsAtom) {
edts = (EdtsAtom) child;
}
else if (child instanceof UdtaAtom) {
udta = (UdtaAtom) child;
}
else {
//throw new AtomError("Can't add " + child + " to trak");
addUnknownChild(child);
}
}
/**
* Recompute the size of the track atom, which needs to be done if
* the contents change.
*/
@Override
public void recomputeSize() {
long newSize = tkhd.size() + mdia.size();
if (tref != null) {
newSize += tref.size();
}
if (edts != null) {
newSize += edts.size();
}
if (udta != null) {
newSize += udta.size();
}
newSize += unknownChildrenSize();
setSize(ATOM_HEADER_SIZE + newSize);
}
/**
* Cut the track atom at the specified time (seconds). The time needs to be normalized
* to the media's time-scale.
* @param time the normalized time, in seconds, to cut the track
* @param l
* @param movieTimeScale the time-scale for the movie
* @return a new track atom that has been cut
*/
public TrakAtom cut(long pos, long scale) {
TrakAtom cutTrak = new TrakAtom();
long mediaTimeScale = mdia.getMdhd().getTimeScale();
long mediaTime;
if (mediaTimeScale == scale) {
mediaTime = pos;
} else {
mediaTime = (long)(((float)pos/(float)scale) * mediaTimeScale);
}
//= (long)(Math.rint(time * mediaTimeScale));
MP4Log.log("DBG: media time " + mediaTime);
// if (edts != null) {
// mediaTime = edts.editTime(time, mediaTimeScale, movieTimeScale);
// MP4Log.log("DBG: media time after edit " + mediaTime);
// }
cutTrak.setTkhd(tkhd.cut());
cutTrak.setMdia(mdia.cut(mediaTime));
if (edts != null) {
cutTrak.setEdts(edts.cut());
}
if (udta != null) {
cutTrak.setUdta(udta.cut());
}
if (tref != null) {
cutTrak.setTref(tref.cut());
}
cutTrak.recomputeSize();
return cutTrak;
}
@Override
public void accept(AtomVisitor v) throws AtomException {
v.visit(this);
}
/**
* Write the trak atom data to the specified output
* @param out where the data goes
* @throws IOException if there is an error writing the data
*/
@Override
public void writeData(DataOutput out) throws IOException {
writeHeader(out);
tkhd.writeData(out);
mdia.writeData(out);
if (edts != null) {
edts.writeData(out);
}
if (udta != null) {
udta.writeData(out);
}
if (tref != null) {
tref.writeData(out);
}
writeUnknownChildren(out);
}
/**
* Change the duration of the track. This requires changing the duration in the track
* header and the edit list. The duration is in the movie timescale.
* @param duration the new track duration in the movie timescale.
*/
public void fixupDuration(long duration) {
tkhd.setDuration(duration);
if (edts != null) {
edts.getElst().setDuration(duration);
}
}
/**
* Fixup the chunk offsets values located in the stco atom. This needs
* to be done if the size of any atoms has changed since the chunk offset
* values are absolute values from the start of the file.
* @param delta the amount to update each chunk offset.
*/
public void fixupOffsets(long delta) {
getMdia().getMinf().getStbl().getStco().fixupOffsets(delta);
}
/**
* Return the duration converted to the specified time-scale.
* @param timeScale the normalized time-scale value
* @return the duration converted to the specified time-scale.
*/
public long convertDuration(long timeScale) {
return (long)(getMdia().getMdhd().getDuration() *
((double)timeScale / (double)getMdia().getMdhd().getTimeScale()));
}
/**
* Convert the specified duration to the media time scale.
* @param duration the specified duration to be converted
* @param timeScale the time-scale of the specified duration
* @return the duration converted to media time-scale.
*/
public long convertToMediaScale(long duration, long timeScale) {
return (long)(duration *
((double)getMdia().getMdhd().getTimeScale() / timeScale));
}
/**
* Add an edit to the track.
* @param editTime the media time of the edit
*/
public void addEdit(long editTime) {
ElstAtom elst = new ElstAtom(1);
elst.addEntry(1, tkhd.getDuration(), editTime, 1);
setEdts(new EdtsAtom(elst));
}
public boolean isEnabled() {
if (tkhd == null)
return false;
return (tkhd.getFlagValue() & 1) == 1;
}
}