package org.jcodec.containers.mp4.demuxer;
import static org.jcodec.containers.mp4.boxes.Box.findFirst;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.jcodec.common.NIOUtils;
import org.jcodec.common.SeekableByteChannel;
import org.jcodec.containers.mp4.MP4Util;
import org.jcodec.containers.mp4.TrackType;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.HandlerBox;
import org.jcodec.containers.mp4.boxes.MovieBox;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleEntry;
import org.jcodec.containers.mp4.boxes.SampleSizesBox;
import org.jcodec.containers.mp4.boxes.TrakBox;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Demuxer frontend for MP4
*
* @author The JCodec project
*
*/
public class MP4Demuxer {
private List<AbstractMP4DemuxerTrack> tracks;
private TimecodeMP4DemuxerTrack timecodeTrack;
MovieBox movie;
private SeekableByteChannel input;
public AbstractMP4DemuxerTrack create(TrakBox trak) {
SampleSizesBox stsz = findFirst(trak, SampleSizesBox.class, "mdia", "minf", "stbl", "stsz");
if (stsz.getDefaultSize() == 0)
return new FramesMP4DemuxerTrack(movie, trak, input);
else
return new PCMMP4DemuxerTrack(movie, trak, input);
}
public MP4Demuxer(SeekableByteChannel input) throws IOException {
this.input = input;
tracks = new LinkedList<AbstractMP4DemuxerTrack>();
findMovieBox(input);
}
public AbstractMP4DemuxerTrack[] getTracks() {
return tracks.toArray(new AbstractMP4DemuxerTrack[] {});
}
private void findMovieBox(SeekableByteChannel input) throws IOException {
movie = MP4Util.parseMovie(input);
if (movie == null)
throw new IOException("Could not find movie meta information box");
processHeader(movie);
}
private void processHeader(NodeBox moov) throws IOException {
TrakBox tt = null;
for (TrakBox trak : Box.findAll(moov, TrakBox.class, "trak")) {
SampleEntry se = Box.findFirst(trak, SampleEntry.class, "mdia", "minf", "stbl", "stsd", null);
if ("tmcd".equals(se.getFourcc())) {
tt = trak;
} else {
tracks.add(create(trak));
}
}
if (tt != null) {
AbstractMP4DemuxerTrack video = getVideoTrack();
if (video != null)
timecodeTrack = new TimecodeMP4DemuxerTrack(movie, tt, input);
}
}
public static TrackType getTrackType(TrakBox trak) {
HandlerBox handler = findFirst(trak, HandlerBox.class, "mdia", "hdlr");
return TrackType.fromHandler(handler.getComponentSubType());
}
public AbstractMP4DemuxerTrack getVideoTrack() {
for (AbstractMP4DemuxerTrack demuxerTrack : tracks) {
if (demuxerTrack.box.isVideo())
return demuxerTrack;
}
return null;
}
public MovieBox getMovie() {
return movie;
}
public AbstractMP4DemuxerTrack getTrack(int no) {
for (AbstractMP4DemuxerTrack track : tracks) {
if (track.getNo() == no)
return track;
}
return null;
}
public List<AbstractMP4DemuxerTrack> getAudioTracks() {
ArrayList<AbstractMP4DemuxerTrack> result = new ArrayList<AbstractMP4DemuxerTrack>();
for (AbstractMP4DemuxerTrack demuxerTrack : tracks) {
if (demuxerTrack.box.isAudio())
result.add(demuxerTrack);
}
return result;
}
public TimecodeMP4DemuxerTrack getTimecodeTrack() {
return timecodeTrack;
}
private static int ftyp = ('f' << 24) | ('t' << 16) | ('y' << 8) | 'p';
private static int free = ('f' << 24) | ('r' << 16) | ('e' << 8) | 'e';
private static int moov = ('m' << 24) | ('o' << 16) | ('o' << 8) | 'v';
private static int mdat = ('m' << 24) | ('d' << 16) | ('a' << 8) | 't';
private static int wide = ('w' << 24) | ('i' << 16) | ('d' << 8) | 'e';
public static int probe(final ByteBuffer b) {
ByteBuffer fork = b.duplicate();
int success = 0;
int total = 0;
while (fork.remaining() >= 8) {
long len = fork.getInt() & 0xffffffffL;
int fcc = fork.getInt();
int hdrLen = 8;
if (len == 1) {
len = fork.getLong();
hdrLen = 16;
} else if (len < 8)
break;
if (fcc == ftyp && len < 64 || fcc == moov && len < 100 * 1024 * 1024 || fcc == free || fcc == mdat || fcc == wide)
success++;
total++;
if (len >= Integer.MAX_VALUE)
break;
NIOUtils.skip(fork, (int) (len - hdrLen));
}
return total == 0 ? 0 : success * 100 / total;
}
}