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

org.jcodec.movtool.streaming.ConcurrentMovieRangeService Maven / Gradle / Ivy

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

import org.jcodec.common.io.NIOUtils;
import org.jcodec.platform.BaseInputStream;

import java.io.IOException;
import java.io.InputStream;
import java.lang.InterruptedException;
import java.lang.Runnable;
import java.lang.Thread;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 * 
 * Retrieves chunks for the range concurrently allowing for concurrent
 * transcode-on-the-fly
 * 
 * @author The JCodec project
 * 
 */
public class ConcurrentMovieRangeService {
    private ExecutorService exec;
    private VirtualMovie movie;

    public ConcurrentMovieRangeService(VirtualMovie movie, int nThreads) {
        this.exec = Executors.newFixedThreadPool(nThreads, new ThreadFactory() {
            public Thread newThread(Runnable runnable) {
                Thread thread = Executors.defaultThreadFactory().newThread(runnable);
                thread.setDaemon(true);
                return thread;
            }
        });
        this.movie = movie;
    }

    public void shutdown() {
        exec.shutdown();
    }

    public InputStream getRange(long from, long to) throws IOException {
        return new ConcurrentMovieRange(this, from, to);
    }

    static class GetCallable implements Callable {
        private MovieSegment segment;

        public GetCallable(MovieSegment segment) {
            this.segment = segment;
        }

        public ByteBuffer call() throws Exception {
            return MovieRange.checkDataLen(segment.getData() == null ? null : segment.getData().duplicate(), segment.getDataLen());
        }
    }

    public static class ConcurrentMovieRange extends BaseInputStream {
        private static final int READ_AHEAD_SEGMENTS = 10;
        private List> segments;
        private int nextReadAheadNo;
        private long remaining;
        private long to;
		private ConcurrentMovieRangeService svc;

        public ConcurrentMovieRange(ConcurrentMovieRangeService svc, long from, long to) throws IOException {
            this.segments = new ArrayList>();

            this.svc = svc;
			if (to < from)
                throw new IllegalArgumentException("from < to");

            this.remaining = to - from + 1;
            this.to = to;

            MovieSegment segment = svc.movie.getPacketAt(from);
            if (segment != null) {

                nextReadAheadNo = segment.getNo();
                scheduleSegmentRetrieve(segment);

                for (int i = 0; i < READ_AHEAD_SEGMENTS; i++)
                    tryReadAhead();

                ByteBuffer data = segmentData();
                NIOUtils.skip(data, (int) (from - segment.getPos()));
            }
        }

        @Override
        protected int readBuffer(byte[] b, int from, int len) throws IOException {
            if (segments.size() == 0 || remaining == 0)
                return -1;

            len = (int) Math.min(len, remaining);
            int totalRead = 0;
            while (len > 0 && segments.size() > 0) {
                ByteBuffer segmentData = segmentData();
                int toRead = Math.min(segmentData.remaining(), len);

                segmentData.get(b, from, toRead);
                totalRead += toRead;
                len -= toRead;
                from += toRead;

                disposeReadAhead(segmentData);
            }
            remaining -= totalRead;

            return totalRead;
        }

        private void disposeReadAhead(ByteBuffer segmentData) {
            if (!segmentData.hasRemaining()) {
                segments.remove(0);
                tryReadAhead();
            }
        }

        private void tryReadAhead() {
            MovieSegment segment = svc.movie.getPacketByNo(nextReadAheadNo);
            if (segment != null && segment.getPos() < to) {
                scheduleSegmentRetrieve(segment);
            }
        }

        private void scheduleSegmentRetrieve(MovieSegment segment) {
            Future submit = svc.exec.submit(new GetCallable(segment));
            segments.add(submit);
            nextReadAheadNo++;
        }

        private ByteBuffer segmentData() throws IOException {
            ByteBuffer segmentData;
            try {
                segmentData = segments.get(0).get();
            } catch (Exception e) {
                throw new IOException(e);
            }
            return segmentData;
        }

        @Override
        public void close() throws IOException {
            for (Future future : segments) {
                future.cancel(false);
            }
        }

        @Override
        protected int readByte() throws IOException {
            if (segments.size() == 0 || remaining == 0)
                return -1;

            ByteBuffer segmentData = segmentData();
            int ret = segmentData.get() & 0xff;

            disposeReadAhead(segmentData);

            --remaining;

            return ret;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy