package org.jcodec.movtool.streaming.tracks; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.jcodec.containers.mp4.boxes.SampleEntry; import org.jcodec.movtool.streaming.VirtualPacket; import org.jcodec.movtool.streaming.VirtualTrack; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * A proxy track that maintains in-memory cache of recently read packets * * @author The JCodec project * */ public class CachingTrack implements VirtualTrack { private VirtualTrack src; private List<CachingPacket> cachedPackets = Collections.synchronizedList(new ArrayList<CachingPacket>()); private ScheduledFuture<?> policyFuture; public CachingTrack(VirtualTrack src, final int policy, ScheduledExecutorService policyExecutor) { if (policy < 1) throw new IllegalArgumentException("Caching track with less then 1 entry."); this.src = src; policyFuture = policyExecutor.scheduleAtFixedRate(new Runnable() { public void run() { while (cachedPackets.size() > policy) { cachedPackets.get(0).wipe(); } } }, 200, 200, TimeUnit.MILLISECONDS); } @Override public SampleEntry getSampleEntry() { return src.getSampleEntry(); } @Override public VirtualPacket nextPacket() throws IOException { VirtualPacket pkt = src.nextPacket(); if (pkt == null) return null; return new CachingPacket(pkt); } public class CachingPacket extends VirtualPacketWrapper { private ByteBuffer cache; public CachingPacket(VirtualPacket src) { super(src); } public synchronized void wipe() { if (cachedPackets.indexOf(this) == 0) { cachedPackets.remove(0); cache = null; } } public synchronized ByteBuffer getData() throws IOException { // This packet will receive new place in the queue cachedPackets.remove(this); if (cache == null) { cache = src.getData(); } cachedPackets.add(this); return cache == null ? null : cache.duplicate(); } } @Override public void close() throws IOException { if (policyFuture != null) policyFuture.cancel(false); cachedPackets.clear(); src.close(); } @Override public VirtualEdit[] getEdits() { return src.getEdits(); } @Override public int getPreferredTimescale() { return src.getPreferredTimescale(); } }