org.jcodec.codecs.mpeg12.SegmentReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcodec Show documentation
Show all versions of jcodec Show documentation
Pure Java implementation of video/audio codecs and formats
package org.jcodec.codecs.mpeg12;
import org.jcodec.common.io.NIOUtils;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Pulls frames from MPEG elementary stream
*
* @author The JCodec project
*
*/
public class SegmentReader {
private ReadableByteChannel channel;
private ByteBuffer buf;
protected int curMarker;
private int fetchSize;
protected boolean done;
private long pos;
private int bytesInMarker;
private int bufferIncrement = 1 << 15; // 32k
public SegmentReader(ReadableByteChannel channel, int fetchSize) throws IOException {
this.channel = channel;
this.fetchSize = fetchSize;
buf = NIOUtils.fetchFromChannel(channel, 4);
pos = buf.remaining();
curMarker = buf.getInt();
bytesInMarker = 4;
}
// FOR TESTCASES
public int getBufferIncrement() {
return bufferIncrement;
}
// FOR TESTCASES
public void setBufferIncrement(int bufferIncrement) {
this.bufferIncrement = bufferIncrement;
}
public static enum State {
MORE_DATA, DONE, STOP
}
/**
* Reads one full segment till the next marker. Will read as much data as
* the provided buffer fits, if the provided buffer doesn't fit all data
* will return MORE_DATA.
*
* @param out
* @return
* @throws IOException
*/
public final State readToNextMarkerPartial(ByteBuffer out) throws IOException {
if (done)
return State.STOP;
int skipOneMarker = curMarker >= 0x100 && curMarker <= 0x1ff ? 1 : 0;
int written = out.position();
do {
while (buf.hasRemaining()) {
if (curMarker >= 0x100 && curMarker <= 0x1ff) {
if (skipOneMarker == 0) {
return State.DONE;
}
--skipOneMarker;
}
if (!out.hasRemaining())
return State.MORE_DATA;
out.put((byte) (curMarker >>> 24));
curMarker = (curMarker << 8) | (buf.get() & 0xff);
}
buf = NIOUtils.fetchFromChannel(channel, fetchSize);
pos += buf.remaining();
} while (buf.hasRemaining());
written = out.position() - written;
if (written > 0 && curMarker >= 0x100 && curMarker <= 0x1ff)
return State.DONE;
for (; bytesInMarker > 0 && out.hasRemaining();) {
out.put((byte) (curMarker >>> 24));
curMarker = (curMarker << 8);
--bytesInMarker;
if (curMarker >= 0x100 && curMarker <= 0x1ff)
return State.DONE;
}
if (bytesInMarker == 0) {
done = true;
return State.STOP;
} else {
return State.MORE_DATA;
}
}
/**
* Reads one full segment till the next marker. Will allocate the necessary
* buffer to hold the full segment. Internally uses a growing collection of
* smaller buffers since the segment size is intitially unkwnown.
*
* @return
* @throws IOException
*/
public ByteBuffer readToNextMarkerNewBuffer() throws IOException {
if (done)
return null;
List buffers = new ArrayList();
readToNextMarkerBuffers(buffers);
return NIOUtils.combineBuffers(buffers);
}
public void readToNextMarkerBuffers(List buffers) throws IOException {
State state;
do {
ByteBuffer curBuffer = ByteBuffer.allocate(bufferIncrement);
state = readToNextMarkerPartial(curBuffer);
curBuffer.flip();
buffers.add(curBuffer);
} while (state == State.MORE_DATA);
}
public final boolean readToNextMarker(ByteBuffer out) throws IOException {
State state = readToNextMarkerPartial(out);
if(state == State.MORE_DATA)
throw new BufferOverflowException();
return state == State.DONE;
}
public final boolean skipToMarker() throws IOException {
if (done)
return false;
do {
while (buf.hasRemaining()) {
curMarker = (curMarker << 8) | (buf.get() & 0xff);
if (curMarker >= 0x100 && curMarker <= 0x1ff) {
return true;
}
}
buf = NIOUtils.fetchFromChannel(channel, fetchSize);
pos += buf.remaining();
} while (buf.hasRemaining());
done = true;
return false;
}
public final boolean read(ByteBuffer out, int length) throws IOException {
if (done)
return false;
do {
while (buf.hasRemaining()) {
if (length-- == 0)
return true;
out.put((byte) (curMarker >>> 24));
curMarker = (curMarker << 8) | (buf.get() & 0xff);
}
buf = NIOUtils.fetchFromChannel(channel, fetchSize);
pos += buf.remaining();
} while (buf.hasRemaining());
out.putInt(curMarker);
done = true;
return false;
}
public final long curPos() {
return pos - buf.remaining() - 4;
}
}