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

org.jcodec.movtool.streaming.tracks.MPSTrackFactory Maven / Gradle / Ivy

The newest version!
package org.jcodec.movtool.streaming.tracks;
import java.lang.IllegalStateException;
import java.lang.System;


import static java.lang.System.arraycopy;
import static org.jcodec.containers.mps.MPSUtils.readPESHeader;

import org.jcodec.common.CodecMeta;
import org.jcodec.common.RunLength;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.io.FileChannelWrapper;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Rational;
import org.jcodec.common.model.Size;
import org.jcodec.containers.mps.MPSUtils;
import org.jcodec.movtool.streaming.VirtualPacket;
import org.jcodec.movtool.streaming.VirtualTrack;
import org.jcodec.platform.Platform;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 * 
 * A factory for MPEG PS virtual tracks coming out of streams of MPEG PS
 * 
 * @author The JCodec project
 * 
 */
public class MPSTrackFactory {

    private Map tracks;
    private FilePool fp;
    private long[] pesTokens;
    private int[] streams;

    public MPSTrackFactory(ByteBuffer index, FilePool fp) throws IOException {
        this.fp = fp;
        tracks = new HashMap();

        readIndex(index);
    }

    protected void readIndex(ByteBuffer index) throws IOException {
        int nTokens = index.getInt();
        pesTokens = new long[nTokens];
        for (int i = 0; i < pesTokens.length; i++)
            pesTokens[i] = index.getLong();
        streams = RunLength.Integer.parse(index).flattern();

        while (index.hasRemaining()) {
            int stream = index.get() & 0xff;
            getStream(tracks, stream).parseIndex(index);
        }
    }

    private Stream getStream(Map streams, int streamId) {
        Stream stream = streams.get(streamId);
        if (stream == null) {
            stream = createStream(streamId);
            streams.put(streamId, stream);
        }
        return stream;
    }

    protected Stream createStream(int streamId) {
        return new Stream(streamId, this);
    }

    public static class Stream implements VirtualTrack {

        private int siLen;
        private int[] fsizes;
        private long[] fpts;
        private int[] sync;
        private long duration;
        private int streamId;
        private long fileOff;
        private int pesIdx;
        private int curFrame;
        private int offInPayload;
        private ByteBuffer si;
		private MPSTrackFactory factory;

        public Stream(int streamId, MPSTrackFactory factory) {
            this.streamId = streamId;
			this.factory = factory;
        }

        public void parseIndex(ByteBuffer index) throws IOException {
            siLen = index.getInt();

            int fCnt = index.getInt();
            fsizes = new int[fCnt];
            fpts = new long[fCnt];
            for (int i = 0; i < fCnt; i++) {
                int size = index.getInt();
                fsizes[i] = size;
            }

            int syncCount = index.getInt();
            sync = new int[syncCount];
            for (int i = 0; i < syncCount; i++)
                sync[i] = index.getInt();

            for (int i = 0; i < fCnt; i++) {
                fpts[i] = index.getInt() & 0xffffffffL;
            }

            long[] seg0 = Platform.copyOfLong(fpts, 10);
            Arrays.sort(seg0);

            long[] seg1 = new long[10];
            arraycopy(fpts, fpts.length - 10, seg1, 0, 10);
            Arrays.sort(seg1);

            duration = (seg1[9] - seg0[0] + (fpts.length >> 1)) / fpts.length;

            offInPayload = siLen;
            for (fileOff = 0; factory.streams[pesIdx] != streamId; fileOff += pesLen(factory.pesTokens[pesIdx])
                    + leadingSize(factory.pesTokens[pesIdx]), pesIdx++)
                ;
            fileOff += leadingSize(factory.pesTokens[pesIdx]);

            SeekableByteChannel ch = null;
            try {
                ch = factory.fp.getChannel();
                ByteBuffer firstPes = readPes(ch, fileOff, pesLen(factory.pesTokens[pesIdx]), payloadLen(factory.pesTokens[pesIdx]),
                        pesIdx);
                si = NIOUtils.read(firstPes, siLen);
            } finally {
                NIOUtils.closeQuietly(ch);
            }
        }

        protected ByteBuffer readPes(SeekableByteChannel ch, long pesPosition, int pesSize, int payloadSize, int pesIdx)
                throws IOException {
            ch.setPosition(pesPosition);
            ByteBuffer pes = NIOUtils.fetchFromChannel(ch, pesSize);
            readPESHeader(pes, 0);
            return pes;
        }

        private int pesLen(long token) {
            return (int) ((token >>> 24) & 0xffffff);
        }

        private int payloadLen(long token) {
            return (int) (token & 0xffffff);
        }

        private int leadingSize(long token) {
            return (int) ((token >>> 48) & 0xffff);
        }

        @Override
        public VirtualPacket nextPacket() throws IOException {
            if (curFrame >= fsizes.length)
                return null;
            VirtualPacket pkt = new MPSPacket(this, offInPayload, fileOff, curFrame, pesIdx);

            offInPayload += fsizes[curFrame];

            while (pesIdx < factory.streams.length && offInPayload >= payloadLen(factory.pesTokens[pesIdx])) {
                int ps = payloadLen(factory.pesTokens[pesIdx]);
                offInPayload -= ps;
                fileOff += pesLen(factory.pesTokens[pesIdx]);
                ++pesIdx;
                if (pesIdx < factory.streams.length) {
                    long posShift = 0;
                    for (; factory.streams[pesIdx] != streamId; pesIdx++)
                        posShift += pesLen(factory.pesTokens[pesIdx]) + leadingSize(factory.pesTokens[pesIdx]);
                    fileOff += posShift + leadingSize(factory.pesTokens[pesIdx]);
                }
            }
            curFrame++;

            return pkt;
        }

        protected static class MPSPacket implements VirtualPacket {

            private long fileOff;
            private int curFrame;
            private int pesOff;
            private int pesIdx;
            private Stream s;

            public MPSPacket(Stream stream, int pesOff, long fileOff, int curFrame, int pesIdx) {
                this.s = stream;
                this.pesOff = pesOff;
                this.fileOff = fileOff;
                this.curFrame = curFrame;
                this.pesIdx = pesIdx;
            }

            @Override
            public ByteBuffer getData() throws IOException {
                ByteBuffer result = ByteBuffer.allocate(s.siLen + s.fsizes[curFrame]);
                result.put(s.si.duplicate());
                SeekableByteChannel ch = null;
                try {
                    ch = s.factory.fp.getChannel();

                    long curOff = fileOff;
                    ByteBuffer pesBuf = s.readPes(ch, curOff, s.pesLen(s.factory.pesTokens[pesIdx]), s.payloadLen(s.factory.pesTokens[pesIdx]),
                            pesIdx);
                    curOff += s.pesLen(s.factory.pesTokens[pesIdx]);

                    NIOUtils.skip(pesBuf, pesOff);
                    result.put(NIOUtils.read(pesBuf, Math.min(pesBuf.remaining(), result.remaining())));

                    for (int idx = pesIdx; result.hasRemaining();) {
                        long posShift = 0;
                        idx++;
                        for (; s.factory.streams[idx] != s.streamId && idx < s.factory.pesTokens.length; idx++)
                            posShift += s.pesLen(s.factory.pesTokens[idx]) + s.leadingSize(s.factory.pesTokens[idx]);

                        pesBuf = s.readPes(ch, curOff + posShift + s.leadingSize(s.factory.pesTokens[idx]), s.pesLen(s.factory.pesTokens[idx]),
                                s.payloadLen(s.factory.pesTokens[idx]), idx);
                        curOff += posShift + s.leadingSize(s.factory.pesTokens[idx]) + s.pesLen(s.factory.pesTokens[idx]);

                        result.put(NIOUtils.read(pesBuf, Math.min(pesBuf.remaining(), result.remaining())));
                    }
                    result.flip();

                    return result;
                } finally {
                    NIOUtils.closeQuietly(ch);
                }
            }

            @Override
            public int getDataLen() throws IOException {
                return s.siLen + s.fsizes[curFrame];
            }

            @Override
            public double getPts() {
                return (double) (s.fpts[curFrame] - s.fpts[0]) / 90000;
            }

            @Override
            public double getDuration() {
                return (double) s.duration / 90000;
            }

            @Override
            public boolean isKeyframe() {
                return s.sync.length == 0 || Arrays.binarySearch(s.sync, curFrame) >= 0;
            }

            @Override
            public int getFrameNo() {
                return curFrame;
            }
        }

        @Override
        public CodecMeta getCodecMeta() {
            return VideoCodecMeta.createVideoCodecMeta("m2v1", ByteBuffer.allocate(0), new Size(1920, 1080), new Rational(1, 1));
        }

        @Override
        public VirtualEdit[] getEdits() {
            return null;
        }

        @Override
        public int getPreferredTimescale() {
            return 90000;
        }

        @Override
        public void close() throws IOException {
        	factory.fp.close();
        }
    }

    public List getVideoStreams() {
        List ret = new ArrayList();
        Set> entrySet = tracks.entrySet();
        for (Entry entry : entrySet) {
            if (MPSUtils.videoStream(entry.getKey()))
                ret.add(entry.getValue());
        }
        return ret;
    }

    public List getAudioStreams() {
        List ret = new ArrayList();
        Set> entrySet = tracks.entrySet();
        for (Entry entry : entrySet) {
            if (MPSUtils.audioStream(entry.getKey()))
                ret.add(entry.getValue());
        }
        return ret;
    }

    public List getStreams() {
        return new ArrayList(tracks.values());
    }

    public static void main1(String[] args) throws IOException {
        FilePool fp = new FilePool(new File(args[0]), 10);
        MPSTrackFactory factory = new MPSTrackFactory(NIOUtils.fetchFromFile(new File(args[1])), fp);
        Stream stream = factory.getVideoStreams().get(0);
        FileChannelWrapper ch = NIOUtils.writableChannel(new File(args[2]));

        List pkt = new ArrayList();
        for (int i = 0; i < 2000; i++) {
            pkt.add(stream.nextPacket());
        }

        for (VirtualPacket virtualPacket : pkt) {
            ch.write(virtualPacket.getData());
        }

        ch.close();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy