package org.jcodec.player.filters.http; import static org.jcodec.player.filters.http.HttpUtils.privilegedExecute; import static org.jcodec.player.filters.http.MediaInfoParser.parseMediaInfo; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.CookiePolicy; import org.apache.http.util.EntityUtils; import org.jcodec.common.NIOUtils; import org.jcodec.common.JCodecUtil; import org.jcodec.common.StringUtils; import org.jcodec.common.model.Packet; import org.jcodec.common.model.TapeTimecode; import org.jcodec.player.filters.MediaInfo; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Downloads frames and media info from JCodec streaming server * * @author The JCodec project * */ public class Downloader { private HttpClient client; private String url; public Downloader(HttpClient client, String url) { this.client = client; this.url = url; } public synchronized List<Packet> seekFrame(long pts, ByteBuffer bfr) throws IOException { return extractFrame(bfr, new HttpGet(url + "?pts=" + pts)); } public synchronized List<Packet> getFrame(int frameNo, ByteBuffer bfr) throws IOException { return extractFrame(bfr, new HttpGet(url + "/" + frameNo)); } public synchronized List<Packet> getFrames(int frameS, int frameE, ByteBuffer bfr) throws IOException { return extractFrame(bfr, new HttpGet(url + "/" + frameS + ":" + frameE)); } private List<Packet> extractFrame(ByteBuffer bfr, HttpGet get) throws IOException, ClientProtocolException { get.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES); HttpResponse response = privilegedExecute(client, get); HttpEntity entity = response.getEntity(); if (response.getStatusLine().getStatusCode() != 200) { if (entity != null) EntityUtils.toByteArray(entity); return null; } ByteBuffer buffer; if (bfr == null) buffer = ByteBuffer.wrap(EntityUtils.toByteArray(entity)); else { buffer = toBuffer(bfr, entity); } Header contentType = response.getLastHeader("Content-Type"); if (contentType != null && contentType.getValue().startsWith("multipart/mixed")) { List<Packet> result = parseMultipart(buffer, contentType.getValue()); return result.size() == 0 ? null : result; } else { return Arrays.asList(new Packet[] { pkt(toMap(response.getAllHeaders()), buffer) }); } } private static List<Packet> parseMultipart(ByteBuffer buffer, String contentType) { byte[] sep1 = JCodecUtil.asciiString("\r\n--" + contentType.split(";")[1].split("=")[1]); List<Packet> result = new ArrayList<Packet>(); ByteBuffer to; do { NIOUtils.search(buffer, 0, (byte) 13, (byte) 10); buffer.getShort(); NIOUtils.search(buffer, 0, sep1); to = NIOUtils.search(buffer, 0, sep1); if (to != null) { result.add(part(to)); buffer.getShort(); } } while (to != null); return result; } public synchronized MediaInfo downloadMediaInfo() throws IOException { HttpGet get = new HttpGet(url); get.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES); HttpResponse response = privilegedExecute(client, get); if (response.getStatusLine().getStatusCode() != 200) { if (response.getEntity() != null) EntityUtils.toByteArray(response.getEntity()); return null; } return parseMediaInfo(EntityUtils.toString(response.getEntity())); } private static TapeTimecode parseTimecode(String timecodeRaw) { if (StringUtils.isEmpty(timecodeRaw)) return null; String[] split = StringUtils.split(timecodeRaw, ":"); if (split.length == 4) { return new TapeTimecode(Short.parseShort(split[0]), Byte.parseByte(split[1]), Byte.parseByte(split[2]), Byte.parseByte(split[3]), false); } else if (split.length == 3) { String[] split1 = StringUtils.split(split[2], ";"); if (split1.length == 2) return new TapeTimecode(Short.parseShort(split[0]), Byte.parseByte(split[1]), Byte.parseByte(split1[0]), Byte.parseByte(split1[1]), true); } return null; } private static long longOr0(String val) { return val == null ? 0 : Long.parseLong(val); } private static int intOr0(String val) { return val == null ? 0 : Integer.parseInt(val); } private static boolean boolOrFalse(String val) { return val == null ? false : Boolean.parseBoolean(val); } private static Map<String, String> parseHeaders(ByteBuffer read) { HashMap<String, String> result = new HashMap<String, String>(); for (String line : getLines(read)) { String[] split = line.split(": "); result.put(split[0], split[1]); } return result; } private static String[] getLines(ByteBuffer read) { return StringUtils.split(new String(NIOUtils.toArray(read)), "\r\n"); } private ByteBuffer toBuffer(ByteBuffer bfr, HttpEntity entity) throws IOException { InputStream in = null; try { in = entity.getContent(); ByteBuffer fork = bfr.duplicate(); NIOUtils.read(Channels.newChannel(in), fork); fork.flip(); return fork; } finally { in.close(); } } private Map<String, String> toMap(Header[] allHeaders) { HashMap<String, String> map = new HashMap<String, String>(); for (Header header : allHeaders) { map.put(header.getName(), header.getValue()); } return map; } public static final ByteBuffer search(ByteBuffer buf) { if (!buf.hasRemaining()) return null; int from = buf.position(); ByteBuffer result = buf.slice(); int val = 0xffffffff; while (buf.hasRemaining()) { val <<= 8; val |= buf.get(); if (val == 0x0d0a0d0a) { result.limit(buf.position() - from - 4); break; } } return result; } private static Packet part(ByteBuffer read) { Map<String, String> headers = parseHeaders(search(read)); return pkt(headers, read); } private static Packet pkt(Map<String, String> headers, ByteBuffer data) { long pts = longOr0(headers.get("JCodec-PTS")); long duration = longOr0(headers.get("JCodec-Duration")); int frameNo = intOr0(headers.get("JCodec-FrameNo")); boolean key = boolOrFalse(headers.get("JCodec-Key")); TapeTimecode timecode = parseTimecode(headers.get("JCodec-TapeTimecode")); return new Packet(data, pts, 0, duration, frameNo, key, timecode); } // private static List<String> getLines(Buffer buffer) { // ArrayList<String> lines = new ArrayList<String>(); // int next = buffer.search(13, 10); // while (next != -1) { // lines.add(new String(buffer.read(next).toArray())); // buffer.read(2); // next = buffer.search(13, 10); // } // // if (buffer.remaining() > 0) // lines.add(new String(buffer.toArray())); // // return lines; // } public void close() { // client.getConnectionManager().shutdown(); } }