org.jcodec.containers.flv.FLVTrackDemuxer 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.containers.flv;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import org.jcodec.common.Codec;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.LongArrayList;
import org.jcodec.common.SeekableDemuxerTrack;
import org.jcodec.common.TrackType;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Packet;
import org.jcodec.containers.flv.FLVTag.Type;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Demuxer frontend for FLV, track based wrapper
*
* @author The JCodec project
*
*/
public class FLVTrackDemuxer {
private static final int MAX_CRAWL_DISTANCE_SEC = 10;
private FLVReader demuxer;
private FLVDemuxerTrack video;
private FLVDemuxerTrack audio;
private LinkedList packets;
private SeekableByteChannel _in;
public static class FLVDemuxerTrack implements SeekableDemuxerTrack {
private Type type;
private int curFrame;
private Codec codec;
private LongArrayList framePositions;
private byte[] codecPrivate;
private FLVTrackDemuxer demuxer;
public FLVDemuxerTrack(FLVTrackDemuxer demuxer, Type type) throws IOException {
this.framePositions = LongArrayList.createLongArrayList();
this.demuxer = demuxer;
this.type = type;
FLVTag frame = demuxer.nextFrameI(type, false);
codec = frame.getTagHeader().getCodec();
}
@Override
public Packet nextFrame() throws IOException {
FLVTag frame = demuxer.nextFrameI(type, true);
framePositions.add(frame.getPosition());
return toPacket(frame);
}
public Packet prevFrame() throws IOException {
FLVTag frame = demuxer.prevFrameI(type, true);
// framePositions.add(nextFrameI.getPosition());
return toPacket(frame);
}
public Packet pickFrame() throws IOException {
FLVTag frame = demuxer.nextFrameI(type, false);
// framePositions.add(nextFrameI.getPosition());
return toPacket(frame);
}
private Packet toPacket(FLVTag frame) {
return null;
}
@Override
public DemuxerTrackMeta getMeta() {
TrackType t = type == Type.VIDEO ? TrackType.VIDEO : TrackType.AUDIO;
return new DemuxerTrackMeta(t, codec, 0, null, 0, ByteBuffer.wrap(codecPrivate), null, null);
}
@Override
public boolean gotoFrame(long i) throws IOException {
if (i >= framePositions.size())
return false;
demuxer.resetToPosition(framePositions.get((int) i));
return true;
}
@Override
public boolean gotoSyncFrame(long i) {
throw new RuntimeException();
}
@Override
public long getCurFrame() {
return curFrame;
}
@Override
public void seek(double second) throws IOException {
demuxer.seekI(second);
}
}
public FLVTrackDemuxer(SeekableByteChannel _in) throws IOException {
this.packets = new LinkedList();
this._in = _in;
_in.setPosition(0);
demuxer = new FLVReader(_in);
video = new FLVDemuxerTrack(this, Type.VIDEO);
audio = new FLVDemuxerTrack(this, Type.AUDIO);
}
private void resetToPosition(long position) throws IOException {
_in.setPosition(position);
demuxer.reset();
packets.clear();
}
private void seekI(double second) throws IOException {
packets.clear();
FLVTag base;
while ((base = demuxer.readNextPacket()) != null && base.getPtsD() == 0)
;
if (base == null) {
//cant seek if base not found
return;
}
_in.setPosition(base.getPosition() + 0x100000);
demuxer.reposition();
FLVTag off = demuxer.readNextPacket();
int byteRate = (int) ((off.getPosition() - base.getPosition()) / (off.getPtsD() - base.getPtsD()));
long offset = base.getPosition() + (long) ((second - base.getPtsD()) * byteRate);
_in.setPosition(offset);
demuxer.reposition();
// TODO: the implementation is incorrect
// 5 reposition attempts
for (int i = 0; i < 5; ++i) {
FLVTag pkt = demuxer.readNextPacket();
double distance = second - pkt.getPtsD();
if (distance > 0 && distance < MAX_CRAWL_DISTANCE_SEC) {
// Read to the right frame
System.out.println("Crawling forward: " + distance);
FLVTag testPkt;
while ((testPkt = demuxer.readNextPacket()) != null && testPkt.getPtsD() < second)
;
if (testPkt != null)
packets.add(pkt);
return;
} else if (distance < 0 && distance > -MAX_CRAWL_DISTANCE_SEC) {
// Read back to the frame
System.out.println("Overshoot by: " + (-distance));
_in.setPosition(pkt.getPosition() + (long) ((distance - 1) * byteRate));
demuxer.reposition();
}
}
}
private FLVTag nextFrameI(Type type, boolean remove) throws IOException {
for (Iterator it = packets.iterator(); it.hasNext();) {
FLVTag pkt = it.next();
if (pkt.getType() == type) {
if (remove)
it.remove();
return pkt;
}
}
FLVTag pkt;
while ((pkt = demuxer.readNextPacket()) != null && pkt.getType() != type)
packets.add(pkt);
if (!remove)
packets.add(pkt);
return pkt;
}
private FLVTag prevFrameI(Type type, boolean remove) throws IOException {
for (ListIterator it = packets.listIterator(); it.hasPrevious();) {
FLVTag pkt = it.previous();
if (pkt.getType() == type) {
if (remove)
it.remove();
return pkt;
}
}
FLVTag pkt;
while ((pkt = demuxer.readPrevPacket()) != null && pkt.getType() != type)
packets.add(0, pkt);
if (!remove)
packets.add(0, pkt);
return pkt;
}
public DemuxerTrack[] getTracks() {
return new DemuxerTrack[] { video, audio };
}
public DemuxerTrack getVideoTrack() {
return video;
}
public DemuxerTrack getAudioTrack() {
return video;
}
}