/*************************************** * ViPER-MPEG * * The Video Processing * * Evaluation Resource * * MPEG-1 Decoder * * Distributed under the LGPL license * * Terms available at gnu.org. * * * * Copyright University of Maryland, * * College Park. * ***************************************/ package edu.umd.cfar.lamp.mpeg1.system; import java.io.*; import java.util.*; import java.util.logging.*; import edu.umd.cfar.lamp.mpeg1.*; public class SystemIndex { public static final int MAGIC_NUMBER = 0x11172101; // for ISO/IEC 11172-1 version 1 /** Contains a list of indices, one for each stream. Maps <code>stream_id</code>s to <code>Vector</code>s of <code>SystemIndexElement</code>s. */ private Hashtable streamList = null; /** Logger for errors in the format and other problems while decoding. */ private static Logger logger = Logger.getLogger("edu.umd.cfar.lamp.mpeg1.system"); public SystemIndex() { streamList = new Hashtable(); } public void writeIndex(DataOutput out) throws IOException { out.writeInt(MAGIC_NUMBER); Vector videoStreamList = getVideoStreamList(); int numStreams = videoStreamList.size(); out.writeInt(numStreams); for (int i = 0; i < numStreams; i++) { Integer streamID = (Integer)videoStreamList.get(i); out.writeByte(streamID.intValue()); Vector packetList = (Vector)streamList.get(streamID); int numPackets = packetList.size(); out.writeInt(numPackets); for (int j = 0; j < numPackets; j++) { SystemIndexElement sie = (SystemIndexElement)packetList.get(j); sie.writeIndex(out); } } } public void readIndex(DataInput in) throws IOException, MpegException { int magicNumber = in.readInt(); byte version = (byte)(magicNumber & 0xFF); // version is last byte of magic number if ((magicNumber >>> 8) != (MAGIC_NUMBER >>> 8)) // compare first 3 bytes of magic number throw new IndexException("Invalid magic number: " + Integer.toHexString(magicNumber)); switch (version) { case 0x01: streamList = new Hashtable(); int numStreams = in.readInt(); for (int i = 0; i < numStreams; i++) { int stream_id = in.readUnsignedByte(); int numPackets = in.readInt(); int j = 0; try { for (j = 0; j < numPackets; j++) { long systemStreamDataStartPosition = in.readLong(); int packetDataLength = in.readInt(); addPacket(stream_id, systemStreamDataStartPosition, packetDataLength); } } catch (EOFException e) { logger.warning(j + " read " + e.getLocalizedMessage()); } } } } public String toString() { String result = new String(); Enumeration enumeration = streamList.keys(); while (enumeration.hasMoreElements()) { Iterator iter = ((Vector)streamList.get(enumeration.nextElement())).iterator(); while (iter.hasNext()) { result += iter.next().toString() + " "; } result += "\n"; } return result; } /** @return a <code>Vector</code> of <code>Integer</code>s, corresponding to the list of <code>stream_id</code>s. */ public Vector getStreamList() { return new Vector(streamList.keySet()); } public Vector getVideoStreamList() { Vector allStreamsList = getStreamList(); Vector result = new Vector(); for (int i = 0; i < allStreamsList.size(); i++) { Integer element = (Integer)allStreamsList.elementAt(i); if (StreamIDs.isVideoStream(element.intValue())) { result.add(element); } } return result; } /** * Adds a <code>SystemIndexElement</code> to the index. The * <code>SystemIndexElement</code> is calculated based on the * <code>stream_id</code>, the * <code>systemStreamBytePosition</code>, and the index so far. * * @param stream_id the stream * @param systemStreamBytePosition the byte position into the system stream * @param packetDataLength the length of the packet */ public void addPacket(int stream_id, long systemStreamBytePosition, int packetDataLength) { Integer streamID = new Integer(stream_id); if (!streamList.containsKey(streamID)) { streamList.put(streamID, new Vector()); } long elementaryStreamBytePosition; try { SystemIndexElement lastSystemIndexElement = ((SystemIndexElement)((Vector)streamList.get(streamID)).lastElement()); elementaryStreamBytePosition = lastSystemIndexElement.getElementaryStreamDataStartPosition() + lastSystemIndexElement.getPacketDataLength(); } catch (NoSuchElementException nsee) { elementaryStreamBytePosition = 0; } ((Vector)streamList.get(streamID)).add(new SystemIndexElement(systemStreamBytePosition,elementaryStreamBytePosition,packetDataLength)); } /** * Gets the byte position in the System Stream of the given byte * position in the elementary stream with the given * <code>stream_id</code>. * @param stream_id the stream * @param bytePosition the position into the stream * @return the position in the file * @throws StreamNotFoundException */ public long getPosition(int stream_id, long bytePosition) throws StreamNotFoundException { Integer streamID = new Integer(stream_id); Vector streamIndex = (Vector)streamList.get(streamID); if (streamIndex == null) { throw new StreamNotFoundException("stream_id: " + stream_id); } // binary search int low = 0; int high = streamIndex.size() - 1; while (low <= high) { int mid = (low + high) / 2; SystemIndexElement sie = (SystemIndexElement)streamIndex.elementAt(mid); int c = sie.findByte(bytePosition); if (c < 0) high = mid - 1; else if (c > 0) low = mid + 1; else { long systemPosition = sie.getSystemStreamDataStartPosition(); long elementaryPosition = sie.getElementaryStreamDataStartPosition(); return (bytePosition - elementaryPosition) + systemPosition; } } return -1; } public long getLastByteInPacket(int stream_id, long bytePosition) throws StreamNotFoundException { Integer streamID = new Integer(stream_id); Vector streamIndex = (Vector)streamList.get(streamID); if (streamIndex == null) { throw new StreamNotFoundException("stream_id: " + stream_id); } // binary search int low = 0; int high = streamIndex.size() - 1; while (low <= high) { int mid = (low + high) / 2; SystemIndexElement sie = (SystemIndexElement)streamIndex.elementAt(mid); int c = sie.findByte(bytePosition); if (c < 0) high = mid - 1; else if (c > 0) low = mid + 1; else { long systemPosition = sie.getSystemStreamDataStartPosition(); int numBytes = sie.getPacketDataLength(); return numBytes + systemPosition - 1; } } return -1; } }