package org.jcodec.player.filters.http; import static org.jcodec.player.filters.http.HttpUtils.getHttpClient; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.ByteBuffer; import java.util.List; import org.jcodec.common.NIOUtils; import org.jcodec.common.model.Packet; import org.jcodec.common.model.RationalLarge; import org.jcodec.common.tools.Debug; import org.jcodec.player.filters.MediaInfo; import org.jcodec.player.filters.PacketSource; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Packet source that deliveres track data over HTTP using JCodec streaming * * @author The JCodec project * */ public class HttpPacketSource implements PacketSource { protected URL url; private int frameNo; private FrameCache cache; private Prefetcher prefetcher; private MediaInfo mi; private long miUpdateTime; private int fetchSpeed; private Downloader downloader; public HttpPacketSource(String trackUrl, File trackCache, MediaInfo mediaInfos) throws IOException { this.downloader = new Downloader(getHttpClient(trackUrl), trackUrl); mi = mediaInfos; if (trackCache.exists()) trackCache.delete(); trackCache.createNewFile(); trackCache.deleteOnExit(); cache = new FrameCache(trackCache); int fps = (int) (((long) mi.getTimescale() * mi.getNFrames()) / mi.getDuration()); fetchSpeed = 3 * fps / 2; prefetcher = new Prefetcher(cache, downloader, 15, fetchSpeed); prefetcher.start(); Debug.println("Opened packet source for: " + trackUrl); } private void updateMediaInfo() throws IOException { long now = System.currentTimeMillis(); if (mi == null || now - miUpdateTime > 1000) { mi = downloader.downloadMediaInfo(); miUpdateTime = now; } } public synchronized Packet getPacket(ByteBuffer buffer) throws IOException { Packet pkt = cache.getFrame(frameNo, buffer); if (pkt == null) { try { List<Packet> frames = downloader.getFrames(frameNo, frameNo + 15, null); if (frames == null) return null; for (Packet packet : frames) { cache.addFrame(packet); } pkt = copyPkt(buffer, frames.get(0)); } catch (IOException e) { e.printStackTrace(); return null; } } pkt.setTimescale(mi.getTimescale()); ++frameNo; return pkt; } private Packet copyPkt(ByteBuffer buffer, Packet pkt) { ByteBuffer out = buffer.duplicate(); NIOUtils.write(out, pkt.getData()); out.flip(); return new Packet(pkt, out); } private void restartDownloader(int frameNo) { prefetcher.cancel(); prefetcher = new Prefetcher(cache, downloader, frameNo, fetchSpeed); prefetcher.start(); } public synchronized void seek(RationalLarge second) throws IOException { long pts = second.multiplyS(mi.getTimescale()); if (cache.pts2frame(pts) == -1) { List<Packet> frames = downloader.seekFrame(pts, null); if (frames == null) throw new RuntimeException("Can not seek to pts " + pts); frameNo = (int) frames.get(0).getFrameNo(); for (Packet packet : frames) { cache.addFrame(packet); } } else { frameNo = cache.pts2frame(pts); } restartDownloader(frameNo + 15); } public boolean drySeek(RationalLarge second) throws IOException { long pts = second.multiplyS(mi.getTimescale()); if (cache.pts2frame(pts) == -1) { List<Packet> frames = downloader.seekFrame(pts, null); if (frames == null) return false; for (Packet packet : frames) { cache.addFrame(packet); } } return true; } public MediaInfo getMediaInfo() throws IOException { updateMediaInfo(); return mi; } @Override public void close() throws IOException { prefetcher.cancel(); cache.close(); downloader.close(); } public int[][] getCached() { return cache.getCached(); } @Override public void gotoFrame(int frame) { if (frameNo == frame) return; frameNo = frame; restartDownloader(frameNo + 15); } }