All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jcodec.containers.flv.FLVTrackDemuxer Maven / Gradle / Ivy

There is a newer version: 0.2.5
Show newest version
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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy