package org.jcodec.containers.imgseq;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.common.Codec;
import org.jcodec.common.Demuxer;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.TrackType;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Packet.FrameType;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* A demuxer that reads image files out of a folder.
*
* Supports both sequences starting with 0 and 1 index.
*
* @author Stanislav Vitvitskyy
*
*/
public class ImageSequenceDemuxer implements Demuxer, DemuxerTrack {
private static final int VIDEO_FPS = 25;
private String namePattern;
private int frameNo;
private Packet curFrame;
private Codec codec;
private int maxAvailableFrame;
private int maxFrames;
private String prevName;
public ImageSequenceDemuxer(String namePattern, int maxFrames) throws IOException {
this.namePattern = namePattern;
this.maxFrames = maxFrames;
this.maxAvailableFrame = -1;
this.curFrame = loadFrame();
// codec = JCodecUtil.detectDecoder(curFrame.getData());
String lowerCase = namePattern.toLowerCase();
if (lowerCase.endsWith(".png")) {
codec = Codec.PNG;
} else if (lowerCase.endsWith(".jpg") || lowerCase.endsWith(".jpeg")) {
codec = Codec.JPEG;
}
}
@Override
public void close() throws IOException {
}
@Override
public List<? extends DemuxerTrack> getTracks() {
ArrayList<DemuxerTrack> tracks = new ArrayList<DemuxerTrack>();
tracks.add(this);
return tracks;
}
@Override
public List<? extends DemuxerTrack> getVideoTracks() {
return getTracks();
}
@Override
public List<? extends DemuxerTrack> getAudioTracks() {
return new ArrayList<DemuxerTrack>();
}
@Override
public Packet nextFrame() throws IOException {
try {
return curFrame;
} finally {
curFrame = loadFrame();
}
}
private Packet loadFrame() throws IOException {
if (frameNo > maxFrames) {
return null;
}
File file = null;
do {
String name = String.format(namePattern, frameNo);
// In case the name doesn't contain placeholders, to prevent infinitely
// looping around the same file.
if (name.equals(prevName)) {
return null;
}
prevName = name;
file = new File(name);
if (file.exists() || frameNo > 0)
break;
frameNo++;
} while (frameNo < 2);
if (!file.exists())
return null;
Packet ret = new Packet(NIOUtils.fetchFromFile(file), frameNo, VIDEO_FPS, 1, frameNo, FrameType.KEY, null, frameNo);
++frameNo;
return ret;
}
private static final int MAX_MAX = 60 * 60 * 60 * 24; // Longest possible
// movie
/**
* Finds maximum frame of a sequence by bisecting the range.
*
* Performs at max at max 48 Stat calls ( 2*log2(MAX_MAX) ).
*
* @return
*/
public int getMaxAvailableFrame() {
if (maxAvailableFrame == -1) {
int firstPoint = 0;
for (int i = MAX_MAX; i > 0; i /= 2) {
if (new File(String.format(namePattern, i)).exists()) {
firstPoint = i;
break;
}
}
int pos = firstPoint;
for (int interv = firstPoint / 2; interv > 1; interv /= 2) {
if (new File(String.format(namePattern, pos + interv)).exists()) {
pos += interv;
}
}
maxAvailableFrame = pos;
Logger.info("Max frame found: " + maxAvailableFrame);
}
return Math.min(maxAvailableFrame, maxFrames);
}
@Override
public DemuxerTrackMeta getMeta() {
int durationFrames = getMaxAvailableFrame();
return new DemuxerTrackMeta(TrackType.VIDEO, codec, (durationFrames + 1) * VIDEO_FPS, null, durationFrames + 1,
null, null, null);
}
}