package org.jcodec.containers.mp4.demuxer;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.jcodec.common.Demuxer;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.containers.mp4.MP4TrackType;
import org.jcodec.containers.mp4.MP4Util;
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;
import org.jcodec.platform.Platform;
/**
* 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 implements Demuxer {
private List<AbstractMP4DemuxerTrack> tracks;
private TimecodeMP4DemuxerTrack timecodeTrack;
MovieBox movie;
private SeekableByteChannel input;
public AbstractMP4DemuxerTrack create(TrakBox trak) {
SampleSizesBox stsz = NodeBox.findFirstPath(trak, SampleSizesBox.class, Box.path("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);
}
private void findMovieBox(SeekableByteChannel input) throws IOException {
movie = MP4Util.parseMovieChannel(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;
TrakBox[] trakBoxs = NodeBox.findAll(moov, TrakBox.class, "trak");
for (int i = 0; i < trakBoxs.length; i++) {
TrakBox trak = trakBoxs[i];
SampleEntry se = NodeBox.findFirstPath(trak, SampleEntry.class, new String[] { "mdia", "minf", "stbl", "stsd", null });
if ("tmcd".equals(se.getFourcc())) {
tt = trak;
} else {
tracks.add(create(trak));
}
}
if (tt != null) {
DemuxerTrack video = getVideoTrack();
if (video != null)
timecodeTrack = new TimecodeMP4DemuxerTrack(movie, tt, input);
}
}
public static MP4TrackType getTrackType(TrakBox trak) {
HandlerBox handler = NodeBox.findFirstPath(trak, HandlerBox.class, Box.path("mdia.hdlr"));
return MP4TrackType.fromHandler(handler.getComponentSubType());
}
public DemuxerTrack 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;
}
@Override
public List<AbstractMP4DemuxerTrack> getTracks() {
return new ArrayList<AbstractMP4DemuxerTrack>(tracks);
}
@Override
public List<DemuxerTrack> getVideoTracks() {
ArrayList<DemuxerTrack> result = new ArrayList<DemuxerTrack>();
for (AbstractMP4DemuxerTrack demuxerTrack : tracks) {
if (demuxerTrack.box.isVideo())
result.add((DemuxerTrack) demuxerTrack);
}
return result;
}
@Override
public List<DemuxerTrack> getAudioTracks() {
ArrayList<DemuxerTrack> result = new ArrayList<DemuxerTrack>();
for (AbstractMP4DemuxerTrack demuxerTrack : tracks) {
if (demuxerTrack.box.isAudio())
result.add((DemuxerTrack) demuxerTrack);
}
return result;
}
public TimecodeMP4DemuxerTrack getTimecodeTrack() {
return timecodeTrack;
}
static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
return (((b3 ) << 24) |
((b2 & 0xff) << 16) |
((b1 & 0xff) << 8) |
((b0 & 0xff) ));
}
private static int intFourcc(String string) {
byte[] b = Platform.getBytes(string);
return makeInt(b[0], b[1], b[2], b[3]);
}
private static int ftyp = intFourcc("ftyp");
private static int free = intFourcc("free");
private static int moov = intFourcc("moov");
private static int mdat = intFourcc("mdat");
private static int wide = intFourcc("wide");
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;
}
@Override
public void close() throws IOException {
input.close();
}
}